takin
Version:
Front end engineering base toolchain and scaffold
191 lines • 7.56 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Takin = void 0;
const lodash_1 = require("lodash");
const tapable_1 = require("tapable");
const config_1 = require("./config");
const constants_1 = require("./constants");
const logger_1 = require("./logger");
const ChangeCwdPlugin_1 = __importDefault(require("./plugins/ChangeCwdPlugin"));
const CustomConfigPlugin_1 = __importDefault(require("./plugins/CustomConfigPlugin"));
const LoadEnvPlugin_1 = __importDefault(require("./plugins/LoadEnvPlugin"));
const MultiConfigPlugin_1 = __importDefault(require("./plugins/MultiConfigPlugin"));
const PluginConfigPlugin_1 = __importDefault(require("./plugins/PluginConfigPlugin"));
const StopRunPlugin_1 = __importDefault(require("./plugins/StopRunPlugin"));
const runner_1 = require("./runner");
/**
* 命令行扩展内核,基于 Runner 和 Plugin
* 提供各类 Utils 方法
*/
class Takin extends config_1.Config {
constructor(name = constants_1.DEFAULT_NAME) {
super(name);
// 重新初始化默认 logger
// 以传入的 name 为准
logger_1.logger.init('info', { debugPrefix: name, prefix: `[${name}]` });
this.currentRunners = new Set();
// 旧版兼容
this.config = this;
this.isConfigLoaded = false;
this.isReloading = false;
this.hooks = {
initialize: new tapable_1.AsyncSeriesHook(['takin']),
prepare: new tapable_1.AsyncSeriesHook(['runnerOptions']),
extendRunner: new tapable_1.SyncWaterfallHook(['Runner', 'runnerOptions']),
configLoaded: new tapable_1.AsyncSeriesHook(['takin', 'commandOptions']),
configFiltered: new tapable_1.AsyncSeriesWaterfallHook([
'UserConfigs',
'commandOptions'
])
};
// 内部插件
this.use([
new ChangeCwdPlugin_1.default(),
new CustomConfigPlugin_1.default(),
new MultiConfigPlugin_1.default(),
new PluginConfigPlugin_1.default()
]);
}
/**
* 应用 Runner 插件
*/
use(plugins) {
this.usePlugins(plugins, config_1.PluginTypes.use);
}
/**
* 执行命令, 分为如下几个步骤
* 1. 执行初始化 hook
* 2. 尝试载入配置文件, 只执行一次
* 3. 基于多配置的设定,获取 filters
* 4. 基于 filters 筛选 用户配置
* 5. 基于不同的用户配置分别运行 Runner
* @param command - 命令参数, 可选, 如不填写则自动从命令行读取
* @param userConfigs - 用户配置, 可选, 如不填写则使用 config 中载入的用户配置
* @param context - Runner 上下文, 可用于初始化时候传入 Runner
* @returns 返回命令执行的结果
*/
async run(command, userConfigs, context) {
// 保存当前参数
this.lastRunState = (0, lodash_1.cloneDeep)({
command,
userConfigs,
context
});
// 执行初始化 hook
await this.hooks.initialize.promise(this);
const results = [];
const baseOptions = {
config: this,
command,
userConfig: {},
context
};
const configs = await this.prepare(command, userConfigs);
// 基于多配置逐一运行 runner
for await (const userConfig of configs.length ? configs : [{}]) {
if (this.isReloading) {
logger_1.logger.debug(`正在 reloading, 跳过 Runner 执行`);
continue;
}
const runner = await this.runExtendedRunner({
...baseOptions,
userConfig
});
results.push(runner.getResult());
}
return results;
}
/**
* 基于最近一次命令传入参数重新运行命令
* @returns 返回命令执行的结果
*/
async reload() {
// 避免重复 reloading
if (this.isReloading)
return [];
// 清理配置项
this.pkg = undefined;
this.pkgPath = '';
this.userConfig = undefined;
this.userConfigFilePath = undefined;
// 标记配置未载入
this.isConfigLoaded = false;
// 标记为正在 reloading
this.isReloading = true;
// 逐个关闭 runner
for await (const runner of this.currentRunners) {
logger_1.logger.debug(`关闭 Runner#${runner.runnerId} 中...`);
await runner.hooks.shutdown.promise(runner);
logger_1.logger.debug(`关闭 Runner#${runner.runnerId} 完成`);
}
this.currentRunners.clear();
// 获取上次运行时传入的参数
const { command, userConfigs, context } = this.lastRunState || {};
this.lastRunState = undefined;
// 标记 reloading 结束
this.isReloading = false;
// 重新执行命令
return await this.run(command, userConfigs, context);
}
/**
* 执行扩展 Runner 方法
*/
async runExtendedRunner(options) {
const RunnerExtended = this.hooks.extendRunner.call(runner_1.Runner, options);
const runner = new RunnerExtended(options.config, options.userConfig, options.context);
this.currentRunners.add(runner);
await runner.run(options.command, options.plugins);
return runner;
}
/**
* 利用 Runner 的能力完成一些前置准备工作
* - 修改 cwd
* - 载入用户配置文件
* - 过滤用户配置
* @param command - 通过接口传入的命令选项
* @param userConfigs - 通过接口传入的用户配置
* @returns 过滤后的用户配置数组
*/
async prepare(command, userConfigs) {
let filteredConfigs = [];
const plugins = [
new ChangeCwdPlugin_1.default(),
new LoadEnvPlugin_1.default(),
new MultiConfigPlugin_1.default(false, async (runner, filters) => {
// 过滤用户配置
filteredConfigs = this.filterBy(filters, userConfigs);
// 过滤用户配置
if (this.hooks.configFiltered.isUsed()) {
filteredConfigs = await this.hooks.configFiltered.promise(filteredConfigs, runner.getCommandOptions());
}
}),
new PluginConfigPlugin_1.default({
registerPluginSchema: false,
loadCliOrEnvPlugins: true
}),
new StopRunPlugin_1.default()
];
// 如果未传入用户配置,则尝试自动载入配置
if (!(userConfigs === null || userConfigs === void 0 ? void 0 : userConfigs.length) && !this.isConfigLoaded) {
plugins.push(new CustomConfigPlugin_1.default(true, async (runner) => {
await this.hooks.configLoaded.promise(this, runner.getCommandOptions());
this.isConfigLoaded = true;
}));
}
const options = {
command,
config: this,
userConfig: {},
plugins
};
// 触发前置准备 Hook
await this.hooks.prepare.promise(options);
await runner_1.Runner.run(options);
return filteredConfigs;
}
}
exports.Takin = Takin;
//# sourceMappingURL=takin.js.map