@ainc/script
Version:
Script compiler for typescript
284 lines (219 loc) • 6.59 kB
text/typescript
/**
*****************************************
* 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);
}