UNPKG

@ima/cli

Version:

IMA.js CLI tool to build, develop and work with IMA.js applications.

238 lines (237 loc) 9.3 kB
"use strict"; 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;