@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
65 lines (62 loc) • 2.28 kB
JavaScript
import { Project, SyntaxKind, Node } from 'ts-morph';
const VALID_JS_IDENTIFIER = /^[A-Za-z_$][\w$]*$/u;
const startMarker = (key) => `// lunora:add:${key}:start`;
const endMarker = (key) => `// lunora:add:${key}:end`;
const extensionImportSpecifier = (key) => `./${key}/schema`;
const findDefineSchemaCall = (callExpressions) => {
for (const call of callExpressions) {
if (call.getExpression().getText() === "defineSchema") {
return call;
}
}
return void 0;
};
const outermostChainExpression = (defineSchemaCall) => {
let node = defineSchemaCall;
for (let parent = node.getParent(); parent !== void 0; parent = node.getParent()) {
if (Node.isPropertyAccessExpression(parent) && parent.getExpression() === node) {
node = parent;
} else if (Node.isCallExpression(parent) && parent.getExpression() === node) {
node = parent;
} else {
break;
}
}
return node;
};
const insertSchemaExtension = (source, key) => {
if (!VALID_JS_IDENTIFIER.test(key)) {
return { ok: false, reason: "invalid-identifier" };
}
if (source.includes(startMarker(key))) {
return { ok: false, reason: "already-applied" };
}
const project = new Project({
compilerOptions: { allowJs: true },
useInMemoryFileSystem: true
});
const sourceFile = project.createSourceFile("schema.ts", source, { overwrite: true });
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
const defineSchemaCall = findDefineSchemaCall(callExpressions);
if (!defineSchemaCall) {
return { ok: false, reason: "no-define-schema" };
}
const tablesArgument = defineSchemaCall.getArguments()[0];
if (tablesArgument?.getKind() !== SyntaxKind.ObjectLiteralExpression) {
return { ok: false, reason: "non-object-argument" };
}
const insertAt = outermostChainExpression(defineSchemaCall).getEnd();
const chainText = `
${startMarker(key)}
.extend(${key}.extension)
${endMarker(key)}
`;
sourceFile.insertText(insertAt, chainText);
const importText = `${startMarker(key)}
import { ${key} } from "${extensionImportSpecifier(key)}";
${endMarker(key)}
`;
sourceFile.insertText(0, importText);
return { ok: true, text: sourceFile.getFullText() };
};
export { insertSchemaExtension };