umijs
- 如何调试umijs
- 1:下载umijs github.com/umijs/umi
- 2:在本地的vsCode调试 umijs
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Remote",
"port": 9230,
"request": "attach",
"type": "node"
}
]
}
- 3: 新建调试文件

umijs 前期需要准备的东西
- esbuild/joi/priates/ tapable/chokider/scripts
- esbuild 是打包工具
- joi 配置校验工具
- pirates 用于处理node加载(require)es6、ts文件
- tapabll发布订阅插件
- chokider 监听文件变化
- scripts 这是umi-scripts 指令是umi内部的指令,bin文件中执行的逻辑大概就是运行
umi-scripts [指令],就可以执行scripts下文件名的ts脚本了,例如umi-scripts turbo --cmd build就是执行scripts/turbo.ts
umijs执行循序
- cli/cli.ts
- 当我们的umi项目执行umi指令时,会执行文件下的run的函数,这里校验了node版本,设置了一些环境信息,然后如果是dev,则执行dev函数,否则直接运行new Service().run2(),由于dev中也会创建Service,我们跳过这块
- cli/dev.ts、cli/fork.ts
- cli/forkedDev.ts
- 创建服务,并运行,并在进程关闭前调用onExit方法出发插件的一些行为
- service/service.ts
- 在构造函数中初始化了默认preset:
'@umijs/preset-umi',默认的插件join(cwd, 'plugin.ts')(即项目根目录下允许创建自定义插件),默认配置文件位置DEFAULT_CONFIG_FILES,应用的启动目录env.APP_ROOT || process.cwd()
run2为run的前置函数,目前适配了版本和帮助指令的简写
umijs
1. cli文件
import {
catchUnhandledRejection,
checkLocal,
checkVersion as checkNodeVersion,
logger,
printHelp,
setNoDeprecation,
setNodeTitle,
yParser,
} from '@umijs/utils';
import { DEV_COMMAND, FRAMEWORK_NAME, MIN_NODE_VERSION } from '../constants';
import { Service } from '../service/service';
import { dev } from './dev';
interface IOpts {
presets?: string[];
}
catchUnhandledRejection();
export async function run(opts?: IOpts) {
checkNodeVersion(MIN_NODE_VERSION);
checkLocal();
setNodeTitle(FRAMEWORK_NAME);
setNoDeprecation();
const args = yParser(process.argv.slice(2), {
alias: {
version: ['v'],
help: ['h'],
},
boolean: ['version'],
});
const command = args._[0];
const FEATURE_COMMANDS = ['mfsu', 'setup', 'deadcode'];
if ([DEV_COMMAND, ...FEATURE_COMMANDS].includes(command)) {
process.env.NODE_ENV = 'development';
} else if (command === 'build') {
process.env.NODE_ENV = 'production';
}
if (opts?.presets) {
process.env[`${FRAMEWORK_NAME}_PRESETS`.toUpperCase()] =
opts.presets.join(',');
}
if (command === DEV_COMMAND) {
dev();
} else {
try {
await new Service().run2({
name: args._[0],
args,
});
} catch (e: any) {
logger.fatal(e);
printHelp.exit();
process.exit(1);
}
}
}
2. 运行命令是npm run dev
import fork from './fork';
export function dev() {
const child = fork({
scriptPath: require.resolve('../../bin/forkedDev'),
});
process.on('SIGINT', () => {
child.kill('SIGINT');
process.exit(0);
});
process.on('SIGTERM', () => {
child.kill('SIGTERM');
process.exit(1);
});
}
import {
logger,
printHelp,
setNoDeprecation,
setNodeTitle,
yParser,
} from '@umijs/utils';
import { DEV_COMMAND, FRAMEWORK_NAME } from '../constants';
import { Service } from '../service/service';
setNodeTitle(`${FRAMEWORK_NAME}-dev`);
setNoDeprecation();
(async () => {
try {
const args = yParser(process.argv.slice(2));
const service = new Service();
await service.run2({
name: DEV_COMMAND,
args,
});
let closed = false;
process.once('SIGINT', () => onSignal('SIGINT'));
process.once('SIGQUIT', () => onSignal('SIGQUIT'));
process.once('SIGTERM', () => onSignal('SIGTERM'));
function onSignal(signal: string) {
if (closed) return;
closed = true;
service.applyPlugins({
key: 'onExit',
args: {
signal,
},
});
process.exit(0);
}
} catch (e: any) {
logger.fatal(e);
printHelp.exit();
process.exit(1);
}
})();
- 2.3 src/service/service.ts
import { Service as CoreService } from '@umijs/core';
import { existsSync } from 'fs';
import { dirname, join } from 'path';
import { DEFAULT_CONFIG_FILES, FRAMEWORK_NAME } from '../constants';
import { getCwd } from './cwd';
export class Service extends CoreService {
constructor(opts?: any) {
process.env.UMI_DIR = dirname(require.resolve('../../package'));
const cwd = getCwd();
require('./requireHook');
super({
...opts,
env: process.env.NODE_ENV,
cwd,
defaultConfigFiles: opts?.defaultConfigFiles || DEFAULT_CONFIG_FILES,
frameworkName: opts?.frameworkName || FRAMEWORK_NAME,
presets: [require.resolve('@umijs/preset-umi'), ...(opts?.presets || [])],
plugins: [
existsSync(join(cwd, 'plugin.ts')) && join(cwd, 'plugin.ts'),
existsSync(join(cwd, 'plugin.js')) && join(cwd, 'plugin.js'),
].filter(Boolean),
});
}
async run2(opts: { name: string; args?: any }) {
let name = opts.name;
if (opts?.args.version || name === 'v') {
name = 'version';
} else if (opts?.args.help || !name || name === 'h') {
name = 'help';
}
return await this.run({ ...opts, name });
}
}
umi/core/src/service/service.ts
run() 方法启动
async run(opts: { name: string; args?: any }) {
const { name, args = {} } = opts;
args._ = args._ || [];
if (args._[0] === name) args._.shift();
this.args = args;
this.name = name;
this.stage = ServiceStage.init;
loadEnv({ cwd: this.cwd, envFile: '.env' });
let pkg: Record<string, string | Record<string, any>> = {};
let pkgPath: string = '';
try {
pkg = require(join(this.cwd, 'package.json'));
pkgPath = join(this.cwd, 'package.json');
} catch (_e) {
if (this.cwd !== process.cwd()) {
try {
pkg = require(join(process.cwd(), 'package.json'));
pkgPath = join(process.cwd(), 'package.json');
} catch (_e) { }
}
}
this.pkg = pkg;
this.pkgPath = pkgPath || join(this.cwd, 'package.json');
const prefix = this.frameworkName;
const specifiedEnv = process.env[`${prefix}_ENV`.toUpperCase()];
const configManager = new Config({
cwd: this.cwd,
env: this.env,
defaultConfigFiles: this.opts.defaultConfigFiles,
specifiedEnv,
});
this.configManager = configManager;
this.userConfig = configManager.getUserConfig().config;
this.paths = await this.getPaths();
const { plugins, presets } = Plugin.getPluginsAndPresets({
cwd: this.cwd,
pkg,
plugins: [require.resolve('./generatePlugin')].concat(
this.opts.plugins || [],
),
presets: [require.resolve('./servicePlugin')].concat(
this.opts.presets || [],
),
userConfig: this.userConfig,
prefix,
});
this.stage = ServiceStage.initPresets;
const presetPlugins: Plugin[] = [];
while (presets.length) {
await this.initPreset({
preset: presets.shift()!,
presets,
plugins: presetPlugins,
});
}
plugins.unshift(...presetPlugins);
this.stage = ServiceStage.initPlugins;
while (plugins.length) {
await this.initPlugin({ plugin: plugins.shift()!, plugins });
}
const command = this.commands[name];
if (!command) {
this.commandGuessHelper(Object.keys(this.commands), name);
throw Error(`Invalid command ${chalk.red(name)}, it's not registered.`);
}
for (const id of Object.keys(this.plugins)) {
const { config, key } = this.plugins[id];
if (config.schema) this.configSchemas[key] = config.schema;
if (config.default !== undefined) {
this.configDefaults[key] = config.default;
}
this.configOnChanges[key] = config.onChange || ConfigChangeType.reload;
}
this.stage = ServiceStage.resolveConfig;
const { defaultConfig } = await this.resolveConfig();
if (this.config.outputPath) {
this.paths.absOutputPath = isAbsolute(this.config.outputPath)
? this.config.outputPath
: join(this.cwd, this.config.outputPath);
}
this.paths = await this.applyPlugins({
key: 'modifyPaths',
initialValue: this.paths,
});
const storage = await this.applyPlugins({
key: 'modifyTelemetryStorage',
initialValue: noopStorage,
});
this.telemetry.useStorage(storage);
this.stage = ServiceStage.collectAppData;
this.appData = await this.applyPlugins({
key: 'modifyAppData',
initialValue: {
cwd: this.cwd,
pkg,
pkgPath,
plugins: this.plugins,
presets,
name,
args,
userConfig: this.userConfig,
mainConfigFile: configManager.mainConfigFile,
config: this.config,
defaultConfig: defaultConfig,
},
});
this.stage = ServiceStage.onCheck;
await this.applyPlugins({
key: 'onCheck',
});
this.stage = ServiceStage.onStart;
await this.applyPlugins({
key: 'onStart',
});
this.stage = ServiceStage.runCommand;
let ret = await command.fn({ args });
this._profilePlugins();
return ret;
}
defaultConfig

