Match-id-4fa852d09eb123b0f54db2b8a318ed2e50145cae
This commit is contained in:
parent
90743f500c
commit
9be2395a60
|
@ -1,40 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
'parser': 'babel-eslint',
|
|
||||||
'env': {
|
|
||||||
'amd': true,
|
|
||||||
'es6': true,
|
|
||||||
'browser': true,
|
|
||||||
'node': false
|
|
||||||
},
|
|
||||||
'parserOptions': {
|
|
||||||
'ecmaVersion': 6,
|
|
||||||
'sourceType': 'module',
|
|
||||||
'ecmaFeatures': {
|
|
||||||
'jsx': true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'ignorePatterns': [
|
|
||||||
"src/template"
|
|
||||||
],
|
|
||||||
'rules': {
|
|
||||||
'indent': [
|
|
||||||
'error',
|
|
||||||
4,
|
|
||||||
{
|
|
||||||
SwitchCase: 1,
|
|
||||||
flatTernaryExpressions: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-unused-vars': 'off', // 允许变量声明后未使用
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
"no-underscore-dangle": ["off", "always"], // 允许私有变量 _xxx的变量命名方式
|
|
||||||
'filenames/match-exported': 0,
|
|
||||||
'consistent-return': 0,
|
|
||||||
"comma-dangle": [2, "never"], // 组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号
|
|
||||||
'global-require': 0, // 允许require语句不出现在顶层中
|
|
||||||
'no-nested-ternary': 0, // 允许嵌套三元表达式
|
|
||||||
'no-unused-expressions': 0, // 允许使用未执行的表达式。比如fn是一个函数,允许 fn && fn()
|
|
||||||
'no-throw-literal': 0, // 允许throw抛出对象格式
|
|
||||||
'@typescript-eslint/member-ordering': 0 // 禁用TypeScript声明规范
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
node_modules/
|
|
||||||
webpack/
|
|
||||||
public/
|
|
|
@ -1,15 +0,0 @@
|
||||||
export default {
|
|
||||||
printWidth: 120, // 一行120字符数,如果超过会进行换行
|
|
||||||
tabWidth: 2, // tab等2个空格
|
|
||||||
useTabs: false, // 用空格缩进行
|
|
||||||
semi: true, // 行尾使用分号
|
|
||||||
singleQuote: true, // 字符串使用单引号
|
|
||||||
quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
|
|
||||||
jsxSingleQuote: false, // 在JSX中使用双引号
|
|
||||||
trailingComma: 'es5', // 使用尾逗号(对象、数组等)
|
|
||||||
bracketSpacing: true, // 对象的括号间增加空格
|
|
||||||
jsxBracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
|
|
||||||
arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
|
|
||||||
vueIndentScriptAndStyle: false, // 不缩进Vue文件中的<script>和<style>标记内的代码
|
|
||||||
endOfLine: 'lf', // 仅限换行(\n)
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
import run from '../lib/cli/cli.js';
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,2 +0,0 @@
|
||||||
declare const _default: (api: any) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,49 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import { build } from 'vite';
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'build',
|
|
||||||
description: 'build application for production',
|
|
||||||
initialState: api.buildConfig,
|
|
||||||
fn: function (args, state) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
switch (api.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeCompile', args: state });
|
|
||||||
state.forEach((s) => {
|
|
||||||
webpack(s.config, (err, stats) => {
|
|
||||||
// api.applyHook({ name: 'afterCompile' });
|
|
||||||
if (err || stats.hasErrors()) {
|
|
||||||
api.logger.error(`Build failed.err: ${err}, stats:${stats}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
api.logger.error(`Build failed. Can't find build config.`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeCompile' });
|
|
||||||
build(state);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
api.logger.error(`Build failed. Can't find build config.`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
declare const _default: (api: API) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,72 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import WebpackDevServer from 'webpack-dev-server';
|
|
||||||
import { createServer } from 'vite';
|
|
||||||
import setupProxy from '../../../utils/setupProxy.js';
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'dev',
|
|
||||||
description: 'build application for development',
|
|
||||||
initialState: api.devBuildConfig,
|
|
||||||
fn: function (args, state) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
api.applyHook({ name: 'beforeDevConfig' });
|
|
||||||
switch (api.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeDevCompile', config: state });
|
|
||||||
const compiler = webpack(state);
|
|
||||||
const devServerOptions = {
|
|
||||||
client: {
|
|
||||||
overlay: false,
|
|
||||||
},
|
|
||||||
host: 'localhost',
|
|
||||||
port: '8888',
|
|
||||||
open: true,
|
|
||||||
historyApiFallback: true,
|
|
||||||
};
|
|
||||||
if (api.userConfig.devBuildConfig.devProxy) {
|
|
||||||
devServerOptions.onBeforeSetupMiddleware = (devServer) => {
|
|
||||||
setupProxy(devServer.app, api);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
api.applyHook({
|
|
||||||
name: 'beforeStartDevServer',
|
|
||||||
config: { compiler: compiler, devServerOptions: devServerOptions },
|
|
||||||
});
|
|
||||||
const server = new WebpackDevServer(compiler, devServerOptions);
|
|
||||||
server.startCallback((err) => {
|
|
||||||
api.applyHook({ name: 'afterStartDevServer' });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
api.logger.error("Can't find config");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
if (state) {
|
|
||||||
yield createServer(state)
|
|
||||||
.then(server => {
|
|
||||||
return server.listen();
|
|
||||||
})
|
|
||||||
.then(server => {
|
|
||||||
server.printUrls();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
api.logger.error("Can't find config");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
declare const _default: (api: API) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,100 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import inquirer from 'inquirer';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { copyFile } from '../../../utils/util.js';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'generate',
|
|
||||||
description: 'generate template',
|
|
||||||
fn: (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
||||||
if (args._[0] === 'g') {
|
|
||||||
args._.shift();
|
|
||||||
}
|
|
||||||
if (args._.length === 0) {
|
|
||||||
api.logger.warn("Can't find any generate options.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (args._[0]) {
|
|
||||||
case 'jest':
|
|
||||||
args._.shift();
|
|
||||||
const isESM = api.packageJson['type'] === 'module';
|
|
||||||
yield generateJest(args, api.cwd, isESM);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const generateJest = (args, cwd, isESM) => __awaiter(void 0, void 0, void 0, function* () {
|
|
||||||
let isTs = false;
|
|
||||||
if (args['ts']) {
|
|
||||||
isTs = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const answers = yield inquirer.prompt([
|
|
||||||
{
|
|
||||||
name: 'useTs',
|
|
||||||
message: 'Do you want to use TypeScript',
|
|
||||||
type: 'confirm',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
isTs = answers['useTs'];
|
|
||||||
}
|
|
||||||
if (checkJestConfigExist(cwd)) {
|
|
||||||
console.log('The jest config is exist.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const testRootPath = path.join(cwd, 'test');
|
|
||||||
if (!fs.existsSync(testRootPath)) {
|
|
||||||
fs.mkdirSync(testRootPath);
|
|
||||||
}
|
|
||||||
let templateDir = path.resolve(__dirname, '../../../../template/test');
|
|
||||||
// 如果是TS, 拷贝ts
|
|
||||||
if (isTs) {
|
|
||||||
templateDir = path.join(templateDir, 'ts');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
// 拷贝mjs
|
|
||||||
if (!isTs && isESM) {
|
|
||||||
templateDir = path.join(templateDir, 'mjs');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
// 拷贝cjs
|
|
||||||
if (!isTs && !isESM) {
|
|
||||||
templateDir = path.join(templateDir, 'cjs');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
function checkJestConfigExist(cwd) {
|
|
||||||
const items = fs.readdirSync(cwd);
|
|
||||||
for (const item of items) {
|
|
||||||
const itemPath = path.resolve(cwd, item);
|
|
||||||
const states = fs.statSync(itemPath);
|
|
||||||
if (states.isFile() && item.startsWith('jest.config')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function copyTestTemplate(cwd, testRootPath, templateDir) {
|
|
||||||
const items = fs.readdirSync(templateDir);
|
|
||||||
for (const item of items) {
|
|
||||||
const itemPath = path.resolve(templateDir, item);
|
|
||||||
if (item.startsWith('jest.config')) {
|
|
||||||
copyFile(path.join(cwd, item), itemPath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
copyFile(path.join(testRootPath, item), itemPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
declare const _default: (api: any) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,31 +0,0 @@
|
||||||
import chalk from 'chalk';
|
|
||||||
import lodash from 'lodash';
|
|
||||||
function getDescriptions(commands) {
|
|
||||||
return Object.keys(commands)
|
|
||||||
.filter(name => typeof commands[name] !== 'string')
|
|
||||||
.map(name => {
|
|
||||||
return getDescription(commands[name]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function getDescription(command) {
|
|
||||||
return ` ${chalk.green(lodash.padEnd(command.name, 10))}${command.description || ''}`;
|
|
||||||
}
|
|
||||||
function padLeft(str) {
|
|
||||||
return str
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => ` ${line}`)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'help',
|
|
||||||
description: 'show command helps',
|
|
||||||
fn: (args, config) => {
|
|
||||||
console.log(`
|
|
||||||
Usage: inula-cli <command> [options]
|
|
||||||
|
|
||||||
${getDescriptions(api.commands).join('\n')}
|
|
||||||
`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,2 +0,0 @@
|
||||||
declare const _default: (api: any) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,24 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import mockServer from '../../../utils/mockServer.js';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
export default (api) => {
|
|
||||||
api.registerHook({
|
|
||||||
name: 'beforeStartDevServer',
|
|
||||||
fn: (state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
||||||
const { compiler, devServerOptions } = state;
|
|
||||||
devServerOptions.setupMiddlewares = (middlewares, devServer) => {
|
|
||||||
mockServer(devServer.app);
|
|
||||||
return middlewares;
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,2 +0,0 @@
|
||||||
declare const _default: (api: any) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,53 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import express from 'express';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'proxy',
|
|
||||||
description: 'remote proxy',
|
|
||||||
initialState: api.userConfig.remoteProxy,
|
|
||||||
fn: function (args, state) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (!state) {
|
|
||||||
api.logger.error(`Invalid proxy config!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const app = express();
|
|
||||||
const proxyConfig = state;
|
|
||||||
const staticList = proxyConfig.localStatic;
|
|
||||||
staticList.forEach(function (value) {
|
|
||||||
app.use(value.url, express.static(value.local));
|
|
||||||
});
|
|
||||||
const remoteProxy = createProxyMiddleware(proxyConfig.fowardingURL, {
|
|
||||||
target: proxyConfig.target,
|
|
||||||
secure: false,
|
|
||||||
autoRewrite: true,
|
|
||||||
protocolRewrite: 'http',
|
|
||||||
ws: true,
|
|
||||||
hostRewrite: '',
|
|
||||||
preserveHeaderKeyCase: true,
|
|
||||||
proxyTimeout: 5 * 60 * 60 * 1000,
|
|
||||||
timeout: 5 * 60 * 60 * 1000,
|
|
||||||
onError: handleProxyError,
|
|
||||||
});
|
|
||||||
function handleProxyError(err) {
|
|
||||||
api.logger.error('Local proxy error. Error is ', err);
|
|
||||||
}
|
|
||||||
app.use(remoteProxy);
|
|
||||||
app.listen(proxyConfig.localPort, () => {
|
|
||||||
api.logger.info(`Start proxy client on http://localhost:${proxyConfig.localPort}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
declare const _default: (api: API) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,19 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import jest from 'jest';
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'jest',
|
|
||||||
description: 'run jest test',
|
|
||||||
fn: (args, config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
||||||
yield jest.run();
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
declare const _default: (api: API) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,21 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const pkgPath = path.resolve(__dirname, '../../../../package.json');
|
|
||||||
// 读取 package.json 文件
|
|
||||||
const packageJson = fs.readFileSync(pkgPath, 'utf8');
|
|
||||||
// 解析 JSON 格式的数据
|
|
||||||
const packageData = JSON.parse(packageJson);
|
|
||||||
// 获取版本号
|
|
||||||
const version = packageData.version;
|
|
||||||
export default (api) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'version',
|
|
||||||
description: 'show inula-cli version',
|
|
||||||
fn: () => {
|
|
||||||
api.logger.info(`Inula-cli version is ${version}.`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
export default function run(): Promise<void>;
|
|
|
@ -1,74 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import Hub from '../core/Hub.js';
|
|
||||||
import initializeEnv from '../utils/initializeEnv.js';
|
|
||||||
import { Logger, LogLevel } from '../utils/logger.js';
|
|
||||||
export default function run() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const args = yargsParser(process.argv.slice(2));
|
|
||||||
const alias = {
|
|
||||||
h: 'help',
|
|
||||||
v: 'version',
|
|
||||||
g: 'generate',
|
|
||||||
};
|
|
||||||
let command = args._[0];
|
|
||||||
if (!command) {
|
|
||||||
if (args['v'] || args['version']) {
|
|
||||||
command = 'v';
|
|
||||||
}
|
|
||||||
if (args['h'] || args['help']) {
|
|
||||||
command = 'h';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const aliasCommand = alias[command];
|
|
||||||
if (aliasCommand) {
|
|
||||||
command = aliasCommand;
|
|
||||||
}
|
|
||||||
initializeEnv();
|
|
||||||
if (command === 'version' || command === 'help') {
|
|
||||||
process.env.INNER_COMMAND = "true";
|
|
||||||
}
|
|
||||||
switch (command) {
|
|
||||||
case 'build':
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
break;
|
|
||||||
case 'dev':
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let enableDebug = false;
|
|
||||||
if (process.env.DEBUG === "true") {
|
|
||||||
enableDebug = true;
|
|
||||||
}
|
|
||||||
const logger = new Logger(enableDebug ? LogLevel.DEBUG : LogLevel.INFO);
|
|
||||||
try {
|
|
||||||
new Hub({
|
|
||||||
logger: logger,
|
|
||||||
}).run({
|
|
||||||
command,
|
|
||||||
args,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
logger.error(chalk.red(err.message));
|
|
||||||
if (err.stack) {
|
|
||||||
logger.error(err.stack);
|
|
||||||
}
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
import { UserConfig } from '../types/types.js';
|
|
||||||
interface ConfigOpts {
|
|
||||||
cwd: string;
|
|
||||||
isLocal?: boolean;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
export default class Config {
|
|
||||||
cwd: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
configFile?: string | null;
|
|
||||||
logger: Logger;
|
|
||||||
constructor(opts: ConfigOpts);
|
|
||||||
getUserConfig(): Promise<UserConfig>;
|
|
||||||
getConfigFile(): string | null;
|
|
||||||
addModePath(file: string, mode: string): string;
|
|
||||||
requireConfigs(configFiles: string[]): Promise<UserConfig[]>;
|
|
||||||
mergeConfig(...configs: UserConfig[]): UserConfig;
|
|
||||||
}
|
|
||||||
export {};
|
|
|
@ -1,96 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { extname, join } from 'path';
|
|
||||||
import { parseRequireDeps, cleanRequireCache } from '../utils/util.js';
|
|
||||||
import deepmerge from 'deepmerge';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
const DEFAULT_CONFIG_FILES = ['.inula.ts', '.inula.js'];
|
|
||||||
export default class Config {
|
|
||||||
constructor(opts) {
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.isLocal = opts.isLocal || process.env.NODE_ENV === 'development';
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
getUserConfig() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const configFile = this.getConfigFile();
|
|
||||||
if (configFile === null) {
|
|
||||||
this.logger.warn(`Can't find .inula.ts or .inula.js in ${this.cwd}`);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
this.configFile = configFile;
|
|
||||||
if (configFile) {
|
|
||||||
let envConfigFile = undefined;
|
|
||||||
if (process.env.RUNNING_MODE) {
|
|
||||||
envConfigFile = this.addModePath(configFile, process.env.RUNNING_MODE);
|
|
||||||
}
|
|
||||||
// 配置文件的来源
|
|
||||||
// 1、默认的configFile 如.inula.ts
|
|
||||||
// 2、带环境变量的configFile 如.inula.cloud.ts
|
|
||||||
// 3、dev模式 包含local 如.inula.local.ts
|
|
||||||
const files = [configFile];
|
|
||||||
if (envConfigFile && existsSync(envConfigFile)) {
|
|
||||||
files.push(envConfigFile);
|
|
||||||
}
|
|
||||||
if (this.isLocal) {
|
|
||||||
const localConfigFile = this.addModePath(configFile, 'local');
|
|
||||||
if (existsSync(localConfigFile)) {
|
|
||||||
files.push(localConfigFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.logger.debug(`Find user config files ${files}`);
|
|
||||||
// 依次加载配置文件中的依赖并刷新require中的缓存
|
|
||||||
const requireDeps = files.reduce((deps, file) => {
|
|
||||||
deps = deps.concat(parseRequireDeps(file));
|
|
||||||
return deps;
|
|
||||||
}, []);
|
|
||||||
requireDeps.forEach(cleanRequireCache);
|
|
||||||
const configs = yield this.requireConfigs(files);
|
|
||||||
return this.mergeConfig(...configs);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getConfigFile() {
|
|
||||||
const configFileList = DEFAULT_CONFIG_FILES.map(f => join(this.cwd, f));
|
|
||||||
for (let configFile of configFileList) {
|
|
||||||
if (existsSync(configFile)) {
|
|
||||||
return configFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
addModePath(file, mode) {
|
|
||||||
const ext = extname(file);
|
|
||||||
return file.replace(new RegExp(`${ext}$`), `.${mode}${ext}`);
|
|
||||||
}
|
|
||||||
requireConfigs(configFiles) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const configs = [];
|
|
||||||
for (const file in configFiles) {
|
|
||||||
const content = yield loadModule(configFiles[file]);
|
|
||||||
if (content) {
|
|
||||||
configs.push(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configs;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mergeConfig(...configs) {
|
|
||||||
let ret = {};
|
|
||||||
for (const config of configs) {
|
|
||||||
ret = deepmerge(ret, config);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import Config from '../config/Config.js';
|
|
||||||
import { BuildConfig, DevProxy, UserConfig } from '../types/types.js';
|
|
||||||
import { ServiceStage } from '../enum/enum.js';
|
|
||||||
import Plugin from '../plugin/Plugin.js';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
interface HubOpts {
|
|
||||||
cwd?: string;
|
|
||||||
logger?: Logger;
|
|
||||||
}
|
|
||||||
export default class Hub {
|
|
||||||
args: any;
|
|
||||||
cwd: string;
|
|
||||||
env: string | undefined;
|
|
||||||
configManager: Config;
|
|
||||||
userConfig: UserConfig;
|
|
||||||
packageJson: PackageJSON;
|
|
||||||
stage: ServiceStage;
|
|
||||||
buildConfig: {
|
|
||||||
name: string;
|
|
||||||
config: object;
|
|
||||||
}[];
|
|
||||||
pluginManager: Plugin;
|
|
||||||
buildConfigPath: BuildConfig[];
|
|
||||||
devBuildConfig: object;
|
|
||||||
compileMode: string;
|
|
||||||
builtInPlugins: string[];
|
|
||||||
pluginPaths: string[];
|
|
||||||
devProxy: DevProxy | null;
|
|
||||||
logger: Logger;
|
|
||||||
[key: string]: any;
|
|
||||||
constructor(opts: HubOpts);
|
|
||||||
setStage(stage: ServiceStage): void;
|
|
||||||
init(): Promise<void>;
|
|
||||||
getBuiltInPlugins(): string[];
|
|
||||||
run({ command, args }: {
|
|
||||||
command: string | number;
|
|
||||||
args: yargsParser.Arguments;
|
|
||||||
}): Promise<void>;
|
|
||||||
runCommand({ command, args }: {
|
|
||||||
command: string | number;
|
|
||||||
args: yargsParser.Arguments;
|
|
||||||
}): Promise<void>;
|
|
||||||
setCompileMode(): void;
|
|
||||||
analyzeBuildConfig(): Promise<void>;
|
|
||||||
getConfigName(name: string): string;
|
|
||||||
}
|
|
||||||
export {};
|
|
|
@ -1,188 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import { join, isAbsolute } from 'path';
|
|
||||||
import Config from '../config/Config.js';
|
|
||||||
import { ServiceStage } from '../enum/enum.js';
|
|
||||||
import Plugin from '../plugin/Plugin.js';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { Logger, LogLevel } from '../utils/logger.js';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
import readDirectory from '../utils/readDirectory.js';
|
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { loadPkg } from '../utils/loadPkg.js';
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
export default class Hub {
|
|
||||||
constructor(opts) {
|
|
||||||
this.userConfig = {};
|
|
||||||
this.stage = ServiceStage.uninitialized;
|
|
||||||
this.buildConfig = [];
|
|
||||||
this.buildConfigPath = [];
|
|
||||||
this.devBuildConfig = {};
|
|
||||||
this.compileMode = '';
|
|
||||||
this.builtInPlugins = [];
|
|
||||||
this.pluginPaths = [];
|
|
||||||
this.devProxy = null;
|
|
||||||
this.setStage(ServiceStage.constructor);
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.env = process.env.NODE_ENV;
|
|
||||||
if (!opts.logger) {
|
|
||||||
this.logger = new Logger(LogLevel.INFO);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
this.packageJson = loadPkg(path.join(this.cwd, './package.json'));
|
|
||||||
this.configManager = new Config({
|
|
||||||
cwd: this.cwd,
|
|
||||||
isLocal: this.env === 'development',
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
this.pluginManager = new Plugin({
|
|
||||||
cwd: this.cwd,
|
|
||||||
hub: this,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setStage(stage) {
|
|
||||||
this.stage = stage;
|
|
||||||
}
|
|
||||||
init() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
this.setStage(ServiceStage.init);
|
|
||||||
// 获取用户配置
|
|
||||||
this.userConfig = yield this.configManager.getUserConfig();
|
|
||||||
// 设置编译模式
|
|
||||||
this.setCompileMode();
|
|
||||||
// 获取编译配置
|
|
||||||
yield this.analyzeBuildConfig();
|
|
||||||
this.setStage(ServiceStage.initPlugins);
|
|
||||||
this.builtInPlugins = this.getBuiltInPlugins();
|
|
||||||
yield this.pluginManager.register(this.builtInPlugins, this.userConfig.plugins);
|
|
||||||
this.setStage(ServiceStage.initHooks);
|
|
||||||
this.pluginManager.initHook();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getBuiltInPlugins() {
|
|
||||||
return readDirectory(path.resolve(__dirname, '../builtInPlugins'));
|
|
||||||
}
|
|
||||||
run({ command, args }) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
args._ = args._ || [];
|
|
||||||
if (args._[0] === command) {
|
|
||||||
args._.shift();
|
|
||||||
}
|
|
||||||
this.args = args;
|
|
||||||
yield this.init();
|
|
||||||
this.setStage(ServiceStage.run);
|
|
||||||
return this.runCommand({ command, args });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
runCommand({ command, args }) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
this.logger.debug(`run command ${command}`);
|
|
||||||
const commands = typeof this.pluginManager.commands[command] === 'string'
|
|
||||||
? this.pluginManager.commands[this.pluginManager.commands[command]]
|
|
||||||
: this.pluginManager.commands[command];
|
|
||||||
if (commands === undefined) {
|
|
||||||
this.logger.error(`Invalid command ${command}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { fn } = commands;
|
|
||||||
return fn(args, this.pluginManager.store[command]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setCompileMode() {
|
|
||||||
this.compileMode = this.userConfig.compileMode || 'webpack';
|
|
||||||
this.logger.debug(`current compile mode is ${this.compileMode}`);
|
|
||||||
}
|
|
||||||
analyzeBuildConfig() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.userConfig.devBuildConfig) {
|
|
||||||
let { name, path, env } = this.userConfig.devBuildConfig;
|
|
||||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
|
||||||
if (!existsSync(path)) {
|
|
||||||
this.logger.warn(`Cant't find dev build config. Path is ${path}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.logger.debug(`Find dev build config. Path is ${path}`);
|
|
||||||
let bc = yield loadModule(path);
|
|
||||||
if (bc == undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let finalBc = {};
|
|
||||||
if (typeof bc === 'function') {
|
|
||||||
finalBc = bc(env);
|
|
||||||
this.devBuildConfig = finalBc;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.devBuildConfig = bc;
|
|
||||||
if (this.userConfig.devBuildConfig.devProxy) {
|
|
||||||
this.devProxy = this.userConfig.devBuildConfig.devProxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.userConfig.buildConfig) {
|
|
||||||
switch (this.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
this.buildConfigPath.push({ name: 'default', path: './webpack.config.js' });
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
this.buildConfigPath.push({ name: 'default', path: './vite.config.js' });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.logger.warn(`Unknown compile mode ${this.compileMode}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.userConfig.buildConfig.forEach((userBuildConfig) => {
|
|
||||||
// if (typeof userBuildConfig === 'string') {
|
|
||||||
// const name = this.getConfigName(userBuildConfig);
|
|
||||||
// this.buildConfigPath.push({name, path: userBuildConfig});
|
|
||||||
// }
|
|
||||||
if (typeof userBuildConfig === 'object') {
|
|
||||||
// const name = userBuildConfig.name;
|
|
||||||
// const path = userBuildConfig.path;
|
|
||||||
this.buildConfigPath.push(userBuildConfig);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.buildConfigPath.forEach((config) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let { name, path } = config;
|
|
||||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
|
||||||
if (!existsSync(path)) {
|
|
||||||
this.logger.warn(`Cant't find build config. Path is ${path}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.logger.debug(`Find build config. Path is ${path}`);
|
|
||||||
let bc = yield loadModule(path);
|
|
||||||
if (bc == undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let finalBc = {};
|
|
||||||
if (typeof bc === 'function') {
|
|
||||||
finalBc = bc(config.env);
|
|
||||||
this.buildConfig.push({ name: name, config: finalBc });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.buildConfig.push({ name: name, config: bc });
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getConfigName(name) {
|
|
||||||
name = name.replace('webpack.', '');
|
|
||||||
name = name.replace('.js', '');
|
|
||||||
name = name.replace('.ts', '');
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
export declare enum PluginType {
|
|
||||||
preset = "preset",
|
|
||||||
plugin = "plugin"
|
|
||||||
}
|
|
||||||
export declare enum ServiceStage {
|
|
||||||
uninitialized = 0,
|
|
||||||
constructor = 1,
|
|
||||||
init = 2,
|
|
||||||
initPlugins = 3,
|
|
||||||
initHooks = 4,
|
|
||||||
pluginReady = 5,
|
|
||||||
getConfig = 6,
|
|
||||||
run = 7
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
export var PluginType;
|
|
||||||
(function (PluginType) {
|
|
||||||
PluginType["preset"] = "preset";
|
|
||||||
PluginType["plugin"] = "plugin";
|
|
||||||
})(PluginType || (PluginType = {}));
|
|
||||||
export var ServiceStage;
|
|
||||||
(function (ServiceStage) {
|
|
||||||
ServiceStage[ServiceStage["uninitialized"] = 0] = "uninitialized";
|
|
||||||
ServiceStage[ServiceStage["constructor"] = 1] = "constructor";
|
|
||||||
ServiceStage[ServiceStage["init"] = 2] = "init";
|
|
||||||
ServiceStage[ServiceStage["initPlugins"] = 3] = "initPlugins";
|
|
||||||
ServiceStage[ServiceStage["initHooks"] = 4] = "initHooks";
|
|
||||||
ServiceStage[ServiceStage["pluginReady"] = 5] = "pluginReady";
|
|
||||||
ServiceStage[ServiceStage["getConfig"] = 6] = "getConfig";
|
|
||||||
ServiceStage[ServiceStage["run"] = 7] = "run";
|
|
||||||
})(ServiceStage || (ServiceStage = {}));
|
|
|
@ -1,43 +0,0 @@
|
||||||
import PluginAPI, { IOpts } from './PluginAPI.js';
|
|
||||||
import { IHook, ICommand } from '../types/types.js';
|
|
||||||
import Hub from '../core/Hub';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
interface pluginManagerOpts {
|
|
||||||
cwd: string;
|
|
||||||
hub: Hub;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
export interface IPlugin {
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
path: string;
|
|
||||||
apply: Function;
|
|
||||||
}
|
|
||||||
export default class Plugin {
|
|
||||||
cwd: string;
|
|
||||||
builtInPlugins: string[];
|
|
||||||
userPlugins: string[];
|
|
||||||
commands: {
|
|
||||||
[name: string]: ICommand | string;
|
|
||||||
};
|
|
||||||
hooksByPluginPath: {
|
|
||||||
[id: string]: IHook[];
|
|
||||||
};
|
|
||||||
hooks: {
|
|
||||||
[key: string]: IHook[];
|
|
||||||
};
|
|
||||||
store: {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
hub: Hub;
|
|
||||||
logger: Logger;
|
|
||||||
registerFunction: Function[];
|
|
||||||
[key: string]: any;
|
|
||||||
constructor(opts: pluginManagerOpts);
|
|
||||||
getPluginPaths(builtInPlugins: string[], userPlugins: string[] | undefined): string[];
|
|
||||||
setStore(name: string, initialValue: any): void;
|
|
||||||
register(builtInPlugins: string[], userPlugins: string[] | undefined): Promise<void>;
|
|
||||||
createPluginAPI(opts: IOpts): PluginAPI;
|
|
||||||
initHook(): void;
|
|
||||||
}
|
|
||||||
export {};
|
|
|
@ -1,116 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import resolve from 'resolve';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import PluginAPI from './PluginAPI.js';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
export default class Plugin {
|
|
||||||
constructor(opts) {
|
|
||||||
this.builtInPlugins = [];
|
|
||||||
this.userPlugins = [];
|
|
||||||
this.commands = {};
|
|
||||||
this.hooksByPluginPath = {};
|
|
||||||
this.hooks = {};
|
|
||||||
this.store = {};
|
|
||||||
this.registerFunction = [];
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.hub = opts.hub;
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
getPluginPaths(builtInPlugins, userPlugins) {
|
|
||||||
const paths = [];
|
|
||||||
paths.push(...builtInPlugins);
|
|
||||||
if (userPlugins) {
|
|
||||||
paths.push(...userPlugins);
|
|
||||||
}
|
|
||||||
// 获取所有插件文件的绝对路径
|
|
||||||
const absPaths = paths.map(path => {
|
|
||||||
return resolve.sync(path, {
|
|
||||||
basedir: this.cwd,
|
|
||||||
extensions: ['.js', '.ts'],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return absPaths;
|
|
||||||
}
|
|
||||||
setStore(name, initialValue) {
|
|
||||||
const store = this.store;
|
|
||||||
if (this.store[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
store[name] = initialValue;
|
|
||||||
}
|
|
||||||
register(builtInPlugins, userPlugins) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const paths = this.getPluginPaths(builtInPlugins, userPlugins);
|
|
||||||
this.hub.pluginPaths = paths;
|
|
||||||
const objs = paths.map(path => {
|
|
||||||
const api = this.createPluginAPI({
|
|
||||||
path,
|
|
||||||
manager: this,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
api,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
for (const obj of objs) {
|
|
||||||
const module = yield loadModule(obj.path);
|
|
||||||
if (module) {
|
|
||||||
try {
|
|
||||||
module(obj.api);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
this.logger.error(chalk.red(err.message));
|
|
||||||
if (err.stack) {
|
|
||||||
this.logger.error(err.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// todo 给API换个名字
|
|
||||||
createPluginAPI(opts) {
|
|
||||||
const pluginAPI = new PluginAPI(opts);
|
|
||||||
// 为PluginAPI添加代理
|
|
||||||
// 除了PluginAPI自有的方法之外,为开发者提供更丰富的api
|
|
||||||
return new Proxy(pluginAPI, {
|
|
||||||
get: (target, prop) => {
|
|
||||||
if (['userConfig', 'devBuildConfig', 'buildConfig', 'compileMode', 'packageJson', 'cwd'].includes(prop)) {
|
|
||||||
return typeof this.hub[prop] === 'function'
|
|
||||||
? this.hub[prop].bind(this.hub)
|
|
||||||
: this.hub[prop];
|
|
||||||
}
|
|
||||||
if (['setStore', 'logger', 'commands'].includes(prop)) {
|
|
||||||
return typeof this[prop] === 'function'
|
|
||||||
? this[prop].bind(this)
|
|
||||||
: this[prop];
|
|
||||||
}
|
|
||||||
return target[prop];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
initHook() {
|
|
||||||
Object.keys(this.hooksByPluginPath).forEach(path => {
|
|
||||||
const hooks = this.hooksByPluginPath[path];
|
|
||||||
hooks.forEach(hook => {
|
|
||||||
const { name } = hook;
|
|
||||||
hook.pluginId = path;
|
|
||||||
if (!this.hooks[name]) {
|
|
||||||
this.hooks[name] = [];
|
|
||||||
}
|
|
||||||
this.hooks[name].push(hook);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import Plugin from './Plugin.js';
|
|
||||||
import { IHook, ICommand } from '../types/types.js';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
export interface IOpts {
|
|
||||||
path: string;
|
|
||||||
manager: Plugin;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
export default class PluginAPI {
|
|
||||||
path: string;
|
|
||||||
manager: Plugin;
|
|
||||||
logger: Logger;
|
|
||||||
[key: string]: any;
|
|
||||||
constructor(opts: IOpts);
|
|
||||||
register(hook: IHook): void;
|
|
||||||
registerCommand(command: ICommand): void;
|
|
||||||
registerHook(hook: IHook): void;
|
|
||||||
registerMethod(fn: Function): void;
|
|
||||||
applyHook(name: string, args?: any): Promise<any>;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
export default class PluginAPI {
|
|
||||||
constructor(opts) {
|
|
||||||
this.path = opts.path;
|
|
||||||
this.manager = opts.manager;
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
register(hook) {
|
|
||||||
if (!this.manager.hooksByPluginPath[this.path]) {
|
|
||||||
this.manager.hooksByPluginPath[this.path] = [];
|
|
||||||
}
|
|
||||||
this.manager.hooksByPluginPath[this.path].push(hook);
|
|
||||||
}
|
|
||||||
registerCommand(command) {
|
|
||||||
const { name } = command;
|
|
||||||
this.manager.commands[name] = command;
|
|
||||||
if (command.initialState) {
|
|
||||||
this.manager.setStore(name, command.initialState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
registerHook(hook) {
|
|
||||||
this.register(hook);
|
|
||||||
}
|
|
||||||
registerMethod(fn) {
|
|
||||||
this.manager.registerFunction.push(fn);
|
|
||||||
}
|
|
||||||
applyHook(name, args) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const hooks = this.manager.hooks[name] || [];
|
|
||||||
let config = undefined;
|
|
||||||
for (const hook of hooks) {
|
|
||||||
if (this.manager.store[name]) {
|
|
||||||
config = this.manager.store[name];
|
|
||||||
}
|
|
||||||
if (hook.fn) {
|
|
||||||
yield hook.fn(args, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.manager.store[name];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
/// <reference types="node" />
|
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
import type * as http from 'http';
|
|
||||||
import type * as express from 'express';
|
|
||||||
interface Request extends express.Request {
|
|
||||||
}
|
|
||||||
interface Response extends express.Response {
|
|
||||||
}
|
|
||||||
export interface IDep {
|
|
||||||
[name: string]: string;
|
|
||||||
}
|
|
||||||
export interface IPackage {
|
|
||||||
name?: string;
|
|
||||||
dependencies?: IDep;
|
|
||||||
devDependencies?: IDep;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
export interface IPlugin {
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
path: string;
|
|
||||||
apply: Function;
|
|
||||||
config?: IPluginConfig;
|
|
||||||
isPreset?: boolean;
|
|
||||||
}
|
|
||||||
export interface IPluginConfig {
|
|
||||||
default?: any;
|
|
||||||
onChange?: string | Function;
|
|
||||||
}
|
|
||||||
export interface IHook {
|
|
||||||
name: string;
|
|
||||||
fn?: {
|
|
||||||
(state: any, config: any): void;
|
|
||||||
};
|
|
||||||
pluginId?: string;
|
|
||||||
}
|
|
||||||
export interface ICommand {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
details?: string;
|
|
||||||
initialState?: any;
|
|
||||||
fn: {
|
|
||||||
(args: yargsParser.Arguments, config: any): void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface IConfig {
|
|
||||||
plugins?: string[];
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
interface applyHookConfig<T = any> {
|
|
||||||
name: string;
|
|
||||||
config?: T;
|
|
||||||
}
|
|
||||||
export interface API {
|
|
||||||
cwd: string;
|
|
||||||
logger: Logger;
|
|
||||||
userConfig: IConfig;
|
|
||||||
buildConfig: any;
|
|
||||||
devBuildConfig: any;
|
|
||||||
compileMode: string;
|
|
||||||
commands: string[];
|
|
||||||
packageJson: PackageJSON;
|
|
||||||
registerCommand: {
|
|
||||||
(command: ICommand): void;
|
|
||||||
};
|
|
||||||
registerHook: {
|
|
||||||
(hook: IHook): void;
|
|
||||||
};
|
|
||||||
registerMethod: {
|
|
||||||
(method: Function): void;
|
|
||||||
};
|
|
||||||
applyHook: {
|
|
||||||
(opts: applyHookConfig): void;
|
|
||||||
};
|
|
||||||
setStore: {
|
|
||||||
(name: string, initialState: any): void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface RemoteProxy {
|
|
||||||
target: string;
|
|
||||||
localPort?: number;
|
|
||||||
localStatic?: StaticFileMatcher[];
|
|
||||||
fowardingURL?: string[];
|
|
||||||
}
|
|
||||||
export interface StaticFileMatcher {
|
|
||||||
url: string;
|
|
||||||
local: string;
|
|
||||||
}
|
|
||||||
export interface UserConfig {
|
|
||||||
mock?: MockConfig;
|
|
||||||
proxy?: RemoteProxy;
|
|
||||||
plugins?: string[];
|
|
||||||
compileMode?: string;
|
|
||||||
buildConfig?: BuildConfig[];
|
|
||||||
devBuildConfig?: DevBuildConfig;
|
|
||||||
}
|
|
||||||
export interface MockConfig {
|
|
||||||
enableMock?: boolean;
|
|
||||||
mockPath?: string;
|
|
||||||
}
|
|
||||||
export interface DevBuildConfig {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
args?: object;
|
|
||||||
env?: object;
|
|
||||||
devProxy?: DevProxy;
|
|
||||||
}
|
|
||||||
export interface DevProxy {
|
|
||||||
target: string;
|
|
||||||
matcher: ((pathname: string, req: Request) => boolean);
|
|
||||||
onProxyRes: (proxyRes: http.IncomingMessage, req: Request, res: Response) => void;
|
|
||||||
}
|
|
||||||
export interface BuildConfig {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
args?: object;
|
|
||||||
env?: object;
|
|
||||||
}
|
|
||||||
export declare type ExportUserConfig = UserConfig | Promise<UserConfig>;
|
|
||||||
export declare function defineConfig(config: ExportUserConfig): ExportUserConfig;
|
|
||||||
export interface Arguments {
|
|
||||||
_: Array<string | number>;
|
|
||||||
'--'?: Array<string | number>;
|
|
||||||
[argName: string]: any;
|
|
||||||
}
|
|
||||||
export {};
|
|
|
@ -1,3 +0,0 @@
|
||||||
export function defineConfig(config) {
|
|
||||||
return config;
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
declare const buildConfig: (fileName: string, format?: 'esm' | 'cjs') => Promise<string>;
|
|
||||||
export default buildConfig;
|
|
|
@ -1,61 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import { build as esbuild } from 'esbuild';
|
|
||||||
const buildConfig = (fileName, format = 'esm') => __awaiter(void 0, void 0, void 0, function* () {
|
|
||||||
// 外部依赖不构建参与构建,减少执行时间
|
|
||||||
const pluginExternalDeps = {
|
|
||||||
name: 'plugin-external-deps',
|
|
||||||
setup(build) {
|
|
||||||
build.onResolve({ filter: /.*/ }, args => {
|
|
||||||
const id = args.path;
|
|
||||||
if (id[0] !== '.' && !path.isAbsolute(id)) {
|
|
||||||
return {
|
|
||||||
external: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// 将文件中的路径改成确定路径,避免执行时调用错误
|
|
||||||
const pluginReplaceImport = {
|
|
||||||
name: 'plugin-replace-import-meta',
|
|
||||||
setup(build) {
|
|
||||||
build.onLoad({ filter: /\.[jt]s$/ }, args => {
|
|
||||||
const contents = fs.readFileSync(args.path, 'utf8');
|
|
||||||
// 替换import路径
|
|
||||||
contents.replace(/\bimport\.meta\.url\b/g, JSON.stringify(`file://${args.path}`));
|
|
||||||
// 替换当前目录路径
|
|
||||||
contents.replace(/\b__dirname\b/g, JSON.stringify(path.dirname(args.path)));
|
|
||||||
// 替换当前文件路径
|
|
||||||
contents.replace(/\b__filename\b/g, JSON.stringify(args.path));
|
|
||||||
return {
|
|
||||||
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
|
|
||||||
contents: contents
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const result = yield esbuild({
|
|
||||||
entryPoints: [fileName],
|
|
||||||
outfile: 'out.js',
|
|
||||||
write: false,
|
|
||||||
platform: 'node',
|
|
||||||
bundle: true,
|
|
||||||
format,
|
|
||||||
metafile: true,
|
|
||||||
plugins: [pluginExternalDeps, pluginReplaceImport],
|
|
||||||
});
|
|
||||||
const { text } = result.outputFiles[0];
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
export default buildConfig;
|
|
|
@ -1 +0,0 @@
|
||||||
export default function dynamicImport(filePath: string): Promise<any>;
|
|
|
@ -1,16 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
export default function dynamicImport(filePath) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let importPath = filePath;
|
|
||||||
importPath = 'file:///' + importPath;
|
|
||||||
return yield import(importPath);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export default function initializeEnv(): void;
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { join } from 'path';
|
|
||||||
import { readFileSync, existsSync } from 'fs';
|
|
||||||
import { parse } from 'dotenv';
|
|
||||||
export default function initializeEnv() {
|
|
||||||
const envPath = join(process.cwd(), '.env');
|
|
||||||
const localEnvPath = join(process.cwd(), '.local.env');
|
|
||||||
loadEnv(envPath);
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
loadEnv(localEnvPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function loadEnv(envPath) {
|
|
||||||
if (existsSync(envPath)) {
|
|
||||||
const parsed = parse(readFileSync(envPath, 'utf-8')) || {};
|
|
||||||
Object.keys(parsed).forEach(key => {
|
|
||||||
process.env[key] = parsed[key];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export declare function loadModule<T>(filePath: string): Promise<T | undefined>;
|
|
|
@ -1,59 +0,0 @@
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
import { join, isAbsolute } from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import buildConfig from './build.js';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import dynamicImport from './dynamicImport.js';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
export function loadModule(filePath) {
|
|
||||||
var _a;
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
filePath = isAbsolute(filePath) ? filePath : join(process.cwd(), filePath);
|
|
||||||
const isTsFile = filePath.endsWith('ts');
|
|
||||||
const isJsFile = filePath.endsWith('js');
|
|
||||||
let content;
|
|
||||||
// js文件,可以直接通过import引用
|
|
||||||
if (isJsFile) {
|
|
||||||
content = (_a = (yield dynamicImport(filePath))) === null || _a === void 0 ? void 0 : _a.default;
|
|
||||||
}
|
|
||||||
// 如果是ts文件,需要先转为js文件,再读取
|
|
||||||
if (isTsFile) {
|
|
||||||
const code = yield buildConfig(filePath, 'esm');
|
|
||||||
content = yield getTypescriptModule(code, filePath);
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function getTypescriptModule(code, filePath, isEsm = true) {
|
|
||||||
var _a, _b;
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const tempFile = `${filePath}.${isEsm ? 'm' : 'c'}js`;
|
|
||||||
let content = null;
|
|
||||||
// todo 臨時文件管理
|
|
||||||
fs.writeFileSync(tempFile, code);
|
|
||||||
delete require.cache[require.resolve(tempFile)];
|
|
||||||
try {
|
|
||||||
const raw = isEsm ? yield dynamicImport(tempFile) : require(tempFile);
|
|
||||||
content = (_a = raw === null || raw === void 0 ? void 0 : raw.default) !== null && _a !== void 0 ? _a : raw;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
fs.unlinkSync(tempFile);
|
|
||||||
if (err instanceof Error) {
|
|
||||||
err.message = err.message.replace(tempFile, filePath);
|
|
||||||
err.stack = (_b = err.stack) === null || _b === void 0 ? void 0 : _b.replace(tempFile, filePath);
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
// todo 刪除失敗加日誌
|
|
||||||
fs.unlinkSync(tempFile);
|
|
||||||
return content;
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
export declare const loadPkg: (path: string) => PackageJSON;
|
|
|
@ -1,6 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
export const loadPkg = (path) => {
|
|
||||||
const packageJson = fs.readFileSync(path, 'utf8');
|
|
||||||
const packageData = JSON.parse(packageJson);
|
|
||||||
return packageData;
|
|
||||||
};
|
|
|
@ -1,14 +0,0 @@
|
||||||
export declare enum LogLevel {
|
|
||||||
DEBUG = 0,
|
|
||||||
INFO = 1,
|
|
||||||
WARN = 2,
|
|
||||||
ERROR = 3
|
|
||||||
}
|
|
||||||
export declare class Logger {
|
|
||||||
private readonly level;
|
|
||||||
constructor(level?: LogLevel);
|
|
||||||
debug(message: string): void;
|
|
||||||
info(message: string): void;
|
|
||||||
warn(message: string): void;
|
|
||||||
error(message: string, error?: Error): void;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
export var LogLevel;
|
|
||||||
(function (LogLevel) {
|
|
||||||
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
||||||
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
||||||
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
||||||
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
||||||
})(LogLevel || (LogLevel = {}));
|
|
||||||
export class Logger {
|
|
||||||
constructor(level) {
|
|
||||||
if (level !== undefined) {
|
|
||||||
this.level = level;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.level = LogLevel.INFO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug(message) {
|
|
||||||
if (this.level <= LogLevel.DEBUG) {
|
|
||||||
console.debug(`[DEBUG] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info(message) {
|
|
||||||
if (this.level <= LogLevel.INFO) {
|
|
||||||
console.info(`[INFO] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warn(message) {
|
|
||||||
if (this.level <= LogLevel.WARN) {
|
|
||||||
console.warn(`[WARN] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error(message, error) {
|
|
||||||
if (this.level <= LogLevel.ERROR) {
|
|
||||||
console.error(`[ERROR] ${message}`, error || '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
declare const _default: (app: {
|
|
||||||
_router: {
|
|
||||||
stack: any[];
|
|
||||||
};
|
|
||||||
}) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,102 +0,0 @@
|
||||||
import chokidar from 'chokidar';
|
|
||||||
import bodyParser from 'body-parser';
|
|
||||||
import { globSync } from 'glob';
|
|
||||||
import { join } from 'path';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const mockDir = join(process.cwd(), 'mock');
|
|
||||||
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
|
|
||||||
const jsonParser = bodyParser.json();
|
|
||||||
const urlencodedParser = bodyParser.urlencoded({
|
|
||||||
extended: true,
|
|
||||||
});
|
|
||||||
// 读取 mock 文件夹下的 js 文件
|
|
||||||
function getMocksFile() {
|
|
||||||
const mockFiles = globSync('**/*.js', {
|
|
||||||
cwd: mockDir,
|
|
||||||
});
|
|
||||||
let ret = mockFiles.reduce((mocks, mockFile) => {
|
|
||||||
if (!mockFile.startsWith('_')) {
|
|
||||||
mocks = Object.assign(Object.assign({}, mocks), require(join(mockDir, mockFile)));
|
|
||||||
console.log('mockFile', require(join(mockDir, mockFile)));
|
|
||||||
}
|
|
||||||
return mocks;
|
|
||||||
}, {});
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
function generateRoutes(app) {
|
|
||||||
let mockStartIndex = app._router.stack.length, mocks = {};
|
|
||||||
try {
|
|
||||||
mocks = getMocksFile();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('Generate mock routes error', error);
|
|
||||||
}
|
|
||||||
for (const mockItem in mocks) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(mocks, mockItem)) {
|
|
||||||
try {
|
|
||||||
const trimMockItemArr = mockItem
|
|
||||||
.replace(/(^\s*)|(\s*$)/g, '')
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.split(' ');
|
|
||||||
const respond = mocks[mockItem];
|
|
||||||
let mockType = 'get', mockUrl;
|
|
||||||
if (trimMockItemArr.length === 1) {
|
|
||||||
mockUrl = trimMockItemArr[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[mockType, mockUrl] = trimMockItemArr;
|
|
||||||
}
|
|
||||||
const mockTypeLowerCase = mockType.toLowerCase();
|
|
||||||
if (!HTTP_METHODS.includes(mockTypeLowerCase)) {
|
|
||||||
throw new Error(`Invalid HTTP request method ${mockType} for path ${mockUrl}`);
|
|
||||||
}
|
|
||||||
app[mockTypeLowerCase](mockUrl, [jsonParser, urlencodedParser], respond instanceof Function
|
|
||||||
? respond
|
|
||||||
: (_req, res) => {
|
|
||||||
res.send(respond);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
mockRoutesLength: app._router.stack.length - mockStartIndex,
|
|
||||||
mockStartIndex: mockStartIndex,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 清除 mock 文件下的 require 缓存
|
|
||||||
function cleanRequireCache() {
|
|
||||||
Object.keys(require.cache).forEach(key => {
|
|
||||||
if (key.includes(mockDir)) {
|
|
||||||
delete require.cache[require.resolve(key)];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export default (app) => {
|
|
||||||
const mockRoutes = generateRoutes(app);
|
|
||||||
let { mockRoutesLength } = mockRoutes;
|
|
||||||
let { mockStartIndex } = mockRoutes;
|
|
||||||
// 监听 mock 文件夹下文件变化
|
|
||||||
chokidar
|
|
||||||
.watch(mockDir, {
|
|
||||||
ignoreInitial: true,
|
|
||||||
})
|
|
||||||
.on('all', (event, _path) => {
|
|
||||||
if (event === 'change' || event === 'add') {
|
|
||||||
try {
|
|
||||||
// 删除中间件映射
|
|
||||||
app._router.stack.splice(mockStartIndex, mockRoutesLength);
|
|
||||||
cleanRequireCache();
|
|
||||||
const mockRoutes = generateRoutes(app);
|
|
||||||
mockRoutesLength = mockRoutes.mockRoutesLength;
|
|
||||||
mockStartIndex = mockRoutes.mockStartIndex;
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
export default function readDirectory(directoryPath: string): string[];
|
|
|
@ -1,26 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
export default function readDirectory(directoryPath) {
|
|
||||||
const filesArray = [];
|
|
||||||
const traverseDirectory = (directoryPath) => {
|
|
||||||
const files = fs.readdirSync(directoryPath);
|
|
||||||
for (const file of files) {
|
|
||||||
const filePath = path.join(directoryPath, file);
|
|
||||||
if (fs.statSync(filePath).isDirectory()) {
|
|
||||||
// 如果是目录,则递归读取该目录下的所有文件
|
|
||||||
traverseDirectory(filePath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (filePath.startsWith('.')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 如果是文件,则将其全路径添加到数组中
|
|
||||||
if (filePath.endsWith('.js')) {
|
|
||||||
filesArray.push(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
traverseDirectory(directoryPath);
|
|
||||||
return filesArray;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import { API } from '../types/types';
|
|
||||||
declare const _default: (app: any, api: API) => void;
|
|
||||||
export default _default;
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
||||||
export default (app, api) => {
|
|
||||||
const { devProxy } = api.userConfig.devBuildConfig;
|
|
||||||
app.use(createProxyMiddleware(devProxy.matcher, {
|
|
||||||
target: devProxy.target,
|
|
||||||
secure: false,
|
|
||||||
changeOrigin: true,
|
|
||||||
ws: false,
|
|
||||||
onProxyRes: devProxy.onProxyRes
|
|
||||||
}));
|
|
||||||
};
|
|
|
@ -1,4 +0,0 @@
|
||||||
export declare function parseRequireDeps(filePath: string): string[];
|
|
||||||
export declare const isWindows: boolean;
|
|
||||||
export declare function cleanRequireCache(cacheKey: string): void;
|
|
||||||
export declare function copyFile(targetPath: string, sourcePath: string): void;
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { dirname } from 'path';
|
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
|
||||||
import resolve from 'resolve';
|
|
||||||
// @ts-ignore
|
|
||||||
import crequire from 'crequire';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
function parse(filePath) {
|
|
||||||
const content = readFileSync(filePath, 'utf-8');
|
|
||||||
return crequire(content)
|
|
||||||
.map(o => o.path)
|
|
||||||
.filter(path => path.charAt(0) === '.')
|
|
||||||
.map(path => resolve.sync(path, {
|
|
||||||
basedir: dirname(filePath),
|
|
||||||
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
export function parseRequireDeps(filePath) {
|
|
||||||
const paths = [filePath];
|
|
||||||
const ret = [filePath];
|
|
||||||
while (paths.length) {
|
|
||||||
const extraPaths = parse(paths.shift()).filter(path => !ret.includes(path));
|
|
||||||
if (extraPaths.length) {
|
|
||||||
paths.push(...extraPaths);
|
|
||||||
ret.push(...extraPaths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
export const isWindows = typeof process !== 'undefined' && process.platform === 'win32';
|
|
||||||
export function cleanRequireCache(cacheKey) {
|
|
||||||
const cachePath = isWindows ? cacheKey.replace(/\//g, '\\') : cacheKey;
|
|
||||||
if (require.cache[cachePath]) {
|
|
||||||
const cacheParent = require.cache[cachePath].parent;
|
|
||||||
let i = (cacheParent === null || cacheParent === void 0 ? void 0 : cacheParent.children.length) || 0;
|
|
||||||
while (i--) {
|
|
||||||
if (cacheParent.children[i].id === cachePath) {
|
|
||||||
cacheParent.children.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete require.cache[cachePath];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export function copyFile(targetPath, sourcePath) {
|
|
||||||
try {
|
|
||||||
const fileContent = readFileSync(sourcePath);
|
|
||||||
writeFileSync(targetPath, fileContent);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('Copy file failed.', error);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
{
|
|
||||||
"name": "inula-cli",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"inula-cli": "bin/start.js"
|
|
||||||
},
|
|
||||||
"files":[
|
|
||||||
"bin",
|
|
||||||
"lib",
|
|
||||||
"template",
|
|
||||||
"package.json",
|
|
||||||
"tsconfig.json"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"type": "module",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/body-parser": "^1.19.2",
|
|
||||||
"@types/chalk": "^2.2.0",
|
|
||||||
"@types/express": "^4.17.17",
|
|
||||||
"@types/inquirer": "^9.0.3",
|
|
||||||
"@types/lodash": "^4.14.194",
|
|
||||||
"@types/node": "^18.16.1",
|
|
||||||
"@types/resolve": "^1.20.2",
|
|
||||||
"@types/webpack": "^5.28.1",
|
|
||||||
"@types/yargs-parser": "^21.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.22.5",
|
|
||||||
"@types/http-proxy-middleware": "^1.0.0",
|
|
||||||
"@types/jest": "^29.5.2",
|
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"chalk": "^4.0.0",
|
|
||||||
"chokidar": "^3.5.3",
|
|
||||||
"crequire": "^1.8.1",
|
|
||||||
"deepmerge": "^4.3.1",
|
|
||||||
"dotenv": "^16.0.3",
|
|
||||||
"esbuild": "^0.18.17",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"glob": "^10.3.3",
|
|
||||||
"http-proxy-middleware": "^2.0.6",
|
|
||||||
"inquirer": "^9.2.7",
|
|
||||||
"install": "^0.13.0",
|
|
||||||
"jest": "^29.5.0",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"resolve": "^1.22.3",
|
|
||||||
"ts-jest": "^29.1.0",
|
|
||||||
"ts-node": "^10.9.1",
|
|
||||||
"vite": "4.4.2",
|
|
||||||
"webpack": "^5.0.0",
|
|
||||||
"webpack-dev-server": "^4.13.3",
|
|
||||||
"yargs-parser": "^21.1.1"
|
|
||||||
},
|
|
||||||
"types": "lib/types/types.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"types": "./lib/types/types.d.ts",
|
|
||||||
"import": "./lib/types/types.js",
|
|
||||||
"require": "./lib/types/types.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
import webpack from 'webpack';
|
|
||||||
import { build } from 'vite';
|
|
||||||
|
|
||||||
export default (api: any) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'build',
|
|
||||||
description: 'build application for production',
|
|
||||||
initialState: api.buildConfig,
|
|
||||||
fn: async function (args: any, state: any) {
|
|
||||||
switch (api.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeCompile', args: state });
|
|
||||||
state.forEach((s: any) => {
|
|
||||||
webpack(s.config, (err: any, stats: any) => {
|
|
||||||
// api.applyHook({ name: 'afterCompile' });
|
|
||||||
if (err || stats.hasErrors()) {
|
|
||||||
api.logger.error(`Build failed.err: ${err}, stats:${stats}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
api.logger.error(`Build failed. Can't find build config.`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeCompile' });
|
|
||||||
build(state);
|
|
||||||
} else {
|
|
||||||
api.logger.error(`Build failed. Can't find build config.`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,65 +0,0 @@
|
||||||
import webpack from 'webpack';
|
|
||||||
import WebpackDevServer from 'webpack-dev-server';
|
|
||||||
import { createServer } from 'vite';
|
|
||||||
import { API } from '../../../types/types';
|
|
||||||
import setupProxy from '../../../utils/setupProxy.js';
|
|
||||||
|
|
||||||
|
|
||||||
export default (api: API) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'dev',
|
|
||||||
description: 'build application for development',
|
|
||||||
initialState: api.devBuildConfig,
|
|
||||||
fn: async function (args: any, state: any) {
|
|
||||||
api.applyHook({ name: 'beforeDevConfig' });
|
|
||||||
switch (api.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
if (state) {
|
|
||||||
api.applyHook({ name: 'beforeDevCompile', config: state });
|
|
||||||
const compiler = webpack(state);
|
|
||||||
|
|
||||||
const devServerOptions: WebpackDevServer.Configuration = {
|
|
||||||
client: {
|
|
||||||
overlay: false,
|
|
||||||
},
|
|
||||||
host: 'localhost',
|
|
||||||
port: '8888',
|
|
||||||
open: true,
|
|
||||||
historyApiFallback: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (api.userConfig.devBuildConfig.devProxy) {
|
|
||||||
devServerOptions.onBeforeSetupMiddleware = (devServer: WebpackDevServer) => {
|
|
||||||
setupProxy(devServer.app, api)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
api.applyHook({
|
|
||||||
name: 'beforeStartDevServer',
|
|
||||||
config: { compiler: compiler, devServerOptions: devServerOptions },
|
|
||||||
});
|
|
||||||
const server = new WebpackDevServer(compiler, devServerOptions);
|
|
||||||
server.startCallback((err: any) => {
|
|
||||||
api.applyHook({ name: 'afterStartDevServer' });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
api.logger.error("Can't find config");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
if (state) {
|
|
||||||
await createServer(state)
|
|
||||||
.then(server => {
|
|
||||||
return server.listen();
|
|
||||||
})
|
|
||||||
.then(server => {
|
|
||||||
server.printUrls();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
api.logger.error("Can't find config");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,104 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import inquirer from 'inquirer';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { copyFile } from '../../../utils/util.js';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
export default (api: API) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'generate',
|
|
||||||
description: 'generate template',
|
|
||||||
fn: async (args: yargsParser.Arguments) => {
|
|
||||||
if (args._[0] === 'g') {
|
|
||||||
args._.shift();
|
|
||||||
}
|
|
||||||
if (args._.length === 0) {
|
|
||||||
api.logger.warn("Can't find any generate options.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (args._[0]) {
|
|
||||||
case 'jest':
|
|
||||||
args._.shift();
|
|
||||||
const isESM = api.packageJson['type'] === 'module';
|
|
||||||
await generateJest(args, api.cwd, isESM);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateJest = async (args: yargsParser.Arguments, cwd: string, isESM: boolean) => {
|
|
||||||
let isTs: boolean = false;
|
|
||||||
if (args['ts']) {
|
|
||||||
isTs = true;
|
|
||||||
} else {
|
|
||||||
const answers = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
name: 'useTs',
|
|
||||||
message: 'Do you want to use TypeScript',
|
|
||||||
type: 'confirm',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
isTs = answers['useTs'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkJestConfigExist(cwd)) {
|
|
||||||
console.log('The jest config is exist.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const testRootPath = path.join(cwd, 'test');
|
|
||||||
|
|
||||||
if (!fs.existsSync(testRootPath)) {
|
|
||||||
fs.mkdirSync(testRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
let templateDir = path.resolve(__dirname, '../../../../template/test');
|
|
||||||
|
|
||||||
// 如果是TS, 拷贝ts
|
|
||||||
if (isTs) {
|
|
||||||
templateDir = path.join(templateDir, 'ts');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拷贝mjs
|
|
||||||
if (!isTs && isESM) {
|
|
||||||
templateDir = path.join(templateDir, 'mjs');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拷贝cjs
|
|
||||||
if (!isTs && !isESM) {
|
|
||||||
templateDir = path.join(templateDir, 'cjs');
|
|
||||||
copyTestTemplate(cwd, testRootPath, templateDir);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function checkJestConfigExist(cwd: string): boolean {
|
|
||||||
const items = fs.readdirSync(cwd);
|
|
||||||
for (const item of items) {
|
|
||||||
const itemPath = path.resolve(cwd, item);
|
|
||||||
const states = fs.statSync(itemPath);
|
|
||||||
if (states.isFile() && item.startsWith('jest.config')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyTestTemplate(cwd: string, testRootPath: string, templateDir: string) {
|
|
||||||
const items = fs.readdirSync(templateDir);
|
|
||||||
for (const item of items) {
|
|
||||||
const itemPath = path.resolve(templateDir, item);
|
|
||||||
if (item.startsWith('jest.config')) {
|
|
||||||
copyFile(path.join(cwd, item), itemPath);
|
|
||||||
} else {
|
|
||||||
copyFile(path.join(testRootPath, item), itemPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import chalk from 'chalk';
|
|
||||||
import lodash from 'lodash';
|
|
||||||
|
|
||||||
function getDescriptions(commands: any) {
|
|
||||||
return Object.keys(commands)
|
|
||||||
.filter(name => typeof commands[name] !== 'string')
|
|
||||||
.map(name => {
|
|
||||||
return getDescription(commands[name]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDescription(command: any) {
|
|
||||||
return ` ${chalk.green(lodash.padEnd(command.name, 10))}${command.description || ''}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function padLeft(str: string) {
|
|
||||||
return str
|
|
||||||
.split('\n')
|
|
||||||
.map((line: string) => ` ${line}`)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (api: any) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'help',
|
|
||||||
description: 'show command helps',
|
|
||||||
|
|
||||||
fn: (args: any, config: any) => {
|
|
||||||
console.log(`
|
|
||||||
Usage: inula-cli <command> [options]
|
|
||||||
|
|
||||||
${getDescriptions(api.commands).join('\n')}
|
|
||||||
`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { createRequire } from 'module';
|
|
||||||
import mockServer from '../../../utils/mockServer.js';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
export default (api: any) => {
|
|
||||||
api.registerHook({
|
|
||||||
name: 'beforeStartDevServer',
|
|
||||||
fn: async (state: any) => {
|
|
||||||
const { compiler, devServerOptions } = state;
|
|
||||||
devServerOptions.setupMiddlewares = (middlewares: any, devServer: { app: any }) => {
|
|
||||||
mockServer(devServer.app);
|
|
||||||
return middlewares;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
import express from 'express';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
export default (api: any) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'proxy',
|
|
||||||
description: 'remote proxy',
|
|
||||||
initialState: api.userConfig.remoteProxy,
|
|
||||||
fn: async function (args: any, state: any) {
|
|
||||||
if (!state) {
|
|
||||||
api.logger.error(`Invalid proxy config!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const app = express();
|
|
||||||
const proxyConfig = state;
|
|
||||||
const staticList = proxyConfig.localStatic;
|
|
||||||
staticList.forEach(function (value: { url: any; local: string }) {
|
|
||||||
app.use(value.url, express.static(value.local));
|
|
||||||
});
|
|
||||||
const remoteProxy = createProxyMiddleware(proxyConfig.fowardingURL, {
|
|
||||||
target: proxyConfig.target,
|
|
||||||
secure: false,
|
|
||||||
autoRewrite: true,
|
|
||||||
protocolRewrite: 'http',
|
|
||||||
ws: true,
|
|
||||||
hostRewrite: '',
|
|
||||||
preserveHeaderKeyCase: true,
|
|
||||||
proxyTimeout: 5 * 60 * 60 * 1000,
|
|
||||||
timeout: 5 * 60 * 60 * 1000,
|
|
||||||
onError: handleProxyError,
|
|
||||||
});
|
|
||||||
function handleProxyError(err: any) {
|
|
||||||
api.logger.error('Local proxy error. Error is ', err);
|
|
||||||
}
|
|
||||||
app.use(remoteProxy);
|
|
||||||
|
|
||||||
app.listen(proxyConfig.localPort, () => {
|
|
||||||
api.logger.info(`Start proxy client on http://localhost:${proxyConfig.localPort}`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { API } from '../../../types/types';
|
|
||||||
import jest from 'jest';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
|
|
||||||
export default (api: API) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'jest',
|
|
||||||
description: 'run jest test',
|
|
||||||
fn: async (args: yargsParser.Arguments, config: any) => {
|
|
||||||
await jest.run();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,31 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { API } from '../../../types/types';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
interface PackageJson {
|
|
||||||
version: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pkgPath = path.resolve(__dirname, '../../../../package.json');
|
|
||||||
|
|
||||||
// 读取 package.json 文件
|
|
||||||
const packageJson = fs.readFileSync(pkgPath, 'utf8');
|
|
||||||
|
|
||||||
// 解析 JSON 格式的数据
|
|
||||||
const packageData: PackageJson = JSON.parse(packageJson);
|
|
||||||
|
|
||||||
// 获取版本号
|
|
||||||
const version = packageData.version;
|
|
||||||
|
|
||||||
export default (api: API) => {
|
|
||||||
api.registerCommand({
|
|
||||||
name: 'version',
|
|
||||||
description: 'show inula-cli version',
|
|
||||||
fn: () => {
|
|
||||||
api.logger.info(`Inula-cli version is ${version}.`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,73 +0,0 @@
|
||||||
import chalk from 'chalk';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import Hub from '../core/Hub.js';
|
|
||||||
import initializeEnv from '../utils/initializeEnv.js';
|
|
||||||
import { Logger, LogLevel } from '../utils/logger.js';
|
|
||||||
|
|
||||||
export default async function run() {
|
|
||||||
const args: yargsParser.Arguments = yargsParser(process.argv.slice(2));
|
|
||||||
const alias: Record<string, string> = {
|
|
||||||
h: 'help',
|
|
||||||
v: 'version',
|
|
||||||
g: 'generate',
|
|
||||||
};
|
|
||||||
let command: string | number | undefined = args._[0];
|
|
||||||
|
|
||||||
if (!command) {
|
|
||||||
if (args['v'] || args['version']) {
|
|
||||||
command = 'v';
|
|
||||||
}
|
|
||||||
if (args['h'] || args['help']) {
|
|
||||||
command = 'h';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const aliasCommand: string | undefined = alias[command];
|
|
||||||
|
|
||||||
if (aliasCommand) {
|
|
||||||
command = aliasCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeEnv();
|
|
||||||
|
|
||||||
if (command === 'version' || command === 'help') {
|
|
||||||
process.env.INNER_COMMAND = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
case 'build':
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
break;
|
|
||||||
case 'dev':
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let enableDebug: boolean = false;
|
|
||||||
|
|
||||||
if (process.env.DEBUG === "true") {
|
|
||||||
enableDebug = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const logger: Logger = new Logger(enableDebug ? LogLevel.DEBUG : LogLevel.INFO);
|
|
||||||
|
|
||||||
try {
|
|
||||||
new Hub({
|
|
||||||
logger: logger,
|
|
||||||
}).run({
|
|
||||||
command,
|
|
||||||
args,
|
|
||||||
});
|
|
||||||
} catch (err: unknown) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
logger.error(chalk.red(err.message));
|
|
||||||
if (err.stack) {
|
|
||||||
logger.error(err.stack);
|
|
||||||
}
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { extname, join } from 'path';
|
|
||||||
import { parseRequireDeps, cleanRequireCache } from '../utils/util.js';
|
|
||||||
import deepmerge from 'deepmerge';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
import { UserConfig } from '../types/types.js';
|
|
||||||
|
|
||||||
interface ConfigOpts {
|
|
||||||
cwd: string;
|
|
||||||
isLocal?: boolean;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG_FILES = ['.inula.ts', '.inula.js'];
|
|
||||||
|
|
||||||
export default class Config {
|
|
||||||
cwd: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
configFile?: string | null;
|
|
||||||
logger: Logger;
|
|
||||||
|
|
||||||
constructor(opts: ConfigOpts) {
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.isLocal = opts.isLocal || process.env.NODE_ENV === 'development';
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUserConfig(): Promise<UserConfig> {
|
|
||||||
const configFile: string | null = this.getConfigFile();
|
|
||||||
if (configFile === null) {
|
|
||||||
this.logger.debug(`Can't find .inula.ts or .inula.js in ${this.cwd}`);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.configFile = configFile;
|
|
||||||
|
|
||||||
if (configFile) {
|
|
||||||
let envConfigFile: string | undefined = undefined;
|
|
||||||
if (process.env.RUNNING_MODE) {
|
|
||||||
envConfigFile = this.addModePath(configFile, process.env.RUNNING_MODE);
|
|
||||||
}
|
|
||||||
// 配置文件的来源
|
|
||||||
// 1、默认的configFile 如.inula.ts
|
|
||||||
// 2、带环境变量的configFile 如.inula.cloud.ts
|
|
||||||
// 3、dev模式 包含local 如.inula.local.ts
|
|
||||||
const files: string[] = [configFile];
|
|
||||||
if (envConfigFile && existsSync(envConfigFile)) {
|
|
||||||
files.push(envConfigFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isLocal) {
|
|
||||||
const localConfigFile = this.addModePath(configFile, 'local');
|
|
||||||
if (existsSync(localConfigFile)) {
|
|
||||||
files.push(localConfigFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug(`Find user config files ${files}`);
|
|
||||||
|
|
||||||
// 依次加载配置文件中的依赖并刷新require中的缓存
|
|
||||||
const requireDeps = files.reduce((deps: string[], file) => {
|
|
||||||
deps = deps.concat(parseRequireDeps(file));
|
|
||||||
return deps;
|
|
||||||
}, []);
|
|
||||||
requireDeps.forEach(cleanRequireCache);
|
|
||||||
|
|
||||||
const configs = await this.requireConfigs(files);
|
|
||||||
return this.mergeConfig(...configs);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfigFile(): string | null {
|
|
||||||
const configFileList: string[] = DEFAULT_CONFIG_FILES.map(f => join(this.cwd, f));
|
|
||||||
for (let configFile of configFileList) {
|
|
||||||
if (existsSync(configFile)) {
|
|
||||||
return configFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
addModePath(file: string, mode: string) {
|
|
||||||
const ext = extname(file);
|
|
||||||
return file.replace(new RegExp(`${ext}$`), `.${mode}${ext}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async requireConfigs(configFiles: string[]) {
|
|
||||||
const configs: UserConfig[] = [];
|
|
||||||
for (const file in configFiles) {
|
|
||||||
const content: UserConfig | undefined = await loadModule<UserConfig>(configFiles[file]);
|
|
||||||
if (content) {
|
|
||||||
configs.push(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configs;
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeConfig(...configs: UserConfig[]) {
|
|
||||||
let ret: UserConfig = {};
|
|
||||||
for (const config of configs) {
|
|
||||||
ret = deepmerge<UserConfig>(ret, config);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,220 +0,0 @@
|
||||||
import { join, isAbsolute } from 'path';
|
|
||||||
import Config from '../config/Config.js';
|
|
||||||
import { BuildConfig, DevBuildConfig, DevProxy, ICommand, UserConfig } from '../types/types.js';
|
|
||||||
import { ServiceStage } from '../enum/enum.js';
|
|
||||||
import Plugin from '../plugin/Plugin.js';
|
|
||||||
import { appendFile, existsSync } from 'fs';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { Logger, LogLevel } from '../utils/logger.js';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
import readDirectory from '../utils/readDirectory.js';
|
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
import { loadPkg } from '../utils/loadPkg.js';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
interface HubOpts {
|
|
||||||
cwd?: string;
|
|
||||||
logger?: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Hub {
|
|
||||||
args: any;
|
|
||||||
cwd: string;
|
|
||||||
env: string | undefined;
|
|
||||||
configManager: Config;
|
|
||||||
userConfig: UserConfig = {};
|
|
||||||
packageJson: PackageJSON;
|
|
||||||
stage: ServiceStage = ServiceStage.uninitialized;
|
|
||||||
buildConfig: {name:string, config: object}[] = [];
|
|
||||||
pluginManager: Plugin;
|
|
||||||
buildConfigPath: BuildConfig[] = [];
|
|
||||||
devBuildConfig: object = {};
|
|
||||||
compileMode: string = '';
|
|
||||||
builtInPlugins: string[] = [];
|
|
||||||
pluginPaths: string[] = [];
|
|
||||||
devProxy: DevProxy | null = null;
|
|
||||||
logger: Logger;
|
|
||||||
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
constructor(opts: HubOpts) {
|
|
||||||
this.setStage(ServiceStage.constructor);
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.env = process.env.NODE_ENV;
|
|
||||||
|
|
||||||
if (!opts.logger) {
|
|
||||||
this.logger = new Logger(LogLevel.INFO);
|
|
||||||
} else {
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.packageJson = loadPkg(path.join(this.cwd, './package.json'));
|
|
||||||
|
|
||||||
this.configManager = new Config({
|
|
||||||
cwd: this.cwd,
|
|
||||||
isLocal: this.env === 'development',
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pluginManager = new Plugin({
|
|
||||||
cwd: this.cwd,
|
|
||||||
hub: this,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setStage(stage: ServiceStage) {
|
|
||||||
this.stage = stage;
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
this.setStage(ServiceStage.init);
|
|
||||||
|
|
||||||
// 获取用户配置
|
|
||||||
this.userConfig = await this.configManager.getUserConfig();
|
|
||||||
|
|
||||||
// 设置编译模式
|
|
||||||
this.setCompileMode()
|
|
||||||
|
|
||||||
// 获取编译配置
|
|
||||||
await this.analyzeBuildConfig();
|
|
||||||
|
|
||||||
this.setStage(ServiceStage.initPlugins);
|
|
||||||
this.builtInPlugins = this.getBuiltInPlugins();
|
|
||||||
await this.pluginManager.register(this.builtInPlugins, this.userConfig.plugins);
|
|
||||||
|
|
||||||
this.setStage(ServiceStage.initHooks);
|
|
||||||
this.pluginManager.initHook();
|
|
||||||
}
|
|
||||||
|
|
||||||
getBuiltInPlugins(): string[] {
|
|
||||||
return readDirectory(path.resolve(__dirname, '../builtInPlugins'));
|
|
||||||
}
|
|
||||||
|
|
||||||
async run({ command, args }: { command: string | number; args: yargsParser.Arguments }) {
|
|
||||||
args._ = args._ || [];
|
|
||||||
if (args._[0] === command) {
|
|
||||||
args._.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.args = args;
|
|
||||||
|
|
||||||
await this.init();
|
|
||||||
|
|
||||||
this.setStage(ServiceStage.run);
|
|
||||||
return this.runCommand({ command, args });
|
|
||||||
}
|
|
||||||
|
|
||||||
async runCommand({ command, args }: { command: string | number; args: yargsParser.Arguments }) {
|
|
||||||
this.logger.debug(`run command ${command}`);
|
|
||||||
|
|
||||||
const commands =
|
|
||||||
typeof this.pluginManager.commands[command] === 'string'
|
|
||||||
? this.pluginManager.commands[this.pluginManager.commands[command] as string]
|
|
||||||
: this.pluginManager.commands[command];
|
|
||||||
|
|
||||||
if (commands === undefined) {
|
|
||||||
this.logger.error(`Invalid command ${command}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const { fn } = commands as ICommand;
|
|
||||||
|
|
||||||
return fn(args, this.pluginManager.store[command]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setCompileMode() {
|
|
||||||
this.compileMode = this.userConfig.compileMode || 'webpack';
|
|
||||||
this.logger.debug(`current compile mode is ${this.compileMode}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async analyzeBuildConfig() {
|
|
||||||
if (this.userConfig.devBuildConfig) {
|
|
||||||
let { name, path, env } = this.userConfig.devBuildConfig;
|
|
||||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
|
||||||
if (!existsSync(path)) {
|
|
||||||
this.logger.warn(`Cant't find dev build config. Path is ${path}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.logger.debug(`Find dev build config. Path is ${path}`);
|
|
||||||
let bc = await loadModule<object | Function>(path);
|
|
||||||
if (bc == undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let finalBc = {};
|
|
||||||
if (typeof bc === 'function') {
|
|
||||||
finalBc = bc(env)
|
|
||||||
this.devBuildConfig = finalBc;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.devBuildConfig = bc;
|
|
||||||
|
|
||||||
if (this.userConfig.devBuildConfig.devProxy) {
|
|
||||||
this.devProxy = this.userConfig.devBuildConfig.devProxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!this.userConfig.buildConfig) {
|
|
||||||
switch (this.compileMode) {
|
|
||||||
case 'webpack':
|
|
||||||
this.buildConfigPath.push({name:'default', path:'./webpack.config.js'})
|
|
||||||
break;
|
|
||||||
case 'vite':
|
|
||||||
this.buildConfigPath.push({name:'default', path:'./vite.config.js'})
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.logger.warn(`Unknown compile mode ${this.compileMode}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.userConfig.buildConfig.forEach((userBuildConfig) => {
|
|
||||||
// if (typeof userBuildConfig === 'string') {
|
|
||||||
// const name = this.getConfigName(userBuildConfig);
|
|
||||||
// this.buildConfigPath.push({name, path: userBuildConfig});
|
|
||||||
// }
|
|
||||||
if (typeof userBuildConfig === 'object') {
|
|
||||||
// const name = userBuildConfig.name;
|
|
||||||
// const path = userBuildConfig.path;
|
|
||||||
this.buildConfigPath.push(userBuildConfig);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.buildConfigPath.forEach(async (config) => {
|
|
||||||
let {name, path} = config;
|
|
||||||
path = isAbsolute(path) ? path : join(process.cwd(), path);
|
|
||||||
if (!existsSync(path)) {
|
|
||||||
this.logger.debug(`Cant't find build config. Path is ${path}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.logger.debug(`Find build config. Path is ${path}`);
|
|
||||||
let bc = await loadModule<object | Function >(path);
|
|
||||||
if (bc == undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let finalBc = {};
|
|
||||||
if (typeof bc === 'function') {
|
|
||||||
finalBc = bc(config.env)
|
|
||||||
this.buildConfig.push({name: name, config: finalBc});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.buildConfig.push({name: name, config: bc});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfigName(name: string): string {
|
|
||||||
name = name.replace('webpack.', '');
|
|
||||||
name = name.replace('.js', '');
|
|
||||||
name = name.replace('.ts', '');
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
export enum PluginType {
|
|
||||||
preset = 'preset',
|
|
||||||
plugin = 'plugin',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ServiceStage {
|
|
||||||
uninitialized,
|
|
||||||
constructor,
|
|
||||||
init,
|
|
||||||
initPlugins,
|
|
||||||
initHooks,
|
|
||||||
pluginReady,
|
|
||||||
getConfig,
|
|
||||||
run,
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
import resolve from 'resolve';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import PluginAPI, { IOpts } from './PluginAPI.js';
|
|
||||||
import { IHook, ICommand } from '../types/types.js';
|
|
||||||
import Hub from '../core/Hub';
|
|
||||||
import { loadModule } from '../utils/loadModule.js';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
|
|
||||||
interface pluginManagerOpts {
|
|
||||||
cwd: string;
|
|
||||||
hub: Hub;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PluginObj {
|
|
||||||
path: string;
|
|
||||||
api: PluginAPI;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPlugin {
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
path: string;
|
|
||||||
apply: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Plugin {
|
|
||||||
cwd: string;
|
|
||||||
builtInPlugins: string[] = [];
|
|
||||||
userPlugins: string[] = [];
|
|
||||||
commands: {
|
|
||||||
[name: string]: ICommand | string;
|
|
||||||
} = {};
|
|
||||||
hooksByPluginPath: {
|
|
||||||
[id: string]: IHook[];
|
|
||||||
} = {};
|
|
||||||
hooks: {
|
|
||||||
[key: string]: IHook[];
|
|
||||||
} = {};
|
|
||||||
store: {
|
|
||||||
[key: string]: any;
|
|
||||||
} = {};
|
|
||||||
hub: Hub;
|
|
||||||
logger: Logger;
|
|
||||||
registerFunction: Function[] = [];
|
|
||||||
// 解决调用this[props]时ts提示属性未知
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
constructor(opts: pluginManagerOpts) {
|
|
||||||
this.cwd = opts.cwd || process.cwd();
|
|
||||||
this.hub = opts.hub;
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPluginPaths(builtInPlugins: string[], userPlugins: string[] | undefined): string[] {
|
|
||||||
const paths: string[] = [];
|
|
||||||
paths.push(...builtInPlugins);
|
|
||||||
if (userPlugins) {
|
|
||||||
paths.push(...userPlugins);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有插件文件的绝对路径
|
|
||||||
const absPaths: string[] = paths.map(path => {
|
|
||||||
return resolve.sync(path, {
|
|
||||||
basedir: this.cwd,
|
|
||||||
extensions: ['.js', '.ts'],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return absPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStore(name: string, initialValue: any) {
|
|
||||||
const store = this.store;
|
|
||||||
if (this.store[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
store[name] = initialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
async register(builtInPlugins: string[], userPlugins: string[] | undefined) {
|
|
||||||
const paths = this.getPluginPaths(builtInPlugins, userPlugins);
|
|
||||||
this.hub.pluginPaths = paths;
|
|
||||||
|
|
||||||
const objs: PluginObj[] = paths.map(path => {
|
|
||||||
const api: PluginAPI = this.createPluginAPI({
|
|
||||||
path,
|
|
||||||
manager: this,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
api,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const obj of objs) {
|
|
||||||
const module: Function | undefined = await loadModule(obj.path);
|
|
||||||
if (module) {
|
|
||||||
try {
|
|
||||||
module(obj.api);
|
|
||||||
} catch (err: unknown) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
this.logger.error(chalk.red(err.message));
|
|
||||||
if (err.stack) {
|
|
||||||
this.logger.error(err.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo 给API换个名字
|
|
||||||
createPluginAPI(opts: IOpts): PluginAPI {
|
|
||||||
const pluginAPI = new PluginAPI(opts);
|
|
||||||
|
|
||||||
// 为PluginAPI添加代理
|
|
||||||
// 除了PluginAPI自有的方法之外,为开发者提供更丰富的api
|
|
||||||
return new Proxy(pluginAPI, {
|
|
||||||
get: (target: PluginAPI, prop: string) => {
|
|
||||||
if (['userConfig', 'devBuildConfig', 'buildConfig', 'compileMode', 'packageJson', 'cwd'].includes(prop)) {
|
|
||||||
return typeof this.hub[prop] === 'function'
|
|
||||||
? this.hub[prop].bind(this.hub)
|
|
||||||
: this.hub[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['setStore', 'logger', 'commands'].includes(prop)) {
|
|
||||||
return typeof this[prop] === 'function'
|
|
||||||
? this[prop].bind(this)
|
|
||||||
: this[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
return target[prop];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initHook() {
|
|
||||||
Object.keys(this.hooksByPluginPath).forEach(path => {
|
|
||||||
const hooks = this.hooksByPluginPath[path];
|
|
||||||
hooks.forEach(hook => {
|
|
||||||
const { name } = hook;
|
|
||||||
hook.pluginId = path;
|
|
||||||
if (!this.hooks[name]) {
|
|
||||||
this.hooks[name] = [];
|
|
||||||
}
|
|
||||||
this.hooks[name].push(hook);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import Plugin from './Plugin.js';
|
|
||||||
import { IHook, ICommand } from '../types/types.js';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
|
|
||||||
export interface IOpts {
|
|
||||||
path: string;
|
|
||||||
manager: Plugin;
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class PluginAPI {
|
|
||||||
path: string;
|
|
||||||
manager: Plugin;
|
|
||||||
logger: Logger;
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
constructor(opts: IOpts) {
|
|
||||||
this.path = opts.path;
|
|
||||||
this.manager = opts.manager;
|
|
||||||
this.logger = opts.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
register(hook: IHook) {
|
|
||||||
if (!this.manager.hooksByPluginPath[this.path]) {
|
|
||||||
this.manager.hooksByPluginPath[this.path] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.manager.hooksByPluginPath[this.path].push(hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerCommand(command: ICommand) {
|
|
||||||
const { name } = command;
|
|
||||||
this.manager.commands[name] = command;
|
|
||||||
if (command.initialState) {
|
|
||||||
this.manager.setStore(name, command.initialState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHook(hook: IHook) {
|
|
||||||
this.register(hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerMethod(fn: Function) {
|
|
||||||
this.manager.registerFunction.push(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
async applyHook(name: string, args?: any ) {
|
|
||||||
const hooks: IHook[] = this.manager.hooks[name] || [];
|
|
||||||
let config: any = undefined;
|
|
||||||
for (const hook of hooks) {
|
|
||||||
if (this.manager.store[name]) {
|
|
||||||
config = this.manager.store[name];
|
|
||||||
}
|
|
||||||
if (hook.fn) {
|
|
||||||
await hook.fn(args, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.manager.store[name];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a, b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = sum;
|
|
|
@ -1,7 +0,0 @@
|
||||||
const sum = require('./sum');
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
export default {
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a, b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default sum;
|
|
|
@ -1,7 +0,0 @@
|
||||||
import sum from './sum';
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
export default {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).(js?(x)|ts?(x))$'],
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
import sum from './sum';
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a: number, b: number): number {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default sum;
|
|
|
@ -1,153 +0,0 @@
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
import yargsParser from 'yargs-parser';
|
|
||||||
import { Logger } from '../utils/logger.js';
|
|
||||||
import type * as http from 'http';
|
|
||||||
import type * as express from 'express';
|
|
||||||
|
|
||||||
|
|
||||||
interface Request extends express.Request {
|
|
||||||
}
|
|
||||||
interface Response extends express.Response {
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDep {
|
|
||||||
[name: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPackage {
|
|
||||||
name?: string;
|
|
||||||
dependencies?: IDep;
|
|
||||||
devDependencies?: IDep;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPlugin {
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
path: string;
|
|
||||||
apply: Function;
|
|
||||||
|
|
||||||
config?: IPluginConfig;
|
|
||||||
isPreset?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPluginConfig {
|
|
||||||
default?: any;
|
|
||||||
onChange?: string | Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IHook {
|
|
||||||
// 触发事件名称
|
|
||||||
name: string;
|
|
||||||
fn?: {
|
|
||||||
(state: any, config: any): void;
|
|
||||||
};
|
|
||||||
pluginId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICommand {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
details?: string;
|
|
||||||
initialState?: any;
|
|
||||||
fn: {
|
|
||||||
(args: yargsParser.Arguments, config: any): void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IConfig {
|
|
||||||
plugins?: string[];
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface applyHookConfig<T = any> {
|
|
||||||
name: string;
|
|
||||||
config?: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface API {
|
|
||||||
cwd: string;
|
|
||||||
logger: Logger;
|
|
||||||
userConfig: IConfig;
|
|
||||||
buildConfig: any;
|
|
||||||
devBuildConfig: any;
|
|
||||||
compileMode: string;
|
|
||||||
commands: string[];
|
|
||||||
packageJson: PackageJSON;
|
|
||||||
|
|
||||||
registerCommand: {
|
|
||||||
(command: ICommand): void;
|
|
||||||
};
|
|
||||||
registerHook: {
|
|
||||||
(hook: IHook): void;
|
|
||||||
};
|
|
||||||
registerMethod: {
|
|
||||||
(method: Function): void;
|
|
||||||
}
|
|
||||||
applyHook: {
|
|
||||||
(opts: applyHookConfig): void;
|
|
||||||
};
|
|
||||||
setStore: {
|
|
||||||
(name: string, initialState: any): void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RemoteProxy {
|
|
||||||
target: string;
|
|
||||||
localPort?: number;
|
|
||||||
localStatic?: StaticFileMatcher[];
|
|
||||||
fowardingURL?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StaticFileMatcher {
|
|
||||||
url: string;
|
|
||||||
local: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserConfig {
|
|
||||||
mock?: MockConfig;
|
|
||||||
proxy?: RemoteProxy;
|
|
||||||
plugins?: string[];
|
|
||||||
compileMode?: string;
|
|
||||||
buildConfig?: BuildConfig[];
|
|
||||||
devBuildConfig?: DevBuildConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MockConfig {
|
|
||||||
enableMock?: boolean;
|
|
||||||
mockPath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DevBuildConfig {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
args?: object;
|
|
||||||
env?: object;
|
|
||||||
devProxy?: DevProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DevProxy {
|
|
||||||
target: string;
|
|
||||||
matcher: ((pathname: string, req: Request) => boolean);
|
|
||||||
onProxyRes: (proxyRes: http.IncomingMessage, req: Request, res: Response) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildConfig {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
args?: object;
|
|
||||||
env?: object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ExportUserConfig = UserConfig | Promise<UserConfig>;
|
|
||||||
|
|
||||||
export function defineConfig(config: ExportUserConfig): ExportUserConfig {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Arguments {
|
|
||||||
_: Array<string | number>;
|
|
||||||
'--'?: Array<string | number>;
|
|
||||||
[argName: string]: any;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import { build as esbuild, Plugin } from 'esbuild';
|
|
||||||
|
|
||||||
const buildConfig = async (fileName: string, format: 'esm' | 'cjs' = 'esm'): Promise<string> => {
|
|
||||||
|
|
||||||
// 外部依赖不构建参与构建,减少执行时间
|
|
||||||
const pluginExternalDeps: Plugin = {
|
|
||||||
name: 'plugin-external-deps',
|
|
||||||
setup(build) {
|
|
||||||
build.onResolve({ filter: /.*/ }, args => {
|
|
||||||
const id = args.path;
|
|
||||||
if (id[0] !== '.' && !path.isAbsolute(id)) {
|
|
||||||
return {
|
|
||||||
external: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 将文件中的路径改成确定路径,避免执行时调用错误
|
|
||||||
const pluginReplaceImport: Plugin = {
|
|
||||||
name: 'plugin-replace-import-meta',
|
|
||||||
setup(build) {
|
|
||||||
build.onLoad({ filter: /\.[jt]s$/ }, args => {
|
|
||||||
const contents: string = fs.readFileSync(args.path, 'utf8');
|
|
||||||
|
|
||||||
// 替换import路径
|
|
||||||
contents.replace(/\bimport\.meta\.url\b/g, JSON.stringify(`file://${args.path}`));
|
|
||||||
|
|
||||||
// 替换当前目录路径
|
|
||||||
contents.replace(/\b__dirname\b/g, JSON.stringify(path.dirname(args.path)));
|
|
||||||
|
|
||||||
// 替换当前文件路径
|
|
||||||
contents.replace(/\b__filename\b/g, JSON.stringify(args.path));
|
|
||||||
|
|
||||||
return {
|
|
||||||
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
|
|
||||||
contents: contents
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await esbuild({
|
|
||||||
entryPoints: [fileName],
|
|
||||||
outfile: 'out.js',
|
|
||||||
write: false,
|
|
||||||
platform: 'node',
|
|
||||||
bundle: true,
|
|
||||||
format,
|
|
||||||
metafile: true,
|
|
||||||
plugins: [pluginExternalDeps, pluginReplaceImport],
|
|
||||||
});
|
|
||||||
const { text } = result.outputFiles[0];
|
|
||||||
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default buildConfig;
|
|
|
@ -1,6 +0,0 @@
|
||||||
export default async function dynamicImport(filePath: string) {
|
|
||||||
let importPath = filePath;
|
|
||||||
|
|
||||||
importPath = 'file:///' + importPath;
|
|
||||||
return await import(importPath);
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { join } from 'path';
|
|
||||||
import { readFileSync, existsSync } from 'fs';
|
|
||||||
import { parse } from 'dotenv';
|
|
||||||
|
|
||||||
export default function initializeEnv(): void {
|
|
||||||
const envPath: string = join(process.cwd(), '.env');
|
|
||||||
const localEnvPath: string = join(process.cwd(), '.local.env');
|
|
||||||
loadEnv(envPath);
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
loadEnv(localEnvPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadEnv(envPath: string): void {
|
|
||||||
if (existsSync(envPath)) {
|
|
||||||
const parsed = parse(readFileSync(envPath, 'utf-8')) || {};
|
|
||||||
Object.keys(parsed).forEach(key => {
|
|
||||||
process.env[key] = parsed[key];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { pathToFileURL } from 'url';
|
|
||||||
import { join, isAbsolute } from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import buildConfig from './build.js';
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import dynamicImport from './dynamicImport.js';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
export async function loadModule<T>(filePath: string): Promise<T | undefined> {
|
|
||||||
filePath = isAbsolute(filePath) ? filePath : join(process.cwd(), filePath);
|
|
||||||
|
|
||||||
const isTsFile: boolean = filePath.endsWith('ts');
|
|
||||||
const isJsFile: boolean = filePath.endsWith('js');
|
|
||||||
|
|
||||||
|
|
||||||
let content: T | undefined;
|
|
||||||
|
|
||||||
// js文件,可以直接通过import引用
|
|
||||||
if (isJsFile) {
|
|
||||||
content = (await dynamicImport(filePath))?.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是ts文件,需要先转为js文件,再读取
|
|
||||||
if (isTsFile) {
|
|
||||||
const code = await buildConfig(filePath, 'esm');
|
|
||||||
content = await getTypescriptModule(code, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTypescriptModule(code: string, filePath: string, isEsm = true) {
|
|
||||||
const tempFile = `${filePath}.${isEsm ? 'm' : 'c'}js`;
|
|
||||||
let content = null;
|
|
||||||
|
|
||||||
// todo 臨時文件管理
|
|
||||||
fs.writeFileSync(tempFile, code);
|
|
||||||
|
|
||||||
delete require.cache[require.resolve(tempFile)];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const raw = isEsm ? await dynamicImport(tempFile) : require(tempFile);
|
|
||||||
content = raw?.default ?? raw;
|
|
||||||
} catch (err: unknown) {
|
|
||||||
fs.unlinkSync(tempFile);
|
|
||||||
if (err instanceof Error) {
|
|
||||||
err.message = err.message.replace(tempFile, filePath);
|
|
||||||
err.stack = err.stack?.replace(tempFile, filePath);
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo 刪除失敗加日誌
|
|
||||||
fs.unlinkSync(tempFile);
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import { PackageJSON } from 'resolve';
|
|
||||||
|
|
||||||
export const loadPkg = (path: string): PackageJSON => {
|
|
||||||
const packageJson = fs.readFileSync(path, 'utf8');
|
|
||||||
const packageData: PackageJSON = JSON.parse(packageJson);
|
|
||||||
return packageData;
|
|
||||||
};
|
|
|
@ -1,42 +0,0 @@
|
||||||
export enum LogLevel {
|
|
||||||
DEBUG = 0,
|
|
||||||
INFO = 1,
|
|
||||||
WARN = 2,
|
|
||||||
ERROR = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Logger {
|
|
||||||
private readonly level: LogLevel;
|
|
||||||
|
|
||||||
constructor(level?: LogLevel) {
|
|
||||||
if (level !== undefined) {
|
|
||||||
this.level = level;
|
|
||||||
} else {
|
|
||||||
this.level = LogLevel.INFO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(message: string): void {
|
|
||||||
if (this.level <= LogLevel.DEBUG) {
|
|
||||||
console.debug(`[DEBUG] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info(message: string): void {
|
|
||||||
if (this.level <= LogLevel.INFO) {
|
|
||||||
console.info(`[INFO] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
warn(message: string): void {
|
|
||||||
if (this.level <= LogLevel.WARN) {
|
|
||||||
console.warn(`[WARN] ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error(message: string, error?: Error): void {
|
|
||||||
if (this.level <= LogLevel.ERROR) {
|
|
||||||
console.error(`[ERROR] ${message}`, error || '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
import chokidar from 'chokidar';
|
|
||||||
import bodyParser from 'body-parser';
|
|
||||||
import {globSync} from 'glob';
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
const mockDir = join(process.cwd(), 'mock');
|
|
||||||
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
|
|
||||||
|
|
||||||
const jsonParser = bodyParser.json();
|
|
||||||
const urlencodedParser = bodyParser.urlencoded({
|
|
||||||
extended: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Mock {
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取 mock 文件夹下的 js 文件
|
|
||||||
function getMocksFile() {
|
|
||||||
const mockFiles = globSync('**/*.js', {
|
|
||||||
cwd: mockDir,
|
|
||||||
});
|
|
||||||
let ret = mockFiles.reduce((mocks: any, mockFile: string) => {
|
|
||||||
if (!mockFile.startsWith('_')) {
|
|
||||||
mocks = {
|
|
||||||
...mocks,
|
|
||||||
...require(join(mockDir, mockFile)),
|
|
||||||
};
|
|
||||||
console.log('mockFile', require(join(mockDir, mockFile)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return mocks;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRoutes(app: any) {
|
|
||||||
let mockStartIndex = app._router.stack.length,
|
|
||||||
mocks: Mock = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
mocks = getMocksFile();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Generate mock routes error', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const mockItem in mocks) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(mocks, mockItem)) {
|
|
||||||
try {
|
|
||||||
const trimMockItemArr = mockItem
|
|
||||||
.replace(/(^\s*)|(\s*$)/g, '')
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.split(' ');
|
|
||||||
|
|
||||||
const respond = mocks[mockItem];
|
|
||||||
|
|
||||||
let mockType = 'get',
|
|
||||||
mockUrl;
|
|
||||||
if (trimMockItemArr.length === 1) {
|
|
||||||
mockUrl = trimMockItemArr[0];
|
|
||||||
} else {
|
|
||||||
[mockType, mockUrl] = trimMockItemArr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mockTypeLowerCase = mockType.toLowerCase();
|
|
||||||
|
|
||||||
if (!HTTP_METHODS.includes(mockTypeLowerCase)) {
|
|
||||||
throw new Error(`Invalid HTTP request method ${mockType} for path ${mockUrl}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
app[mockTypeLowerCase](
|
|
||||||
mockUrl,
|
|
||||||
[jsonParser, urlencodedParser],
|
|
||||||
respond instanceof Function
|
|
||||||
? respond
|
|
||||||
: (_req: any, res: { send: (arg0: any) => void }) => {
|
|
||||||
res.send(respond);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
mockRoutesLength: app._router.stack.length - mockStartIndex,
|
|
||||||
mockStartIndex: mockStartIndex,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除 mock 文件下的 require 缓存
|
|
||||||
function cleanRequireCache() {
|
|
||||||
Object.keys(require.cache).forEach(key => {
|
|
||||||
if (key.includes(mockDir)) {
|
|
||||||
delete require.cache[require.resolve(key)];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (app: { _router: { stack: any[] } }) => {
|
|
||||||
const mockRoutes = generateRoutes(app);
|
|
||||||
let { mockRoutesLength } = mockRoutes;
|
|
||||||
let { mockStartIndex } = mockRoutes;
|
|
||||||
|
|
||||||
// 监听 mock 文件夹下文件变化
|
|
||||||
chokidar
|
|
||||||
.watch(mockDir, {
|
|
||||||
ignoreInitial: true,
|
|
||||||
})
|
|
||||||
.on('all', (event: string, _path: any) => {
|
|
||||||
if (event === 'change' || event === 'add') {
|
|
||||||
try {
|
|
||||||
// 删除中间件映射
|
|
||||||
app._router.stack.splice(mockStartIndex, mockRoutesLength);
|
|
||||||
|
|
||||||
cleanRequireCache();
|
|
||||||
const mockRoutes = generateRoutes(app);
|
|
||||||
|
|
||||||
mockRoutesLength = mockRoutes.mockRoutesLength;
|
|
||||||
mockStartIndex = mockRoutes.mockStartIndex;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,28 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
export default function readDirectory(directoryPath: string): string[] {
|
|
||||||
const filesArray: string[] = [];
|
|
||||||
const traverseDirectory = (directoryPath: string) => {
|
|
||||||
const files = fs.readdirSync(directoryPath);
|
|
||||||
for (const file of files) {
|
|
||||||
const filePath = path.join(directoryPath, file);
|
|
||||||
|
|
||||||
if (fs.statSync(filePath).isDirectory()) {
|
|
||||||
// 如果是目录,则递归读取该目录下的所有文件
|
|
||||||
traverseDirectory(filePath);
|
|
||||||
} else {
|
|
||||||
if (filePath.startsWith('.')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 如果是文件,则将其全路径添加到数组中
|
|
||||||
if (filePath.endsWith('.js')) {
|
|
||||||
filesArray.push(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
traverseDirectory(directoryPath);
|
|
||||||
return filesArray;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
||||||
import { API } from '../types/types';
|
|
||||||
|
|
||||||
export default (app: any, api: API) => {
|
|
||||||
const { devProxy } = api.userConfig.devBuildConfig;
|
|
||||||
app.use(createProxyMiddleware(devProxy.matcher, {
|
|
||||||
target: devProxy.target,
|
|
||||||
secure: false,
|
|
||||||
changeOrigin: true,
|
|
||||||
ws: false,
|
|
||||||
onProxyRes: devProxy.onProxyRes
|
|
||||||
}));
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { dirname } from 'path';
|
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
|
||||||
import resolve from 'resolve';
|
|
||||||
// @ts-ignore
|
|
||||||
import crequire from 'crequire'
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
function parse(filePath: string): string[] {
|
|
||||||
const content = readFileSync(filePath, 'utf-8');
|
|
||||||
return (crequire(content) as any[])
|
|
||||||
.map<string>(o => o.path)
|
|
||||||
.filter(path => path.charAt(0) === '.')
|
|
||||||
.map(path =>
|
|
||||||
resolve.sync(path, {
|
|
||||||
basedir: dirname(filePath),
|
|
||||||
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseRequireDeps(filePath: string): string[] {
|
|
||||||
const paths: string[] = [filePath];
|
|
||||||
const ret: string[] = [filePath];
|
|
||||||
|
|
||||||
while (paths.length) {
|
|
||||||
const extraPaths = parse(paths.shift()!).filter(path => !ret.includes(path));
|
|
||||||
if (extraPaths.length) {
|
|
||||||
paths.push(...extraPaths);
|
|
||||||
ret.push(...extraPaths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isWindows = typeof process !== 'undefined' && process.platform === 'win32';
|
|
||||||
|
|
||||||
export function cleanRequireCache(cacheKey: string): void {
|
|
||||||
const cachePath = isWindows ? cacheKey.replace(/\//g, '\\') : cacheKey;
|
|
||||||
if (require.cache[cachePath]) {
|
|
||||||
const cacheParent = (require.cache[cachePath] as any).parent;
|
|
||||||
let i = cacheParent?.children.length || 0;
|
|
||||||
while (i--) {
|
|
||||||
if (cacheParent!.children[i].id === cachePath) {
|
|
||||||
cacheParent!.children.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete require.cache[cachePath];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function copyFile(targetPath: string, sourcePath: string): void {
|
|
||||||
try {
|
|
||||||
const fileContent = readFileSync(sourcePath);
|
|
||||||
writeFileSync(targetPath, fileContent);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Copy file failed.', error);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a, b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = sum;
|
|
|
@ -1,7 +0,0 @@
|
||||||
const sum = require('./sum');
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
export default {
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a, b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default sum;
|
|
|
@ -1,7 +0,0 @@
|
||||||
import sum from './sum';
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
export default {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node', // 使用 Node.js 环境进行测试
|
|
||||||
|
|
||||||
// 匹配的测试文件模式
|
|
||||||
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).(js?(x)|ts?(x))$'],
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
import sum from './sum';
|
|
||||||
|
|
||||||
describe('sum', () => {
|
|
||||||
it('should add two numbers', () => {
|
|
||||||
expect(sum(1, 2)).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
function sum(a: number, b: number): number {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default sum;
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2015",
|
|
||||||
"module": "esnext",
|
|
||||||
"sourceMap": false,
|
|
||||||
"outDir": "./lib",
|
|
||||||
"strict": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
},
|
|
||||||
"include": ["src/**/*"],
|
|
||||||
"exclude": ["node_modules", "**/*.spec.ts", "./src/template/**/*"],
|
|
||||||
"ts-node": {
|
|
||||||
"esm": true,
|
|
||||||
},
|
|
||||||
}
|
|
Loading…
Reference in New Issue