@ainc/script
Version:
Script compiler for typescript
182 lines (143 loc) • 4.41 kB
text/typescript
/**
*****************************************
* Created by edonet@163.com
* Created on 2020-05-21 14:03:13
*****************************************
*/
'use strict';
/**
*****************************************
* 加载依赖
*****************************************
*/
import * as path from 'path';
import * as vm from './vm';
import { watch, Watcher } from './watcher';
/**
*****************************************
* 执行文件接口
*****************************************
*/
export function invoke(file: string, args: unknown[] = [], context: unknown = null): unknown {
const script = execute(file);
// 处理函数
if (typeof script === 'function') {
return script.apply(context, args);
}
// 处理默认函数
if (script && typeof script === 'object') {
const { default: handler } = script as { default?(...args: any[]): unknown };
// 执行默认函数
if (typeof handler === 'function') {
return handler.apply(context, args);
}
}
}
/**
*****************************************
* 执行代码
*****************************************
*/
export function run<T>(code: string, filename = '_script.js'): T {
if (code && typeof code === 'string') {
return vm.load<T>(path.resolve(filename), code).exports;
}
// 返回空对象
return {} as T;
}
/**
*****************************************
* 执行文件
*****************************************
*/
export function execute<T>(filename: string, context = process.cwd()): T {
return run<T>(
`module.exports = require(${JSON.stringify(path.resolve(context, filename))});`
);
}
/**
*****************************************
* 脚本
*****************************************
*/
export class Script<T> {
/** 脚本对象 */
private $script?: vm.Script;
/** 文件列表 */
public get files(): string[] {
return this.$script ? vm.findDependencies(this.$script) : [];
}
/** 抛出接口 */
public get exports(): T {
return (this.$script ? this.$script.exports : {}) as T;
}
/** 脚本目录 */
public get dirname(): string {
return path.dirname(this.filename);
}
/** 脚本文件 */
public get filename(): string {
return this.$script ? this.$script.filename : path.resolve('_script.js');
}
/** 执行文件 */
public execute(filename: string, context = process.cwd()): T {
return this.run(
`module.exports = require(${JSON.stringify(path.resolve(context, filename))});`
);
}
/** 执行代码 */
public run(code: string, filename = '_script.js'): T {
// 校验参数
if (typeof code !== 'string') {
throw new Error('expect `code` to be string!');
}
// 生成脚本
const script = vm.load<T>(path.resolve(filename), code);
// 缓存脚本对象
Object.defineProperty(this, '$script', {
value: script,
enumerable: false,
configurable: true,
});
// 返回结果
return script.exports;
}
/** 重载模块 */
public reload(files?: string[]): T {
// 不存在脚本文件
if (!this.$script) {
return {} as T;
}
// 获取代码
const code = this.$script._code || '';
const filename = this.$script.filename;
// 刷新依赖文件
vm.refresh(this.$script, files || []);
// 执行模块
return this.run(code, filename);
}
/** 监听模块变更 */
public watch(callback: (data: T) => void): Watcher {
const watcher = watch(this.files);
// 监听文件变更
watcher.on('change', (file: string) => {
const deps = new Set(this.files);
// 执行回调
callback(this.reload([file]));
// 添加依赖
this.files.forEach(file => {
deps.has(file) ? deps.delete(file) : watcher.add(file);
});
// 清除依赖
deps.forEach(file => watcher.unwatch(vm.clear(file)));
});
// 返回监听器
return watcher;
}
}
/**
*****************************************
* 抛出接口
*****************************************
*/
export default Script;