When using babel-plugin-relay, graphql template tag calls are converted into a function that uses require to load another file.
It would be nice to be able to opt-in to using an ECMAScript module-friendly import statement rather than relying on the commonjs require.
This would ideally make it easier to integrate into tools like rollup.
Alright, I got this working locally.
I ripped apart babel-plugin-relay and took only the parts I needed (modern-only, no hash checking, simple regex to find the name instead of full graphql parse).
The meat of the change is in createModernNode which uses t.importDeclaration and the like.
'use strict';
function getValidGraphQLTag(path) {
const tag = path.get('tag');
if (!tag.isIdentifier({ name: 'graphql' })) {
return null;
}
const quasis = path.node.quasi.quasis;
if (quasis.length !== 1) {
throw new Error(
'BabelPluginRelay: Substitutions are not allowed in graphql fragments. ' +
'Included fragments should be referenced as `...MyModule_propName`.',
);
}
const text = quasis[0].value.raw;
const match = /^\s*(query|mutation|subscription|fragment)\s*(\w+)/.exec(text);
if (!match) {
throw new Error('Unable to parse ' + text);
}
return { type: match[1], name: match[2] };
}
/**
* Given a graphql`` tagged template literal, replace it with the appropriate
* runtime artifact.
*/
function compileGraphQLTag(t, path, state, type, name) {
return replaceMemoized(t, path, createAST(t, state, path, type, name));
}
function createAST(t, state, path, type, name) {
const isHasteMode = Boolean(state.opts && state.opts.haste);
const isDevVariable = state.opts && state.opts.isDevVariable;
const artifactDirectory = state.opts && state.opts.artifactDirectory;
const buildCommand =
(state.opts && state.opts.buildCommand) || 'relay-compiler';
// Fallback is 'true'
const isDevelopment =
(process.env.BABEL_ENV || process.env.NODE_ENV) !== 'production';
const modernNode = createModernNode(t, path, type, name, state, {
artifactDirectory,
buildCommand,
isDevelopment,
isHasteMode,
isDevVariable,
});
return modernNode;
}
function getTopScope(path) {
let topScope = path.scope;
while (topScope.parent) {
topScope = topScope.parent;
}
return topScope;
}
function replaceMemoized(t, path, ast) {
const topScope = getTopScope(path);
if (path.scope === topScope) {
path.replaceWith(ast);
} else {
const id = topScope.generateDeclaredUidIdentifier('graphql');
path.replaceWith(
t.logicalExpression('||', id, t.assignmentExpression('=', id, ast)),
);
}
}
const GENERATED = './__generated__/';
function createModernNode(t, path, type, definitionName, state, options) {
const requiredFile = definitionName + '.graphql';
const requiredPath = options.isHasteMode
? requiredFile
: options.artifactDirectory
? getRelativeImportPath(state, options.artifactDirectory, requiredFile)
: GENERATED + requiredFile;
const topScope = getTopScope(path);
const nodeVariable = topScope.generateUidIdentifier(definitionName);
const importDefaultSpecifier = t.importDefaultSpecifier(nodeVariable);
const importDeclaration = t.importDeclaration(
[importDefaultSpecifier],
t.stringLiteral(requiredPath),
);
topScope.path.unshiftContainer('body', importDeclaration);
const bodyStatements = [t.returnStatement(nodeVariable)];
return t.functionExpression(null, [], t.blockStatement(bodyStatements));
}
function BabelPluginRelay(context) {
const { types: t } = context;
if (!t) {
throw new Error(
'BabelPluginRelay: Expected plugin context to include "types", but got:' +
String(context),
);
}
const visitor = {
TaggedTemplateExpression(path, state) {
// Convert graphql`` literals
const tag = getValidGraphQLTag(path);
if (tag) {
compileGraphQLTag(t, path, state, tag.type, tag.name);
}
},
};
return {
visitor: {
Program(path, state) {
path.traverse(visitor, state);
},
},
};
}
module.exports = BabelPluginRelay;
Thanks @ckknight , I was trying this out but getRelativeImportPath is not defined.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Has anyone found a sustainable solution to this? Started a new project with rollup plus snowpack and trying out relay and ran into this exact same issue :( Seems there's only appetite to support bundlers like Webpack for this library. I believe this issue https://github.com/facebook/relay/issues/2445 is also related
I added eagerESModules option to relay config some time ago. Please try it.