UNPKG

@ainc/script

Version:

Script compiler for typescript

292 lines (239 loc) 8.26 kB
/** ***************************************** * Created by edonet@163.com * Created on 2021-08-14 11:03:15 ***************************************** */ 'use strict'; /** ***************************************** * 加载依赖 ***************************************** */ import * as ts from 'typescript'; /** ***************************************** * 声明节点源属性 ***************************************** */ declare module 'typescript' { interface Node { original?: ts.Node; } } /** ***************************************** * 获取工厂 ***************************************** */ const factory = ts.factory; /** ***************************************** * 判断是否为入口文件 ***************************************** */ function isMainFile(path: string): boolean { return path.endsWith('index.ts') || path.endsWith('index.js'); } /** ***************************************** * 判断标识值 ***************************************** */ function isIdentifierValueOf(node: ts.Node, value: string): node is ts.Identifier { return ts.isIdentifier(node) && node.text === value; } /** ***************************************** * 判断是否为载入函数 ***************************************** */ function isRequireCallExpression(node?: ts.Node): node is ts.CallExpression { return node ? ts.isCallExpression(node) && isIdentifierValueOf(node.expression, 'require') : false; } /** ***************************************** * 判断是否为导出标识 ***************************************** */ function isExportIdentifier(node: ts.Node): node is ts.Identifier { return ts.isIdentifier(node) && ts.isExportDeclaration(getOriginalNode(node)); } /** ***************************************** * 获取源节点 ***************************************** */ function getOriginalNode(node: ts.Node): ts.Node { return node.original ? getOriginalNode(node.original) : node; } /** ***************************************** * 创建函数调用声明 ***************************************** */ function createCallExpression(name: ts.Identifier, args?: readonly ts.Expression[]): ts.CallExpression { return factory.createCallExpression(name, [], args || []); } /** ***************************************** * 创建匿名函数表达式 ***************************************** */ function createAnonymousFunctionExpression(statements: readonly ts.Statement[]): ts.FunctionExpression { return factory.createFunctionExpression( undefined, undefined, undefined, [], [], undefined, factory.createBlock(statements, false), ); } /** ***************************************** * 创建懒加载函数表达式 ***************************************** */ function createLazyRequireFunctionExpression(name: ts.Identifier, call: ts.CallExpression): ts.FunctionExpression { const moduleId = factory.createIdentifier('__exports'); const moduleDecl = factory.createVariableDeclaration(moduleId, undefined, undefined, call); // 创建函数 return createAnonymousFunctionExpression([ factory.createVariableStatement( undefined, factory.createVariableDeclarationList([moduleDecl], ts.NodeFlags.None) ), factory.createExpressionStatement( factory.createBinaryExpression( name, factory.createToken(ts.SyntaxKind.EqualsToken), createAnonymousFunctionExpression([factory.createReturnStatement(moduleId)]), ), ), factory.createReturnStatement(moduleId), ]); } /** ***************************************** * 创建属性定义声明 ***************************************** */ function createObjectDefinePropertyExpression(target: ts.Identifier, prop: string, initializer: ts.Expression): ts.Expression { return factory.createCallExpression( factory.createPropertyAccessExpression( factory.createIdentifier('Object'), factory.createIdentifier('defineProperty'), ), undefined, [ target, factory.createStringLiteral(prop), factory.createObjectLiteralExpression( [ factory.createPropertyAssignment(factory.createIdentifier('enumerable'), factory.createTrue()), factory.createPropertyAssignment(factory.createIdentifier('get'), initializer), ], false, ), ], ); } /** ***************************************** * 转换文件内容 ***************************************** */ function transformSourceFile(sourceFile: ts.SourceFile, context: ts.TransformationContext): ts.SourceFile { const moduleSymbols = new Set(); // 访问文件子节点 function visitNode(node: ts.Node): ts.Node { // 处理载入标识 if (ts.isIdentifier(node) && moduleSymbols.has(node)) { return createCallExpression(node); } // 返回节点 return ts.visitEachChild(node, visitNode, context); } // 访问变量声明 function visitVariableDeclaration(node: ts.Node): ts.Node { // 判断非声明语句 if (!ts.isVariableDeclaration(node)) { return ts.visitEachChild(node, visitVariableDeclaration, context); } // 获取初始初化声明 const call = node.initializer; const name = node.name; // 判断非载入函数初始化 if (!call || !isRequireCallExpression(call) || !isExportIdentifier(name)) { return ts.visitEachChild(node, visitNode, context); } // 添加标识 moduleSymbols.add(name); // 替换声明节点 return factory.updateVariableDeclaration( node, name, node.exclamationToken, node.type, createLazyRequireFunctionExpression(name, call), ); } // 访问赋值表达式 function visitBinaryExpression(node: ts.Node): ts.Node { // 判断非载入函数初始化 if (!ts.isBinaryExpression(node) || !isRequireCallExpression(node.right)) { return ts.visitEachChild(node, visitNode, context); } // 获取表达式值 const expr = node.left; const call = node.right; // 判断非导出属性 if (!ts.isPropertyAccessExpression(expr) || !isIdentifierValueOf(expr.expression, 'exports')) { return ts.visitEachChild(node, visitNode, context); } // 创建赋值语句 return createObjectDefinePropertyExpression( expr.expression, expr.name.text, createAnonymousFunctionExpression([factory.createReturnStatement(call)]), ); } // 访问文件子节点 function visitSourceFile(node: ts.Node): ts.Node { // 处理载入函数 if (ts.isVariableStatement(node)) { return ts.visitEachChild(node, visitVariableDeclaration, context); } // 处理二元表达式 if (ts.isExpressionStatement(node)) { return ts.visitEachChild(node, visitBinaryExpression, context); } // 处理载入标识 if (moduleSymbols.size) { return ts.visitEachChild(node, visitNode, context); } // 返回节点 return node; } // 访问节点 return ts.visitEachChild(sourceFile, visitSourceFile, context); } /** ***************************************** * 转换器 ***************************************** */ export function transformer(context: ts.TransformationContext): ts.Transformer<ts.SourceFile> { const { target, module: moduleKind } = context.getCompilerOptions(); const isCommonJS = !moduleKind || moduleKind === ts.ModuleKind.CommonJS; // 不做处理 if (!target || !isCommonJS) { return sourceFile => sourceFile; } // 转换文件节点 return (sourceFile: ts.SourceFile) => { return isMainFile(sourceFile.fileName) ? transformSourceFile(sourceFile, context) : sourceFile; }; }