UNPKG

@ainc/script

Version:

Script compiler for typescript

166 lines (135 loc) 4.4 kB
/** ***************************************** * Created by edonet@163.com * Created on 2021-07-09 23:16:56 ***************************************** */ 'use strict'; /** ***************************************** * 加载依赖 ***************************************** */ import { types, NodePath, PluginPass } from '@babel/core'; import { declare } from '@babel/helper-plugin-utils'; import { dirname } from '@ainc/fs/dist/dirname'; import { includePaths } from '@ainc/fs/dist/includePaths'; import { resolvePath, Options as ResolvePathOptions } from '@ainc/fs/dist/resolvePath'; /** ***************************************** * 插件状态 ***************************************** */ interface State extends PluginPass { dirname?: string; calls?: string[]; resolveFile?(source: string, context?: string): void | string; } /** ***************************************** * 转换的资源路径 ***************************************** */ function transformSourceNode(expr: NodePath<types.StringLiteral>, state: State): void { if (!expr || !state.resolveFile || !types.isStringLiteral(expr)) { return; } // 获取路径 const sourcePath = expr.node.value; const resolvedPath = state.resolveFile(sourcePath, state.dirname); // 无需处理 if (!resolvedPath || sourcePath === resolvedPath) { return; } // 替换路径 expr.replaceWith(types.stringLiteral(resolvedPath)); } /** ***************************************** * 匹配调用函数 ***************************************** */ function matchSourceCall(name: string, expr: NodePath<types.Identifier>): boolean { const { node } = expr; // 匹配成员节点 if (types.isMemberExpression(node)) { return expr.matchesPattern(name); } // 非标识府或匹配成员 if (!types.isIdentifier(node) || name.indexOf('.') > -1) { return false; } // 匹配函数名 return node.name === name; } /** ***************************************** * 访问调用表达式 ***************************************** */ function visitCallExpression(expr: NodePath<types.CallExpression>, state: State): void { // 处理`import`调用 if (types.isImport(expr.node.callee)) { return transformSourceNode(expr.get('arguments.0') as NodePath<types.StringLiteral>, state); } // 不存在转换函数 if (!state.calls) { return; } // 获取调用节点 const callee = expr.get('callee') as NodePath<types.Identifier>; const isSourceCall = state.calls.some(value => matchSourceCall(value, callee)); // 处理函数调用 if (isSourceCall) { transformSourceNode(expr.get('arguments.0') as NodePath<types.StringLiteral>, state); } } /** ***************************************** * 访问载入声明 ***************************************** */ function visitImportDeclaration(expr: NodePath<types.ImportDeclaration | types.ExportDeclaration>, state: State): void { transformSourceNode(expr.get('source') as NodePath<types.StringLiteral>, state); } /** ***************************************** * 插件配置 ***************************************** */ export interface Options extends ResolvePathOptions { calls?: string[]; exclude?: string[]; } /** ***************************************** * 定义插件 ***************************************** */ export default declare((api, opts: Options = {}) => { const include = includePaths(opts.exclude || ['node_modules']); const calls = ['require', 'require.resolve']; const resolveFile = resolvePath(opts); // 校验版本 api.assertVersion(7); // 添加处理函数 opts.calls?.length && calls.push(...opts.calls); // 返回插件配置 return { name: 'module-paths-plugin', pre(file) { const { filename } = file.opts; // 过滤文件 if (!filename || !include(filename)) { this.calls = calls; this.resolveFile = resolveFile; this.dirname = filename ? dirname(filename) : process.cwd(); } }, visitor: { ImportDeclaration: visitImportDeclaration, ExportDeclaration: visitImportDeclaration, CallExpression: visitCallExpression, }, }; });