@ima/cli
Version:
IMA.js CLI tool to build, develop and work with IMA.js applications.
238 lines (237 loc) • 9.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stubProcessor = void 0;
const traverse_1 = __importDefault(require("@babel/traverse"));
const b = __importStar(require("@babel/types"));
/**
* Checks if the import path is for the super class.
*/
function isImportsForSuperClass(importPath, superName) {
return importPath.node.specifiers.some(spec => (b.isImportSpecifier(spec) && spec.local.name === superName) ||
(b.isImportDefaultSpecifier(spec) && spec.local.name === superName));
}
/**
* Process the body of a class. We're looking for $dependencies static field
* and adding it to the class if it's not present. To make sure the
* runtime still works as expected.
*/
function processClassBody(members) {
const newMembers = [];
let hasDependenciesGetter = false;
for (const m of members) {
// Handle class properties (static $dependencies = [];)
if (b.isClassProperty(m) &&
m.static &&
b.isIdentifier(m.key) &&
m.key.name === '$dependencies') {
hasDependenciesGetter = true;
continue;
}
// Handle class method getters (static get $dependencies() {})
if (b.isClassMethod(m) &&
m.static &&
m.kind === 'get' &&
b.isIdentifier(m.key) &&
m.key.name === '$dependencies') {
hasDependenciesGetter = true;
continue;
}
}
if (hasDependenciesGetter) {
const newMember = b.classProperty(b.identifier('$dependencies'), b.arrayExpression([]));
newMember.static = true;
newMembers.push(newMember);
}
return newMembers;
}
/**
* Create a class stub declaration.
*/
function createClassStubDeclaration(decl) {
const body = processClassBody(decl.body.body);
return b.classDeclaration(decl.id, decl.superClass, b.classBody(body));
}
/**
* Create a class stub expression.
*/
function createClassStubExpression(expr) {
const body = processClassBody(expr.body.body);
return b.classExpression(expr.id, expr.superClass, b.classBody(body));
}
/**
* Creates a throw statement for a function.
*/
function createThrowBody(name) {
return b.blockStatement([
b.throwStatement(b.newExpression(b.identifier('Error'), [
b.stringLiteral(`Cannot call server-only function${name ? ` "${name}"` : ''} on client`),
])),
]);
}
/**
* Create a function stub declaration.
*/
function createFunctionStubDeclaration(decl) {
const name = decl.id?.name ?? '';
const body = createThrowBody(name);
return b.functionDeclaration(decl.id, decl.params, body, decl.generator, decl.async);
}
/**
* Create a function stub expression.
*/
function createFunctionStubExpression(expr) {
const name = 'id' in expr && expr.id?.name ? expr.id.name : '';
const body = createThrowBody(name);
if (b.isArrowFunctionExpression(expr)) {
return b.arrowFunctionExpression(expr.params, body, expr.async);
}
else {
return b.functionExpression(expr.id, expr.params, body, expr.generator, expr.async);
}
}
/**
* General use server processor, that will stub out all server-only code.
* Handles functions and classes, while having special handling for
* $dependencies static fields.
*/
const stubProcessor = ast => {
const stubExports = [];
const imports = new Set();
/**
* We're looking for export declarations and stubbing out the code
* along the way, while maintaining the original imports/exports.
*/
(0, traverse_1.default)(ast, {
ExportNamedDeclaration(path) {
const { node } = path;
const declaration = node.declaration;
if (!declaration) {
return;
}
if (b.isClassDeclaration(declaration)) {
if (declaration.superClass && b.isIdentifier(declaration.superClass)) {
const superName = declaration.superClass.name;
const program = path.findParent(p => b.isProgram(p.node));
if (!program) {
return;
}
program.traverse({
ImportDeclaration(importPath) {
if (isImportsForSuperClass(importPath, superName)) {
imports.add(importPath.node);
}
},
});
}
const stub = createClassStubDeclaration(declaration);
stubExports.push(b.exportNamedDeclaration(stub, []));
return;
}
if (b.isFunctionDeclaration(declaration)) {
const stub = createFunctionStubDeclaration(declaration);
stubExports.push(b.exportNamedDeclaration(stub, []));
return;
}
if (b.isVariableDeclaration(declaration)) {
const newDeclarations = [];
for (const declarator of declaration.declarations) {
let init = declarator.init;
let stubbed = false;
if (b.isClassExpression(init)) {
init = createClassStubExpression(init);
stubbed = true;
}
else if (b.isFunctionExpression(init) ||
b.isArrowFunctionExpression(init)) {
init = createFunctionStubExpression(init);
stubbed = true;
}
if (stubbed) {
newDeclarations.push(b.variableDeclarator(declarator.id, init));
}
}
if (newDeclarations.length > 0) {
const newVar = b.variableDeclaration(declaration.kind, newDeclarations);
stubExports.push(b.exportNamedDeclaration(newVar, []));
}
}
},
ExportDefaultDeclaration(path) {
const { node } = path;
let stub;
if (b.isClassDeclaration(node.declaration) &&
node.declaration.superClass &&
b.isIdentifier(node.declaration.superClass)) {
const superName = node.declaration.superClass.name;
const program = path.findParent(p => b.isProgram(p.node));
if (!program) {
return;
}
program.traverse({
ImportDeclaration(importPath) {
if (isImportsForSuperClass(importPath, superName)) {
imports.add(importPath.node);
}
},
});
}
if (b.isClassDeclaration(node.declaration)) {
stub = createClassStubDeclaration(node.declaration);
}
else if (b.isFunctionDeclaration(node.declaration)) {
stub = createFunctionStubDeclaration(node.declaration);
}
else if (b.isClassExpression(node.declaration)) {
stub = createClassStubExpression(node.declaration);
}
else if (b.isFunctionExpression(node.declaration) ||
b.isArrowFunctionExpression(node.declaration)) {
stub = createFunctionStubExpression(node.declaration);
}
if (stub) {
stubExports.push(b.exportDefaultDeclaration(stub));
}
},
});
// Prepend collected imports to body
ast.program.body = [...Array.from(imports), ...stubExports];
ast.program.directives = [];
return ast;
};
exports.stubProcessor = stubProcessor;