Merge branch 'dev' of gitea.51mars.com:Oak-Team/oak-cli into dev
This commit is contained in:
commit
b75a783aae
|
|
@ -37,7 +37,7 @@ export declare function writeFile(path: string | PathLike, data: any): void;
|
|||
export declare function readFile(path: string | PathLike, options?: {
|
||||
encoding?: null | undefined;
|
||||
flag?: string | undefined;
|
||||
} | null): Buffer | undefined;
|
||||
} | null): Buffer<ArrayBufferLike> | undefined;
|
||||
/**
|
||||
* @name 拷贝文件夹
|
||||
* @export
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
export type GenerateIdOption = {
|
||||
shuffle?: boolean;
|
||||
};
|
||||
export type LogFormatterProp = {
|
||||
level: "info" | "warn" | "error";
|
||||
caller: NodeJS.CallSite | null;
|
||||
args: any[];
|
||||
};
|
||||
export type LogFormatter = (props: LogFormatterProp) => any[];
|
||||
export declare const polyfillConsole: (id: string, trace: boolean, formatter?: LogFormatter) => void;
|
||||
export declare const removePolyfill: (id: string) => void;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.removePolyfill = exports.polyfillConsole = void 0;
|
||||
const uuid_1 = require("uuid");
|
||||
async function generateNewId(option) {
|
||||
if (option?.shuffle && process.env.NODE_ENV === 'development') {
|
||||
|
|
@ -10,3 +11,97 @@ async function generateNewId(option) {
|
|||
Object.assign(global, {
|
||||
generateNewId,
|
||||
});
|
||||
// 存储所有的 polyfill 配置栈
|
||||
const polyfillStack = [];
|
||||
const originalConsoleMethods = {};
|
||||
// 获取调用堆栈信息的工厂函数
|
||||
const createGetCallerInfo = (trace) => () => {
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
const originalFunc = Error.prepareStackTrace;
|
||||
let callerInfo = null;
|
||||
try {
|
||||
const err = new Error();
|
||||
Error.prepareStackTrace = (err, stack) => stack;
|
||||
const stack = err.stack;
|
||||
const currentFile = stack[0].getFileName();
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
const callSite = stack[i];
|
||||
if (currentFile !== callSite.getFileName()) {
|
||||
callerInfo = callSite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
Error.prepareStackTrace = originalFunc;
|
||||
return callerInfo;
|
||||
};
|
||||
const polyfillConsole = (id, trace, formatter) => {
|
||||
// 检查是否已经添加过相同 id 的 polyfill
|
||||
if (polyfillStack.some(item => item.id === id)) {
|
||||
return;
|
||||
}
|
||||
// 第一次调用时保存原始方法
|
||||
if (polyfillStack.length === 0) {
|
||||
originalConsoleMethods.info = console.info;
|
||||
originalConsoleMethods.warn = console.warn;
|
||||
originalConsoleMethods.error = console.error;
|
||||
}
|
||||
// 添加新的 polyfill 到栈顶
|
||||
polyfillStack.push({ id, trace, formatter });
|
||||
// 重新设置 console 方法
|
||||
["info", "warn", "error"].forEach((level) => {
|
||||
const levelStr = level;
|
||||
const originalFunc = originalConsoleMethods[levelStr];
|
||||
console[levelStr] = function (...args) {
|
||||
let processedArgs = args;
|
||||
// 从后往前执行所有 formatter
|
||||
for (let i = polyfillStack.length - 1; i >= 0; i--) {
|
||||
const item = polyfillStack[i];
|
||||
if (item.formatter) {
|
||||
const getCallerInfo = createGetCallerInfo(item.trace);
|
||||
processedArgs = item.formatter({
|
||||
level: levelStr,
|
||||
caller: getCallerInfo(),
|
||||
args: processedArgs,
|
||||
});
|
||||
}
|
||||
}
|
||||
originalFunc(...processedArgs);
|
||||
};
|
||||
});
|
||||
console.info(`new console polyfill added: ${id}`);
|
||||
};
|
||||
exports.polyfillConsole = polyfillConsole;
|
||||
// 可选:提供移除 polyfill 的功能
|
||||
const removePolyfill = (id) => {
|
||||
const index = polyfillStack.findIndex(item => item.id === id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
polyfillStack.splice(index, 1);
|
||||
// 如果栈为空,恢复原始方法
|
||||
if (polyfillStack.length === 0) {
|
||||
console.info = originalConsoleMethods.info;
|
||||
console.warn = originalConsoleMethods.warn;
|
||||
console.error = originalConsoleMethods.error;
|
||||
}
|
||||
else {
|
||||
// 否则重新应用所有 polyfill
|
||||
const tempStack = [...polyfillStack];
|
||||
polyfillStack.length = 0;
|
||||
Object.keys(originalConsoleMethods).forEach(level => {
|
||||
const levelStr = level;
|
||||
console[levelStr] = originalConsoleMethods[levelStr];
|
||||
});
|
||||
tempStack.forEach(item => {
|
||||
polyfillStack.length = 0; // 清空以便重新添加
|
||||
(0, exports.polyfillConsole)(item.id, item.trace, item.formatter);
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.removePolyfill = removePolyfill;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ const koa_mount_1 = tslib_1.__importDefault(require("koa-mount"));
|
|||
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
||||
const utils_1 = require("../utils");
|
||||
const bcryptjs_1 = tslib_1.__importDefault(require("bcryptjs"));
|
||||
const polyfill_1 = require("./polyfill");
|
||||
(0, utils_1.checkNodeVersion)();
|
||||
const socketAdminUI = (0, path_1.join)(__dirname, '../../ui/socket-admin');
|
||||
const DATA_SUBSCRIBE_NAMESPACE = '/dsn';
|
||||
|
|
@ -38,6 +39,7 @@ function concat(...paths) {
|
|||
});
|
||||
}
|
||||
async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
||||
const errorHandler = require((0, path_1.join)(path, 'lib', 'configuration', 'errors')).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'));
|
||||
|
|
@ -164,6 +166,16 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
|||
await appLoader.unmount();
|
||||
return result;
|
||||
}
|
||||
if (errorHandler && typeof errorHandler === 'function') {
|
||||
(0, polyfill_1.polyfillConsole)("startup", true, (props) => {
|
||||
if (props.level === "error") {
|
||||
appLoader.execRoutine(async (ctx) => {
|
||||
await errorHandler(props.caller, props.args, ctx);
|
||||
});
|
||||
}
|
||||
return props.args;
|
||||
});
|
||||
}
|
||||
// 否则启动服务器模式
|
||||
koa.use(async (ctx, next) => {
|
||||
try {
|
||||
|
|
@ -365,6 +377,7 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) {
|
|||
await httpServer.close();
|
||||
await koa.removeAllListeners();
|
||||
await appLoader.unmount();
|
||||
(0, polyfill_1.removePolyfill)("startup");
|
||||
};
|
||||
return shutdown;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
export type LogFormatterProp = {
|
||||
level: "info" | "warn" | "error";
|
||||
caller: NodeJS.CallSite | null;
|
||||
args: any[];
|
||||
};
|
||||
export type LogFormatter = (props: LogFormatterProp) => any[];
|
||||
import { LogFormatter } from "./polyfill";
|
||||
export type WatchConfig = {
|
||||
autoUpdateI18n?: boolean;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|||
const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
||||
const fs_1 = tslib_1.__importDefault(require("fs"));
|
||||
const lodash_1 = require("lodash");
|
||||
const polyfill_1 = require("./polyfill");
|
||||
// 创建事件发射器
|
||||
const createEventEmitter = () => {
|
||||
const listeners = new Map();
|
||||
|
|
@ -580,50 +581,9 @@ const watch = (projectPath, config) => {
|
|||
}
|
||||
};
|
||||
};
|
||||
const polyfillConsole = (trace) => {
|
||||
// 获取调用堆栈信息
|
||||
const getCallerInfo = () => {
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
const originalFunc = Error.prepareStackTrace;
|
||||
let callerInfo = null;
|
||||
try {
|
||||
const err = new Error();
|
||||
Error.prepareStackTrace = (err, stack) => stack;
|
||||
const stack = err.stack; // Type assertion here
|
||||
const currentFile = stack[0].getFileName();
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
// Start from index 1
|
||||
const callSite = stack[i];
|
||||
if (currentFile !== callSite.getFileName()) {
|
||||
callerInfo = callSite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
Error.prepareStackTrace = originalFunc;
|
||||
return callerInfo;
|
||||
};
|
||||
// polyfill console.log 添加时间和文件位置
|
||||
["info", "warn", "error"].forEach((level) => {
|
||||
const levelStr = level;
|
||||
const oldFunc = console[levelStr];
|
||||
console[levelStr] = function (...args) {
|
||||
oldFunc(...(realConfig.polyfill?.console?.formatter({
|
||||
level: levelStr,
|
||||
caller: getCallerInfo(),
|
||||
args,
|
||||
}) || []));
|
||||
};
|
||||
});
|
||||
};
|
||||
// 初始化polyfill
|
||||
polyfillLoader();
|
||||
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
||||
realConfig.polyfill.console.enable && (0, polyfill_1.polyfillConsole)("watch", enableTrace, realConfig.polyfill?.console?.formatter);
|
||||
// 初始编译检查
|
||||
const initialCompile = () => {
|
||||
const serverConfigFile = path_1.default.join(projectPath, "lib/configuration/server.js");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
import { EntityDict } from "oak-domain/lib/types";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||
export type ErrorHandler<ED extends EntityDict & BaseEntityDict> = (caller: NodeJS.CallSite | null, args: any[], ctx: AsyncContext<ED>) => Promise<void>;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -14,3 +14,125 @@ async function generateNewId(option?: GenerateIdOption) {
|
|||
Object.assign(global, {
|
||||
generateNewId,
|
||||
});
|
||||
|
||||
export type LogFormatterProp = {
|
||||
level: "info" | "warn" | "error";
|
||||
caller: NodeJS.CallSite | null;
|
||||
args: any[];
|
||||
};
|
||||
|
||||
export type LogFormatter = (props: LogFormatterProp) => any[];
|
||||
|
||||
type PolyfillItem = {
|
||||
id: string;
|
||||
trace: boolean;
|
||||
formatter?: LogFormatter;
|
||||
};
|
||||
|
||||
// 存储所有的 polyfill 配置栈
|
||||
const polyfillStack: PolyfillItem[] = [];
|
||||
const originalConsoleMethods: {
|
||||
info?: typeof console.info;
|
||||
warn?: typeof console.warn;
|
||||
error?: typeof console.error;
|
||||
} = {};
|
||||
|
||||
// 获取调用堆栈信息的工厂函数
|
||||
const createGetCallerInfo = (trace: boolean) => (): NodeJS.CallSite | null => {
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
const originalFunc = Error.prepareStackTrace;
|
||||
let callerInfo: NodeJS.CallSite | null = null;
|
||||
try {
|
||||
const err = new Error();
|
||||
Error.prepareStackTrace = (err, stack) => stack;
|
||||
const stack = err.stack as unknown as NodeJS.CallSite[];
|
||||
const currentFile = stack[0].getFileName();
|
||||
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
const callSite = stack[i];
|
||||
if (currentFile !== callSite.getFileName()) {
|
||||
callerInfo = callSite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
Error.prepareStackTrace = originalFunc;
|
||||
return callerInfo;
|
||||
};
|
||||
|
||||
export const polyfillConsole = (id: string, trace: boolean, formatter?: LogFormatter) => {
|
||||
// 检查是否已经添加过相同 id 的 polyfill
|
||||
if (polyfillStack.some(item => item.id === id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 第一次调用时保存原始方法
|
||||
if (polyfillStack.length === 0) {
|
||||
originalConsoleMethods.info = console.info;
|
||||
originalConsoleMethods.warn = console.warn;
|
||||
originalConsoleMethods.error = console.error;
|
||||
}
|
||||
|
||||
// 添加新的 polyfill 到栈顶
|
||||
polyfillStack.push({ id, trace, formatter });
|
||||
|
||||
// 重新设置 console 方法
|
||||
["info", "warn", "error"].forEach((level: string) => {
|
||||
const levelStr = level as "info" | "warn" | "error";
|
||||
const originalFunc = originalConsoleMethods[levelStr]!;
|
||||
|
||||
console[levelStr] = function (...args) {
|
||||
let processedArgs = args;
|
||||
|
||||
// 从后往前执行所有 formatter
|
||||
for (let i = polyfillStack.length - 1; i >= 0; i--) {
|
||||
const item = polyfillStack[i];
|
||||
if (item.formatter) {
|
||||
const getCallerInfo = createGetCallerInfo(item.trace);
|
||||
processedArgs = item.formatter({
|
||||
level: levelStr,
|
||||
caller: getCallerInfo(),
|
||||
args: processedArgs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
originalFunc(...processedArgs);
|
||||
};
|
||||
});
|
||||
|
||||
console.info(`new console polyfill added: ${id}`);
|
||||
};
|
||||
|
||||
// 可选:提供移除 polyfill 的功能
|
||||
export const removePolyfill = (id: string) => {
|
||||
const index = polyfillStack.findIndex(item => item.id === id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
polyfillStack.splice(index, 1);
|
||||
|
||||
// 如果栈为空,恢复原始方法
|
||||
if (polyfillStack.length === 0) {
|
||||
console.info = originalConsoleMethods.info!;
|
||||
console.warn = originalConsoleMethods.warn!;
|
||||
console.error = originalConsoleMethods.error!;
|
||||
} else {
|
||||
// 否则重新应用所有 polyfill
|
||||
const tempStack = [...polyfillStack];
|
||||
polyfillStack.length = 0;
|
||||
Object.keys(originalConsoleMethods).forEach(level => {
|
||||
const levelStr = level as "info" | "warn" | "error";
|
||||
console[levelStr] = originalConsoleMethods[levelStr]!;
|
||||
});
|
||||
tempStack.forEach(item => {
|
||||
polyfillStack.length = 0; // 清空以便重新添加
|
||||
polyfillConsole(item.id, item.trace, item.formatter);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -25,6 +25,8 @@ import mount from 'koa-mount';
|
|||
import chalk from 'chalk';
|
||||
import { checkNodeVersion, randomString } from '../utils';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { LogFormatter, polyfillConsole, removePolyfill } from './polyfill';
|
||||
import { ErrorHandler } from '../types';
|
||||
|
||||
checkNodeVersion()
|
||||
|
||||
|
|
@ -54,6 +56,14 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
|||
omitTimers?: boolean,
|
||||
routine?: (context: AsyncContext<ED>) => Promise<void>,
|
||||
): Promise<(() => Promise<any>) | any> {
|
||||
|
||||
const errorHandler: ErrorHandler<ED> = require(join(
|
||||
path,
|
||||
'lib',
|
||||
'configuration',
|
||||
'errors'
|
||||
)).default;
|
||||
|
||||
const serverConfiguration: ServerConfiguration = require(join(
|
||||
path,
|
||||
'lib',
|
||||
|
|
@ -204,6 +214,17 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
|||
return result;
|
||||
}
|
||||
|
||||
if (errorHandler && typeof errorHandler === 'function') {
|
||||
polyfillConsole("startup", true, (props) => {
|
||||
if (props.level === "error") {
|
||||
appLoader.execRoutine(async (ctx) => {
|
||||
await errorHandler( props.caller, props.args, ctx);
|
||||
});
|
||||
}
|
||||
return props.args;
|
||||
});
|
||||
}
|
||||
|
||||
// 否则启动服务器模式
|
||||
koa.use(async (ctx, next) => {
|
||||
try {
|
||||
|
|
@ -451,6 +472,8 @@ export async function startup<ED extends EntityDict & BaseEntityDict, FrontCxt e
|
|||
await httpServer.close();
|
||||
await koa.removeAllListeners();
|
||||
await appLoader.unmount();
|
||||
|
||||
removePolyfill("startup");
|
||||
}
|
||||
|
||||
return shutdown
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import dayjs from "dayjs";
|
|||
import fs from "fs";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore";
|
||||
import { LogFormatter, LogFormatterProp, polyfillConsole } from "./polyfill";
|
||||
|
||||
/*
|
||||
* 工作流程
|
||||
|
|
@ -18,13 +19,6 @@ import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore";
|
|||
declare const require: NodeRequire;
|
||||
declare const process: NodeJS.Process;
|
||||
|
||||
export type LogFormatterProp = {
|
||||
level: "info" | "warn" | "error";
|
||||
caller: NodeJS.CallSite | null;
|
||||
args: any[];
|
||||
};
|
||||
|
||||
export type LogFormatter = (props: LogFormatterProp) => any[];
|
||||
|
||||
export type WatchConfig = {
|
||||
autoUpdateI18n?: boolean;
|
||||
|
|
@ -856,53 +850,9 @@ export const watch = (
|
|||
};
|
||||
};
|
||||
|
||||
const polyfillConsole = (trace: boolean) => {
|
||||
// 获取调用堆栈信息
|
||||
const getCallerInfo = (): NodeJS.CallSite | null => {
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
const originalFunc = Error.prepareStackTrace;
|
||||
let callerInfo: NodeJS.CallSite | null = null;
|
||||
try {
|
||||
const err = new Error();
|
||||
Error.prepareStackTrace = (err, stack) => stack;
|
||||
const stack = err.stack as unknown as NodeJS.CallSite[]; // Type assertion here
|
||||
const currentFile = stack[0].getFileName();
|
||||
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
// Start from index 1
|
||||
const callSite = stack[i];
|
||||
if (currentFile !== callSite.getFileName()) {
|
||||
callerInfo = callSite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
Error.prepareStackTrace = originalFunc;
|
||||
return callerInfo;
|
||||
};
|
||||
// polyfill console.log 添加时间和文件位置
|
||||
["info", "warn", "error"].forEach((level: string) => {
|
||||
const levelStr = level as "info" | "warn" | "error";
|
||||
const oldFunc = console[levelStr];
|
||||
console[levelStr] = function (...args) {
|
||||
oldFunc(
|
||||
...(realConfig.polyfill?.console?.formatter({
|
||||
level: levelStr,
|
||||
caller: getCallerInfo(),
|
||||
args,
|
||||
}) || [])
|
||||
);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化polyfill
|
||||
polyfillLoader();
|
||||
realConfig.polyfill.console.enable && polyfillConsole(enableTrace);
|
||||
realConfig.polyfill.console.enable && polyfillConsole("watch", enableTrace, realConfig.polyfill?.console?.formatter);
|
||||
|
||||
// 初始编译检查
|
||||
const initialCompile = () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { EntityDict } from "oak-domain/lib/types";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||
|
||||
export type ErrorHandler<ED extends EntityDict & BaseEntityDict> = ( caller: NodeJS.CallSite | null, args: any[], ctx: AsyncContext<ED>) => Promise<void>;
|
||||
Loading…
Reference in New Issue