@tobiastengler/create-relay-app
Version:
Easy configuration of Relay for existing projects
88 lines (87 loc) • 4.59 kB
JavaScript
import traverse from "@babel/traverse";
import t from "@babel/types";
import { parseAst, printAst, mergeProperties } from "../../utils/ast.js";
import { bold } from "../../utils/cli.js";
import { TaskBase } from "../TaskBase.js";
export class Next_ConfigureNextCompilerTask extends TaskBase {
constructor(context) {
super();
this.context = context;
this.message = `Configure Next.js compiler`;
}
isEnabled() {
return this.context.is("next");
}
async run() {
const configFilename = "next.config.js";
const configFile = this.context.env.rel(configFilename);
this.updateMessage(this.message + " in " + bold(configFile.rel));
const configCode = await this.context.fs.readFromFile(configFile.abs);
const ast = parseAst(configCode);
let configured = false;
traverse.default(ast, {
AssignmentExpression: (path) => {
if (configured) {
return;
}
const node = path.node;
// We are looking for module.exports = ???.
if (node.operator !== "=" ||
!t.isMemberExpression(node.left) ||
!t.isIdentifier(node.left.object) ||
!t.isIdentifier(node.left.property) ||
node.left.object.name !== "module" ||
node.left.property.name !== "exports") {
return;
}
let objExp;
// We are looking for the object expression
// that was assigned to module.exports.
if (t.isIdentifier(node.right)) {
// The export is linked to a variable,
// so we need to resolve the variable declaration.
const binding = path.scope.getBinding(node.right.name);
if (!binding || !t.isVariableDeclarator(binding.path.node) || !t.isObjectExpression(binding.path.node.init)) {
throw new Error("`module.exports` references a variable, but the variable is not an object.");
}
objExp = binding.path.node.init;
}
else if (t.isObjectExpression(node.right)) {
objExp = node.right;
}
else {
throw new Error("Expected to find an object initializer or variable assigned to `module.exports`.");
}
// We are creating or getting the 'compiler' property.
let compiler_Prop = objExp.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === "compiler");
if (!compiler_Prop) {
compiler_Prop = t.objectProperty(t.identifier("compiler"), t.objectExpression([]));
objExp.properties.push(compiler_Prop);
}
if (!t.isObjectExpression(compiler_Prop.value)) {
throw new Error("Expected the `compiler` property to be an object.");
}
let relay_ObjProps = [
t.objectProperty(t.identifier("src"), t.stringLiteral(this.context.srcPath.rel)),
t.objectProperty(t.identifier("language"), t.stringLiteral(this.context.compilerLanguage)),
];
if (this.context.artifactPath) {
relay_ObjProps.push(t.objectProperty(t.identifier("artifactDirectory"), t.stringLiteral(this.context.artifactPath.rel)));
}
const compiler_relayProp = compiler_Prop.value.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === "relay");
if (compiler_relayProp && t.isObjectExpression(compiler_relayProp.value)) {
// We already have a "relay" property, so we merge its properties,
// with the new ones.
compiler_relayProp.value = t.objectExpression(mergeProperties(compiler_relayProp.value.properties, relay_ObjProps));
}
else {
// We do not yet have a "relay" propery, so we add it.
compiler_Prop.value.properties.push(t.objectProperty(t.identifier("relay"), t.objectExpression(relay_ObjProps)));
}
path.skip();
},
});
const updatedConfigCode = printAst(ast, configCode);
await this.context.fs.writeToFile(configFile.abs, updatedConfigCode);
}
}