/** ***************************************** * 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 { /** 模块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; /** 扩展对象 */ _extensions: Record; /** 初始化对象 */ new (filename: string, script: Script): Script; }; /** ***************************************** * 模块加载函数 ***************************************** */ 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(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(this: Script, id: string): T { return invoke(() => moduleImportHandler.call(this, id)); } /** ***************************************** * 加载脚本 ***************************************** */ export function load(filename: string, code: string): Script { const script = new Script(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