import type { BuildResult } from '@umijs/bundler-utils/compiled/esbuild';
import {
AsyncSeriesWaterfallHook,
SyncWaterfallHook,
} from '@umijs/bundler-utils/compiled/tapable';
import { chalk, fastestLevenshtein, lodash, yParser } from '@umijs/utils';
import assert from 'assert';
import { existsSync } from 'fs';
import { isAbsolute, join } from 'path';
import { Config } from '../config/config';
import { DEFAULT_FRAMEWORK_NAME } from '../constants';
import {
ApplyPluginsType,
ConfigChangeType,
EnableBy,
Env,
IEvent,
IFrameworkType,
IModify,
PluginType,
ServiceStage,
} from '../types';
import { Command } from './command';
import { loadEnv } from './env';
import { Generator } from './generator';
import { Hook } from './hook';
import { getPaths } from './path';
import { Plugin } from './plugin';
import { PluginAPI } from './pluginAPI';
import { noopStorage, Telemetry } from './telemetry';
interface IOpts {
cwd: string;
env: Env;
plugins?: string[];
presets?: string[];
frameworkName?: string;
defaultConfigFiles?: string[];
}
export class Service {
private opts: IOpts;
appData: {
deps?: Record<
string,
{
version: string;
matches: string[];
subpaths: string[];
external?: boolean;
}
>;
framework?: IFrameworkType;
prepare?: {
buildResult: Omit<BuildResult, 'outputFiles'>;
fileImports?: Record<string, Declaration[]>;
};
mpa?: {
entry?: { [key: string]: string }[];
};
bundler?: string;
[key: string]: any;
} = {};
args: yParser.Arguments = { _: [], $0: '' };
commands: Record<string, Command> = {};
generators: Record<string, Generator> = {};
config: Record<string, any> = {};
configSchemas: Record<string, any> = {};
configDefaults: Record<string, any> = {};
configOnChanges: Record<string, any> = {};
cwd: string;
env: Env;
hooks: Record<string, Hook[]> = {};
name: string = '';
paths: {
cwd?: string;
absSrcPath?: string;
absPagesPath?: string;
absApiRoutesPath?: string;
absTmpPath?: string;
absNodeModulesPath?: string;
absOutputPath?: string;
} = {};
plugins: Record<string, Plugin> = {};
keyToPluginMap: Record<string, Plugin> = {};
pluginMethods: Record<string, { plugin: Plugin; fn: Function }> = {};
skipPluginIds: Set<string> = new Set<string>();
stage: ServiceStage = ServiceStage.uninitialized;
userConfig: Record<string, any> = {};
configManager: Config | null = null;
pkg: {
name?: string;
version?: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
[key: string]: any;
} = {};
pkgPath: string = '';
telemetry = new Telemetry();
constructor(opts: IOpts) {
this.cwd = opts.cwd;
this.env = opts.env;
this.opts = opts;
assert(existsSync(this.cwd), `Invalid cwd ${this.cwd}, it's not found.`);
}
applyPlugins<T>(opts: {
key: string;
type?: ApplyPluginsType.event;
initialValue?: any;
args?: any;
sync: true;
}): typeof opts.initialValue | T;
applyPlugins<T>(opts: {
key: string;
type?: ApplyPluginsType;
initialValue?: any;
args?: any;
}): Promise<typeof opts.initialValue | T>;
applyPlugins<T>(opts: {
key: string;
type?: ApplyPluginsType;
initialValue?: any;
args?: any;
sync?: boolean;
}): Promise<typeof opts.initialValue | T> | (typeof opts.initialValue | T) {
const hooks = this.hooks[opts.key] || [];
let type = opts.type;
if (!type) {
if (opts.key.startsWith('on')) {
type = ApplyPluginsType.event;
} else if (opts.key.startsWith('modify')) {
type = ApplyPluginsType.modify;
} else if (opts.key.startsWith('add')) {
type = ApplyPluginsType.add;
} else {
throw new Error(
`Invalid applyPlugins arguments, type must be supplied for key ${opts.key}.`,
);
}
}
switch (type) {
case ApplyPluginsType.add:
assert(
!('initialValue' in opts) || Array.isArray(opts.initialValue),
`applyPlugins failed, opts.initialValue must be Array if opts.type is add.`,
);
const tAdd = new AsyncSeriesWaterfallHook(['memo']);
for (const hook of hooks) {
if (!this.isPluginEnable(hook)) continue;
tAdd.tapPromise(
{
name: hook.plugin.key,
stage: hook.stage || 0,
before: hook.before,
},
async (memo: any) => {
const dateStart = new Date();
const items = await hook.fn(opts.args);
hook.plugin.time.hooks[opts.key] ||= [];
hook.plugin.time.hooks[opts.key].push(
new Date().getTime() - dateStart.getTime(),
);
return memo.concat(items);
},
);
}
return tAdd.promise(opts.initialValue || []) as Promise<T>;
case ApplyPluginsType.modify:
const tModify = new AsyncSeriesWaterfallHook(['memo']);
for (const hook of hooks) {
if (!this.isPluginEnable(hook)) continue;
tModify.tapPromise(
{
name: hook.plugin.key,
stage: hook.stage || 0,
before: hook.before,
},
async (memo: any) => {
const dateStart = new Date();
const ret = await hook.fn(memo, opts.args);
hook.plugin.time.hooks[opts.key] ||= [];
hook.plugin.time.hooks[opts.key].push(
new Date().getTime() - dateStart.getTime(),
);
return ret;
},
);
}
return tModify.promise(opts.initialValue) as Promise<T>;
case ApplyPluginsType.event:
if (opts.sync) {
const tEvent = new SyncWaterfallHook(['_']);
hooks.forEach((hook) => {
if (this.isPluginEnable(hook)) {
tEvent.tap(
{
name: hook.plugin.key,
stage: hook.stage || 0,
before: hook.before,
},
() => {
const dateStart = new Date();
hook.fn(opts.args);
hook.plugin.time.hooks[opts.key] ||= [];
hook.plugin.time.hooks[opts.key].push(
new Date().getTime() - dateStart.getTime(),
);
},
);
}
});
return tEvent.call(1) as T;
}
const tEvent = new AsyncSeriesWaterfallHook(['_']);
for (const hook of hooks) {
if (!this.isPluginEnable(hook)) continue;
tEvent.tapPromise(
{
name: hook.plugin.key,
stage: hook.stage || 0,
before: hook.before,
},
async () => {
const dateStart = new Date();
await hook.fn(opts.args);
hook.plugin.time.hooks[opts.key] ||= [];
hook.plugin.time.hooks[opts.key].push(
new Date().getTime() - dateStart.getTime(),
);
},
);
}
return tEvent.promise(1) as Promise<T>;
default:
throw new Error(
`applyPlugins failed, type is not defined or is not matched, got ${opts.type}.`,
);
}
}
async run(opts: { name: string; args?: any }) {
const { name, args = {} } = opts;
args._ = args._ || [];
if (args._[0] === name) args._.shift();
this.args = args;
this.name = name;
this.stage = ServiceStage.init;
loadEnv({ cwd: this.cwd, envFile: '.env' });
let pkg: Record<string, string | Record<string, any>> = {};
let pkgPath: string = '';
try {
pkg = require(join(this.cwd, 'package.json'));
pkgPath = join(this.cwd, 'package.json');
} catch (_e) {
if (this.cwd !== process.cwd()) {
try {
pkg = require(join(process.cwd(), 'package.json'));
pkgPath = join(process.cwd(), 'package.json');
} catch (_e) { }
}
}
this.pkg = pkg;
this.pkgPath = pkgPath || join(this.cwd, 'package.json');
const prefix = this.frameworkName;
const specifiedEnv = process.env[`${prefix}_ENV`.toUpperCase()];
const configManager = new Config({
cwd: this.cwd,
env: this.env,
defaultConfigFiles: this.opts.defaultConfigFiles,
specifiedEnv,
});
this.configManager = configManager;
this.userConfig = configManager.getUserConfig().config;
this.paths = await this.getPaths();
const { plugins, presets } = Plugin.getPluginsAndPresets({
cwd: this.cwd,
pkg,
plugins: [require.resolve('./generatePlugin')].concat(
this.opts.plugins || [],
),
presets: [require.resolve('./servicePlugin')].concat(
this.opts.presets || [],
),
userConfig: this.userConfig,
prefix,
});
this.stage = ServiceStage.initPresets;
const presetPlugins: Plugin[] = [];
while (presets.length) {
await this.initPreset({
preset: presets.shift()!,
presets,
plugins: presetPlugins,
});
}
plugins.unshift(...presetPlugins);
this.stage = ServiceStage.initPlugins;
while (plugins.length) {
await this.initPlugin({ plugin: plugins.shift()!, plugins });
}
const command = this.commands[name];
if (!command) {
this.commandGuessHelper(Object.keys(this.commands), name);
throw Error(`Invalid command ${chalk.red(name)}, it's not registered.`);
}
for (const id of Object.keys(this.plugins)) {
const { config, key } = this.plugins[id];
if (config.schema) this.configSchemas[key] = config.schema;
if (config.default !== undefined) {
this.configDefaults[key] = config.default;
}
this.configOnChanges[key] = config.onChange || ConfigChangeType.reload;
}
this.stage = ServiceStage.resolveConfig;
const { defaultConfig } = await this.resolveConfig();
if (this.config.outputPath) {
this.paths.absOutputPath = isAbsolute(this.config.outputPath)
? this.config.outputPath
: join(this.cwd, this.config.outputPath);
}
this.paths = await this.applyPlugins({
key: 'modifyPaths',
initialValue: this.paths,
});
const storage = await this.applyPlugins({
key: 'modifyTelemetryStorage',
initialValue: noopStorage,
});
this.telemetry.useStorage(storage);
this.stage = ServiceStage.collectAppData;
this.appData = await this.applyPlugins({
key: 'modifyAppData',
initialValue: {
cwd: this.cwd,
pkg,
pkgPath,
plugins: this.plugins,
presets,
name,
args,
userConfig: this.userConfig,
mainConfigFile: configManager.mainConfigFile,
config: this.config,
defaultConfig: defaultConfig,
},
});
this.stage = ServiceStage.onCheck;
await this.applyPlugins({
key: 'onCheck',
});
this.stage = ServiceStage.onStart;
await this.applyPlugins({
key: 'onStart',
});
this.stage = ServiceStage.runCommand;
let ret = await command.fn({ args });
this._profilePlugins();
return ret;
}
async getPaths() {
const paths = getPaths({
cwd: this.cwd,
env: this.env,
prefix: this.frameworkName,
});
return paths;
}
async resolveConfig() {
assert(
this.stage > ServiceStage.init,
`Can't generate final config before init stage`,
);
const resolveMode = this.commands[this.name].configResolveMode;
const config = await this.applyPlugins({
key: 'modifyConfig',
initialValue: lodash.cloneDeep(
resolveMode === 'strict'
? this.configManager!.getConfig({
schemas: this.configSchemas,
}).config
: this.configManager!.getUserConfig().config,
),
args: { paths: this.paths },
});
const defaultConfig = await this.applyPlugins({
key: 'modifyDefaultConfig',
initialValue: lodash.cloneDeep(this.configDefaults),
});
this.config = lodash.merge(defaultConfig, config) as Record<string, any>;
return { config, defaultConfig };
}
_profilePlugins() {
if (this.args.profilePlugins) {
console.log();
Object.keys(this.plugins)
.map((id) => {
const plugin = this.plugins[id];
const total = totalTime(plugin);
return {
id,
total,
register: plugin.time.register || 0,
hooks: plugin.time.hooks,
};
})
.filter((time) => {
return time.total > (this.args.profilePluginsLimit ?? 10);
})
.sort((a, b) => (b.total > a.total ? 1 : -1))
.forEach((time) => {
console.log(chalk.green('plugin'), time.id, time.total);
if (this.args.profilePluginsVerbose) {
console.log(' ', chalk.green('register'), time.register);
console.log(
' ',
chalk.green('hooks'),
JSON.stringify(sortHooks(time.hooks)),
);
}
});
}
function sortHooks(hooks: Record<string, number[]>) {
const ret: Record<string, number[]> = {};
Object.keys(hooks)
.sort((a, b) => {
return add(hooks[b]) - add(hooks[a]);
})
.forEach((key) => {
ret[key] = hooks[key];
});
return ret;
}
function totalTime(plugin: Plugin) {
const time = plugin.time;
return (
(time.register || 0) +
Object.values(time.hooks).reduce<number>((a, b) => a + add(b), 0)
);
}
function add(nums: number[]) {
return nums.reduce((a, b) => a + b, 0);
}
}
async initPreset(opts: {
preset: Plugin;
presets: Plugin[];
plugins: Plugin[];
}) {
const { presets, plugins } = await this.initPlugin({
plugin: opts.preset,
presets: opts.presets,
plugins: opts.plugins,
});
opts.presets.unshift(...(presets || []));
opts.plugins.push(...(plugins || []));
}
async initPlugin(opts: {
plugin: Plugin;
presets?: Plugin[];
plugins: Plugin[];
}) {
assert(
!this.plugins[opts.plugin.id],
`${opts.plugin.type} ${opts.plugin.id} is already registered by ${this.plugins[opts.plugin.id]?.path
}, ${opts.plugin.type} from ${opts.plugin.path} register failed.`,
);
this.plugins[opts.plugin.id] = opts.plugin;
const pluginAPI = new PluginAPI({
plugin: opts.plugin,
service: this,
});
pluginAPI.registerPresets = pluginAPI.registerPresets.bind(
pluginAPI,
opts.presets || [],
);
pluginAPI.registerPlugins = pluginAPI.registerPlugins.bind(
pluginAPI,
opts.plugins,
);
const proxyPluginAPI = PluginAPI.proxyPluginAPI({
service: this,
pluginAPI,
serviceProps: [
'appData',
'applyPlugins',
'args',
'config',
'cwd',
'pkg',
'pkgPath',
'name',
'paths',
'userConfig',
'env',
'isPluginEnable',
],
staticProps: {
ApplyPluginsType,
ConfigChangeType,
EnableBy,
ServiceStage,
service: this,
},
});
let dateStart = new Date();
let ret = await opts.plugin.apply()(proxyPluginAPI);
opts.plugin.time.register = new Date().getTime() - dateStart.getTime();
if (opts.plugin.type === 'plugin') {
assert(!ret, `plugin should return nothing`);
}
assert(
!this.keyToPluginMap[opts.plugin.key],
`key ${opts.plugin.key} is already registered by ${this.keyToPluginMap[opts.plugin.key]?.path
}, ${opts.plugin.type} from ${opts.plugin.path} register failed.`,
);
this.keyToPluginMap[opts.plugin.key] = opts.plugin;
if (ret?.presets) {
ret.presets = ret.presets.map(
(preset: string) =>
new Plugin({
path: preset,
type: PluginType.preset,
cwd: this.cwd,
}),
);
}
if (ret?.plugins) {
ret.plugins = ret.plugins.map(
(plugin: string) =>
new Plugin({
path: plugin,
type: PluginType.plugin,
cwd: this.cwd,
}),
);
}
return ret || {};
}
isPluginEnable(hook: Hook | string) {
let plugin: Plugin;
if ((hook as Hook).plugin) {
plugin = (hook as Hook).plugin;
} else {
plugin = this.keyToPluginMap[hook as string];
if (!plugin) return false;
}
const { id, key, enableBy } = plugin;
if (this.skipPluginIds.has(id)) return false;
if (this.userConfig[key] === false) return false;
if (this.config[key] === false) return false;
if (enableBy === EnableBy.config) {
return key in this.userConfig || (this.config && key in this.config);
}
if (typeof enableBy === 'function')
return enableBy({
userConfig: this.userConfig,
config: this.config,
env: this.env,
});
return true;
}
commandGuessHelper(commands: string[], currentCmd: string) {
const altCmds = commands.filter((cmd) => {
return (
fastestLevenshtein.distance(currentCmd, cmd) <
currentCmd.length * 0.6 && currentCmd !== cmd
);
});
const printHelper = altCmds
.slice(0, 3)
.map((cmd) => {
return ` - ${chalk.green(cmd)}`;
})
.join('\n');
if (altCmds.length) {
console.log();
console.log(
[
chalk.cyan(
altCmds.length === 1
? 'Did you mean this command ?'
: 'Did you mean one of these commands ?',
),
printHelper,
].join('\n'),
);
console.log();
}
}
get frameworkName() {
return this.opts.frameworkName || DEFAULT_FRAMEWORK_NAME;
}
}
export interface IServicePluginAPI {
appData: typeof Service.prototype.appData;
applyPlugins: typeof Service.prototype.applyPlugins;
args: typeof Service.prototype.args;
config: typeof Service.prototype.config;
cwd: typeof Service.prototype.cwd;
generators: typeof Service.prototype.generators;
pkg: typeof Service.prototype.pkg;
pkgPath: typeof Service.prototype.pkgPath;
name: typeof Service.prototype.name;
paths: Required<typeof Service.prototype.paths>;
userConfig: typeof Service.prototype.userConfig;
env: typeof Service.prototype.env;
isPluginEnable: typeof Service.prototype.isPluginEnable;
onCheck: IEvent<null>;
onStart: IEvent<null>;
modifyAppData: IModify<typeof Service.prototype.appData, null>;
modifyConfig: IModify<
typeof Service.prototype.config,
{ paths: Record<string, string> }
>;
modifyDefaultConfig: IModify<typeof Service.prototype.config, null>;
modifyPaths: IModify<typeof Service.prototype.paths, null>;
modifyTelemetryStorage: IModify<typeof Service.prototype.telemetry, null>;
ApplyPluginsType: typeof ApplyPluginsType;
ConfigChangeType: typeof ConfigChangeType;
EnableBy: typeof EnableBy;
ServiceStage: typeof ServiceStage;
registerPresets: (presets: any[]) => void;
registerPlugins: (plugins: (Plugin | {})[]) => void;
}
type DeclareKind = 'value' | 'type';
type Declaration =
| {
type: 'ImportDeclaration';
source: string;
specifiers: Array<SimpleImportSpecifier>;
importKind: DeclareKind;
start: number;
end: number;
}
| {
type: 'DynamicImport';
source: string;
start: number;
end: number;
}
| {
type: 'ExportNamedDeclaration';
source: string;
specifiers: Array<SimpleExportSpecifier>;
exportKind: DeclareKind;
start: number;
end: number;
}
| {
type: 'ExportAllDeclaration';
source: string;
start: number;
end: number;
};
type SimpleImportSpecifier =
| {
type: 'ImportDefaultSpecifier';
local: string;
}
| {
type: 'ImportNamespaceSpecifier';
local: string;
imported: string;
}
| {
type: 'ImportNamespaceSpecifier';
local?: string;
};
type SimpleExportSpecifier =
| {
type: 'ExportDefaultSpecifier';
exported: string;
}
| {
type: 'ExportNamespaceSpecifier';
exported?: string;
}
| {
type: 'ExportSpecifier';
exported: string;
local: string;
};
umi/preset-umi/src/commands/dev.ts