260 lines
10 KiB
JavaScript
260 lines
10 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.watch = void 0;
|
||
const tslib_1 = require("tslib");
|
||
const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
|
||
const path_1 = require("path");
|
||
const typescript_1 = tslib_1.__importDefault(require("typescript"));
|
||
const path_2 = tslib_1.__importDefault(require("path"));
|
||
const start_1 = require("./start");
|
||
const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
||
const fs_1 = tslib_1.__importDefault(require("fs"));
|
||
const lodash_1 = require("lodash");
|
||
const watch = (projectPath) => {
|
||
const enableTrace = !!process.env.ENABLE_TRACE;
|
||
//polyfill console.log 添加时间
|
||
const polyfill = (trace) => {
|
||
const getTime = () => (0, dayjs_1.default)().format("YYYY-MM-DD HH:mm:ss.SSS");
|
||
const infoStart = "\x1B[36m[ Info";
|
||
const warnStart = "\x1B[33m[ Warn";
|
||
const errorStart = "\x1B[31m[ Error";
|
||
const clearColor = trace ? "]\x1B[0m\n" : "]\x1B[0m";
|
||
// 获取调用堆栈信息
|
||
const getCallerInfo = () => {
|
||
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;
|
||
};
|
||
const getFileInfo = () => {
|
||
if (!trace) {
|
||
return "";
|
||
}
|
||
const callerInfo = getCallerInfo();
|
||
const fileInfo = callerInfo
|
||
? `${callerInfo.getFileName()}:${callerInfo.getLineNumber()}`
|
||
: "";
|
||
return fileInfo.trim();
|
||
};
|
||
// polyfill console.log 添加时间和文件位置
|
||
const oldLog = console.log;
|
||
console.log = function (...args) {
|
||
oldLog(infoStart, getTime(), getFileInfo(), clearColor, ...args);
|
||
};
|
||
const oldWarn = console.warn;
|
||
console.warn = function (...args) {
|
||
oldWarn(warnStart, getTime(), getFileInfo(), clearColor, ...args);
|
||
};
|
||
const oldError = console.error;
|
||
console.error = function (...args) {
|
||
oldError(errorStart, getTime(), getFileInfo(), clearColor, ...args);
|
||
};
|
||
};
|
||
polyfill(enableTrace);
|
||
let shutdown;
|
||
const restart = async () => {
|
||
if (shutdown) {
|
||
await shutdown();
|
||
}
|
||
console.warn("----> Clearing require cache of project......");
|
||
let deleteCount = 0;
|
||
// 清空lib以下目录的缓存
|
||
Object.keys(require.cache).forEach((key) => {
|
||
// 如果不是项目目录下的文件,不删除
|
||
if (!key.startsWith(projectPath)) {
|
||
return;
|
||
}
|
||
else if (key.includes("lib") && !key.includes("node_modules")) {
|
||
delete require.cache[key];
|
||
deleteCount++;
|
||
}
|
||
});
|
||
console.warn(`----> ${deleteCount} modules has been removed from require.cache.`);
|
||
const pwd = process.cwd();
|
||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||
const simpleConnector = require((0, path_1.join)(projectPath, 'lib/config/connector')).default;
|
||
console.warn("----> Starting service......");
|
||
shutdown = await (0, start_1.startup)(pwd, simpleConnector);
|
||
};
|
||
const watchSourcePath = (0, path_1.join)(projectPath, "src");
|
||
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_2.default.dirname(configFileName));
|
||
const watcher = chokidar_1.default.watch(watchSourcePath, {
|
||
persistent: true,
|
||
ignored: (file) => file.endsWith(".tsx") ||
|
||
file.includes("components") ||
|
||
file.includes("pages") ||
|
||
file.includes("hooks"),
|
||
interval: 100,
|
||
binaryInterval: 100,
|
||
cwd: projectPath,
|
||
depth: 99,
|
||
followSymlinks: true,
|
||
ignoreInitial: false,
|
||
ignorePermissionErrors: false,
|
||
usePolling: false,
|
||
alwaysStat: false,
|
||
});
|
||
let startWatching = false;
|
||
watcher.on("ready", () => {
|
||
console.warn("Initial scan complete. Ready for changes");
|
||
startWatching = true;
|
||
});
|
||
watcher.on("error", (error) => console.log(`Watcher error: ${error}`));
|
||
let isProcessing = false;
|
||
const fileChangeHandler = async (path, type) => {
|
||
// 判断一下是不是以下扩展名:ts,tsx
|
||
if (!path.endsWith(".ts")) {
|
||
// 如果是json或者xml文件,复制或者删除
|
||
if (path.endsWith(".json") || path.endsWith(".xml")) {
|
||
const targetPath = path.replace("src", "lib");
|
||
if (type === "remove") {
|
||
fs_1.default.unlinkSync(targetPath);
|
||
console.warn(`File ${targetPath} has been removed.`);
|
||
}
|
||
else if (type === "add") {
|
||
fs_1.default.copyFileSync(path, targetPath, fs_1.default.constants.COPYFILE_FICLONE);
|
||
console.warn(`File ${path} has been created at ${targetPath}.`);
|
||
}
|
||
else if (type === "change") {
|
||
// 强制覆盖文件
|
||
if (fs_1.default.existsSync(targetPath)) {
|
||
fs_1.default.unlinkSync(targetPath);
|
||
}
|
||
fs_1.default.copyFileSync(path, targetPath, fs_1.default.constants.COPYFILE_FICLONE);
|
||
console.warn(`File ${path} has been copied to ${targetPath}.`);
|
||
}
|
||
}
|
||
else {
|
||
console.warn(`File ${path} is not [ts,json,xml] file, skiped.`);
|
||
}
|
||
return;
|
||
}
|
||
// 控制台清空
|
||
console.clear();
|
||
console.warn(`File ${path} has been ${type}d`);
|
||
// 先判断一下这个文件在不在require.cache里面
|
||
const modulePath = (0, path_1.resolve)(path);
|
||
// 将src替换为lib
|
||
const libPath = modulePath
|
||
.replace("\\src\\", "\\lib\\")
|
||
.replace(".ts", ".js");
|
||
let compileOnly = false;
|
||
if (!require.cache[libPath]) {
|
||
// 如果是删除的话,直接尝试删除lib下的文件
|
||
if (type === "remove") {
|
||
try {
|
||
fs_1.default.unlinkSync(libPath);
|
||
}
|
||
catch (e) {
|
||
console.error(`Error in delete ${libPath}`, e);
|
||
}
|
||
console.warn(`File ${libPath} has been removed.`);
|
||
return;
|
||
}
|
||
console.warn(`File ${libPath} is not in module cache, will compile only.`);
|
||
compileOnly = true;
|
||
}
|
||
else {
|
||
// 如果是删除,则需要发出警告,文件正在被进程使用
|
||
if (type === "remove") {
|
||
console.error(`File ${libPath} is being used, skiped.`);
|
||
return;
|
||
}
|
||
}
|
||
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;
|
||
}
|
||
}
|
||
// 只输出单个文件
|
||
const emitResult = program.emit(sourceFile);
|
||
// 是否成功
|
||
const result = emitResult.emitSkipped;
|
||
if (result) {
|
||
console.error(`Emit failed for ${path}!`);
|
||
}
|
||
else {
|
||
console.log(`Emit succeeded. ${compileOnly ? "" : "reload service......"}`);
|
||
if (compileOnly) {
|
||
return;
|
||
}
|
||
await restart();
|
||
}
|
||
};
|
||
const onChangeDebounced = (0, lodash_1.debounce)(async (path, type) => {
|
||
if (isProcessing) {
|
||
console.log("Processing, please wait...");
|
||
return;
|
||
}
|
||
try {
|
||
isProcessing = true;
|
||
await fileChangeHandler(path, type);
|
||
}
|
||
catch (e) {
|
||
console.clear();
|
||
console.error(e);
|
||
}
|
||
finally {
|
||
isProcessing = false;
|
||
}
|
||
}, 100);
|
||
watcher
|
||
.on("add", (path) => {
|
||
if (startWatching) {
|
||
onChangeDebounced(path, "add");
|
||
}
|
||
})
|
||
.on("change", (path) => {
|
||
if (startWatching) {
|
||
onChangeDebounced(path, "change");
|
||
}
|
||
})
|
||
.on("unlink", (path) => {
|
||
if (startWatching) {
|
||
onChangeDebounced(path, "remove");
|
||
}
|
||
});
|
||
restart();
|
||
};
|
||
exports.watch = watch;
|