watch不再需要提前build
This commit is contained in:
parent
dc5ab0953d
commit
35b3ff3694
|
|
@ -175,72 +175,76 @@ const watch = (projectPath, config) => {
|
|||
// 测试:在src下新建一个ts文件,然后删除lib下的js文件,然后require这个文件,会发现会自动编译ts文件
|
||||
const polyfillLoader = () => {
|
||||
const BuiltinModule = require("module");
|
||||
// 兼容一些模拟环境下的 module 构造函数
|
||||
// 模拟环境下的 module 构造函数
|
||||
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||
// 保存原始的 _resolveFilename 方法
|
||||
// 保存原始 _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; // 用于存储替换后的路径
|
||||
// 使用 replaceAliasWithPath 进行 alias 路径替换
|
||||
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); // 更新解析路径
|
||||
console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`);
|
||||
resolvedRequest = path_1.default.join(projectPath, replacedPath);
|
||||
}
|
||||
let filename; // 用于存储最终解析的文件名
|
||||
try {
|
||||
// 调用原始的 _resolveFilename 方法尝试解析文件
|
||||
filename = oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||
return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||
}
|
||||
catch (error) {
|
||||
console.log(`[resolve] Failed to resolve: ${resolvedRequest}`); // 打印解析失败信息
|
||||
// 如果解析失败,尝试处理 .ts 源文件的动态编译
|
||||
if (error.code === "MODULE_NOT_FOUND") {
|
||||
// 构造 .js 文件路径
|
||||
const jsPath = path_1.default.resolve(path_1.default.dirname(parent.filename), resolvedRequest + ".js");
|
||||
// 替换为对应的 .ts 文件路径
|
||||
const tsPath = jsPath
|
||||
.replace(/\.js$/, ".ts")
|
||||
.replace(path_1.default.join(projectPath, "lib"), // 替换 lib 为 src
|
||||
path_1.default.join(projectPath, "src"));
|
||||
// 检查对应的 .ts 文件是否存在
|
||||
if (fs_1.default.existsSync(tsPath)) {
|
||||
console.log(`[resolve] Found TypeScript source file: ${tsPath}, attempting to compile...`);
|
||||
// 调用自定义函数进行 TypeScript 编译
|
||||
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(tsPath, options, projectReferences);
|
||||
// 如果存在编译错误,抛出异常
|
||||
if (diagnostics.length) {
|
||||
console.error(`[resolve] Compilation failed for: ${tsPath}`);
|
||||
throw error;
|
||||
}
|
||||
// 执行编译,并检查是否成功
|
||||
const emitResult = program.emit(sourceFile);
|
||||
if (emitResult.emitSkipped) {
|
||||
console.error(`[resolve] Emit skipped for: ${tsPath}`);
|
||||
throw error;
|
||||
}
|
||||
console.log(`[resolve] Successfully compiled: ${tsPath}`);
|
||||
// 将解析路径更新为对应的 .js 文件路径
|
||||
filename = jsPath;
|
||||
}
|
||||
else {
|
||||
// 如果 .ts 文件不存在,打印错误并抛出异常
|
||||
console.error(`[resolve] ${tsPath} does not exist. Unable to resolve module.`);
|
||||
throw error;
|
||||
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);
|
||||
}
|
||||
else {
|
||||
// 如果不是 MODULE_NOT_FOUND 异常,直接抛出
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
// 返回最终解析的文件名
|
||||
return filename;
|
||||
};
|
||||
};
|
||||
polyfillLoader();
|
||||
|
|
@ -289,6 +293,45 @@ const watch = (projectPath, config) => {
|
|||
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/aspects/index.ts",
|
||||
"src/checkers/index.ts",
|
||||
"src/triggers/index.ts",
|
||||
"src/timers/index.ts",
|
||||
"src/routines/start.ts",
|
||||
"src/watchers/index.ts"
|
||||
];
|
||||
compileFiles.forEach(tryCompile);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
realConfig.lifecycle.onInit();
|
||||
let shutdown;
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ const getOverrideConfig = (config?: WatchConfig): RealWatchConfig => {
|
|||
};
|
||||
|
||||
type AliasConfig = Record<string, string | string[]>;
|
||||
type ModuleType = import('module')
|
||||
|
||||
/**
|
||||
* 根据 alias 配置表将路径中的别名替换为真实路径
|
||||
|
|
@ -186,32 +187,32 @@ type AliasConfig = Record<string, string | string[]>;
|
|||
* @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/(.*)'
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
// If no alias matches, return the original path
|
||||
return path;
|
||||
}
|
||||
|
||||
export const watch = (
|
||||
|
|
@ -293,92 +294,105 @@ export const watch = (
|
|||
// 测试:在src下新建一个ts文件,然后删除lib下的js文件,然后require这个文件,会发现会自动编译ts文件
|
||||
const polyfillLoader = () => {
|
||||
const BuiltinModule = require("module");
|
||||
|
||||
// 兼容一些模拟环境下的 module 构造函数
|
||||
// 模拟环境下的 module 构造函数
|
||||
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
|
||||
|
||||
// 保存原始的 _resolveFilename 方法
|
||||
// 保存原始 _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: typeof Module, // 调用方模块
|
||||
parent: ModuleType, // 调用方模块
|
||||
isMain: boolean, // 是否是主模块
|
||||
rFoptions: object | undefined // 解析选项
|
||||
) {
|
||||
let resolvedRequest = request; // 用于存储替换后的路径
|
||||
|
||||
// 使用 replaceAliasWithPath 进行 alias 路径替换
|
||||
let resolvedRequest = request;
|
||||
const replacedPath = replaceAliasWithPath(request, aliasConfig);
|
||||
if (replacedPath !== request) {
|
||||
console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`); // 记录替换日志
|
||||
resolvedRequest = pathLib.join(projectPath, replacedPath); // 更新解析路径
|
||||
}
|
||||
|
||||
let filename; // 用于存储最终解析的文件名
|
||||
if (replacedPath !== request) {
|
||||
console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`);
|
||||
resolvedRequest = pathLib.join(projectPath, replacedPath);
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用原始的 _resolveFilename 方法尝试解析文件
|
||||
filename = oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||
return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions);
|
||||
} catch (error: any) {
|
||||
console.log(`[resolve] Failed to resolve: ${resolvedRequest}`); // 打印解析失败信息
|
||||
|
||||
// 如果解析失败,尝试处理 .ts 源文件的动态编译
|
||||
if (error.code === "MODULE_NOT_FOUND") {
|
||||
// 构造 .js 文件路径
|
||||
const jsPath = pathLib.resolve(pathLib.dirname(parent.filename), resolvedRequest + ".js");
|
||||
const requestPath = pathLib.resolve(pathLib.dirname(parent.filename), resolvedRequest);
|
||||
|
||||
// 替换为对应的 .ts 文件路径
|
||||
const tsPath = jsPath
|
||||
.replace(/\.js$/, ".ts")
|
||||
.replace(
|
||||
pathLib.join(projectPath, "lib"), // 替换 lib 为 src
|
||||
pathLib.join(projectPath, "src")
|
||||
);
|
||||
|
||||
// 检查对应的 .ts 文件是否存在
|
||||
if (fs.existsSync(tsPath)) {
|
||||
console.log(`[resolve] Found TypeScript source file: ${tsPath}, attempting to compile...`);
|
||||
|
||||
// 调用自定义函数进行 TypeScript 编译
|
||||
const { program, sourceFile, diagnostics } = createProgramAndSourceFile(
|
||||
tsPath,
|
||||
options,
|
||||
projectReferences
|
||||
);
|
||||
|
||||
// 如果存在编译错误,抛出异常
|
||||
if (diagnostics.length) {
|
||||
console.error(`[resolve] Compilation failed for: ${tsPath}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 执行编译,并检查是否成功
|
||||
const emitResult = program.emit(sourceFile);
|
||||
|
||||
if (emitResult.emitSkipped) {
|
||||
console.error(`[resolve] Emit skipped for: ${tsPath}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log(`[resolve] Successfully compiled: ${tsPath}`);
|
||||
|
||||
// 将解析路径更新为对应的 .js 文件路径
|
||||
filename = jsPath;
|
||||
} else {
|
||||
// 如果 .ts 文件不存在,打印错误并抛出异常
|
||||
console.error(`[resolve] ${tsPath} does not exist. Unable to resolve module.`);
|
||||
throw error;
|
||||
// 处理文件夹导入
|
||||
if (fs.existsSync(requestPath) && fs.statSync(requestPath).isDirectory()) {
|
||||
return resolveDirectory(requestPath, parent, options, projectReferences);
|
||||
}
|
||||
} else {
|
||||
// 如果不是 MODULE_NOT_FOUND 异常,直接抛出
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回最终解析的文件名
|
||||
return filename;
|
||||
// 处理单文件导入
|
||||
return resolveAndCompile(requestPath, parent, options, projectReferences);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -436,6 +450,53 @@ export const watch = (
|
|||
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/aspects/index.ts",
|
||||
"src/checkers/index.ts",
|
||||
"src/triggers/index.ts",
|
||||
"src/timers/index.ts",
|
||||
"src/routines/start.ts",
|
||||
"src/watchers/index.ts"
|
||||
];
|
||||
compileFiles.forEach(tryCompile);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
realConfig.lifecycle.onInit();
|
||||
let shutdown: (() => Promise<void>) | undefined;
|
||||
|
|
|
|||
Loading…
Reference in New Issue