Merge branch 'release'
This commit is contained in:
commit
85f8846c69
|
|
@ -0,0 +1,27 @@
|
||||||
|
const { join } = require('path');
|
||||||
|
const t = require('@babel/types');
|
||||||
|
const { assert } = require('console');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (babel) => {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
FunctionDeclaration(path, state) {
|
||||||
|
const { cwd } = state;
|
||||||
|
const { node } = path;
|
||||||
|
if (t.isIdentifier(node.id) && node.id.name === 'oakGetPackageJsonVersion') {
|
||||||
|
const { body } = node;
|
||||||
|
assert(t.isBlockStatement(body));
|
||||||
|
assert(body.body.length === 1);
|
||||||
|
|
||||||
|
const pkgJson = join(cwd, 'package.json');
|
||||||
|
const { version } = require(pkgJson);
|
||||||
|
|
||||||
|
body.body = [
|
||||||
|
t.returnStatement(t.stringLiteral(version))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -59,6 +59,7 @@ function getClientEnvironment() {
|
||||||
// process.env.OAK_PLATFORM: wechatMp | web | node
|
// process.env.OAK_PLATFORM: wechatMp | web | node
|
||||||
OAK_PLATFORM: 'wechatMp',
|
OAK_PLATFORM: 'wechatMp',
|
||||||
OAK_DEV_MODE: process.env.OAK_DEV_MODE || 'server',
|
OAK_DEV_MODE: process.env.OAK_DEV_MODE || 'server',
|
||||||
|
SUB_DIR_NAME: process.env.SUB_DIR_NAME || 'wechatMp',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Stringify all values so we can feed into webpack DefinePlugin
|
// Stringify all values so we can feed into webpack DefinePlugin
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ const createEnvironmentHash = require('./webpack/persistentCache/createEnvironme
|
||||||
|
|
||||||
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
||||||
const oakPathPlugin = require('../babel-plugin/oakPath');
|
const oakPathPlugin = require('../babel-plugin/oakPath');
|
||||||
|
const oakGetAppVersionPlugin = require('../babel-plugin/oakGetAppVersion');
|
||||||
|
const reuseOakComponentPlugin = require('../babel-plugin/reuse-oak-component');
|
||||||
|
|
||||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||||
const shouldAnalyze = process.env.COMPILE_ANALYZE === 'true';
|
const shouldAnalyze = process.env.COMPILE_ANALYZE === 'true';
|
||||||
|
|
@ -242,7 +244,7 @@ module.exports = function (webpackEnv) {
|
||||||
),
|
),
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
plugins: [oakI18nPlugin, oakPathPlugin],
|
plugins: [oakI18nPlugin, oakPathPlugin, reuseOakComponentPlugin, oakGetAppVersionPlugin],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ const assert = require('assert');
|
||||||
|
|
||||||
const REPLACE_HOLDERS = {
|
const REPLACE_HOLDERS = {
|
||||||
OAK_PLATFORM: 'native',
|
OAK_PLATFORM: 'native',
|
||||||
PROD: process.env.PROD,
|
|
||||||
OAK_DEV_MODE: process.env.OAK_DEV_MODE,
|
OAK_DEV_MODE: process.env.OAK_DEV_MODE,
|
||||||
|
SUB_DIR_NAME: process.env.SUB_DIR_NAME || 'native',
|
||||||
};
|
};
|
||||||
|
|
||||||
function replaceEnvExpressionPlugin() {
|
function replaceEnvExpressionPlugin() {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ const replaceEnvExpressionPlugin = require('./babelEnvPlugin');
|
||||||
const { injectGetRender } = require('../utils/injectGetRender');
|
const { injectGetRender } = require('../utils/injectGetRender');
|
||||||
const oakPathTsxPlugin = require('../babel-plugin/oakPath');
|
const oakPathTsxPlugin = require('../babel-plugin/oakPath');
|
||||||
const oakRenderNativePlugin = require('../babel-plugin/oakRenderNative');
|
const oakRenderNativePlugin = require('../babel-plugin/oakRenderNative');
|
||||||
|
const oakGetAppVersionPlugin = require('../babel-plugin/oakGetAppVersion');
|
||||||
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
||||||
|
const reuseOakComponentPlugin = require('../babel-plugin/reuse-oak-component');
|
||||||
|
|
||||||
async function renderToCSS({ src, filename, options = {} }) {
|
async function renderToCSS({ src, filename, options = {} }) {
|
||||||
const { lessOptions = {} } = options;
|
const { lessOptions = {} } = options;
|
||||||
|
|
@ -56,7 +58,7 @@ function transform({ filename, options, plugins, src }) {
|
||||||
cwd: options.projectRoot,
|
cwd: options.projectRoot,
|
||||||
highlightCode: true,
|
highlightCode: true,
|
||||||
filename,
|
filename,
|
||||||
plugins: plugins.concat([replaceEnvExpressionPlugin, oakPathTsxPlugin, oakRenderNativePlugin, oakI18nPlugin]),
|
plugins: plugins.concat([replaceEnvExpressionPlugin, oakPathTsxPlugin, oakRenderNativePlugin, oakI18nPlugin, reuseOakComponentPlugin, oakGetAppVersionPlugin]),
|
||||||
sourceType: "module",
|
sourceType: "module",
|
||||||
// NOTE(EvanBacon): We split the parse/transform steps up to accommodate
|
// NOTE(EvanBacon): We split the parse/transform steps up to accommodate
|
||||||
// Hermes parsing, but this defaults to cloning the AST which increases
|
// Hermes parsing, but this defaults to cloning the AST which increases
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ function getClientEnvironment(publicUrl) {
|
||||||
// process.env.OAK_PLATFORM: wechatMp | web | native
|
// process.env.OAK_PLATFORM: wechatMp | web | native
|
||||||
OAK_PLATFORM: 'web',
|
OAK_PLATFORM: 'web',
|
||||||
OAK_DEV_MODE: process.env.OAK_DEV_MODE || 'server',
|
OAK_DEV_MODE: process.env.OAK_DEV_MODE || 'server',
|
||||||
|
SUB_DIR_NAME: process.env.SUB_DIR_NAME || 'web',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Stringify all values so we can feed into webpack DefinePlugin
|
// Stringify all values so we can feed into webpack DefinePlugin
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ const createEnvironmentHash = require('./webpack/persistentCache/createEnvironme
|
||||||
|
|
||||||
const oakPathTsxPlugin = require('../babel-plugin/oakPath');
|
const oakPathTsxPlugin = require('../babel-plugin/oakPath');
|
||||||
const oakRenderTsxPlugin = require('../babel-plugin/oakRender');
|
const oakRenderTsxPlugin = require('../babel-plugin/oakRender');
|
||||||
|
const oakGetAppVersionPlugin = require('../babel-plugin/oakGetAppVersion');
|
||||||
// const oakRouterPlugin = require('../babel-plugin/oakRouter2');
|
// const oakRouterPlugin = require('../babel-plugin/oakRouter2');
|
||||||
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
const oakI18nPlugin = require('../babel-plugin/oakI18n');
|
||||||
const oakStylePlugin = require('../babel-plugin/oakStyle');
|
const oakStylePlugin = require('../babel-plugin/oakStyle');
|
||||||
|
|
@ -609,6 +610,7 @@ module.exports = function (webpackEnv) {
|
||||||
oakRenderTsxPlugin,
|
oakRenderTsxPlugin,
|
||||||
// oakRouterPlugin,
|
// oakRouterPlugin,
|
||||||
oakI18nPlugin,
|
oakI18nPlugin,
|
||||||
|
oakGetAppVersionPlugin,
|
||||||
reuseOakComponentPlugin,
|
reuseOakComponentPlugin,
|
||||||
// [
|
// [
|
||||||
// 'import',
|
// 'import',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = build;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -153,4 +154,3 @@ async function build(cmd) {
|
||||||
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`target could only be web or mp(wechatMp) or rn(native)`)}`);
|
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`target could only be web or mp(wechatMp) or rn(native)`)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = build;
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = run;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -48,4 +49,3 @@ async function run(options) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = run;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.update = exports.create = void 0;
|
exports.create = create;
|
||||||
|
exports.update = update;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const ts = tslib_1.__importStar(require("typescript"));
|
const ts = tslib_1.__importStar(require("typescript"));
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
|
|
@ -160,13 +161,6 @@ async function create(dirName, cmd) {
|
||||||
const isDev = cmd.dev ? true : false;
|
const isDev = cmd.dev ? true : false;
|
||||||
const isModule = cmd.module ? true : false;
|
const isModule = cmd.module ? true : false;
|
||||||
const { name, version, title, description, useOgb, moreDeps, example } = await inquirer_1.default.prompt(prompt);
|
const { name, version, title, description, useOgb, moreDeps, example } = await inquirer_1.default.prompt(prompt);
|
||||||
// 获取tsconfig.json内容
|
|
||||||
const tsconfigJson = (0, template_1.tsConfigJsonContent)();
|
|
||||||
const tsConfigBuildJson = (0, template_1.tsConfigBuildJsonContent)();
|
|
||||||
const tsConfigBuildPathsJson = (0, template_1.tsConfigBuildPathsJsonContent)();
|
|
||||||
const tsConfigPathsJson = (0, template_1.tsConfigPathsJsonContent)();
|
|
||||||
const tsConfigMpJson = (0, template_1.tsConfigMpJsonContent)();
|
|
||||||
const tsConfigWebJson = (0, template_1.tsConfigWebJsonContent)();
|
|
||||||
// 项目根路径
|
// 项目根路径
|
||||||
const rootPath = process.cwd() + '/' + dirName;
|
const rootPath = process.cwd() + '/' + dirName;
|
||||||
// package.json路径
|
// package.json路径
|
||||||
|
|
@ -195,18 +189,6 @@ async function create(dirName, cmd) {
|
||||||
try {
|
try {
|
||||||
// 创建根目录
|
// 创建根目录
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(rootPath);
|
(0, file_handle_1.checkFileExistsAndCreate)(rootPath);
|
||||||
// 创建tsconfig.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsconfigJsonPath, tsconfigJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 创建tsconfig.build.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigBuildJsonPath, tsConfigBuildJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 创建tsconfig.build.paths.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigBuildPathsJsonPath, tsConfigBuildPathsJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 创建tsconfig.paths.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsconfigPathsJsonPath, tsConfigPathsJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 创建tsconfig.mp.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigMpJsonPath, tsConfigMpJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 创建tsconfig.web.json
|
|
||||||
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigWebJsonPath, tsConfigWebJson, enum_1.checkFileExistsAndCreateType.FILE);
|
|
||||||
// 复制项目文件
|
// 复制项目文件
|
||||||
if (isModule) {
|
if (isModule) {
|
||||||
// 模块化的项目,只拷贝src和typings目录
|
// 模块化的项目,只拷贝src和typings目录
|
||||||
|
|
@ -226,7 +208,6 @@ async function create(dirName, cmd) {
|
||||||
Success(`${success(`Dependencies are now being installed`)}`);
|
Success(`${success(`Dependencies are now being installed`)}`);
|
||||||
shell.cd(dirName).exec('npm install'); */
|
shell.cd(dirName).exec('npm install'); */
|
||||||
}
|
}
|
||||||
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Successfully created project ${(0, tip_style_1.primary)(name)}, directory name is ${(0, tip_style_1.primary)(dirName)}`)}`);
|
|
||||||
const deps = [];
|
const deps = [];
|
||||||
if (useOgb) {
|
if (useOgb) {
|
||||||
deps.push('oak-general-business');
|
deps.push('oak-general-business');
|
||||||
|
|
@ -234,13 +215,35 @@ async function create(dirName, cmd) {
|
||||||
if (moreDeps) {
|
if (moreDeps) {
|
||||||
deps.push(...(moreDeps.split(',').map(ele => ele.trim()).filter(ele => !!ele)));
|
deps.push(...(moreDeps.split(',').map(ele => ele.trim()).filter(ele => !!ele)));
|
||||||
}
|
}
|
||||||
|
// 获取tsconfig.json内容
|
||||||
|
const tsconfigJson = (0, template_1.tsConfigJsonContent)();
|
||||||
|
const tsConfigBuildJson = (0, template_1.tsConfigBuildJsonContent)();
|
||||||
|
const tsConfigBuildPathsJson = (0, template_1.tsConfigPathsJsonContent)(deps);
|
||||||
|
const tsConfigPathsJson = (0, template_1.tsConfigPathsJsonContent)(deps);
|
||||||
|
const tsConfigMpJson = (0, template_1.tsConfigMpJsonContent)();
|
||||||
|
const tsConfigWebJson = (0, template_1.tsConfigWebJsonContent)();
|
||||||
|
// 创建tsconfig.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsconfigJsonPath, tsconfigJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 创建tsconfig.build.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigBuildJsonPath, tsConfigBuildJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 创建tsconfig.build.paths.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigBuildPathsJsonPath, tsConfigBuildPathsJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 创建tsconfig.paths.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsconfigPathsJsonPath, tsConfigPathsJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 创建tsconfig.mp.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigMpJsonPath, tsConfigMpJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 创建tsconfig.web.json
|
||||||
|
(0, file_handle_1.checkFileExistsAndCreate)(tsConfigWebJsonPath, tsConfigWebJson, enum_1.checkFileExistsAndCreateType.FILE);
|
||||||
|
// 更新configuration/compiler.js
|
||||||
|
(0, template_1.updateCompilerJsContent)(rootPath, deps);
|
||||||
|
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Successfully created project ${(0, tip_style_1.primary)(name)}, directory name is ${(0, tip_style_1.primary)(dirName)}`)}`);
|
||||||
shelljs_1.default.cd(dirName);
|
shelljs_1.default.cd(dirName);
|
||||||
if (deps.length > 0) {
|
if (deps.length > 0) {
|
||||||
addDependencies(deps);
|
addDependencies(deps);
|
||||||
}
|
}
|
||||||
// 获取package.json内容
|
// 获取package.json内容
|
||||||
const packageJson = (0, template_1.packageJsonContent)({
|
const packageJson = (0, template_1.packageJsonContent)({
|
||||||
name: DEFAULT_PROJECT_NAME,
|
name: DEFAULT_PROJECT_NAME, // 后面再统一rename
|
||||||
version,
|
version,
|
||||||
description,
|
description,
|
||||||
cliName: config_1.CLI_NAME,
|
cliName: config_1.CLI_NAME,
|
||||||
|
|
@ -263,7 +266,6 @@ async function create(dirName, cmd) {
|
||||||
(0, tip_style_1.Error)((0, tip_style_1.error)(err));
|
(0, tip_style_1.Error)((0, tip_style_1.error)(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.create = create;
|
|
||||||
async function update(dirName, subDirName, cmd) {
|
async function update(dirName, subDirName, cmd) {
|
||||||
const isDev = cmd.dev ? true : false;
|
const isDev = cmd.dev ? true : false;
|
||||||
try {
|
try {
|
||||||
|
|
@ -290,4 +292,3 @@ async function update(dirName, subDirName, cmd) {
|
||||||
console.error((0, tip_style_1.error)(err.message));
|
console.error((0, tip_style_1.error)(err.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.update = update;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.CreateCompilerConfig = void 0;
|
exports.CreateCompilerConfig = CreateCompilerConfig;
|
||||||
/**
|
/**
|
||||||
* 创建一个oak编译器配置
|
* 创建一个oak编译器配置
|
||||||
* @param raw 原始配置
|
* @param raw 原始配置
|
||||||
|
|
@ -11,7 +11,6 @@ function CreateCompilerConfig(raw) {
|
||||||
// 在这里可以做配置的预处理
|
// 在这里可以做配置的预处理
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
exports.CreateCompilerConfig = CreateCompilerConfig;
|
|
||||||
/**
|
/**
|
||||||
* 将compiler.js中的模块导出使用以下形式:
|
* 将compiler.js中的模块导出使用以下形式:
|
||||||
* module.exports = CreateComilerConfig({})
|
* module.exports = CreateComilerConfig({})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
/// <reference types="node" />
|
|
||||||
/// <reference types="node" />
|
|
||||||
import { PathLike } from 'fs';
|
import { PathLike } from 'fs';
|
||||||
import { checkFileExistsAndCreateType } from './enum';
|
import { checkFileExistsAndCreateType } from './enum';
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.checkFileExistsAndCreate = exports.checkFileExists = exports.copyFolder = exports.readFile = exports.writeFile = exports.deleteFolderRecursive = exports.parseJsonFile = exports.parseJsonFiles = exports.readDirGetFile = exports.readDirPath = void 0;
|
exports.readDirPath = readDirPath;
|
||||||
|
exports.readDirGetFile = readDirGetFile;
|
||||||
|
exports.parseJsonFiles = parseJsonFiles;
|
||||||
|
exports.parseJsonFile = parseJsonFile;
|
||||||
|
exports.deleteFolderRecursive = deleteFolderRecursive;
|
||||||
|
exports.writeFile = writeFile;
|
||||||
|
exports.readFile = readFile;
|
||||||
|
exports.copyFolder = copyFolder;
|
||||||
|
exports.checkFileExists = checkFileExists;
|
||||||
|
exports.checkFileExistsAndCreate = checkFileExistsAndCreate;
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const enum_1 = require("./enum");
|
const enum_1 = require("./enum");
|
||||||
|
|
@ -25,7 +34,6 @@ function readDirPath(entry) {
|
||||||
}
|
}
|
||||||
return pathList;
|
return pathList;
|
||||||
}
|
}
|
||||||
exports.readDirPath = readDirPath;
|
|
||||||
/**
|
/**
|
||||||
* @name 读取指定目录的文件(不进行深度遍历,只获取根目录)
|
* @name 读取指定目录的文件(不进行深度遍历,只获取根目录)
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -36,7 +44,6 @@ function readDirGetFile(entry) {
|
||||||
const dirInfo = (0, fs_1.readdirSync)(entry);
|
const dirInfo = (0, fs_1.readdirSync)(entry);
|
||||||
return dirInfo;
|
return dirInfo;
|
||||||
}
|
}
|
||||||
exports.readDirGetFile = readDirGetFile;
|
|
||||||
/**
|
/**
|
||||||
* @name 解析json文件(数组)
|
* @name 解析json文件(数组)
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -51,7 +58,6 @@ function parseJsonFiles(arr) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
exports.parseJsonFiles = parseJsonFiles;
|
|
||||||
/**
|
/**
|
||||||
* @name 解析单个文件json
|
* @name 解析单个文件json
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -67,7 +73,6 @@ function parseJsonFile(file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.parseJsonFile = parseJsonFile;
|
|
||||||
/**
|
/**
|
||||||
* @name 删除文件夹
|
* @name 删除文件夹
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -97,7 +102,6 @@ function deleteFolderRecursive(entry) {
|
||||||
// console.log("文件夹不存在");
|
// console.log("文件夹不存在");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.deleteFolderRecursive = deleteFolderRecursive;
|
|
||||||
;
|
;
|
||||||
function writeFile(path, data) {
|
function writeFile(path, data) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -108,7 +112,6 @@ function writeFile(path, data) {
|
||||||
(0, tip_style_1.Error)((0, tip_style_1.error)('文件写入失败'));
|
(0, tip_style_1.Error)((0, tip_style_1.error)('文件写入失败'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.writeFile = writeFile;
|
|
||||||
function readFile(path, options) {
|
function readFile(path, options) {
|
||||||
try {
|
try {
|
||||||
const data = (0, fs_1.readFileSync)(path, options);
|
const data = (0, fs_1.readFileSync)(path, options);
|
||||||
|
|
@ -119,7 +122,6 @@ function readFile(path, options) {
|
||||||
(0, tip_style_1.Error)((0, tip_style_1.error)('文件读取失败'));
|
(0, tip_style_1.Error)((0, tip_style_1.error)('文件读取失败'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.readFile = readFile;
|
|
||||||
/**
|
/**
|
||||||
* @name 拷贝文件夹
|
* @name 拷贝文件夹
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -181,7 +183,6 @@ function copyFolder(currentDir, targetDir, overwrite = false) {
|
||||||
throw new global.Error(`需要copy的文件夹不存在: ${currentDir}`);
|
throw new global.Error(`需要copy的文件夹不存在: ${currentDir}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.copyFolder = copyFolder;
|
|
||||||
/**
|
/**
|
||||||
* @name 检测文件/文件夹是否存在
|
* @name 检测文件/文件夹是否存在
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -191,7 +192,6 @@ exports.copyFolder = copyFolder;
|
||||||
function checkFileExists(path) {
|
function checkFileExists(path) {
|
||||||
return (0, fs_1.existsSync)(path);
|
return (0, fs_1.existsSync)(path);
|
||||||
}
|
}
|
||||||
exports.checkFileExists = checkFileExists;
|
|
||||||
/**
|
/**
|
||||||
* @name 检测文件/文件夹是否存在,不存在则创建
|
* @name 检测文件/文件夹是否存在,不存在则创建
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -217,4 +217,3 @@ function checkFileExistsAndCreate(path, data, type = enum_1.checkFileExistsAndCr
|
||||||
throw new global.Error(`${path} already exists!`);
|
throw new global.Error(`${path} already exists!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.checkFileExistsAndCreate = checkFileExistsAndCreate;
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = make;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -24,4 +25,3 @@ async function make(cmd, rebuild) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = make;
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = make;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -20,4 +21,3 @@ async function make() {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = make;
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = make;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const localeBuilder_1 = tslib_1.__importDefault(require("oak-domain/lib/compiler/localeBuilder"));
|
const localeBuilder_1 = tslib_1.__importDefault(require("oak-domain/lib/compiler/localeBuilder"));
|
||||||
|
|
@ -18,4 +19,3 @@ async function make(cmd) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = make;
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = make;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -30,4 +31,3 @@ async function make(cmd, watch) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = make;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.rename = exports.renameProject = void 0;
|
exports.renameProject = renameProject;
|
||||||
|
exports.rename = rename;
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
const editTemplate_1 = require("@react-native-community/cli/build/commands/init/editTemplate");
|
const editTemplate_1 = require("@react-native-community/cli/build/commands/init/editTemplate");
|
||||||
|
|
@ -42,12 +43,10 @@ async function renameProject(dir, name, title, placeholderName, placeholderTitle
|
||||||
process.chdir(cwd);
|
process.chdir(cwd);
|
||||||
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Change project name to ${(0, tip_style_1.primary)(name)}, project title to ${(0, tip_style_1.primary)(title)}`)}`);
|
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Change project name to ${(0, tip_style_1.primary)(name)}, project title to ${(0, tip_style_1.primary)(title)}`)}`);
|
||||||
}
|
}
|
||||||
exports.renameProject = renameProject;
|
|
||||||
async function rename(cmd) {
|
async function rename(cmd) {
|
||||||
const { projectName, displayName } = cmd;
|
const { projectName, displayName } = cmd;
|
||||||
// todo 取native/android下的name和title作为placeholder,再调用renameProject
|
// todo 取native/android下的name和title作为placeholder,再调用renameProject
|
||||||
}
|
}
|
||||||
exports.rename = rename;
|
|
||||||
/* changePlaceholderInTemplate({
|
/* changePlaceholderInTemplate({
|
||||||
projectName: 'taicang',
|
projectName: 'taicang',
|
||||||
projectTitle: '太藏',
|
projectTitle: '太藏',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.default = run;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const tip_style_1 = require("./tip-style");
|
const tip_style_1 = require("./tip-style");
|
||||||
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
const cross_spawn_1 = tslib_1.__importDefault(require("cross-spawn"));
|
||||||
|
|
@ -59,4 +60,3 @@ async function run(options) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.default = run;
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
type AnalysisOptions = {
|
||||||
|
outputDir: string;
|
||||||
|
includeNodeModules: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 分析项目中的模块
|
||||||
|
* @param dir 项目目录
|
||||||
|
* @param output 输出目录
|
||||||
|
*/
|
||||||
|
export declare const analysis: (dir: string, config: AnalysisOptions) => void;
|
||||||
|
export {};
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.analysis = void 0;
|
||||||
|
const tslib_1 = require("tslib");
|
||||||
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
||||||
|
const path_1 = tslib_1.__importDefault(require("path"));
|
||||||
|
/**
|
||||||
|
* 分析项目中的模块
|
||||||
|
* @param dir 项目目录
|
||||||
|
* @param output 输出目录
|
||||||
|
*/
|
||||||
|
const analysis = (dir, config) => {
|
||||||
|
const BuiltinModule = require("module");
|
||||||
|
// 兼容一些模拟环境下的 module 构造函数
|
||||||
|
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||||
|
// 保存原始的 _resolveFilename 方法
|
||||||
|
const oldResolveFilename = Module._resolveFilename;
|
||||||
|
const successImported = new Set();
|
||||||
|
// 重写 _resolveFilename 方法
|
||||||
|
Module._resolveFilename = function (request, // 模块请求路径
|
||||||
|
parent, // 调用方模块
|
||||||
|
isMain, // 是否是主模块
|
||||||
|
rFoptions // 解析选项
|
||||||
|
) {
|
||||||
|
// 调用原始的 _resolveFilename 方法
|
||||||
|
const filename = oldResolveFilename.call(this, request, parent, isMain, rFoptions);
|
||||||
|
// 记录成功导入的模块
|
||||||
|
successImported.add(filename);
|
||||||
|
// 返回解析后的模块路径
|
||||||
|
return filename;
|
||||||
|
};
|
||||||
|
const filterProjectModules = (modulePath) => {
|
||||||
|
if (config.includeNodeModules) {
|
||||||
|
return modulePath.includes(dir);
|
||||||
|
}
|
||||||
|
return modulePath.includes(dir) && !modulePath.includes('node_modules');
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 将所有的文件复制到指定目录,并保持目录结构
|
||||||
|
* @param files 文件列表 string[]
|
||||||
|
* @param dest 目标目录
|
||||||
|
* @param root 原目录的根目录
|
||||||
|
*/
|
||||||
|
const copyFiles = (files, dest, root) => {
|
||||||
|
files.forEach(file => {
|
||||||
|
const relativePath = path_1.default.relative(root, file);
|
||||||
|
const destPath = path_1.default.join(dest, relativePath);
|
||||||
|
fs_1.default.mkdirSync(path_1.default.dirname(destPath), { recursive: true });
|
||||||
|
fs_1.default.copyFileSync(file, destPath);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const { watch } = require("./watch");
|
||||||
|
watch(dir).then(s => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('shutting down...');
|
||||||
|
s().then(() => {
|
||||||
|
console.log('server stoped');
|
||||||
|
// 把导入成功的输出
|
||||||
|
const project = Array.from(successImported).filter(filterProjectModules);
|
||||||
|
// 添加本地的scripts下的所有文件和package.json
|
||||||
|
const scriptsDir = path_1.default.join(dir, 'scripts');
|
||||||
|
const pkgPath = path_1.default.join(dir, 'package.json');
|
||||||
|
const files = fs_1.default.readdirSync(scriptsDir);
|
||||||
|
const scriptFiles = files.map(file => path_1.default.join(scriptsDir, file));
|
||||||
|
project.push(pkgPath, ...scriptFiles);
|
||||||
|
// 复制文件
|
||||||
|
const dest = path_1.default.join(dir, config.outputDir);
|
||||||
|
fs_1.default.mkdirSync(dest, { recursive: true });
|
||||||
|
copyFiles(project, dest, dir);
|
||||||
|
console.warn('分析结果以项目启动所需依赖为准,如果涉及动态导入,请设计routine,在项目启动时进行加载!');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.analysis = analysis;
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/// <reference path="../../src/typings/polyfill.d.ts" />
|
|
||||||
import { EntityDict } from 'oak-domain/lib/types';
|
import { EntityDict } from 'oak-domain/lib/types';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.initialize = void 0;
|
exports.initialize = initialize;
|
||||||
/// <reference path="../typings/polyfill.d.ts" />
|
/// <reference path="../typings/polyfill.d.ts" />
|
||||||
const oak_backend_base_1 = require("oak-backend-base");
|
const oak_backend_base_1 = require("oak-backend-base");
|
||||||
async function initialize(path) {
|
async function initialize(path) {
|
||||||
|
|
@ -10,4 +10,3 @@ async function initialize(path) {
|
||||||
await appLoader.unmount();
|
await appLoader.unmount();
|
||||||
console.log('data initialized');
|
console.log('data initialized');
|
||||||
}
|
}
|
||||||
exports.initialize = initialize;
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/// <reference path="../../src/typings/polyfill.d.ts" />
|
|
||||||
import './polyfill';
|
import './polyfill';
|
||||||
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
||||||
import { Connector, EntityDict } from 'oak-domain/lib/types';
|
import { Connector, EntityDict } from 'oak-domain/lib/types';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.startup = void 0;
|
exports.startup = startup;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
/// <reference path="../typings/polyfill.d.ts" />
|
/// <reference path="../typings/polyfill.d.ts" />
|
||||||
require("./polyfill");
|
require("./polyfill");
|
||||||
|
|
@ -14,6 +14,8 @@ const oak_backend_base_1 = require("oak-backend-base");
|
||||||
const types_1 = require("oak-domain/lib/types");
|
const types_1 = require("oak-domain/lib/types");
|
||||||
const cluster_adapter_1 = require("@socket.io/cluster-adapter");
|
const cluster_adapter_1 = require("@socket.io/cluster-adapter");
|
||||||
const sticky_1 = require("@socket.io/sticky");
|
const sticky_1 = require("@socket.io/sticky");
|
||||||
|
const redis_adapter_1 = require("@socket.io/redis-adapter");
|
||||||
|
const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
|
||||||
const socket_io_1 = require("socket.io");
|
const socket_io_1 = require("socket.io");
|
||||||
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
||||||
const SOCKET_NAMESPACE = '/sn';
|
const SOCKET_NAMESPACE = '/sn';
|
||||||
|
|
@ -29,6 +31,8 @@ function concat(...paths) {
|
||||||
}
|
}
|
||||||
async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
const serverConfiguration = require((0, path_1.join)(path, 'lib', 'configuration', 'server')).default;
|
const serverConfiguration = require((0, path_1.join)(path, 'lib', 'configuration', 'server')).default;
|
||||||
|
// 拿到package.json,用作项目的唯一标识,否则无法区分不同项目的Redis+socketIO连接
|
||||||
|
const packageJson = require((0, path_1.join)(path, 'package.json'));
|
||||||
const corsHeaders = [
|
const corsHeaders = [
|
||||||
'Content-Type',
|
'Content-Type',
|
||||||
'Content-Length',
|
'Content-Length',
|
||||||
|
|
@ -51,7 +55,7 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
}
|
}
|
||||||
: serverConfiguration.cors
|
: serverConfiguration.cors
|
||||||
? {
|
? {
|
||||||
origin: serverConfiguration.cors.origin,
|
origin: serverConfiguration.cors.origin, //socket.io配置cors origin是支持数组和字符串
|
||||||
allowedHeaders: [
|
allowedHeaders: [
|
||||||
...corsHeaders.concat(connector.getCorsHeader()),
|
...corsHeaders.concat(connector.getCorsHeader()),
|
||||||
...(serverConfiguration.cors.headers || []),
|
...(serverConfiguration.cors.headers || []),
|
||||||
|
|
@ -65,8 +69,62 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
// 目前只支持单物理结点的pm2模式
|
// 目前只支持单物理结点的pm2模式
|
||||||
// pm2环境下要接入clusterAdapter
|
// pm2环境下要接入clusterAdapter
|
||||||
// https://socket.io/zh-CN/docs/v4/pm2/
|
// https://socket.io/zh-CN/docs/v4/pm2/
|
||||||
|
// 在单机的所有实例之间使用pm2的集群适配器
|
||||||
io.adapter((0, cluster_adapter_1.createAdapter)());
|
io.adapter((0, cluster_adapter_1.createAdapter)());
|
||||||
(0, sticky_1.setupWorker)(io);
|
(0, sticky_1.setupWorker)(io);
|
||||||
|
if (clusterInfo.enableRedis) {
|
||||||
|
// 在多机器之间使用redis适配器
|
||||||
|
const redisConfig = serverConfiguration.redis;
|
||||||
|
if (!redisConfig) {
|
||||||
|
console.error('未配置Redis连接信息!');
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
const isCluster = Array.isArray(redisConfig);
|
||||||
|
// 创建 Redis 客户端
|
||||||
|
const pubClient = isCluster ?
|
||||||
|
new ioredis_1.default.Cluster(redisConfig.map((config) => ({
|
||||||
|
...config,
|
||||||
|
lazyConnect: true,
|
||||||
|
})))
|
||||||
|
: new ioredis_1.default({
|
||||||
|
...redisConfig,
|
||||||
|
lazyConnect: true,
|
||||||
|
});
|
||||||
|
const subClient = pubClient.duplicate();
|
||||||
|
pubClient.on('connect', () => {
|
||||||
|
console.log('PUB已成功连接到Redis服务器');
|
||||||
|
});
|
||||||
|
pubClient.on('error', (err) => {
|
||||||
|
console.error('连接到Redis失败!', err);
|
||||||
|
// 如果连接到Redis失败,直接退出
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
subClient.on('connect', () => {
|
||||||
|
console.log('SUB已成功连接到Redis服务器');
|
||||||
|
});
|
||||||
|
subClient.on('error', (err) => {
|
||||||
|
console.error('连接到Redis失败!', err);
|
||||||
|
// 如果连接到Redis失败,直接退出
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
await Promise.all([
|
||||||
|
pubClient.connect(),
|
||||||
|
subClient.connect(),
|
||||||
|
]);
|
||||||
|
io.adapter(
|
||||||
|
// 为了使单台Redis可以在多个项目之间复用,需要为每个项目创建一个唯一的key
|
||||||
|
(0, redis_adapter_1.createAdapter)(pubClient, subClient, {
|
||||||
|
key: `${packageJson.name}-socket.io-${process.env.NODE_ENV || 'development'}`
|
||||||
|
.replace(/[^a-zA-Z0-9-_:.]/g, '') // 移除特殊字符(只保留字母、数字、-、_、:、.)
|
||||||
|
.replace(/\s+/g, '-') // 将空格替换为-
|
||||||
|
.toLowerCase(), // 转换为小写(统一格式)
|
||||||
|
}));
|
||||||
|
console.log('已启用Redis适配器');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 如果没有启用Redis,不应该出现instanceCount大于实际实例数的情况
|
||||||
|
console.warn('正处于单机集群环境,请确保实例数正确!');
|
||||||
|
}
|
||||||
console.log(`以集群模式启动,实例总数『${clusterInfo.instanceCount}』,当前实例号『${clusterInfo.instanceId}』`);
|
console.log(`以集群模式启动,实例总数『${clusterInfo.instanceCount}』,当前实例号『${clusterInfo.instanceId}』`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -244,4 +302,3 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||||
};
|
};
|
||||||
return shutdown;
|
return shutdown;
|
||||||
}
|
}
|
||||||
exports.startup = startup;
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/// <reference types="node" />
|
|
||||||
export type LogFormatterProp = {
|
export type LogFormatterProp = {
|
||||||
level: "info" | "warn" | "error";
|
level: "info" | "warn" | "error";
|
||||||
caller: NodeJS.CallSite | null;
|
caller: NodeJS.CallSite | null;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ const tslib_1 = require("tslib");
|
||||||
const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
|
const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
|
||||||
const typescript_1 = tslib_1.__importDefault(require("typescript"));
|
const typescript_1 = tslib_1.__importDefault(require("typescript"));
|
||||||
const path_1 = tslib_1.__importDefault(require("path"));
|
const path_1 = tslib_1.__importDefault(require("path"));
|
||||||
const start_1 = require("./start");
|
|
||||||
const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
||||||
const fs_1 = tslib_1.__importDefault(require("fs"));
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
||||||
const lodash_1 = require("lodash");
|
const lodash_1 = require("lodash");
|
||||||
|
|
@ -95,9 +94,160 @@ const getOverrideConfig = (config) => {
|
||||||
? overrideInner(config, defaultConfig)
|
? overrideInner(config, defaultConfig)
|
||||||
: defaultConfig;
|
: defaultConfig;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 根据 alias 配置表将路径中的别名替换为真实路径
|
||||||
|
* @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module"
|
||||||
|
* @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组
|
||||||
|
* @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径
|
||||||
|
*/
|
||||||
|
function replaceAliasWithPath(path, aliasConfig) {
|
||||||
|
for (const [alias, targets] of Object.entries(aliasConfig)) {
|
||||||
|
// If alias ends with "*", handle it as a dynamic alias.
|
||||||
|
if (alias.endsWith('*')) {
|
||||||
|
// Create a regex pattern that matches paths starting with the alias, followed by any characters
|
||||||
|
const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)'
|
||||||
|
const match = path.match(aliasPattern);
|
||||||
|
if (match) {
|
||||||
|
// Replace the alias with the target path, appending the matched part from the original path
|
||||||
|
const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array)
|
||||||
|
// Ensure that the target path ends with a slash if it's not already
|
||||||
|
const replacedPath = target.replace(/\/\*$/, "/") + match[1];
|
||||||
|
return replacedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle static alias without "*" by directly matching the path
|
||||||
|
if (path.startsWith(alias)) {
|
||||||
|
const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array)
|
||||||
|
// Replace the alias part with the target path
|
||||||
|
return path.replace(alias, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no alias matches, return the original path
|
||||||
|
return path;
|
||||||
|
}
|
||||||
const watch = (projectPath, config) => {
|
const watch = (projectPath, config) => {
|
||||||
const realConfig = getOverrideConfig(config);
|
const realConfig = getOverrideConfig(config);
|
||||||
const enableTrace = !!process.env.ENABLE_TRACE;
|
const enableTrace = !!process.env.ENABLE_TRACE;
|
||||||
|
// 查找配置文件
|
||||||
|
const configFileName = typescript_1.default.findConfigFile(projectPath, typescript_1.default.sys.fileExists, "tsconfig.build.json");
|
||||||
|
if (!configFileName) {
|
||||||
|
throw new Error("Could not find a valid 'tsconfig.build.json'.");
|
||||||
|
}
|
||||||
|
// 读取配置文件
|
||||||
|
const configFile = typescript_1.default.readConfigFile(configFileName, typescript_1.default.sys.readFile);
|
||||||
|
// 解析配置文件内容
|
||||||
|
const { options, projectReferences } = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configFileName));
|
||||||
|
const aliasConfig = (0, lodash_1.cloneDeep)(options.paths) || {};
|
||||||
|
// 输出原始配置
|
||||||
|
// console.log("[DEBUG] Original alias config:", aliasConfig);
|
||||||
|
Object.keys(aliasConfig).forEach((key) => {
|
||||||
|
const value = aliasConfig[key];
|
||||||
|
// 替换src
|
||||||
|
aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v) => v.replace("src", "lib"));
|
||||||
|
});
|
||||||
|
// 输出真实的alias配置
|
||||||
|
console.debug("[DEBUG] Running Alias config:", aliasConfig);
|
||||||
|
const createProgramAndSourceFile = (path, options, projectReferences) => {
|
||||||
|
const program = typescript_1.default.createProgram({
|
||||||
|
rootNames: [path],
|
||||||
|
options,
|
||||||
|
projectReferences,
|
||||||
|
});
|
||||||
|
const sourceFile = program.getSourceFile(path);
|
||||||
|
// 是否有语法错误
|
||||||
|
const diagnostics = typescript_1.default.getPreEmitDiagnostics(program, sourceFile);
|
||||||
|
if (diagnostics.length) {
|
||||||
|
const syntaxErrors = diagnostics.filter((diagnostic) => diagnostic.category === typescript_1.default.DiagnosticCategory.Error);
|
||||||
|
if (syntaxErrors.length) {
|
||||||
|
console.error(`Error in ${path}`);
|
||||||
|
syntaxErrors.forEach((diagnostic) => {
|
||||||
|
console.error(`${typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
|
||||||
|
});
|
||||||
|
console.error(`文件存在语法错误,请检查修复后重试!`);
|
||||||
|
return { program, sourceFile, diagnostics };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { program, sourceFile, diagnostics };
|
||||||
|
};
|
||||||
|
// 这个函数用于解决require的时候,如果文件不存在,会尝试编译ts文件
|
||||||
|
// 测试:在src下新建一个ts文件,然后删除lib下的js文件,然后require这个文件,会发现会自动编译ts文件
|
||||||
|
const polyfillLoader = () => {
|
||||||
|
const BuiltinModule = require("module");
|
||||||
|
// 模拟环境下的 module 构造函数
|
||||||
|
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||||
|
// 保存原始 _resolveFilename 方法
|
||||||
|
const oldResolveFilename = Module._resolveFilename;
|
||||||
|
// 通用的路径检查与编译函数
|
||||||
|
function resolveAndCompile(requestPath, parent, options, projectReferences) {
|
||||||
|
const jsPath = path_1.default.resolve(requestPath + ".js");
|
||||||
|
const tsPath = jsPath.replace(/\.js$/, ".ts").replace(path_1.default.join(projectPath, "lib"), path_1.default.join(projectPath, "src"));
|
||||||
|
// 检查并编译 .ts 文件
|
||||||
|
if (fs_1.default.existsSync(tsPath)) {
|
||||||
|
console.log(`[resolve] Found TypeScript source file: ${tsPath}, attempting to compile...`);
|
||||||
|
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(tsPath, options, projectReferences);
|
||||||
|
if (diagnostics.length) {
|
||||||
|
console.error(`[resolve] Compilation failed for: ${tsPath}`);
|
||||||
|
throw new Error("TypeScript compilation error");
|
||||||
|
}
|
||||||
|
const emitResult = program.emit(sourceFile);
|
||||||
|
if (emitResult.emitSkipped) {
|
||||||
|
console.error(`[resolve] Emit skipped for: ${tsPath}`);
|
||||||
|
throw new Error("TypeScript emit skipped");
|
||||||
|
}
|
||||||
|
console.log(`[resolve] Successfully compiled: ${tsPath}`);
|
||||||
|
return jsPath;
|
||||||
|
}
|
||||||
|
// 如果没有找到对应的 .ts 文件
|
||||||
|
if (fs_1.default.existsSync(jsPath)) {
|
||||||
|
return jsPath;
|
||||||
|
}
|
||||||
|
throw new Error(`[resolve] Unable to find module: ${requestPath}`);
|
||||||
|
}
|
||||||
|
// 处理文件夹导入的情况
|
||||||
|
function resolveDirectory(requestPath, parent, options, projectReferences) {
|
||||||
|
const indexJs = path_1.default.join(requestPath, "index.js");
|
||||||
|
const indexTs = path_1.default.join(requestPath, "index.ts").replace(path_1.default.join(projectPath, "lib"), path_1.default.join(projectPath, "src"));
|
||||||
|
if (fs_1.default.existsSync(indexTs)) {
|
||||||
|
console.log(`[resolve] Found TypeScript index file: ${indexTs}, attempting to compile...`);
|
||||||
|
return resolveAndCompile(indexTs, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
if (fs_1.default.existsSync(indexJs)) {
|
||||||
|
return indexJs;
|
||||||
|
}
|
||||||
|
throw new Error(`[resolve] No index file found in directory: ${requestPath}`);
|
||||||
|
}
|
||||||
|
// 重写 _resolveFilename 方法
|
||||||
|
Module._resolveFilename = function (request, // 模块请求路径
|
||||||
|
parent, // 调用方模块
|
||||||
|
isMain, // 是否是主模块
|
||||||
|
rFoptions // 解析选项
|
||||||
|
) {
|
||||||
|
let resolvedRequest = request;
|
||||||
|
const replacedPath = replaceAliasWithPath(request, aliasConfig);
|
||||||
|
if (replacedPath !== request) {
|
||||||
|
console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`);
|
||||||
|
resolvedRequest = path_1.default.join(projectPath, replacedPath);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error.code === "MODULE_NOT_FOUND") {
|
||||||
|
const requestPath = path_1.default.resolve(path_1.default.dirname(parent.filename), resolvedRequest);
|
||||||
|
// 处理文件夹导入
|
||||||
|
if (fs_1.default.existsSync(requestPath) && fs_1.default.statSync(requestPath).isDirectory()) {
|
||||||
|
return resolveDirectory(requestPath, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
// 处理单文件导入
|
||||||
|
return resolveAndCompile(requestPath, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
polyfillLoader();
|
||||||
//polyfill console.log 添加时间
|
//polyfill console.log 添加时间
|
||||||
const polyfillConsole = (trace) => {
|
const polyfillConsole = (trace) => {
|
||||||
// 获取调用堆栈信息
|
// 获取调用堆栈信息
|
||||||
|
|
@ -141,6 +291,51 @@ const watch = (projectPath, config) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
||||||
|
// 这里注意要在require之前,因为require会触发编译
|
||||||
|
const { startup } = require('./start');
|
||||||
|
// 如果lib目录是空的,则直接编译所有的ts文件
|
||||||
|
const serverConfigFile = path_1.default.join(projectPath, "lib/configuration/server.js");
|
||||||
|
if (!fs_1.default.existsSync(serverConfigFile)) {
|
||||||
|
// 尝试编译src/configuration/server.ts
|
||||||
|
console.log(`[watch] Server configuration file not found, attempting to compile the project......`);
|
||||||
|
const tryCompile = (tsFile) => {
|
||||||
|
console.log(`[watch] Compiling: ${tsFile}`);
|
||||||
|
if (fs_1.default.existsSync(tsFile)) {
|
||||||
|
const { program, diagnostics } = createProgramAndSourceFile(tsFile, options, projectReferences);
|
||||||
|
if (diagnostics.length) {
|
||||||
|
console.error(`Error in ${tsFile}`);
|
||||||
|
diagnostics.forEach((diagnostic) => {
|
||||||
|
console.error(`${typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
|
||||||
|
});
|
||||||
|
console.error(`文件存在语法错误,请检查修复后重试!`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// const emitResult = program.emit(sourceFile);
|
||||||
|
// 编译所有的文件
|
||||||
|
const emitResult = program.emit();
|
||||||
|
if (emitResult.emitSkipped) {
|
||||||
|
console.error(`Emit failed for ${tsFile}!`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(`Emit succeeded. ${tsFile} has been compiled.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 所有要编译的目录
|
||||||
|
// 其他涉及到的目录会在运行的时候自动编译,这里主要处理的是动态require的文件
|
||||||
|
const compileFiles = [
|
||||||
|
"src/configuration/index.ts",
|
||||||
|
"src/aspects/index.ts",
|
||||||
|
"src/checkers/index.ts",
|
||||||
|
"src/triggers/index.ts",
|
||||||
|
"src/timers/index.ts",
|
||||||
|
"src/routines/start.ts",
|
||||||
|
"src/watchers/index.ts",
|
||||||
|
"src/endpoints/index.ts",
|
||||||
|
"src/data/index.ts",
|
||||||
|
"src/ports/index.ts",
|
||||||
|
];
|
||||||
|
compileFiles.forEach(tryCompile);
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
realConfig.lifecycle.onInit();
|
realConfig.lifecycle.onInit();
|
||||||
let shutdown;
|
let shutdown;
|
||||||
|
|
@ -148,6 +343,8 @@ const watch = (projectPath, config) => {
|
||||||
if (shutdown) {
|
if (shutdown) {
|
||||||
console.log("----> Shutting down service......");
|
console.log("----> Shutting down service......");
|
||||||
await shutdown().then(realConfig.lifecycle.onServerShutdown);
|
await shutdown().then(realConfig.lifecycle.onServerShutdown);
|
||||||
|
// reset shutdown
|
||||||
|
shutdown = undefined;
|
||||||
}
|
}
|
||||||
console.warn("----> Clearing require cache of project......");
|
console.warn("----> Clearing require cache of project......");
|
||||||
let deleteCount = 0;
|
let deleteCount = 0;
|
||||||
|
|
@ -168,22 +365,13 @@ const watch = (projectPath, config) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const simpleConnector = require(path_1.default.join(projectPath, "lib/config/connector")).default;
|
const simpleConnector = require(path_1.default.join(projectPath, "lib/config/connector")).default;
|
||||||
console.warn("----> Starting service......");
|
console.warn("----> Starting service......");
|
||||||
shutdown = await (0, start_1.startup)(pwd, simpleConnector).then((shutdown) => {
|
shutdown = await startup(pwd, simpleConnector).then((shutdown) => {
|
||||||
realConfig.lifecycle.onServerStart();
|
realConfig.lifecycle.onServerStart();
|
||||||
return shutdown;
|
return shutdown;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const watchSourcePath = path_1.default.join(projectPath, "src");
|
const watchSourcePath = path_1.default.join(projectPath, "src");
|
||||||
console.log("Watching for changes in", watchSourcePath);
|
console.log("Watching for changes in", watchSourcePath);
|
||||||
// 查找配置文件
|
|
||||||
const configFileName = typescript_1.default.findConfigFile(projectPath, typescript_1.default.sys.fileExists, "tsconfig.build.json");
|
|
||||||
if (!configFileName) {
|
|
||||||
throw new Error("Could not find a valid 'tsconfig.build.json'.");
|
|
||||||
}
|
|
||||||
// 读取配置文件
|
|
||||||
const configFile = typescript_1.default.readConfigFile(configFileName, typescript_1.default.sys.readFile);
|
|
||||||
// 解析配置文件内容
|
|
||||||
const { options, projectReferences } = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configFileName));
|
|
||||||
const watcher = chokidar_1.default.watch(watchSourcePath, {
|
const watcher = chokidar_1.default.watch(watchSourcePath, {
|
||||||
persistent: true,
|
persistent: true,
|
||||||
ignored: (file) => file.endsWith(".tsx") ||
|
ignored: (file) => file.endsWith(".tsx") ||
|
||||||
|
|
@ -207,13 +395,14 @@ const watch = (projectPath, config) => {
|
||||||
startWatching = true;
|
startWatching = true;
|
||||||
});
|
});
|
||||||
watcher.on("error", (error) => console.log(`Watcher error: ${error}`));
|
watcher.on("error", (error) => console.log(`Watcher error: ${error}`));
|
||||||
let isProcessing = false;
|
let processingQueue = [];
|
||||||
const fileChangeHandler = async (path, type) => {
|
const fileChangeHandler = async (path, type) => {
|
||||||
// 判断一下是不是以下扩展名:ts
|
// 判断一下是不是以下扩展名:ts
|
||||||
if (!path.endsWith(".ts")) {
|
if (!path.endsWith(".ts")) {
|
||||||
// 如果是json文件,复制或者删除
|
// 如果是json文件,复制或者删除
|
||||||
if (path.endsWith(".json")) {
|
if (path.endsWith(".json")) {
|
||||||
const targetPath = path.replace("src", "lib");
|
// const targetPath = path.replace("src", "lib"); // 这里直接替换不对,应该是拿到项目目录,把项目目录/src 换成项目目录/lib
|
||||||
|
const targetPath = path.replace(path_1.default.join(projectPath, "src"), path_1.default.join(projectPath, "lib"));
|
||||||
if (type === "remove") {
|
if (type === "remove") {
|
||||||
fs_1.default.unlinkSync(targetPath);
|
fs_1.default.unlinkSync(targetPath);
|
||||||
console.warn(`File ${targetPath} has been removed.`);
|
console.warn(`File ${targetPath} has been removed.`);
|
||||||
|
|
@ -243,8 +432,8 @@ const watch = (projectPath, config) => {
|
||||||
const modulePath = path_1.default.resolve(path);
|
const modulePath = path_1.default.resolve(path);
|
||||||
// 将src替换为lib
|
// 将src替换为lib
|
||||||
const libPath = modulePath
|
const libPath = modulePath
|
||||||
.replace("\\src\\", "\\lib\\")
|
.replace(path_1.default.join(projectPath, "src"), path_1.default.join(projectPath, "lib"))
|
||||||
.replace(".ts", ".js");
|
.replace(/\.ts$/, ".js");
|
||||||
let compileOnly = false;
|
let compileOnly = false;
|
||||||
if (!require.cache[libPath]) {
|
if (!require.cache[libPath]) {
|
||||||
// 如果是删除的话,直接尝试删除lib下的文件
|
// 如果是删除的话,直接尝试删除lib下的文件
|
||||||
|
|
@ -258,8 +447,11 @@ const watch = (projectPath, config) => {
|
||||||
console.warn(`File ${libPath} has been removed.`);
|
console.warn(`File ${libPath} has been removed.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.warn(`File ${libPath} is not in module cache, will compile only.`);
|
// console.warn(
|
||||||
compileOnly = true;
|
// `File ${libPath} is not in module cache, will compile only.`
|
||||||
|
// );
|
||||||
|
// compileOnly = true;
|
||||||
|
// 这里现在不需要仅编译了,因为在require的时候会自动编译ts文件
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// 如果是删除,则需要发出警告,文件正在被进程使用
|
// 如果是删除,则需要发出警告,文件正在被进程使用
|
||||||
|
|
@ -268,24 +460,9 @@ const watch = (projectPath, config) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const program = typescript_1.default.createProgram({
|
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(path, options, projectReferences);
|
||||||
rootNames: [path],
|
|
||||||
options,
|
|
||||||
projectReferences,
|
|
||||||
});
|
|
||||||
const sourceFile = program.getSourceFile(path);
|
|
||||||
// 是否有语法错误
|
|
||||||
const diagnostics = typescript_1.default.getPreEmitDiagnostics(program, sourceFile);
|
|
||||||
if (diagnostics.length) {
|
if (diagnostics.length) {
|
||||||
const syntaxErrors = diagnostics.filter((diagnostic) => diagnostic.category === typescript_1.default.DiagnosticCategory.Error);
|
return;
|
||||||
if (syntaxErrors.length) {
|
|
||||||
console.error(`Error in ${path}`);
|
|
||||||
syntaxErrors.forEach((diagnostic) => {
|
|
||||||
console.error(`${typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
|
|
||||||
});
|
|
||||||
console.error(`文件存在语法错误,请检查修复后重试!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 只输出单个文件
|
// 只输出单个文件
|
||||||
realConfig.lifecycle.onBeforeCompile();
|
realConfig.lifecycle.onBeforeCompile();
|
||||||
|
|
@ -302,40 +479,47 @@ const watch = (projectPath, config) => {
|
||||||
if (compileOnly) {
|
if (compileOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await restart();
|
// await restart(); // 只有在队列里面的最后一个文件编译完成后才会重启服务
|
||||||
|
if (processingQueue.length === 1) {
|
||||||
|
await restart();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Waiting for other operations to complete...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onChangeDebounced = (0, lodash_1.debounce)(async (path, type) => {
|
const onChangeDebounced = async (path, type) => {
|
||||||
if (isProcessing) {
|
if (processingQueue.includes(path)) {
|
||||||
console.log("Processing, please wait...");
|
console.log("Processing, please wait...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
isProcessing = true;
|
processingQueue.push(path);
|
||||||
await fileChangeHandler(path, type);
|
await fileChangeHandler(path, type);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.clear();
|
console.clear();
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
isProcessing = false;
|
processingQueue = processingQueue.filter((p) => p !== path);
|
||||||
}
|
}
|
||||||
}, 100);
|
};
|
||||||
watcher
|
watcher
|
||||||
.on("add", (path) => {
|
.on("add", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "add");
|
await onChangeDebounced(path, "add");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("change", (path) => {
|
.on("change", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "change");
|
await onChangeDebounced(path, "change");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("unlink", (path) => {
|
.on("unlink", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "remove");
|
await onChangeDebounced(path, "remove");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const dispose = async () => {
|
const dispose = async () => {
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { PackageJsonInput } from './interface';
|
||||||
export declare function packageJsonContent({ name, version, description, cliName, cliBinName, isDev, isModule, dependencies }: PackageJsonInput): string;
|
export declare function packageJsonContent({ name, version, description, cliName, cliBinName, isDev, isModule, dependencies }: PackageJsonInput): string;
|
||||||
export declare function tsConfigJsonContent(): string;
|
export declare function tsConfigJsonContent(): string;
|
||||||
export declare function tsConfigBuildJsonContent(): string;
|
export declare function tsConfigBuildJsonContent(): string;
|
||||||
export declare function tsConfigBuildPathsJsonContent(): string;
|
export declare function tsConfigPathsJsonContent(deps: string[]): string;
|
||||||
export declare function tsConfigPathsJsonContent(): string;
|
|
||||||
export declare function tsConfigMpJsonContent(): string;
|
export declare function tsConfigMpJsonContent(): string;
|
||||||
export declare function tsConfigWebJsonContent(): string;
|
export declare function tsConfigWebJsonContent(): string;
|
||||||
export declare function projectConfigContentWithWeChatMp(oakConfigName: string, projectname: string, miniVersion: string): string;
|
export declare function projectConfigContentWithWeChatMp(oakConfigName: string, projectname: string, miniVersion: string): string;
|
||||||
export declare function appJsonContentWithWeChatMp(isDev: boolean): string;
|
export declare function appJsonContentWithWeChatMp(isDev: boolean): string;
|
||||||
export declare function oakConfigContentWithWeChatMp(): string;
|
export declare function oakConfigContentWithWeChatMp(): string;
|
||||||
export declare function oakConfigContentWithWeb(): string;
|
export declare function oakConfigContentWithWeb(): string;
|
||||||
|
export declare function updateCompilerJsContent(directory: string, deps: string[]): void;
|
||||||
|
|
|
||||||
173
lib/template.js
173
lib/template.js
|
|
@ -1,9 +1,24 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.oakConfigContentWithWeb = exports.oakConfigContentWithWeChatMp = exports.appJsonContentWithWeChatMp = exports.projectConfigContentWithWeChatMp = exports.tsConfigWebJsonContent = exports.tsConfigMpJsonContent = exports.tsConfigPathsJsonContent = exports.tsConfigBuildPathsJsonContent = exports.tsConfigBuildJsonContent = exports.tsConfigJsonContent = exports.packageJsonContent = void 0;
|
exports.packageJsonContent = packageJsonContent;
|
||||||
|
exports.tsConfigJsonContent = tsConfigJsonContent;
|
||||||
|
exports.tsConfigBuildJsonContent = tsConfigBuildJsonContent;
|
||||||
|
exports.tsConfigPathsJsonContent = tsConfigPathsJsonContent;
|
||||||
|
exports.tsConfigMpJsonContent = tsConfigMpJsonContent;
|
||||||
|
exports.tsConfigWebJsonContent = tsConfigWebJsonContent;
|
||||||
|
exports.projectConfigContentWithWeChatMp = projectConfigContentWithWeChatMp;
|
||||||
|
exports.appJsonContentWithWeChatMp = appJsonContentWithWeChatMp;
|
||||||
|
exports.oakConfigContentWithWeChatMp = oakConfigContentWithWeChatMp;
|
||||||
|
exports.oakConfigContentWithWeb = oakConfigContentWithWeb;
|
||||||
|
exports.updateCompilerJsContent = updateCompilerJsContent;
|
||||||
|
const tslib_1 = require("tslib");
|
||||||
const child_process_1 = require("child_process");
|
const child_process_1 = require("child_process");
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
|
const core_1 = require("@babel/core");
|
||||||
|
const t = tslib_1.__importStar(require("@babel/types"));
|
||||||
|
const generator_1 = tslib_1.__importDefault(require("@babel/generator"));
|
||||||
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
/**
|
/**
|
||||||
* 利用npm info获得相应库的最新版本
|
* 利用npm info获得相应库的最新版本
|
||||||
* @param name
|
* @param name
|
||||||
|
|
@ -88,6 +103,7 @@ function packageJsonContent({ name, version, description, cliName, cliBinName, i
|
||||||
"run:android": "oak-cli run -p android",
|
"run:android": "oak-cli run -p android",
|
||||||
"server:init": "${serverInitScript}",
|
"server:init": "${serverInitScript}",
|
||||||
"server:start": "${serverStartWatchScript}",
|
"server:start": "${serverStartWatchScript}",
|
||||||
|
"server:ana": "cross-env ENABLE_TRACE=true cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/analysis.js",
|
||||||
"postinstall": "npm run make:dep"
|
"postinstall": "npm run make:dep"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|
@ -257,8 +273,7 @@ function packageJsonContent({ name, version, description, cliName, cliBinName, i
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"webpack-manifest-plugin": "^4.0.2",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"workbox-webpack-plugin": "^6.4.1",
|
"workbox-webpack-plugin": "^6.4.1",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.1"
|
||||||
"module-alias": "^2.2.3"
|
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
@ -290,18 +305,10 @@ function packageJsonContent({ name, version, description, cliName, cliBinName, i
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"readable-stream": "3.6.2"
|
"readable-stream": "3.6.2"
|
||||||
},
|
|
||||||
"_moduleAliases": {
|
|
||||||
"@project": "./lib",
|
|
||||||
"@oak-general-business": "./node_modules/oak-general-business/lib",
|
|
||||||
"@oak-frontend-base": "./node_modules/oak-frontend-base/lib",
|
|
||||||
"@oak-app-domain": "./lib/oak-app-domain"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
// _moduleAliases用于lib内运行时的模块声明,重载之后require的路径还会保留@project,需要这样的方式来进行路径alias
|
|
||||||
}
|
}
|
||||||
exports.packageJsonContent = packageJsonContent;
|
|
||||||
function tsConfigJsonContent() {
|
function tsConfigJsonContent() {
|
||||||
return `{
|
return `{
|
||||||
"extends": "./tsconfig.paths.json",
|
"extends": "./tsconfig.paths.json",
|
||||||
|
|
@ -353,7 +360,6 @@ function tsConfigJsonContent() {
|
||||||
]
|
]
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.tsConfigJsonContent = tsConfigJsonContent;
|
|
||||||
function tsConfigBuildJsonContent() {
|
function tsConfigBuildJsonContent() {
|
||||||
return `{
|
return `{
|
||||||
"extends": "./tsconfig.build.paths.json",
|
"extends": "./tsconfig.build.paths.json",
|
||||||
|
|
@ -393,47 +399,34 @@ function tsConfigBuildJsonContent() {
|
||||||
]
|
]
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.tsConfigBuildJsonContent = tsConfigBuildJsonContent;
|
function tsConfigPathsJsonContent(deps) {
|
||||||
function tsConfigBuildPathsJsonContent() {
|
const paths = {
|
||||||
return `{
|
"@project/*": [
|
||||||
"compilerOptions": {
|
"src/*"
|
||||||
"baseUrl": "./",
|
],
|
||||||
"paths": {
|
"@oak-app-domain": [
|
||||||
"@project/*": [
|
"src/oak-app-domain/index"
|
||||||
"src/*"
|
],
|
||||||
],
|
"@oak-app-domain/*": [
|
||||||
"@oak-app-domain": [
|
"src/oak-app-domain/*"
|
||||||
"src/oak-app-domain/index"
|
],
|
||||||
],
|
"@oak-frontend-base/*": [
|
||||||
"@oak-app-domain/*": [
|
"node_modules/oak-frontend-base/lib/*"
|
||||||
"src/oak-app-domain/*"
|
],
|
||||||
],
|
};
|
||||||
},
|
deps.forEach((ele) => {
|
||||||
"typeRoots": ["./typings"]
|
Object.assign(paths, {
|
||||||
}
|
[`@${ele}/*`]: [`node_modules/${ele}/lib/*`],
|
||||||
}`;
|
});
|
||||||
|
});
|
||||||
|
return JSON.stringify({
|
||||||
|
compilerOptions: {
|
||||||
|
baseUrl: "./",
|
||||||
|
paths,
|
||||||
|
typeRoots: ["./typings"]
|
||||||
|
}
|
||||||
|
}, null, '\t');
|
||||||
}
|
}
|
||||||
exports.tsConfigBuildPathsJsonContent = tsConfigBuildPathsJsonContent;
|
|
||||||
function tsConfigPathsJsonContent() {
|
|
||||||
return `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "./",
|
|
||||||
"paths": {
|
|
||||||
"@project/*": [
|
|
||||||
"src/*"
|
|
||||||
],
|
|
||||||
"@oak-app-domain": [
|
|
||||||
"src/oak-app-domain/index"
|
|
||||||
],
|
|
||||||
"@oak-app-domain/*": [
|
|
||||||
"src/oak-app-domain/*"
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"typeRoots": ["./typings"]
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
exports.tsConfigPathsJsonContent = tsConfigPathsJsonContent;
|
|
||||||
function tsConfigMpJsonContent() {
|
function tsConfigMpJsonContent() {
|
||||||
return `{
|
return `{
|
||||||
"extends": "./tsconfig.paths.json",
|
"extends": "./tsconfig.paths.json",
|
||||||
|
|
@ -481,7 +474,6 @@ function tsConfigMpJsonContent() {
|
||||||
]
|
]
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.tsConfigMpJsonContent = tsConfigMpJsonContent;
|
|
||||||
function tsConfigWebJsonContent() {
|
function tsConfigWebJsonContent() {
|
||||||
return `{
|
return `{
|
||||||
"extends": "./tsconfig.paths.json",
|
"extends": "./tsconfig.paths.json",
|
||||||
|
|
@ -531,7 +523,6 @@ function tsConfigWebJsonContent() {
|
||||||
]
|
]
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.tsConfigWebJsonContent = tsConfigWebJsonContent;
|
|
||||||
function projectConfigContentWithWeChatMp(oakConfigName, projectname, miniVersion) {
|
function projectConfigContentWithWeChatMp(oakConfigName, projectname, miniVersion) {
|
||||||
return `{
|
return `{
|
||||||
"description": "项目配置文件",
|
"description": "项目配置文件",
|
||||||
|
|
@ -607,7 +598,6 @@ function projectConfigContentWithWeChatMp(oakConfigName, projectname, miniVersio
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.projectConfigContentWithWeChatMp = projectConfigContentWithWeChatMp;
|
|
||||||
function appJsonContentWithWeChatMp(isDev) {
|
function appJsonContentWithWeChatMp(isDev) {
|
||||||
const pages = [
|
const pages = [
|
||||||
'@project/pages/store/list/index',
|
'@project/pages/store/list/index',
|
||||||
|
|
@ -631,18 +621,73 @@ function appJsonContentWithWeChatMp(isDev) {
|
||||||
"sitemapLocation": "sitemap.json"
|
"sitemapLocation": "sitemap.json"
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.appJsonContentWithWeChatMp = appJsonContentWithWeChatMp;
|
|
||||||
function oakConfigContentWithWeChatMp() {
|
function oakConfigContentWithWeChatMp() {
|
||||||
return `{
|
return `{
|
||||||
"theme": {
|
"theme": {
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
exports.oakConfigContentWithWeChatMp = oakConfigContentWithWeChatMp;
|
|
||||||
function oakConfigContentWithWeb() {
|
function oakConfigContentWithWeb() {
|
||||||
return `{
|
return `{
|
||||||
"theme": {
|
"theme": {
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
}
|
||||||
|
function updateCompilerJsContent(directory, deps) {
|
||||||
|
const compilerJsPath = (0, path_1.join)(directory, 'configuration', 'compiler.js');
|
||||||
|
(0, assert_1.default)((0, fs_1.existsSync)(compilerJsPath));
|
||||||
|
if (deps.length > 0) {
|
||||||
|
const { ast } = (0, core_1.transformFileSync)(compilerJsPath, { ast: true });
|
||||||
|
const { program } = ast;
|
||||||
|
const { body } = program;
|
||||||
|
const moduleExportStmt = body[body.length - 1];
|
||||||
|
/**
|
||||||
|
* 导出语句:
|
||||||
|
module.exports = CreateCompilerConfig({
|
||||||
|
webpack: {
|
||||||
|
extraOakModules: [],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@oak-frontend-base': oakFrontendBasePath,
|
||||||
|
'@project': path.resolve('src'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
(0, assert_1.default)(t.isExpressionStatement(moduleExportStmt));
|
||||||
|
const { expression } = moduleExportStmt;
|
||||||
|
(0, assert_1.default)(t.isAssignmentExpression(expression));
|
||||||
|
const { left, right } = expression;
|
||||||
|
(0, assert_1.default)(t.isMemberExpression(left));
|
||||||
|
const { property, object } = left;
|
||||||
|
(0, assert_1.default)(t.isIdentifier(object) && object.name === 'module');
|
||||||
|
(0, assert_1.default)(t.isIdentifier(property) && property.name === 'exports');
|
||||||
|
(0, assert_1.default)(t.isCallExpression(right));
|
||||||
|
const [webPackExpr] = right.arguments;
|
||||||
|
(0, assert_1.default)(t.isObjectExpression(webPackExpr));
|
||||||
|
const [webPackProp] = webPackExpr.properties;
|
||||||
|
(0, assert_1.default)(t.isObjectProperty(webPackProp));
|
||||||
|
const { key, value } = webPackProp;
|
||||||
|
(0, assert_1.default)(t.isIdentifier(key) && key.name === 'webpack');
|
||||||
|
const [modulesProp, resolveProp] = value.properties;
|
||||||
|
(0, assert_1.default)(t.isObjectProperty(modulesProp));
|
||||||
|
const { value: value2 } = modulesProp;
|
||||||
|
(0, assert_1.default)(t.isArrayExpression(value2));
|
||||||
|
value2.elements.push(...deps.map((ele) => t.regExpLiteral(ele)));
|
||||||
|
(0, assert_1.default)(t.isObjectProperty(resolveProp));
|
||||||
|
const { value: value3 } = resolveProp;
|
||||||
|
(0, assert_1.default)(t.isObjectExpression(value3));
|
||||||
|
const [aliasProp] = value3.properties;
|
||||||
|
(0, assert_1.default)(t.isObjectProperty(aliasProp));
|
||||||
|
const { value: value4 } = aliasProp;
|
||||||
|
(0, assert_1.default)(t.isObjectExpression(value4));
|
||||||
|
const { properties: aliasProperties } = value4;
|
||||||
|
(0, assert_1.default)(aliasProperties.length === 2); // @project和@oak-frontend-base
|
||||||
|
aliasProperties.push(...deps.map((ele) => t.objectProperty(t.stringLiteral(`@${ele}`), t.callExpression(t.memberExpression(t.identifier('path'), t.identifier('resolve')), [
|
||||||
|
t.stringLiteral(`node_modules/${ele}/es`)
|
||||||
|
]))));
|
||||||
|
const { code } = (0, generator_1.default)(ast);
|
||||||
|
(0, fs_1.writeFileSync)(compilerJsPath, code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.oakConfigContentWithWeb = oakConfigContentWithWeb;
|
|
||||||
|
|
|
||||||
15
lib/utils.js
15
lib/utils.js
|
|
@ -1,6 +1,12 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.deWeight = exports.formatJsonByFile = exports.union = exports.intersect = exports.difference = exports.getStr = exports.findJson = void 0;
|
exports.findJson = findJson;
|
||||||
|
exports.getStr = getStr;
|
||||||
|
exports.difference = difference;
|
||||||
|
exports.intersect = intersect;
|
||||||
|
exports.union = union;
|
||||||
|
exports.formatJsonByFile = formatJsonByFile;
|
||||||
|
exports.deWeight = deWeight;
|
||||||
/**
|
/**
|
||||||
* @name 从一组路径里查找到所有json文件
|
* @name 从一组路径里查找到所有json文件
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -18,7 +24,6 @@ function findJson(pathArr) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
exports.findJson = findJson;
|
|
||||||
/**
|
/**
|
||||||
* @name 已知前后文取中间文本
|
* @name 已知前后文取中间文本
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -32,7 +37,6 @@ function getStr(str, start, end) {
|
||||||
let res = str.match(reg);
|
let res = str.match(reg);
|
||||||
return res ? res[1] : null;
|
return res ? res[1] : null;
|
||||||
}
|
}
|
||||||
exports.getStr = getStr;
|
|
||||||
/**
|
/**
|
||||||
* @name 差集
|
* @name 差集
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -44,7 +48,6 @@ exports.getStr = getStr;
|
||||||
function difference(current, target) {
|
function difference(current, target) {
|
||||||
return new Set([...target].filter(x => !current.has(x)));
|
return new Set([...target].filter(x => !current.has(x)));
|
||||||
}
|
}
|
||||||
exports.difference = difference;
|
|
||||||
/**
|
/**
|
||||||
* @name 获取交集
|
* @name 获取交集
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -56,7 +59,6 @@ exports.difference = difference;
|
||||||
function intersect(current, target) {
|
function intersect(current, target) {
|
||||||
return new Set([...target].filter(x => current.has(x)));
|
return new Set([...target].filter(x => current.has(x)));
|
||||||
}
|
}
|
||||||
exports.intersect = intersect;
|
|
||||||
/**
|
/**
|
||||||
* @name 获取并集
|
* @name 获取并集
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -68,7 +70,6 @@ exports.intersect = intersect;
|
||||||
function union(current, target) {
|
function union(current, target) {
|
||||||
return new Set([...current, ...target]);
|
return new Set([...current, ...target]);
|
||||||
}
|
}
|
||||||
exports.union = union;
|
|
||||||
/**
|
/**
|
||||||
* @name 格式化json
|
* @name 格式化json
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -79,7 +80,6 @@ exports.union = union;
|
||||||
function formatJsonByFile(data) {
|
function formatJsonByFile(data) {
|
||||||
return JSON.stringify(data, null, 2);
|
return JSON.stringify(data, null, 2);
|
||||||
}
|
}
|
||||||
exports.formatJsonByFile = formatJsonByFile;
|
|
||||||
/**
|
/**
|
||||||
* @name 数组对象去重
|
* @name 数组对象去重
|
||||||
* @export
|
* @export
|
||||||
|
|
@ -96,4 +96,3 @@ function deWeight(arr, type) {
|
||||||
}
|
}
|
||||||
return new Set([...map.values()]);
|
return new Set([...map.values()]);
|
||||||
}
|
}
|
||||||
exports.deWeight = deWeight;
|
|
||||||
|
|
|
||||||
16
package.json
16
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@xuchangzju/oak-cli",
|
"name": "@xuchangzju/oak-cli",
|
||||||
"version": "4.0.19",
|
"version": "4.0.20",
|
||||||
"description": "client for oak framework",
|
"description": "client for oak framework",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||||
"@socket.io/cluster-adapter": "^0.2.2",
|
"@socket.io/cluster-adapter": "^0.2.2",
|
||||||
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"@socket.io/sticky": "^1.0.4",
|
"@socket.io/sticky": "^1.0.4",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"@xmldom/xmldom": "0.8.2",
|
"@xmldom/xmldom": "0.8.2",
|
||||||
|
|
@ -89,6 +90,7 @@
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"inquirer": "^7.3.3",
|
"inquirer": "^7.3.3",
|
||||||
|
"ioredis": "^5.4.2",
|
||||||
"jest": "^27.4.3",
|
"jest": "^27.4.3",
|
||||||
"jest-resolve": "^27.4.2",
|
"jest-resolve": "^27.4.2",
|
||||||
"jest-watch-typeahead": "^1.0.0",
|
"jest-watch-typeahead": "^1.0.0",
|
||||||
|
|
@ -102,9 +104,9 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mini-css-extract-plugin": "^2.5.3",
|
"mini-css-extract-plugin": "^2.5.3",
|
||||||
"node-watch": "^0.7.4",
|
"node-watch": "^0.7.4",
|
||||||
"oak-backend-base": "^4.1.13",
|
"oak-backend-base": "^4.1.16",
|
||||||
"oak-domain": "^5.1.14",
|
"oak-domain": "^5.1.19",
|
||||||
"oak-frontend-base": "^5.3.22",
|
"oak-frontend-base": "^5.3.27",
|
||||||
"parse-asn1": "5.1.6",
|
"parse-asn1": "5.1.6",
|
||||||
"postcss": "^8.4.4",
|
"postcss": "^8.4.4",
|
||||||
"postcss-flexbugs-fixes": "^5.0.2",
|
"postcss-flexbugs-fixes": "^5.0.2",
|
||||||
|
|
@ -127,7 +129,7 @@
|
||||||
"sass-loader": "^12.3.0",
|
"sass-loader": "^12.3.0",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"socket.io": "^4.7.2",
|
"socket.io": "^4.8.1",
|
||||||
"source-map-loader": "^3.0.0",
|
"source-map-loader": "^3.0.0",
|
||||||
"stream-browserify": "^3.0.0",
|
"stream-browserify": "^3.0.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
|
|
@ -142,7 +144,7 @@
|
||||||
"webpack-bundle-analyzer": "^4.9.0",
|
"webpack-bundle-analyzer": "^4.9.0",
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"webpack-manifest-plugin": "^4.0.2",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"workbox-webpack-plugin": "^6.4.1",
|
"webpackbar": "^7.0.0",
|
||||||
"webpackbar": "^7.0.0"
|
"workbox-webpack-plugin": "^6.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import {
|
||||||
packageJsonContent,
|
packageJsonContent,
|
||||||
tsConfigJsonContent,
|
tsConfigJsonContent,
|
||||||
tsConfigBuildJsonContent,
|
tsConfigBuildJsonContent,
|
||||||
tsConfigBuildPathsJsonContent,
|
|
||||||
tsConfigPathsJsonContent,
|
tsConfigPathsJsonContent,
|
||||||
tsConfigMpJsonContent,
|
tsConfigMpJsonContent,
|
||||||
tsConfigWebJsonContent,
|
tsConfigWebJsonContent,
|
||||||
|
|
@ -29,6 +28,7 @@ import {
|
||||||
projectConfigContentWithWeChatMp,
|
projectConfigContentWithWeChatMp,
|
||||||
oakConfigContentWithWeChatMp,
|
oakConfigContentWithWeChatMp,
|
||||||
oakConfigContentWithWeb,
|
oakConfigContentWithWeb,
|
||||||
|
updateCompilerJsContent,
|
||||||
} from './template';
|
} from './template';
|
||||||
import { PromptInput } from './interface';
|
import { PromptInput } from './interface';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
@ -66,7 +66,7 @@ const prompt = [
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
message: 'add oak-general-business into dependency?',
|
message: 'add oak-general-business into dependency?',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'moreDeps',
|
name: 'moreDeps',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
|
@ -78,7 +78,7 @@ const prompt = [
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
message: 'load an initial example about personal blog? It will load correspond Entities/Pages/Components/Checkers and etc.',
|
message: 'load an initial example about personal blog? It will load correspond Entities/Pages/Components/Checkers and etc.',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -247,13 +247,6 @@ export async function create(dirName: string, cmd: any) {
|
||||||
prompt
|
prompt
|
||||||
);
|
);
|
||||||
|
|
||||||
// 获取tsconfig.json内容
|
|
||||||
const tsconfigJson = tsConfigJsonContent();
|
|
||||||
const tsConfigBuildJson = tsConfigBuildJsonContent();
|
|
||||||
const tsConfigBuildPathsJson = tsConfigBuildPathsJsonContent();
|
|
||||||
const tsConfigPathsJson = tsConfigPathsJsonContent();
|
|
||||||
const tsConfigMpJson = tsConfigMpJsonContent();
|
|
||||||
const tsConfigWebJson = tsConfigWebJsonContent();
|
|
||||||
|
|
||||||
// 项目根路径
|
// 项目根路径
|
||||||
const rootPath = process.cwd() + '/' + dirName;
|
const rootPath = process.cwd() + '/' + dirName;
|
||||||
|
|
@ -284,6 +277,49 @@ export async function create(dirName: string, cmd: any) {
|
||||||
try {
|
try {
|
||||||
// 创建根目录
|
// 创建根目录
|
||||||
checkFileExistsAndCreate(rootPath);
|
checkFileExistsAndCreate(rootPath);
|
||||||
|
// 复制项目文件
|
||||||
|
if (isModule) {
|
||||||
|
// 模块化的项目,只拷贝src和typings目录
|
||||||
|
copyFolder(join(emptyTemplatePath, 'src'), join(rootPath, 'src'));
|
||||||
|
copyFolder(join(emptyTemplatePath, 'typings'), join(rootPath, 'typings'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copyFolder(emptyTemplatePath, rootPath);
|
||||||
|
|
||||||
|
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
||||||
|
await createWebBoilplate(webRootPath, isDev);
|
||||||
|
/* if (!shell.which('npm')) {
|
||||||
|
Warn(warn('Sorry, this script requires npm! Please install npm!'));
|
||||||
|
shell.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Success(`${success(`Waiting...`)}`);
|
||||||
|
Success(`${success(`Dependencies are now being installed`)}`);
|
||||||
|
shell.cd(dirName).exec('npm install'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
const deps = [] as string[];
|
||||||
|
if (useOgb) {
|
||||||
|
deps.push('oak-general-business');
|
||||||
|
}
|
||||||
|
if (moreDeps) {
|
||||||
|
deps.push(
|
||||||
|
...((<string>moreDeps).split(',').map(
|
||||||
|
ele => ele.trim()
|
||||||
|
).filter(
|
||||||
|
ele => !!ele
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取tsconfig.json内容
|
||||||
|
const tsconfigJson = tsConfigJsonContent();
|
||||||
|
const tsConfigBuildJson = tsConfigBuildJsonContent();
|
||||||
|
const tsConfigBuildPathsJson = tsConfigPathsJsonContent(deps);
|
||||||
|
const tsConfigPathsJson = tsConfigPathsJsonContent(deps);
|
||||||
|
const tsConfigMpJson = tsConfigMpJsonContent();
|
||||||
|
const tsConfigWebJson = tsConfigWebJsonContent();
|
||||||
|
|
||||||
// 创建tsconfig.json
|
// 创建tsconfig.json
|
||||||
checkFileExistsAndCreate(
|
checkFileExistsAndCreate(
|
||||||
tsconfigJsonPath,
|
tsconfigJsonPath,
|
||||||
|
|
@ -320,27 +356,8 @@ export async function create(dirName: string, cmd: any) {
|
||||||
tsConfigWebJson,
|
tsConfigWebJson,
|
||||||
checkFileExistsAndCreateType.FILE
|
checkFileExistsAndCreateType.FILE
|
||||||
);
|
);
|
||||||
// 复制项目文件
|
// 更新configuration/compiler.js
|
||||||
if (isModule) {
|
updateCompilerJsContent(rootPath, deps);
|
||||||
// 模块化的项目,只拷贝src和typings目录
|
|
||||||
copyFolder(join(emptyTemplatePath, 'src'), join(rootPath, 'src'));
|
|
||||||
copyFolder(join(emptyTemplatePath, 'typings'), join(rootPath, 'typings'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
copyFolder(emptyTemplatePath, rootPath);
|
|
||||||
|
|
||||||
await createWechatMpBoilplate(weChatMpRootPath, isDev);
|
|
||||||
await createWebBoilplate(webRootPath, isDev);
|
|
||||||
/* if (!shell.which('npm')) {
|
|
||||||
Warn(warn('Sorry, this script requires npm! Please install npm!'));
|
|
||||||
shell.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Success(`${success(`Waiting...`)}`);
|
|
||||||
Success(`${success(`Dependencies are now being installed`)}`);
|
|
||||||
shell.cd(dirName).exec('npm install'); */
|
|
||||||
}
|
|
||||||
|
|
||||||
Success(
|
Success(
|
||||||
`${success(
|
`${success(
|
||||||
`Successfully created project ${primary(
|
`Successfully created project ${primary(
|
||||||
|
|
@ -349,20 +366,6 @@ export async function create(dirName: string, cmd: any) {
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const deps = [] as string[];
|
|
||||||
if (useOgb) {
|
|
||||||
deps.push('oak-general-business');
|
|
||||||
}
|
|
||||||
if (moreDeps) {
|
|
||||||
deps.push(
|
|
||||||
...((<string>moreDeps).split(',').map(
|
|
||||||
ele => ele.trim()
|
|
||||||
).filter(
|
|
||||||
ele => !!ele
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
shell.cd(dirName);
|
shell.cd(dirName);
|
||||||
if (deps.length > 0) {
|
if (deps.length > 0) {
|
||||||
addDependencies(deps);
|
addDependencies(deps);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
type AnalysisOptions = {
|
||||||
|
outputDir: string
|
||||||
|
includeNodeModules: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析项目中的模块
|
||||||
|
* @param dir 项目目录
|
||||||
|
* @param output 输出目录
|
||||||
|
*/
|
||||||
|
export const analysis = (dir: string, config: AnalysisOptions) => {
|
||||||
|
const BuiltinModule = require("module");
|
||||||
|
// 兼容一些模拟环境下的 module 构造函数
|
||||||
|
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||||
|
// 保存原始的 _resolveFilename 方法
|
||||||
|
const oldResolveFilename = Module._resolveFilename;
|
||||||
|
const successImported = new Set<string>();
|
||||||
|
// 重写 _resolveFilename 方法
|
||||||
|
Module._resolveFilename = function (
|
||||||
|
request: string, // 模块请求路径
|
||||||
|
parent: typeof Module, // 调用方模块
|
||||||
|
isMain: boolean, // 是否是主模块
|
||||||
|
rFoptions: object | undefined // 解析选项
|
||||||
|
) {
|
||||||
|
// 调用原始的 _resolveFilename 方法
|
||||||
|
const filename = oldResolveFilename.call(this, request, parent, isMain, rFoptions);
|
||||||
|
|
||||||
|
// 记录成功导入的模块
|
||||||
|
successImported.add(filename);
|
||||||
|
|
||||||
|
// 返回解析后的模块路径
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterProjectModules = (modulePath: string) => {
|
||||||
|
if (config.includeNodeModules) {
|
||||||
|
return modulePath.includes(dir)
|
||||||
|
}
|
||||||
|
return modulePath.includes(dir) && !modulePath.includes('node_modules');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将所有的文件复制到指定目录,并保持目录结构
|
||||||
|
* @param files 文件列表 string[]
|
||||||
|
* @param dest 目标目录
|
||||||
|
* @param root 原目录的根目录
|
||||||
|
*/
|
||||||
|
const copyFiles = (files: string[], dest: string, root: string) => {
|
||||||
|
files.forEach(file => {
|
||||||
|
const relativePath = path.relative(root, file)
|
||||||
|
const destPath = path.join(dest, relativePath)
|
||||||
|
fs.mkdirSync(path.dirname(destPath), { recursive: true })
|
||||||
|
fs.copyFileSync(file, destPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const { watch } = require("./watch") as { watch: (pwd: string) => Promise<() => Promise<void>> }
|
||||||
|
|
||||||
|
watch(dir).then(s => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('shutting down...')
|
||||||
|
s().then(() => {
|
||||||
|
console.log('server stoped')
|
||||||
|
// 把导入成功的输出
|
||||||
|
const project = Array.from(successImported).filter(filterProjectModules)
|
||||||
|
|
||||||
|
// 添加本地的scripts下的所有文件和package.json
|
||||||
|
const scriptsDir = path.join(dir, 'scripts')
|
||||||
|
const pkgPath = path.join(dir, 'package.json')
|
||||||
|
const files = fs.readdirSync(scriptsDir)
|
||||||
|
const scriptFiles = files.map(file => path.join(scriptsDir, file))
|
||||||
|
project.push(pkgPath, ...scriptFiles)
|
||||||
|
|
||||||
|
// 复制文件
|
||||||
|
const dest = path.join(dir, config.outputDir)
|
||||||
|
fs.mkdirSync(dest, { recursive: true });
|
||||||
|
copyFiles(project, dest, dir)
|
||||||
|
console.warn('分析结果以项目启动所需依赖为准,如果涉及动态导入,请设计routine,在项目启动时进行加载!');
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ import { AsyncRowStore, AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'
|
||||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||||
import { createAdapter } from "@socket.io/cluster-adapter";
|
import { createAdapter } from "@socket.io/cluster-adapter";
|
||||||
import { setupWorker } from "@socket.io/sticky";
|
import { setupWorker } from "@socket.io/sticky";
|
||||||
|
import { createAdapter as createRedisAdapter } from "@socket.io/redis-adapter";
|
||||||
|
import Redis from "ioredis";
|
||||||
import { Server, ServerOptions } from "socket.io";
|
import { Server, ServerOptions } from "socket.io";
|
||||||
import { ServerConfiguration } from 'oak-domain/lib/types/Configuration';
|
import { ServerConfiguration } from 'oak-domain/lib/types/Configuration';
|
||||||
|
|
||||||
|
|
@ -47,6 +49,8 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
||||||
'configuration',
|
'configuration',
|
||||||
'server'
|
'server'
|
||||||
)).default;
|
)).default;
|
||||||
|
// 拿到package.json,用作项目的唯一标识,否则无法区分不同项目的Redis+socketIO连接
|
||||||
|
const packageJson = require(join(path, 'package.json'));
|
||||||
const corsHeaders = [
|
const corsHeaders = [
|
||||||
'Content-Type',
|
'Content-Type',
|
||||||
'Content-Length',
|
'Content-Length',
|
||||||
|
|
@ -84,8 +88,64 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
||||||
// 目前只支持单物理结点的pm2模式
|
// 目前只支持单物理结点的pm2模式
|
||||||
// pm2环境下要接入clusterAdapter
|
// pm2环境下要接入clusterAdapter
|
||||||
// https://socket.io/zh-CN/docs/v4/pm2/
|
// https://socket.io/zh-CN/docs/v4/pm2/
|
||||||
|
|
||||||
|
// 在单机的所有实例之间使用pm2的集群适配器
|
||||||
io.adapter(createAdapter());
|
io.adapter(createAdapter());
|
||||||
setupWorker(io);
|
setupWorker(io);
|
||||||
|
|
||||||
|
if (clusterInfo.enableRedis) {
|
||||||
|
// 在多机器之间使用redis适配器
|
||||||
|
const redisConfig = serverConfiguration.redis
|
||||||
|
if (!redisConfig) {
|
||||||
|
console.error('未配置Redis连接信息!');
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
const isCluster = Array.isArray(redisConfig);
|
||||||
|
// 创建 Redis 客户端
|
||||||
|
const pubClient = isCluster ?
|
||||||
|
new Redis.Cluster(redisConfig.map((config) => ({
|
||||||
|
...config,
|
||||||
|
lazyConnect: true,
|
||||||
|
})))
|
||||||
|
: new Redis({
|
||||||
|
...redisConfig,
|
||||||
|
lazyConnect: true,
|
||||||
|
});
|
||||||
|
const subClient = pubClient.duplicate();
|
||||||
|
pubClient.on('connect', () => {
|
||||||
|
console.log('PUB已成功连接到Redis服务器');
|
||||||
|
});
|
||||||
|
pubClient.on('error', (err) => {
|
||||||
|
console.error('连接到Redis失败!', err);
|
||||||
|
// 如果连接到Redis失败,直接退出
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
subClient.on('connect', () => {
|
||||||
|
console.log('SUB已成功连接到Redis服务器');
|
||||||
|
});
|
||||||
|
subClient.on('error', (err) => {
|
||||||
|
console.error('连接到Redis失败!', err);
|
||||||
|
// 如果连接到Redis失败,直接退出
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
await Promise.all([
|
||||||
|
pubClient.connect(),
|
||||||
|
subClient.connect(),
|
||||||
|
]);
|
||||||
|
io.adapter(
|
||||||
|
// 为了使单台Redis可以在多个项目之间复用,需要为每个项目创建一个唯一的key
|
||||||
|
createRedisAdapter(pubClient, subClient, {
|
||||||
|
key: `${packageJson.name}-socket.io-${process.env.NODE_ENV || 'development'}`
|
||||||
|
.replace(/[^a-zA-Z0-9-_:.]/g, '') // 移除特殊字符(只保留字母、数字、-、_、:、.)
|
||||||
|
.replace(/\s+/g, '-') // 将空格替换为-
|
||||||
|
.toLowerCase(), // 转换为小写(统一格式)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
console.log('已启用Redis适配器');
|
||||||
|
} else {
|
||||||
|
// 如果没有启用Redis,不应该出现instanceCount大于实际实例数的情况
|
||||||
|
console.warn('正处于单机集群环境,请确保实例数正确!');
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`以集群模式启动,实例总数『${clusterInfo.instanceCount}』,当前实例号『${clusterInfo.instanceId}』`
|
`以集群模式启动,实例总数『${clusterInfo.instanceCount}』,当前实例号『${clusterInfo.instanceId}』`
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import chokidar from "chokidar";
|
import chokidar from "chokidar";
|
||||||
import ts from "typescript";
|
import ts, { CompilerOptions, ProjectReference } from "typescript";
|
||||||
import pathLib from "path";
|
import pathLib from "path";
|
||||||
import { startup } from "./start";
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { debounce } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
|
|
||||||
declare const require: NodeRequire;
|
declare const require: NodeRequire;
|
||||||
declare const process: NodeJS.Process;
|
declare const process: NodeJS.Process;
|
||||||
|
|
@ -80,14 +79,14 @@ export type WatchConfig = {
|
||||||
// 真实config不会出现?,所以这里新增一个type表示真实的config
|
// 真实config不会出现?,所以这里新增一个type表示真实的config
|
||||||
type DeepRequiredIfPresent<T> = {
|
type DeepRequiredIfPresent<T> = {
|
||||||
[P in keyof T]-?: NonNullable<T[P]> extends (...args: any) => any // Check for function type
|
[P in keyof T]-?: NonNullable<T[P]> extends (...args: any) => any // Check for function type
|
||||||
? T[P] // Preserve the original function type
|
? T[P] // Preserve the original function type
|
||||||
: NonNullable<T[P]> extends (infer U)[]
|
: NonNullable<T[P]> extends (infer U)[]
|
||||||
? DeepRequiredIfPresent<U>[]
|
? DeepRequiredIfPresent<U>[]
|
||||||
: NonNullable<T[P]> extends object
|
: NonNullable<T[P]> extends object
|
||||||
? DeepRequiredIfPresent<NonNullable<T[P]>>
|
? DeepRequiredIfPresent<NonNullable<T[P]>>
|
||||||
: T[P] extends undefined | null
|
: T[P] extends undefined | null
|
||||||
? T[P]
|
? T[P]
|
||||||
: NonNullable<T[P]>;
|
: NonNullable<T[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RealWatchConfig = DeepRequiredIfPresent<WatchConfig>;
|
export type RealWatchConfig = DeepRequiredIfPresent<WatchConfig>;
|
||||||
|
|
@ -116,8 +115,8 @@ const defaultConfig: RealWatchConfig = {
|
||||||
level === "info"
|
level === "info"
|
||||||
? infoStart
|
? infoStart
|
||||||
: level === "warn"
|
: level === "warn"
|
||||||
? warnStart
|
? warnStart
|
||||||
: errorStart;
|
: errorStart;
|
||||||
return [
|
return [
|
||||||
levelStart,
|
levelStart,
|
||||||
getTime(),
|
getTime(),
|
||||||
|
|
@ -178,6 +177,44 @@ const getOverrideConfig = (config?: WatchConfig): RealWatchConfig => {
|
||||||
: defaultConfig;
|
: defaultConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AliasConfig = Record<string, string | string[]>;
|
||||||
|
type ModuleType = import('module')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 alias 配置表将路径中的别名替换为真实路径
|
||||||
|
* @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module"
|
||||||
|
* @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组
|
||||||
|
* @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径
|
||||||
|
*/
|
||||||
|
function replaceAliasWithPath(path: string, aliasConfig: Record<string, string | string[]>): string {
|
||||||
|
for (const [alias, targets] of Object.entries(aliasConfig)) {
|
||||||
|
// If alias ends with "*", handle it as a dynamic alias.
|
||||||
|
if (alias.endsWith('*')) {
|
||||||
|
// Create a regex pattern that matches paths starting with the alias, followed by any characters
|
||||||
|
const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)'
|
||||||
|
|
||||||
|
const match = path.match(aliasPattern);
|
||||||
|
if (match) {
|
||||||
|
// Replace the alias with the target path, appending the matched part from the original path
|
||||||
|
const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array)
|
||||||
|
// Ensure that the target path ends with a slash if it's not already
|
||||||
|
const replacedPath = target.replace(/\/\*$/, "/") + match[1];
|
||||||
|
return replacedPath;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle static alias without "*" by directly matching the path
|
||||||
|
if (path.startsWith(alias)) {
|
||||||
|
const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array)
|
||||||
|
// Replace the alias part with the target path
|
||||||
|
return path.replace(alias, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no alias matches, return the original path
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
export const watch = (
|
export const watch = (
|
||||||
projectPath: string,
|
projectPath: string,
|
||||||
config?: WatchConfig
|
config?: WatchConfig
|
||||||
|
|
@ -185,6 +222,182 @@ export const watch = (
|
||||||
const realConfig = getOverrideConfig(config);
|
const realConfig = getOverrideConfig(config);
|
||||||
const enableTrace = !!process.env.ENABLE_TRACE;
|
const enableTrace = !!process.env.ENABLE_TRACE;
|
||||||
|
|
||||||
|
// 查找配置文件
|
||||||
|
const configFileName = ts.findConfigFile(
|
||||||
|
projectPath,
|
||||||
|
ts.sys.fileExists,
|
||||||
|
"tsconfig.build.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!configFileName) {
|
||||||
|
throw new Error("Could not find a valid 'tsconfig.build.json'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
const configFile = ts.readConfigFile(configFileName, ts.sys.readFile);
|
||||||
|
|
||||||
|
// 解析配置文件内容
|
||||||
|
const { options, projectReferences } = ts.parseJsonConfigFileContent(
|
||||||
|
configFile.config,
|
||||||
|
ts.sys,
|
||||||
|
pathLib.dirname(configFileName)
|
||||||
|
);
|
||||||
|
|
||||||
|
const aliasConfig: AliasConfig = cloneDeep(options.paths) || {};
|
||||||
|
|
||||||
|
// 输出原始配置
|
||||||
|
// console.log("[DEBUG] Original alias config:", aliasConfig);
|
||||||
|
|
||||||
|
Object.keys(aliasConfig).forEach((key) => {
|
||||||
|
const value = aliasConfig[key];
|
||||||
|
// 替换src
|
||||||
|
aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v) => v.replace("src", "lib"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 输出真实的alias配置
|
||||||
|
console.debug("[DEBUG] Running Alias config:", aliasConfig);
|
||||||
|
|
||||||
|
const createProgramAndSourceFile = (path: string, options: CompilerOptions, projectReferences: readonly ProjectReference[] | undefined) => {
|
||||||
|
const program = ts.createProgram({
|
||||||
|
rootNames: [path],
|
||||||
|
options,
|
||||||
|
projectReferences,
|
||||||
|
});
|
||||||
|
const sourceFile = program.getSourceFile(path);
|
||||||
|
// 是否有语法错误
|
||||||
|
const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
|
||||||
|
if (diagnostics.length) {
|
||||||
|
const syntaxErrors = diagnostics.filter(
|
||||||
|
(diagnostic) =>
|
||||||
|
diagnostic.category === ts.DiagnosticCategory.Error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (syntaxErrors.length) {
|
||||||
|
console.error(`Error in ${path}`);
|
||||||
|
syntaxErrors.forEach((diagnostic) => {
|
||||||
|
console.error(
|
||||||
|
`${ts.flattenDiagnosticMessageText(
|
||||||
|
diagnostic.messageText,
|
||||||
|
"\n"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
console.error(`文件存在语法错误,请检查修复后重试!`);
|
||||||
|
return { program, sourceFile, diagnostics };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { program, sourceFile, diagnostics };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这个函数用于解决require的时候,如果文件不存在,会尝试编译ts文件
|
||||||
|
// 测试:在src下新建一个ts文件,然后删除lib下的js文件,然后require这个文件,会发现会自动编译ts文件
|
||||||
|
const polyfillLoader = () => {
|
||||||
|
const BuiltinModule = require("module");
|
||||||
|
// 模拟环境下的 module 构造函数
|
||||||
|
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||||
|
|
||||||
|
// 保存原始 _resolveFilename 方法
|
||||||
|
const oldResolveFilename = Module._resolveFilename;
|
||||||
|
|
||||||
|
// 通用的路径检查与编译函数
|
||||||
|
function resolveAndCompile(requestPath: string, parent: ModuleType, options: CompilerOptions, projectReferences: readonly ts.ProjectReference[] | undefined) {
|
||||||
|
const jsPath = pathLib.resolve(requestPath + ".js");
|
||||||
|
const tsPath = jsPath.replace(/\.js$/, ".ts").replace(
|
||||||
|
pathLib.join(projectPath, "lib"),
|
||||||
|
pathLib.join(projectPath, "src")
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查并编译 .ts 文件
|
||||||
|
if (fs.existsSync(tsPath)) {
|
||||||
|
console.log(`[resolve] Found TypeScript source file: ${tsPath}, attempting to compile...`);
|
||||||
|
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(
|
||||||
|
tsPath,
|
||||||
|
options,
|
||||||
|
projectReferences
|
||||||
|
);
|
||||||
|
|
||||||
|
if (diagnostics.length) {
|
||||||
|
console.error(`[resolve] Compilation failed for: ${tsPath}`);
|
||||||
|
throw new Error("TypeScript compilation error");
|
||||||
|
}
|
||||||
|
|
||||||
|
const emitResult = program.emit(sourceFile);
|
||||||
|
|
||||||
|
if (emitResult.emitSkipped) {
|
||||||
|
console.error(`[resolve] Emit skipped for: ${tsPath}`);
|
||||||
|
throw new Error("TypeScript emit skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[resolve] Successfully compiled: ${tsPath}`);
|
||||||
|
return jsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到对应的 .ts 文件
|
||||||
|
if (fs.existsSync(jsPath)) {
|
||||||
|
return jsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`[resolve] Unable to find module: ${requestPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件夹导入的情况
|
||||||
|
function resolveDirectory(requestPath: string, parent: ModuleType, options: CompilerOptions, projectReferences: readonly ts.ProjectReference[] | undefined) {
|
||||||
|
const indexJs = pathLib.join(requestPath, "index.js");
|
||||||
|
const indexTs = pathLib.join(requestPath, "index.ts").replace(
|
||||||
|
pathLib.join(projectPath, "lib"),
|
||||||
|
pathLib.join(projectPath, "src")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fs.existsSync(indexTs)) {
|
||||||
|
console.log(`[resolve] Found TypeScript index file: ${indexTs}, attempting to compile...`);
|
||||||
|
return resolveAndCompile(indexTs, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(indexJs)) {
|
||||||
|
return indexJs;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`[resolve] No index file found in directory: ${requestPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重写 _resolveFilename 方法
|
||||||
|
Module._resolveFilename = function (
|
||||||
|
request: string, // 模块请求路径
|
||||||
|
parent: ModuleType, // 调用方模块
|
||||||
|
isMain: boolean, // 是否是主模块
|
||||||
|
rFoptions: object | undefined // 解析选项
|
||||||
|
) {
|
||||||
|
let resolvedRequest = request;
|
||||||
|
const replacedPath = replaceAliasWithPath(request, aliasConfig);
|
||||||
|
|
||||||
|
if (replacedPath !== request) {
|
||||||
|
console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`);
|
||||||
|
resolvedRequest = pathLib.join(projectPath, replacedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === "MODULE_NOT_FOUND") {
|
||||||
|
const requestPath = pathLib.resolve(pathLib.dirname(parent.filename), resolvedRequest);
|
||||||
|
|
||||||
|
// 处理文件夹导入
|
||||||
|
if (fs.existsSync(requestPath) && fs.statSync(requestPath).isDirectory()) {
|
||||||
|
return resolveDirectory(requestPath, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理单文件导入
|
||||||
|
return resolveAndCompile(requestPath, parent, options, projectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
polyfillLoader();
|
||||||
|
|
||||||
//polyfill console.log 添加时间
|
//polyfill console.log 添加时间
|
||||||
const polyfillConsole = (trace: boolean) => {
|
const polyfillConsole = (trace: boolean) => {
|
||||||
// 获取调用堆栈信息
|
// 获取调用堆栈信息
|
||||||
|
|
@ -232,6 +445,62 @@ export const watch = (
|
||||||
|
|
||||||
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
||||||
|
|
||||||
|
// 这里注意要在require之前,因为require会触发编译
|
||||||
|
const { startup } = require('./start') as {
|
||||||
|
startup: (pwd: string, connector: any) => Promise<() => Promise<void>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果lib目录是空的,则直接编译所有的ts文件
|
||||||
|
const serverConfigFile = pathLib.join(projectPath, "lib/configuration/server.js");
|
||||||
|
if (!fs.existsSync(serverConfigFile)) {
|
||||||
|
// 尝试编译src/configuration/server.ts
|
||||||
|
console.log(`[watch] Server configuration file not found, attempting to compile the project......`);
|
||||||
|
const tryCompile = (tsFile: string) => {
|
||||||
|
console.log(`[watch] Compiling: ${tsFile}`);
|
||||||
|
if (fs.existsSync(tsFile)) {
|
||||||
|
const { program, diagnostics } = createProgramAndSourceFile(tsFile, options, projectReferences);
|
||||||
|
if (diagnostics.length) {
|
||||||
|
console.error(`Error in ${tsFile}`);
|
||||||
|
diagnostics.forEach((diagnostic) => {
|
||||||
|
console.error(
|
||||||
|
`${ts.flattenDiagnosticMessageText(
|
||||||
|
diagnostic.messageText,
|
||||||
|
"\n"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
console.error(`文件存在语法错误,请检查修复后重试!`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// const emitResult = program.emit(sourceFile);
|
||||||
|
// 编译所有的文件
|
||||||
|
const emitResult = program.emit();
|
||||||
|
|
||||||
|
if (emitResult.emitSkipped) {
|
||||||
|
console.error(`Emit failed for ${tsFile}!`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Emit succeeded. ${tsFile} has been compiled.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 所有要编译的目录
|
||||||
|
// 其他涉及到的目录会在运行的时候自动编译,这里主要处理的是动态require的文件
|
||||||
|
const compileFiles = [
|
||||||
|
"src/configuration/index.ts",
|
||||||
|
"src/aspects/index.ts",
|
||||||
|
"src/checkers/index.ts",
|
||||||
|
"src/triggers/index.ts",
|
||||||
|
"src/timers/index.ts",
|
||||||
|
"src/routines/start.ts",
|
||||||
|
"src/watchers/index.ts",
|
||||||
|
"src/endpoints/index.ts",
|
||||||
|
"src/data/index.ts",
|
||||||
|
"src/ports/index.ts",
|
||||||
|
];
|
||||||
|
compileFiles.forEach(tryCompile);
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
realConfig.lifecycle.onInit();
|
realConfig.lifecycle.onInit();
|
||||||
let shutdown: (() => Promise<void>) | undefined;
|
let shutdown: (() => Promise<void>) | undefined;
|
||||||
|
|
@ -240,6 +509,8 @@ export const watch = (
|
||||||
if (shutdown) {
|
if (shutdown) {
|
||||||
console.log("----> Shutting down service......");
|
console.log("----> Shutting down service......");
|
||||||
await shutdown().then(realConfig.lifecycle.onServerShutdown);
|
await shutdown().then(realConfig.lifecycle.onServerShutdown);
|
||||||
|
// reset shutdown
|
||||||
|
shutdown = undefined;
|
||||||
}
|
}
|
||||||
console.warn("----> Clearing require cache of project......");
|
console.warn("----> Clearing require cache of project......");
|
||||||
let deleteCount = 0;
|
let deleteCount = 0;
|
||||||
|
|
@ -276,26 +547,7 @@ export const watch = (
|
||||||
|
|
||||||
console.log("Watching for changes in", watchSourcePath);
|
console.log("Watching for changes in", watchSourcePath);
|
||||||
|
|
||||||
// 查找配置文件
|
|
||||||
const configFileName = ts.findConfigFile(
|
|
||||||
projectPath,
|
|
||||||
ts.sys.fileExists,
|
|
||||||
"tsconfig.build.json"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!configFileName) {
|
|
||||||
throw new Error("Could not find a valid 'tsconfig.build.json'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取配置文件
|
|
||||||
const configFile = ts.readConfigFile(configFileName, ts.sys.readFile);
|
|
||||||
|
|
||||||
// 解析配置文件内容
|
|
||||||
const { options, projectReferences } = ts.parseJsonConfigFileContent(
|
|
||||||
configFile.config,
|
|
||||||
ts.sys,
|
|
||||||
pathLib.dirname(configFileName)
|
|
||||||
);
|
|
||||||
|
|
||||||
const watcher = chokidar.watch(watchSourcePath, {
|
const watcher = chokidar.watch(watchSourcePath, {
|
||||||
persistent: true,
|
persistent: true,
|
||||||
|
|
@ -325,7 +577,7 @@ export const watch = (
|
||||||
|
|
||||||
watcher.on("error", (error) => console.log(`Watcher error: ${error}`));
|
watcher.on("error", (error) => console.log(`Watcher error: ${error}`));
|
||||||
|
|
||||||
let isProcessing = false;
|
let processingQueue: string[] = [];
|
||||||
const fileChangeHandler = async (
|
const fileChangeHandler = async (
|
||||||
path: string,
|
path: string,
|
||||||
type: "add" | "remove" | "change"
|
type: "add" | "remove" | "change"
|
||||||
|
|
@ -334,7 +586,8 @@ export const watch = (
|
||||||
if (!path.endsWith(".ts")) {
|
if (!path.endsWith(".ts")) {
|
||||||
// 如果是json文件,复制或者删除
|
// 如果是json文件,复制或者删除
|
||||||
if (path.endsWith(".json")) {
|
if (path.endsWith(".json")) {
|
||||||
const targetPath = path.replace("src", "lib");
|
// const targetPath = path.replace("src", "lib"); // 这里直接替换不对,应该是拿到项目目录,把项目目录/src 换成项目目录/lib
|
||||||
|
const targetPath = path.replace(pathLib.join(projectPath, "src"), pathLib.join(projectPath, "lib"));
|
||||||
if (type === "remove") {
|
if (type === "remove") {
|
||||||
fs.unlinkSync(targetPath);
|
fs.unlinkSync(targetPath);
|
||||||
console.warn(`File ${targetPath} has been removed.`);
|
console.warn(`File ${targetPath} has been removed.`);
|
||||||
|
|
@ -373,8 +626,8 @@ export const watch = (
|
||||||
const modulePath = pathLib.resolve(path);
|
const modulePath = pathLib.resolve(path);
|
||||||
// 将src替换为lib
|
// 将src替换为lib
|
||||||
const libPath = modulePath
|
const libPath = modulePath
|
||||||
.replace("\\src\\", "\\lib\\")
|
.replace(pathLib.join(projectPath, "src"), pathLib.join(projectPath, "lib"))
|
||||||
.replace(".ts", ".js");
|
.replace(/\.ts$/, ".js");
|
||||||
let compileOnly = false;
|
let compileOnly = false;
|
||||||
if (!require.cache[libPath]) {
|
if (!require.cache[libPath]) {
|
||||||
// 如果是删除的话,直接尝试删除lib下的文件
|
// 如果是删除的话,直接尝试删除lib下的文件
|
||||||
|
|
@ -387,10 +640,11 @@ export const watch = (
|
||||||
console.warn(`File ${libPath} has been removed.`);
|
console.warn(`File ${libPath} has been removed.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.warn(
|
// console.warn(
|
||||||
`File ${libPath} is not in module cache, will compile only.`
|
// `File ${libPath} is not in module cache, will compile only.`
|
||||||
);
|
// );
|
||||||
compileOnly = true;
|
// compileOnly = true;
|
||||||
|
// 这里现在不需要仅编译了,因为在require的时候会自动编译ts文件
|
||||||
} else {
|
} else {
|
||||||
// 如果是删除,则需要发出警告,文件正在被进程使用
|
// 如果是删除,则需要发出警告,文件正在被进程使用
|
||||||
if (type === "remove") {
|
if (type === "remove") {
|
||||||
|
|
@ -398,34 +652,13 @@ export const watch = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const program = ts.createProgram({
|
|
||||||
rootNames: [path],
|
|
||||||
options,
|
|
||||||
projectReferences,
|
|
||||||
});
|
|
||||||
const sourceFile = program.getSourceFile(path);
|
|
||||||
// 是否有语法错误
|
|
||||||
const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
|
|
||||||
if (diagnostics.length) {
|
|
||||||
const syntaxErrors = diagnostics.filter(
|
|
||||||
(diagnostic) =>
|
|
||||||
diagnostic.category === ts.DiagnosticCategory.Error
|
|
||||||
);
|
|
||||||
|
|
||||||
if (syntaxErrors.length) {
|
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(path, options, projectReferences);
|
||||||
console.error(`Error in ${path}`);
|
|
||||||
syntaxErrors.forEach((diagnostic) => {
|
if (diagnostics.length) {
|
||||||
console.error(
|
return;
|
||||||
`${ts.flattenDiagnosticMessageText(
|
|
||||||
diagnostic.messageText,
|
|
||||||
"\n"
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
console.error(`文件存在语法错误,请检查修复后重试!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只输出单个文件
|
// 只输出单个文件
|
||||||
realConfig.lifecycle.onBeforeCompile();
|
realConfig.lifecycle.onBeforeCompile();
|
||||||
const emitResult = program.emit(sourceFile);
|
const emitResult = program.emit(sourceFile);
|
||||||
|
|
@ -436,51 +669,54 @@ export const watch = (
|
||||||
realConfig.lifecycle.onAfterCompile();
|
realConfig.lifecycle.onAfterCompile();
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
`Emit succeeded. ${
|
`Emit succeeded. ${compileOnly ? "" : "reload service......"
|
||||||
compileOnly ? "" : "reload service......"
|
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
realConfig.lifecycle.onAfterCompile();
|
realConfig.lifecycle.onAfterCompile();
|
||||||
if (compileOnly) {
|
if (compileOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await restart();
|
// await restart(); // 只有在队列里面的最后一个文件编译完成后才会重启服务
|
||||||
|
if (processingQueue.length === 1) {
|
||||||
|
await restart();
|
||||||
|
} else {
|
||||||
|
console.log("Waiting for other operations to complete...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeDebounced = debounce(
|
const onChangeDebounced =
|
||||||
async (path: string, type: "add" | "remove" | "change") => {
|
async (path: string, type: "add" | "remove" | "change") => {
|
||||||
if (isProcessing) {
|
if (processingQueue.includes(path)) {
|
||||||
console.log("Processing, please wait...");
|
console.log("Processing, please wait...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
isProcessing = true;
|
processingQueue.push(path);
|
||||||
await fileChangeHandler(path, type);
|
await fileChangeHandler(path, type);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.clear();
|
console.clear();
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
isProcessing = false;
|
processingQueue = processingQueue.filter((p) => p !== path);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
100
|
|
||||||
);
|
|
||||||
|
|
||||||
watcher
|
watcher
|
||||||
.on("add", (path) => {
|
.on("add", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "add");
|
await onChangeDebounced(path, "add");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("change", (path) => {
|
.on("change", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "change");
|
await onChangeDebounced(path, "change");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("unlink", (path) => {
|
.on("unlink", async (path) => {
|
||||||
if (startWatching) {
|
if (startWatching) {
|
||||||
onChangeDebounced(path, "remove");
|
await onChangeDebounced(path, "remove");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
182
src/template.ts
182
src/template.ts
|
|
@ -1,7 +1,11 @@
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync, writeFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { PackageJsonInput } from './interface';
|
import { PackageJsonInput } from './interface';
|
||||||
|
import { transformFileSync } from '@babel/core';
|
||||||
|
import * as t from '@babel/types';
|
||||||
|
import generate from "@babel/generator";
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 利用npm info获得相应库的最新版本
|
* 利用npm info获得相应库的最新版本
|
||||||
|
|
@ -69,7 +73,7 @@ export function packageJsonContent({
|
||||||
const serverInitScript = isDev ? "cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node scripts/initServer.js" : "cross-env OAK_PLATFORM=server node scripts/initServer.js";
|
const serverInitScript = isDev ? "cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node scripts/initServer.js" : "cross-env OAK_PLATFORM=server node scripts/initServer.js";
|
||||||
// const serverStartScript = isDev ? "cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node scripts/startServer.js" : "cross-env OAK_PLATFORM=server node scripts/startServer.js";
|
// const serverStartScript = isDev ? "cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node scripts/startServer.js" : "cross-env OAK_PLATFORM=server node scripts/startServer.js";
|
||||||
const serverStartWatchScript = isDev ? "cross-env ENABLE_TRACE=true cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/watchServer.js" : "cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/watchServer.js";
|
const serverStartWatchScript = isDev ? "cross-env ENABLE_TRACE=true cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/watchServer.js" : "cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/watchServer.js";
|
||||||
return `{
|
return `{
|
||||||
"name": "${name}",
|
"name": "${name}",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"description": "${description}",
|
"description": "${description}",
|
||||||
|
|
@ -106,6 +110,7 @@ export function packageJsonContent({
|
||||||
"run:android": "oak-cli run -p android",
|
"run:android": "oak-cli run -p android",
|
||||||
"server:init": "${serverInitScript}",
|
"server:init": "${serverInitScript}",
|
||||||
"server:start": "${serverStartWatchScript}",
|
"server:start": "${serverStartWatchScript}",
|
||||||
|
"server:ana": "cross-env ENABLE_TRACE=true cross-env NODE_ENV=development cross-env OAK_PLATFORM=server node --stack-size=65500 scripts/analysis.js",
|
||||||
"postinstall": "npm run make:dep"
|
"postinstall": "npm run make:dep"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|
@ -275,8 +280,7 @@ export function packageJsonContent({
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"webpack-manifest-plugin": "^4.0.2",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"workbox-webpack-plugin": "^6.4.1",
|
"workbox-webpack-plugin": "^6.4.1",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.1"
|
||||||
"module-alias": "^2.2.3"
|
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
@ -308,16 +312,9 @@ export function packageJsonContent({
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"readable-stream": "3.6.2"
|
"readable-stream": "3.6.2"
|
||||||
},
|
|
||||||
"_moduleAliases": {
|
|
||||||
"@project": "./lib",
|
|
||||||
"@oak-general-business": "./node_modules/oak-general-business/lib",
|
|
||||||
"@oak-frontend-base": "./node_modules/oak-frontend-base/lib",
|
|
||||||
"@oak-app-domain": "./lib/oak-app-domain"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
// _moduleAliases用于lib内运行时的模块声明,重载之后require的路径还会保留@project,需要这样的方式来进行路径alias
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tsConfigJsonContent() {
|
export function tsConfigJsonContent() {
|
||||||
|
|
@ -412,45 +409,37 @@ export function tsConfigBuildJsonContent() {
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tsConfigBuildPathsJsonContent() {
|
export function tsConfigPathsJsonContent(deps: string[]) {
|
||||||
return `{
|
const paths = {
|
||||||
"compilerOptions": {
|
"@project/*": [
|
||||||
"baseUrl": "./",
|
"src/*"
|
||||||
"paths": {
|
],
|
||||||
"@project/*": [
|
"@oak-app-domain": [
|
||||||
"src/*"
|
"src/oak-app-domain/index"
|
||||||
],
|
],
|
||||||
"@oak-app-domain": [
|
"@oak-app-domain/*": [
|
||||||
"src/oak-app-domain/index"
|
"src/oak-app-domain/*"
|
||||||
],
|
],
|
||||||
"@oak-app-domain/*": [
|
"@oak-frontend-base/*": [
|
||||||
"src/oak-app-domain/*"
|
"node_modules/oak-frontend-base/lib/*"
|
||||||
],
|
],
|
||||||
},
|
};
|
||||||
"typeRoots": ["./typings"]
|
deps.forEach(
|
||||||
}
|
(ele) => {
|
||||||
}`;
|
Object.assign(paths, {
|
||||||
|
[`@${ele}/*`]: [`node_modules/${ele}/lib/*`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return JSON.stringify({
|
||||||
|
compilerOptions: {
|
||||||
|
baseUrl: "./",
|
||||||
|
paths,
|
||||||
|
typeRoots: ["./typings"]
|
||||||
|
}
|
||||||
|
}, null, '\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tsConfigPathsJsonContent() {
|
|
||||||
return `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "./",
|
|
||||||
"paths": {
|
|
||||||
"@project/*": [
|
|
||||||
"src/*"
|
|
||||||
],
|
|
||||||
"@oak-app-domain": [
|
|
||||||
"src/oak-app-domain/index"
|
|
||||||
],
|
|
||||||
"@oak-app-domain/*": [
|
|
||||||
"src/oak-app-domain/*"
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"typeRoots": ["./typings"]
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tsConfigMpJsonContent() {
|
export function tsConfigMpJsonContent() {
|
||||||
return `{
|
return `{
|
||||||
|
|
@ -657,15 +646,98 @@ export function appJsonContentWithWeChatMp(isDev: boolean) {
|
||||||
|
|
||||||
export function oakConfigContentWithWeChatMp() {
|
export function oakConfigContentWithWeChatMp() {
|
||||||
return `{
|
return `{
|
||||||
"theme": {
|
"theme": {
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function oakConfigContentWithWeb() {
|
export function oakConfigContentWithWeb() {
|
||||||
return `{
|
return `{
|
||||||
"theme": {
|
"theme": {
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function updateCompilerJsContent(directory: string, deps: string[]) {
|
||||||
|
const compilerJsPath = join(directory, 'configuration', 'compiler.js');
|
||||||
|
assert(existsSync(compilerJsPath));
|
||||||
|
|
||||||
|
if (deps.length > 0) {
|
||||||
|
const { ast } = transformFileSync(compilerJsPath, { ast: true })!;
|
||||||
|
const { program } = ast!;
|
||||||
|
const { body } = program!;
|
||||||
|
|
||||||
|
const moduleExportStmt = body[body.length - 1];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出语句:
|
||||||
|
module.exports = CreateCompilerConfig({
|
||||||
|
webpack: {
|
||||||
|
extraOakModules: [],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@oak-frontend-base': oakFrontendBasePath,
|
||||||
|
'@project': path.resolve('src'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
assert(t.isExpressionStatement(moduleExportStmt));
|
||||||
|
const { expression } = moduleExportStmt;
|
||||||
|
assert(t.isAssignmentExpression(expression));
|
||||||
|
const { left, right } = expression;
|
||||||
|
assert(t.isMemberExpression(left));
|
||||||
|
const { property, object } = left;
|
||||||
|
assert(t.isIdentifier(object) && object.name === 'module');
|
||||||
|
assert(t.isIdentifier(property) && property.name === 'exports');
|
||||||
|
assert(t.isCallExpression(right));
|
||||||
|
const [webPackExpr] = right.arguments;
|
||||||
|
assert(t.isObjectExpression(webPackExpr));
|
||||||
|
const [ webPackProp ] = webPackExpr.properties;
|
||||||
|
assert(t.isObjectProperty(webPackProp));
|
||||||
|
const { key, value } = webPackProp;
|
||||||
|
assert(t.isIdentifier(key) && key.name === 'webpack');
|
||||||
|
const [ modulesProp, resolveProp ] = (<t.ObjectExpression>value).properties;
|
||||||
|
|
||||||
|
assert(t.isObjectProperty(modulesProp));
|
||||||
|
const { value: value2 } = modulesProp;
|
||||||
|
assert(t.isArrayExpression(value2));
|
||||||
|
value2.elements.push(
|
||||||
|
...deps.map(
|
||||||
|
(ele) => t.regExpLiteral(ele)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(t.isObjectProperty(resolveProp));
|
||||||
|
const { value: value3 } = resolveProp;
|
||||||
|
assert(t.isObjectExpression(value3));
|
||||||
|
const [ aliasProp ] = value3.properties;
|
||||||
|
assert(t.isObjectProperty(aliasProp));
|
||||||
|
const { value: value4 } = aliasProp;
|
||||||
|
assert(t.isObjectExpression(value4));
|
||||||
|
const { properties: aliasProperties } = value4;
|
||||||
|
assert(aliasProperties.length === 2); // @project和@oak-frontend-base
|
||||||
|
aliasProperties.push(
|
||||||
|
...deps.map(
|
||||||
|
(ele) => t.objectProperty(
|
||||||
|
t.stringLiteral(`@${ele}`),
|
||||||
|
t.callExpression(
|
||||||
|
t.memberExpression(
|
||||||
|
t.identifier('path'),
|
||||||
|
t.identifier('resolve')
|
||||||
|
),
|
||||||
|
[
|
||||||
|
t.stringLiteral(`node_modules/${ele}/es`)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const { code } = generate(ast!);
|
||||||
|
writeFileSync(compilerJsPath, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,16 @@
|
||||||
// import { CompilerConfiguration } from 'oak-domain/lib/types/Configuration';
|
// import { CompilerConfiguration } from 'oak-domain/lib/types/Configuration';
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { CreateCompilerConfig } = require('@xuchangzju/oak-cli/lib/createConfig')
|
const { CreateCompilerConfig } = require('@xuchangzju/oak-cli/lib/createConfig');
|
||||||
const oakGeneralBusinessPath = path.resolve(
|
|
||||||
'node_modules/oak-general-business/es'
|
|
||||||
);
|
|
||||||
const oakFrontendBasePath = path.resolve(
|
|
||||||
'node_modules/oak-frontend-base/es'
|
|
||||||
);
|
|
||||||
|
|
||||||
const oakPayBusinessPath = path.resolve(
|
|
||||||
'node_modules/oak-pay-business/es'
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = CreateCompilerConfig({
|
module.exports = CreateCompilerConfig({
|
||||||
webpack: {
|
webpack: {
|
||||||
extraOakModules: [/oak-pay-business/, /oak-general-business/],
|
extraOakModules: [],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@oak-general-business': oakGeneralBusinessPath,
|
'@oak-frontend-base': path.resolve('node_modules/oak-frontend-base/es'),
|
||||||
'@oak-frontend-base': oakFrontendBasePath,
|
'@oak-app-domain': path.resolve('src', 'oak-app-domain'),
|
||||||
'@project': path.resolve('src'),
|
'@project': path.resolve('src'),
|
||||||
|
'@assets': path.resolve('src', 'assets'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
|
const { analysis } = require('@xuchangzju/oak-cli/lib/server/analysis');
|
||||||
|
const pwd = process.cwd();
|
||||||
|
|
||||||
|
analysis(pwd, {
|
||||||
|
outputDir: 'backend_gen',
|
||||||
|
includeNodeModules: true,
|
||||||
|
});
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
require('module-alias/register');
|
|
||||||
const { startup } = require('@xuchangzju/oak-cli/lib/server/start');
|
const { startup } = require('@xuchangzju/oak-cli/lib/server/start');
|
||||||
const simpleConnector = require('../lib/config/connector').default;
|
const simpleConnector = require('../lib/config/connector').default;
|
||||||
const pwd = process.cwd();
|
const pwd = process.cwd();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
require('module-alias/register');
|
|
||||||
const { watch } = require('@xuchangzju/oak-cli/lib/server/watch');
|
const { watch } = require('@xuchangzju/oak-cli/lib/server/watch');
|
||||||
const pwd = process.cwd();
|
const pwd = process.cwd();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Typography, Divider } from 'antd';
|
||||||
|
|
||||||
import SystemList from '../system';
|
import SystemList from '../system';
|
||||||
|
|
||||||
export default function render(props: WebComponentProps<EntityDict, 'system', true, {
|
export default function render(props: WebComponentProps<EntityDict, 'system', false, {
|
||||||
entity: 'system';
|
entity: 'system';
|
||||||
entityId: string;
|
entityId: string;
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
OakException,
|
OakException,
|
||||||
OakUnloggedInException,
|
OakUnloggedInException,
|
||||||
OakUserUnpermittedException,
|
|
||||||
OakAttrNotNullException,
|
OakAttrNotNullException,
|
||||||
OakInputIllegalException,
|
OakInputIllegalException,
|
||||||
} from 'oak-domain/lib/types/Exception';
|
} from 'oak-domain/lib/types/Exception';
|
||||||
|
|
@ -10,7 +9,50 @@ import {
|
||||||
} from '@project/types/Exception';
|
} from '@project/types/Exception';
|
||||||
import { FeatureDict } from '@project/features';
|
import { FeatureDict } from '@project/features';
|
||||||
import { AFD } from '@project/types/RuntimeCxt';
|
import { AFD } from '@project/types/RuntimeCxt';
|
||||||
|
import { OakApplicationLoadingException, OakHasToVerifyPassword, OakPasswordUnset, OakUserInfoLoadingException } from 'oak-general-business';
|
||||||
// import { OakUserInfoLoadingException, OakApplicationLoadingException } from 'oak-general-business';
|
// import { OakUserInfoLoadingException, OakApplicationLoadingException } from 'oak-general-business';
|
||||||
|
import { MessageProps } from 'oak-frontend-base';
|
||||||
|
|
||||||
|
function showMessage(reason: OakException<any>, features: AFD, type?: MessageProps['type']) {
|
||||||
|
const { message, _module, params} = reason;
|
||||||
|
if (_module) {
|
||||||
|
features.message.setMessage({
|
||||||
|
content: features.locales.t(message, {
|
||||||
|
['#oakModule']: _module,
|
||||||
|
...(params || {}),
|
||||||
|
}),
|
||||||
|
type: type || 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
features.message.setMessage({
|
||||||
|
content: message,
|
||||||
|
type: type || 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateTo(features: AFD, url: string, state?: Record<string, any>) {
|
||||||
|
const { pathname } = features.navigator.getLocation();
|
||||||
|
// 页面已经在本页的话,就不在跳转。这里要处理路径字符串的区别
|
||||||
|
const s1 = pathname.split('/').filter(ele => !!ele);
|
||||||
|
const s2 = url.split('/').filter(ele => !!ele);
|
||||||
|
|
||||||
|
if (s1.length === s2.length) {
|
||||||
|
let same = true;
|
||||||
|
s1.forEach(
|
||||||
|
(ele, idx) => {
|
||||||
|
if (ele !== s2[idx]) {
|
||||||
|
same = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (same) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
features.navigator.navigateTo({ url }, state);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造backUrl
|
* 构造backUrl
|
||||||
|
|
@ -18,39 +60,30 @@ import { AFD } from '@project/types/RuntimeCxt';
|
||||||
* @param encode
|
* @param encode
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const handler = async (reason: any, features: AFD) => {
|
export const handler = async (reason: any, features: AFD) => {
|
||||||
if (reason instanceof OakException) {
|
if (reason instanceof OakException) {
|
||||||
if (reason instanceof OakUnloggedInException) {
|
if (reason instanceof OakUnloggedInException) {
|
||||||
// await features.token.logout();
|
navigateTo(features, '/login');
|
||||||
features.navigator.navigateTo(
|
}
|
||||||
{
|
else if (reason instanceof OakInputIllegalException) {
|
||||||
url: '/login',
|
showMessage(reason, features);
|
||||||
},
|
}
|
||||||
{ isGoBack: true },
|
else if (reason instanceof OakHasToVerifyPassword) {
|
||||||
true
|
navigateTo(features, '/my/password/verify');
|
||||||
);
|
}
|
||||||
} else if (reason instanceof OakInputIllegalException) {
|
else if (reason instanceof OakPasswordUnset) {
|
||||||
features.message.setMessage({
|
navigateTo(features, 'my/password/update');
|
||||||
content: reason.message,
|
}
|
||||||
type: 'error',
|
else if (reason instanceof OakAttrNotNullException) {
|
||||||
});
|
showMessage(reason, features);
|
||||||
} else if (reason instanceof OakAttrNotNullException) {
|
} else if (reason instanceof OakUserInfoLoadingException || reason instanceof OakApplicationLoadingException) {
|
||||||
features.message.setMessage({
|
|
||||||
content: reason.message,
|
|
||||||
type: 'error',
|
|
||||||
});
|
|
||||||
}/* else if (reason instanceof OakUserInfoLoadingException || reason instanceof OakApplicationLoadingException) {
|
|
||||||
// 暂不处理,在oak-general-business的page的生命周期里,等token加载完成了会刷新页面的
|
// 暂不处理,在oak-general-business的page的生命周期里,等token加载完成了会刷新页面的
|
||||||
console.warn(reason);
|
// console.warn(reason);
|
||||||
} */else if (reason instanceof ExampleException) {
|
} else if (reason instanceof ExampleException) {
|
||||||
console.log('在此处理ExampleException');
|
console.log('在此处理ExampleException');
|
||||||
} else {
|
} else {
|
||||||
console.warn(reason);
|
console.warn(reason);
|
||||||
features.message.setMessage({
|
showMessage(reason, features);
|
||||||
content: reason.message,
|
|
||||||
type: 'error',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"name": "Project_Name",
|
||||||
"ptrActivate": "松开刷新",
|
"ptrActivate": "松开刷新",
|
||||||
"ptrDeactivate": "下拉刷新",
|
"ptrDeactivate": "下拉刷新",
|
||||||
"ptrRelease": "正在刷新...",
|
"ptrRelease": "正在刷新...",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"System": "系统管理",
|
||||||
|
"userManage": "用户管理",
|
||||||
|
"relationManage": "关系管理"
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
import { OakException, OakUserException } from 'oak-domain/lib/types';
|
import { OakException, OakUserException } from 'oak-domain/lib/types';
|
||||||
import { EntityDict } from '@oak-app-domain';
|
import { EntityDict } from '@project/oak-app-domain';
|
||||||
import makeDepedentException from './DependentExceptions';
|
import makeDepedentException from './DependentExceptions';
|
||||||
|
|
||||||
export class ExampleException extends OakException<EntityDict> {};
|
export class ExampleException extends OakException<EntityDict> {};
|
||||||
|
|
||||||
export class ConsoleModeIllegalException extends OakException<EntityDict> {};
|
|
||||||
|
|
||||||
export class ConsoleLoadingDataException extends OakException<EntityDict> {};
|
|
||||||
|
|
||||||
export class ExistsNewBidException extends OakUserException<EntityDict> {};
|
|
||||||
|
|
||||||
export function makeException(msg: string | object) {
|
export function makeException(msg: string | object) {
|
||||||
const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
|
const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
|
||||||
|
|
||||||
|
|
@ -18,10 +12,10 @@ export function makeException(msg: string | object) {
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, message } = data;
|
const { name, message, _module, params } = data;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'ExampleException': {
|
case 'ExampleException': {
|
||||||
return new ExampleException(message);
|
return new ExampleException(message, _module, params);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new OakException(`不可解读的exception信息「${msg}」`);
|
throw new OakException(`不可解读的exception信息「${msg}」`);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../templateFiles/polyfill.d.ts" />
|
/// <reference path="./polyfill.d.ts" />
|
||||||
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Layout } from 'antd';
|
import { Layout } from 'antd';
|
||||||
import Style from './index.module.less';
|
import Style from './index.module.less';
|
||||||
|
import useFeatures from '@project/hooks/useFeatures';
|
||||||
|
|
||||||
const { Footer } = Layout;
|
const { Footer } = Layout;
|
||||||
|
|
||||||
export default function Render() {
|
export default function Render() {
|
||||||
|
|
||||||
|
const features = useFeatures();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Footer className={Style.footer}>
|
<Footer className={Style.footer}>
|
||||||
<div className={Style.panel}>
|
<div className={Style.panel}>
|
||||||
<span>
|
<span>
|
||||||
Copyright © {new Date().getFullYear()}{' '}
|
Copyright © {new Date().getFullYear()}{' '}
|
||||||
浙江大学计算机创新技术研究院
|
{features.locales.t('copyright')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Footer>
|
</Footer>
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,4 @@ export default OakComponent({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
features: ['console'],
|
features: ['console'],
|
||||||
methods: {
|
|
||||||
logout() {
|
|
||||||
// this.features.xxxx.logout();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"name":"Oak模板项目", "close": "关闭"}
|
{"name":"Oak模板项目", "close": "关闭","modeUnset": "取消设置"}
|
||||||
|
|
@ -17,6 +17,15 @@ window.addEventListener('unhandledrejection', async (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('error', async (event) => {
|
||||||
|
const { error } = event;
|
||||||
|
|
||||||
|
const result = await exceptionHandler(error, features);
|
||||||
|
if (result) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue