UNPKG

@ainc/script

Version:

Script compiler for typescript

284 lines (219 loc) 6.59 kB
/** ***************************************** * Created by edonet@163.com * Created on 2020-07-04 21:01:05 ***************************************** */ 'use strict'; /** ***************************************** * 加载依赖 ***************************************** */ import * as fs from 'fs'; import * as transform from './transform'; import { json } from './helpers/json'; /** ***************************************** * 处理函数 ***************************************** */ interface Handler { (script: Script, filename: string): void; } /** ***************************************** * 脚本对象 ***************************************** */ export interface Script<T = unknown> { /** 模块ID */ id: string; /** 文件名 */ filename: string; /** 子模块 */ children: Script[]; /** 抛出接口 */ exports: T; /** 源码 */ _code?: string; /** 加载文件 */ load(filename: string): void; /** 导入模块 */ require(id: string): unknown; /** 编译模块 */ _compile(code: string, filename: string): void; } /** ***************************************** * 脚本对象 ***************************************** */ const Script = require('module') as { /** 缓存对象 */ _cache: Record<string, Script>; /** 扩展对象 */ _extensions: Record<string, void | Handler>; /** 初始化对象 */ new <T>(filename: string, script: Script): Script<T>; }; /** ***************************************** * 模块加载函数 ***************************************** */ const moduleExtensions = Script._extensions; const moduleImportHandler = Script.prototype.require; /** ***************************************** * 脚本转换函数 ***************************************** */ type TransformHandler = (filename: string, code: string) => string; /** ***************************************** * 生成脚本解析函数 ***************************************** */ function makeCompileHandler(transformScript: TransformHandler): Handler { return function compile(script, filename) { const code = script._code ?? fs.readFileSync(filename, 'utf8'); // 转换代码 const source = transformScript(filename, code); // 更新加载函数 script.require = dynamicImportScript; // 编译内容 script._compile(transformScript(filename, source), filename); }; } /** ***************************************** * 模块解析器 ***************************************** */ const moduleCompiler = { js: makeCompileHandler(transform.transformJavaScript), ts: makeCompileHandler(transform.transformTypeScript), jsx: makeCompileHandler(transform.transformJSXScript), tsx: makeCompileHandler(transform.transformTSXScript), json: json, }; /** ***************************************** * 执行函数 ***************************************** */ function invoke<T>(handler: () => T): T { const cached = { ...Script._extensions }; // 更新处理函数 moduleExtensions['.ts'] = moduleCompiler.ts; moduleExtensions['.js'] = moduleCompiler.js; moduleExtensions['.mjs'] = moduleCompiler.js; moduleExtensions['.jsx'] = moduleCompiler.jsx; moduleExtensions['.tsx'] = moduleCompiler.tsx; moduleExtensions['.json'] = moduleCompiler.json; // 执行函数 const result = handler(); // 恢复处理函数 moduleExtensions['.ts'] = cached['.ts']; moduleExtensions['.js'] = cached['.js']; moduleExtensions['.mjs'] = cached['.mjs']; moduleExtensions['.jsx'] = cached['.jsx']; moduleExtensions['.tsx'] = cached['.tsx']; moduleExtensions['.json'] = cached['.json']; // 返回结果 return result; } /** ***************************************** * 动态载入脚本 ***************************************** */ function dynamicImportScript<T>(this: Script, id: string): T { return invoke<T>(() => moduleImportHandler.call(this, id)); } /** ***************************************** * 加载脚本 ***************************************** */ export function load<T>(filename: string, code: string): Script<T> { const script = new Script<T>(filename, module as unknown as Script); // 设置代码 script._code = code; // 加载文件 invoke(() => script.load(filename)); // 返回结果 return script; } /** ***************************************** * 清除缓存 ***************************************** */ export function clear(file: string): string { // 清除缓存 if (Script._cache[file]) { Script._cache[file] = undefined as unknown as Script; } // 返回文件地址 return file; } /** ***************************************** * 刷新脚本依赖 ***************************************** */ export function refresh(script: Script, files: string[]): void { const refreshAll = !files.length; // 清除子脚本 function refreshChildren(children: Script[]): void | Script[] { if (children && children.length) { return children.filter(refreshScript); } } // 清除函数 function refreshScript(script: Script): void | boolean { const file = script.filename; const children = refreshChildren(script.children); // 清除当前模块 if (refreshAll || files.includes(file) || (children && children.length)) { return !!clear(file); } } // 清除缓存 script.children && script.children.forEach(refreshScript); } /** ***************************************** * 解析依赖 ***************************************** */ export function findDependencies(script: Script): string[] { const cached = new Set<Script>(); const deps: string[] = []; // 遍历函数 function each(list: Script[]): string[] { // 遍历子模块 if (list && list.length) { list.forEach(script => cached.has(script) || add(script)); } // 返回依赖列表 return deps; } // 添加依赖 function add(script: Script): void { const filename = script.filename; // 添加缓存 cached.add(script); // 过滤三方模块 if (filename.indexOf('node_modules') === -1) { // 添加文件 deps.push(filename); // 处理子模块 each(script.children); } } // 遍历子节点 return each(script.children); }