@boristype/bt-cli 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/build/builder/config.js +88 -0
- package/build/cli/commands/artifact.js +14 -0
- package/build/cli/commands/build.js +25 -0
- package/build/cli/commands/dev.js +190 -0
- package/build/cli/commands/index.js +18 -0
- package/build/cli/commands/init.js +15 -0
- package/build/cli/commands/link.js +18 -0
- package/build/cli/commands/push.js +28 -0
- package/build/cli/index.js +14 -0
- package/build/cli/types.js +2 -0
- package/build/core/artifacting/context.js +54 -0
- package/build/core/artifacting/index.js +37 -0
- package/build/core/artifacting/stages/index.js +10 -0
- package/build/core/artifacting/stages/main-archive.js +72 -0
- package/build/core/artifacting/stages/validate.js +70 -0
- package/build/core/artifacting/types.js +6 -0
- package/build/core/artifacting/utils/index.js +10 -0
- package/build/core/artifacting/utils/zip.js +94 -0
- package/build/core/babel.js +96 -0
- package/build/core/btconfig.types.js +6 -0
- package/build/core/build.js +280 -0
- package/build/core/building/compile-mode.js +146 -0
- package/build/core/building/compiler.js +281 -0
- package/build/core/building/coordinator.js +71 -0
- package/build/core/building/files.js +290 -0
- package/build/core/building/index.js +102 -0
- package/build/core/building/output.js +92 -0
- package/build/core/building/transformers.js +110 -0
- package/build/core/building/types.js +19 -0
- package/build/core/config.js +157 -0
- package/build/core/dependencies.js +223 -0
- package/build/core/linking/cache.js +260 -0
- package/build/core/linking/context.js +149 -0
- package/build/core/linking/dependencies.js +240 -0
- package/build/core/linking/executables.js +61 -0
- package/build/core/linking/generators/api-ext.js +57 -0
- package/build/core/linking/generators/component.js +83 -0
- package/build/core/linking/generators/filemap.js +53 -0
- package/build/core/linking/generators/index.js +21 -0
- package/build/core/linking/generators/init-xml.js +37 -0
- package/build/core/linking/generators/package-json.js +50 -0
- package/build/core/linking/index.js +213 -0
- package/build/core/linking/linkers/component.js +175 -0
- package/build/core/linking/linkers/index.js +69 -0
- package/build/core/linking/linkers/standalone.js +144 -0
- package/build/core/linking/linkers/system.js +86 -0
- package/build/core/linking/parsers.js +278 -0
- package/build/core/linking/types.js +6 -0
- package/build/core/linking/utils/copy.js +101 -0
- package/build/core/linking/utils/index.js +26 -0
- package/build/core/linking/utils/node-modules.js +226 -0
- package/build/core/linking/utils/package-type.js +101 -0
- package/build/core/linking/utils/url.js +73 -0
- package/build/core/linking/utils/write.js +91 -0
- package/build/core/logger.js +10 -0
- package/build/core/pushing/config.js +90 -0
- package/build/core/pushing/index.js +96 -0
- package/build/core/pushing/init-scripts.js +173 -0
- package/build/core/pushing/queue.js +95 -0
- package/build/core/pushing/reinit.js +61 -0
- package/build/core/pushing/session.js +167 -0
- package/build/core/pushing/types.js +6 -0
- package/build/core/pushing/upload.js +35 -0
- package/build/core/tsconfig.js +78 -0
- package/build/core/utils/index.js +17 -0
- package/build/core/utils/logger.js +46 -0
- package/build/core/utils/properties.js +81 -0
- package/build/core/utils/xml.js +44 -0
- package/build/core/utils.js +59 -0
- package/build/index.js +76 -0
- package/build/plugins/destructuring.js +83 -0
- package/build/plugins/forOfToForIn.js +14 -0
- package/build/plugins/loopHoistVariables.js +160 -0
- package/build/plugins/precedence.js +172 -0
- package/build/plugins/removeImportExport.js +42 -0
- package/build/plugins/replaceDollar.js +16 -0
- package/build/plugins/spreadArray.js +42 -0
- package/build/plugins/spreadObject.js +91 -0
- package/build/transformers/arrayFunctional.js +467 -0
- package/build/transformers/arrayGeneral.js +222 -0
- package/build/transformers/blockScoping.js +212 -0
- package/build/transformers/destructuring.js +133 -0
- package/build/transformers/dirname.js +79 -0
- package/build/transformers/enumsToObjects.js +25 -0
- package/build/transformers/execObj.js +220 -0
- package/build/transformers/forOfToForIn.js +45 -0
- package/build/transformers/funcSemantic.js +113 -0
- package/build/transformers/functions.js +270 -0
- package/build/transformers/globalCache.js +34 -0
- package/build/transformers/loopHoistVariables.js +352 -0
- package/build/transformers/math.js +39 -0
- package/build/transformers/namespaces.js +22 -0
- package/build/transformers/numericSeparator.js +46 -0
- package/build/transformers/objectProperties.js +54 -0
- package/build/transformers/precedence.js +192 -0
- package/build/transformers/propSemantic.js +467 -0
- package/build/transformers/remodule.js +620 -0
- package/build/transformers/removeImportExport.js +135 -0
- package/build/transformers/replaceDollar.js +46 -0
- package/build/transformers/shorthandProperties.js +34 -0
- package/build/transformers/spreadArray.js +68 -0
- package/build/transformers/spreadObject.js +134 -0
- package/build/transformers/string.js +138 -0
- package/build/transformers/templateLiterals.js +104 -0
- package/build/transformers/tocodelibrary.js +178 -0
- package/build/transformers/utils.js +202 -0
- package/build/wshcm/client.js +193 -0
- package/build/wshcm/evaluator.js +111 -0
- package/build/wshcm/exceptions.js +25 -0
- package/build/wshcm/index.js +20 -0
- package/build/wshcm/soap-utils.js +228 -0
- package/build/wshcm/types.js +2 -0
- package/build/wshcm/uploader.js +320 -0
- package/package.json +51 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Определение режима транспиляции для файла
|
|
4
|
+
*
|
|
5
|
+
* @module build/compile-mode
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.resolveCompileMode = resolveCompileMode;
|
|
12
|
+
exports.collectExecutables = collectExecutables;
|
|
13
|
+
exports.computeFileKey = computeFileKey;
|
|
14
|
+
exports.computeCurrentFileJs = computeCurrentFileJs;
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
18
|
+
const EXECUTABLE_OBJECTS = new Set([
|
|
19
|
+
"remoteAction",
|
|
20
|
+
"remoteCollection",
|
|
21
|
+
"systemEventHandler",
|
|
22
|
+
"serverAgent",
|
|
23
|
+
"codeLibrary",
|
|
24
|
+
"statisticRec",
|
|
25
|
+
]);
|
|
26
|
+
const BT_MODE_DIRECTIVE = /\/\/\/\s*@bt-mode\s+(bare|script|module)/;
|
|
27
|
+
/**
|
|
28
|
+
* Проверяет, содержит ли файл импорты executable objects из @boristype/types
|
|
29
|
+
*/
|
|
30
|
+
function hasExecutableObjectImports(sourceFile) {
|
|
31
|
+
let found = false;
|
|
32
|
+
function check(node) {
|
|
33
|
+
if (typescript_1.default.isImportDeclaration(node)) {
|
|
34
|
+
const spec = node.moduleSpecifier;
|
|
35
|
+
if (typescript_1.default.isStringLiteral(spec) && spec.text.startsWith("@boristype/types")) {
|
|
36
|
+
const bindings = node.importClause?.namedBindings;
|
|
37
|
+
if (bindings && typescript_1.default.isNamedImports(bindings)) {
|
|
38
|
+
for (const el of bindings.elements) {
|
|
39
|
+
if (EXECUTABLE_OBJECTS.has(el.name.text)) {
|
|
40
|
+
found = true;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
typescript_1.default.forEachChild(node, check);
|
|
48
|
+
}
|
|
49
|
+
typescript_1.default.forEachChild(sourceFile, check);
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Ищет директиву /// @bt-mode в начале файла
|
|
54
|
+
*/
|
|
55
|
+
function getDirectiveMode(sourceFile) {
|
|
56
|
+
const text = sourceFile.getFullText();
|
|
57
|
+
const firstLines = text.split("\n").slice(0, 30).join("\n");
|
|
58
|
+
const match = firstLines.match(BT_MODE_DIRECTIVE);
|
|
59
|
+
return match ? match[1] : null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Вычисляет compileMode из usePolyfill/useRemodule (обратная совместимость)
|
|
63
|
+
*/
|
|
64
|
+
function getCompileModeFromOptions(options) {
|
|
65
|
+
if (options.compileMode) {
|
|
66
|
+
return options.compileMode;
|
|
67
|
+
}
|
|
68
|
+
if (options.usePolyfill === false && options.useRemodule === false) {
|
|
69
|
+
return "bare";
|
|
70
|
+
}
|
|
71
|
+
return "module";
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Определяет режим транспиляции для конкретного файла
|
|
75
|
+
*
|
|
76
|
+
* Приоритет: директива > .test.ts/executable → script > options.compileMode
|
|
77
|
+
*/
|
|
78
|
+
function resolveCompileMode(sourceFile, options, executablePaths) {
|
|
79
|
+
const directive = getDirectiveMode(sourceFile);
|
|
80
|
+
if (directive) {
|
|
81
|
+
return directive;
|
|
82
|
+
}
|
|
83
|
+
if (sourceFile.fileName.endsWith(".test.ts")) {
|
|
84
|
+
return "script";
|
|
85
|
+
}
|
|
86
|
+
if (executablePaths?.has(sourceFile.fileName) ?? hasExecutableObjectImports(sourceFile)) {
|
|
87
|
+
return "script";
|
|
88
|
+
}
|
|
89
|
+
return getCompileModeFromOptions(options);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Собирает список executable objects из исходных файлов
|
|
93
|
+
*/
|
|
94
|
+
function collectExecutables(program, sourceFiles) {
|
|
95
|
+
const paths = new Set();
|
|
96
|
+
const executables = [];
|
|
97
|
+
const packageJson = getPackageJson(program);
|
|
98
|
+
if (!packageJson) {
|
|
99
|
+
return { executables, paths };
|
|
100
|
+
}
|
|
101
|
+
for (const sf of sourceFiles) {
|
|
102
|
+
if (hasExecutableObjectImports(sf)) {
|
|
103
|
+
paths.add(sf.fileName);
|
|
104
|
+
executables.push({
|
|
105
|
+
packageName: packageJson.name,
|
|
106
|
+
packageVersion: packageJson.version,
|
|
107
|
+
filePath: sf.fileName,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { executables, paths };
|
|
112
|
+
}
|
|
113
|
+
function getPackageJson(program) {
|
|
114
|
+
let root = program.getCurrentDirectory();
|
|
115
|
+
while (root !== node_path_1.default.dirname(root)) {
|
|
116
|
+
const p = node_path_1.default.join(root, "package.json");
|
|
117
|
+
if (node_fs_1.default.existsSync(p)) {
|
|
118
|
+
const j = JSON.parse(node_fs_1.default.readFileSync(p, "utf-8"));
|
|
119
|
+
return {
|
|
120
|
+
name: j.name ?? "unknown",
|
|
121
|
+
version: j.version ?? "0.0.0",
|
|
122
|
+
root,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
root = node_path_1.default.dirname(root);
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Вычисляет fileKey для script mode: packageName+version+relativePath
|
|
131
|
+
* Используется для import.meta и bt.getFileUrl
|
|
132
|
+
*/
|
|
133
|
+
function computeFileKey(sourceFile, program) {
|
|
134
|
+
const pkg = getPackageJson(program);
|
|
135
|
+
if (!pkg)
|
|
136
|
+
return undefined;
|
|
137
|
+
const rel = node_path_1.default.relative(pkg.root, sourceFile.fileName).replace(/\\/g, "/");
|
|
138
|
+
return `${pkg.name}+${pkg.version}+${rel}`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Вычисляет имя текущего .js файла для module mode (только basename).
|
|
142
|
+
* AbsoluteUrl уже знает директорию, нужен только filename.
|
|
143
|
+
*/
|
|
144
|
+
function computeCurrentFileJs(sourceFile) {
|
|
145
|
+
return node_path_1.default.basename(sourceFile.fileName).replace(/\.tsx?$/, ".js");
|
|
146
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TypeScript компилятор
|
|
4
|
+
*
|
|
5
|
+
* Отвечает за:
|
|
6
|
+
* - Создание TypeScript program (noEmit для диагностики)
|
|
7
|
+
* - Emit через bt-ir (compileSourceFile)
|
|
8
|
+
* - Пост-обработку через transformOutput
|
|
9
|
+
*
|
|
10
|
+
* @module build/compiler
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.compile = compile;
|
|
17
|
+
exports.createWatchProgram = createWatchProgram;
|
|
18
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
20
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
21
|
+
const bt_ir_1 = require("@boristype/bt-ir");
|
|
22
|
+
const logger_js_1 = require("../logger.js");
|
|
23
|
+
const output_js_1 = require("./output.js");
|
|
24
|
+
const compile_mode_js_1 = require("./compile-mode.js");
|
|
25
|
+
/**
|
|
26
|
+
* Фильтрует список файлов по заданному списку
|
|
27
|
+
* Если files пустой, возвращает все rawFileNames
|
|
28
|
+
*/
|
|
29
|
+
function selectFiles(rawFileNames, files) {
|
|
30
|
+
if (files.length === 0) {
|
|
31
|
+
return rawFileNames;
|
|
32
|
+
}
|
|
33
|
+
return rawFileNames.filter(x => files.includes(x));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Получает директорию вывода из конфигурации program
|
|
37
|
+
*/
|
|
38
|
+
function getOutputDirectory(program) {
|
|
39
|
+
const options = program.getCompilerOptions();
|
|
40
|
+
const outDir = options.outDir || '';
|
|
41
|
+
// Если outDir уже абсолютный путь, используем его напрямую
|
|
42
|
+
if (node_path_1.default.isAbsolute(outDir)) {
|
|
43
|
+
return node_path_1.default.normalize(outDir);
|
|
44
|
+
}
|
|
45
|
+
return node_path_1.default.normalize(node_path_1.default.join(program.getCurrentDirectory(), outDir));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Выводит диагностику TypeScript в лог
|
|
49
|
+
*/
|
|
50
|
+
function reportDiagnostics(diagnostics) {
|
|
51
|
+
diagnostics.forEach(diagnostic => {
|
|
52
|
+
if (diagnostic.file) {
|
|
53
|
+
const { line, character } = typescript_1.default.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
|
54
|
+
const message = typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
55
|
+
logger_js_1.logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
logger_js_1.logger.error(typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Вычисляет путь выходного файла для sourceFile
|
|
64
|
+
*/
|
|
65
|
+
function getOutputPath(sourceFile, program) {
|
|
66
|
+
const opts = program.getCompilerOptions();
|
|
67
|
+
const cwd = program.getCurrentDirectory();
|
|
68
|
+
const outDir = node_path_1.default.resolve(cwd, opts.outDir || ".");
|
|
69
|
+
const rootDir = opts.rootDir ? node_path_1.default.resolve(cwd, opts.rootDir) : node_path_1.default.dirname(program.getRootFileNames()[0] || sourceFile.fileName);
|
|
70
|
+
const rel = node_path_1.default.relative(rootDir, sourceFile.fileName);
|
|
71
|
+
const outFile = rel.replace(/\.tsx?$/, ".js");
|
|
72
|
+
return node_path_1.default.join(outDir, outFile);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Компилирует TypeScript файлы через bt-ir
|
|
76
|
+
*
|
|
77
|
+
* Создаёт program с noEmit (диагностика), emit выполняет bt-ir.
|
|
78
|
+
*/
|
|
79
|
+
function compile(context) {
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
const { tsConfig, options, files } = context;
|
|
82
|
+
const fileNames = selectFiles(tsConfig.fileNames, files);
|
|
83
|
+
const compilerOptions = {
|
|
84
|
+
...tsConfig.options,
|
|
85
|
+
noEmit: true,
|
|
86
|
+
};
|
|
87
|
+
const program = typescript_1.default.createProgram(fileNames, compilerOptions);
|
|
88
|
+
const diagnostics = typescript_1.default.getPreEmitDiagnostics(program);
|
|
89
|
+
reportDiagnostics(diagnostics);
|
|
90
|
+
const allSourceFiles = program
|
|
91
|
+
.getSourceFiles()
|
|
92
|
+
.filter((sf) => !sf.isDeclarationFile && !sf.fileName.includes("node_modules"));
|
|
93
|
+
const sourceFiles = fileNames.length === 0
|
|
94
|
+
? allSourceFiles
|
|
95
|
+
: fileNames
|
|
96
|
+
.map((fn) => program.getSourceFile(fn))
|
|
97
|
+
.filter((sf) => !!sf);
|
|
98
|
+
const { executables, paths: executablePaths } = (0, compile_mode_js_1.collectExecutables)(program, sourceFiles);
|
|
99
|
+
const emittedFiles = [];
|
|
100
|
+
const outputDir = getOutputDirectory(program);
|
|
101
|
+
for (const sourceFile of sourceFiles) {
|
|
102
|
+
const mode = (0, compile_mode_js_1.resolveCompileMode)(sourceFile, options, executablePaths);
|
|
103
|
+
const outputPath = getOutputPath(sourceFile, program);
|
|
104
|
+
const result = (0, bt_ir_1.compileSourceFile)(sourceFile, program, {
|
|
105
|
+
mode,
|
|
106
|
+
outputPath,
|
|
107
|
+
filename: sourceFile.fileName,
|
|
108
|
+
fileKey: mode === "script" ? (0, compile_mode_js_1.computeFileKey)(sourceFile, program) : undefined,
|
|
109
|
+
currentFileJs: mode === "module" ? (0, compile_mode_js_1.computeCurrentFileJs)(sourceFile) : undefined,
|
|
110
|
+
});
|
|
111
|
+
for (const output of result.outputs) {
|
|
112
|
+
const { fileName: finalFileName, content } = (0, output_js_1.transformOutput)(output.path, output.code, options);
|
|
113
|
+
const dir = node_path_1.default.dirname(finalFileName);
|
|
114
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
115
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
116
|
+
}
|
|
117
|
+
node_fs_1.default.writeFileSync(finalFileName, content, "utf-8");
|
|
118
|
+
emittedFiles.push(finalFileName);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(outputDir, ".executables.json"), JSON.stringify(executables, null, 2), "utf-8");
|
|
122
|
+
const duration = Date.now() - startTime;
|
|
123
|
+
const hasErrors = diagnostics.filter((d) => d.category === typescript_1.default.DiagnosticCategory.Error).length > 0;
|
|
124
|
+
return {
|
|
125
|
+
success: !hasErrors,
|
|
126
|
+
emitResult: { emitSkipped: false, diagnostics: [] },
|
|
127
|
+
executables,
|
|
128
|
+
diagnostics,
|
|
129
|
+
duration,
|
|
130
|
+
emittedFiles,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Создаёт watch program для инкрементальной компиляции
|
|
135
|
+
*
|
|
136
|
+
* Использует TypeScript watch API для эффективной инкрементальной сборки.
|
|
137
|
+
* Применяет те же трансформеры и пост-обработку, что и обычная компиляция.
|
|
138
|
+
*
|
|
139
|
+
* @param context - Контекст сборки
|
|
140
|
+
* @param onRebuild - Callback при каждой пересборке
|
|
141
|
+
* @returns Контроллер для остановки watch
|
|
142
|
+
*/
|
|
143
|
+
function createWatchProgram(context, onRebuild) {
|
|
144
|
+
const { tsConfig, options, cwd } = context;
|
|
145
|
+
// Определяем путь к tsconfig.json
|
|
146
|
+
const configPath = typescript_1.default.findConfigFile(cwd || process.cwd(), typescript_1.default.sys.fileExists, 'tsconfig.json');
|
|
147
|
+
if (!configPath) {
|
|
148
|
+
throw new Error('Could not find tsconfig.json');
|
|
149
|
+
}
|
|
150
|
+
// Хранилище для executables (обновляется при каждой сборке)
|
|
151
|
+
let currentExecutables = [];
|
|
152
|
+
let currentEmittedFiles = [];
|
|
153
|
+
let buildStartTime = Date.now();
|
|
154
|
+
let hasErrors = false;
|
|
155
|
+
// Watch mode использует noEmit - мы сами делаем emit для affected файлов
|
|
156
|
+
const watchOptions = {
|
|
157
|
+
...tsConfig.options,
|
|
158
|
+
noEmit: true,
|
|
159
|
+
};
|
|
160
|
+
// Создаём watch compiler host с кастомным sys
|
|
161
|
+
const host = typescript_1.default.createWatchCompilerHost(configPath, watchOptions, typescript_1.default.sys, // <-- используем кастомный sys
|
|
162
|
+
typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram,
|
|
163
|
+
// Обработчик диагностики (ошибок)
|
|
164
|
+
(diagnostic) => {
|
|
165
|
+
hasErrors = true;
|
|
166
|
+
const message = typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
167
|
+
if (diagnostic.file) {
|
|
168
|
+
const { line, character } = typescript_1.default.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
|
169
|
+
const fileName = diagnostic.file.fileName;
|
|
170
|
+
logger_js_1.logger.error(`${fileName}:${line + 1}:${character + 1} - ${message}`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
logger_js_1.logger.error(message);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
// Обработчик watch статуса
|
|
177
|
+
(diagnostic) => {
|
|
178
|
+
const message = typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
179
|
+
// Код 6031 = "Starting compilation in watch mode..."
|
|
180
|
+
// Код 6032 = "File change detected. Starting incremental compilation..."
|
|
181
|
+
// Код 6193 = "File change detected. Starting incremental compilation..."
|
|
182
|
+
// Код 6194 = "Found 0 errors. Watching for file changes."
|
|
183
|
+
if (diagnostic.code === 6031 || diagnostic.code === 6032 || diagnostic.code === 6193) {
|
|
184
|
+
buildStartTime = Date.now();
|
|
185
|
+
hasErrors = false;
|
|
186
|
+
currentEmittedFiles = []; // Сбрасываем список emitted файлов
|
|
187
|
+
logger_js_1.logger.info(`🔄 ${message}`);
|
|
188
|
+
}
|
|
189
|
+
else if (diagnostic.code === 6194) {
|
|
190
|
+
// Этот callback вызывается ПОСЛЕ afterProgramCreate,
|
|
191
|
+
// поэтому emit уже выполнен там
|
|
192
|
+
const duration = Date.now() - buildStartTime;
|
|
193
|
+
// Сохраняем executables
|
|
194
|
+
const outputDir = tsConfig.options.outDir || cwd || process.cwd();
|
|
195
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(outputDir, '.executables.json'), JSON.stringify(currentExecutables, null, 2), 'utf-8');
|
|
196
|
+
// Вызываем callback
|
|
197
|
+
if (onRebuild) {
|
|
198
|
+
onRebuild({
|
|
199
|
+
success: !hasErrors,
|
|
200
|
+
emitResult: { emitSkipped: false, diagnostics: [] },
|
|
201
|
+
executables: currentExecutables,
|
|
202
|
+
diagnostics: [],
|
|
203
|
+
duration,
|
|
204
|
+
emittedFiles: currentEmittedFiles,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
logger_js_1.logger.success(`✅ Build successful (${duration}ms)`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
logger_js_1.logger.info(message);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// Сохраняем оригинальный afterProgramCreate
|
|
214
|
+
const originalAfterProgramCreate = host.afterProgramCreate;
|
|
215
|
+
let isFirstBuild = true;
|
|
216
|
+
// Перехватываем afterProgramCreate
|
|
217
|
+
host.afterProgramCreate = (builderProgram) => {
|
|
218
|
+
const program = builderProgram.getProgram();
|
|
219
|
+
// Собираем affected files ДО создания трансформеров
|
|
220
|
+
const affectedFiles = [];
|
|
221
|
+
let result;
|
|
222
|
+
while ((result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile())) {
|
|
223
|
+
if (result.affected && typescript_1.default.isSourceFile(result.affected)) {
|
|
224
|
+
affectedFiles.push(result.affected);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// На первой сборке emit все файлы, потом только affected
|
|
228
|
+
// Фильтруем declaration files и файлы из node_modules
|
|
229
|
+
const filesToEmit = (isFirstBuild
|
|
230
|
+
? program.getSourceFiles()
|
|
231
|
+
: affectedFiles).filter(sf => !sf.isDeclarationFile &&
|
|
232
|
+
!sf.fileName.includes('node_modules'));
|
|
233
|
+
isFirstBuild = false;
|
|
234
|
+
if (filesToEmit.length === 0) {
|
|
235
|
+
logger_js_1.logger.info('No files to emit');
|
|
236
|
+
if (originalAfterProgramCreate) {
|
|
237
|
+
originalAfterProgramCreate(builderProgram);
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
logger_js_1.logger.info(`Emitting ${filesToEmit.length} file(s): ${filesToEmit.map((f) => node_path_1.default.basename(f.fileName)).join(", ")}`);
|
|
242
|
+
const { executables, paths: executablePaths } = (0, compile_mode_js_1.collectExecutables)(program, filesToEmit);
|
|
243
|
+
currentExecutables = executables;
|
|
244
|
+
const outputDir = getOutputDirectory(program);
|
|
245
|
+
for (const sourceFile of filesToEmit) {
|
|
246
|
+
const mode = (0, compile_mode_js_1.resolveCompileMode)(sourceFile, options, executablePaths);
|
|
247
|
+
const outputPath = getOutputPath(sourceFile, program);
|
|
248
|
+
const result = (0, bt_ir_1.compileSourceFile)(sourceFile, program, {
|
|
249
|
+
mode,
|
|
250
|
+
outputPath,
|
|
251
|
+
filename: sourceFile.fileName,
|
|
252
|
+
fileKey: mode === "script" ? (0, compile_mode_js_1.computeFileKey)(sourceFile, program) : undefined,
|
|
253
|
+
currentFileJs: mode === "module" ? (0, compile_mode_js_1.computeCurrentFileJs)(sourceFile) : undefined,
|
|
254
|
+
});
|
|
255
|
+
for (const output of result.outputs) {
|
|
256
|
+
const { fileName: finalFileName, content } = (0, output_js_1.transformOutput)(output.path, output.code, options);
|
|
257
|
+
const dir = node_path_1.default.dirname(finalFileName);
|
|
258
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
259
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
260
|
+
}
|
|
261
|
+
node_fs_1.default.writeFileSync(finalFileName, content, "utf-8");
|
|
262
|
+
currentEmittedFiles.push(finalFileName);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Переопределяем emit на пустышку
|
|
266
|
+
builderProgram.emit = () => ({
|
|
267
|
+
emitSkipped: true,
|
|
268
|
+
diagnostics: [],
|
|
269
|
+
});
|
|
270
|
+
if (originalAfterProgramCreate) {
|
|
271
|
+
originalAfterProgramCreate(builderProgram);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
// Запускаем watch
|
|
275
|
+
const watchProgram = typescript_1.default.createWatchProgram(host);
|
|
276
|
+
return {
|
|
277
|
+
close: () => {
|
|
278
|
+
watchProgram.close();
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Координатор dev mode для multi-package проектов
|
|
4
|
+
*
|
|
5
|
+
* @module building/coordinator
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DevCoordinator = void 0;
|
|
9
|
+
const logger_js_1 = require("../logger.js");
|
|
10
|
+
const index_js_1 = require("../linking/index.js");
|
|
11
|
+
/**
|
|
12
|
+
* Координатор dev mode для multi-package проектов
|
|
13
|
+
*
|
|
14
|
+
* Отслеживает initial build всех пакетов и координирует линковку:
|
|
15
|
+
* - Ждёт завершения initial build от всех пакетов
|
|
16
|
+
* - После этого выполняет одну полную линковку
|
|
17
|
+
* - Далее — инкрементальная линковка для каждого изменения
|
|
18
|
+
*/
|
|
19
|
+
class DevCoordinator {
|
|
20
|
+
pendingInitialBuilds;
|
|
21
|
+
initialLinkDone = false;
|
|
22
|
+
projectPath;
|
|
23
|
+
allPackages;
|
|
24
|
+
pushQueue;
|
|
25
|
+
constructor(projectPath, watchPackages, allPackages, pushQueue) {
|
|
26
|
+
this.projectPath = projectPath;
|
|
27
|
+
this.allPackages = allPackages;
|
|
28
|
+
this.pendingInitialBuilds = new Set(watchPackages.map(p => p.wsName));
|
|
29
|
+
this.pushQueue = pushQueue;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Вызывается при завершении build пакета
|
|
33
|
+
*/
|
|
34
|
+
async onPackageBuild(pkg, emittedFiles) {
|
|
35
|
+
if (this.pendingInitialBuilds.has(pkg.wsName)) {
|
|
36
|
+
// Это initial build
|
|
37
|
+
this.pendingInitialBuilds.delete(pkg.wsName);
|
|
38
|
+
logger_js_1.logger.success(` ✅ Initial build completed: ${pkg.wsName}`);
|
|
39
|
+
if (this.pendingInitialBuilds.size === 0) {
|
|
40
|
+
// Все пакеты собрались — полная линковка
|
|
41
|
+
logger_js_1.logger.info('🔗 All packages built. Running full link...');
|
|
42
|
+
await (0, index_js_1.processPackagesLinking)(this.projectPath, this.allPackages, {});
|
|
43
|
+
this.initialLinkDone = true;
|
|
44
|
+
logger_js_1.logger.success('✅ Dev mode ready. Watching for changes...');
|
|
45
|
+
this.pushQueue?.schedule();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (this.initialLinkDone) {
|
|
49
|
+
// Инкрементальная линковка
|
|
50
|
+
await (0, index_js_1.processPackagesLinking)(this.projectPath, [{ name: pkg.name }], {
|
|
51
|
+
devMode: true,
|
|
52
|
+
changedFiles: emittedFiles,
|
|
53
|
+
});
|
|
54
|
+
this.pushQueue?.schedule();
|
|
55
|
+
}
|
|
56
|
+
// Если !initialLinkDone и это не initial build — игнорируем (промежуточное состояние)
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Вызывается при изменении non-TS файла
|
|
60
|
+
*/
|
|
61
|
+
async onNonTsFileChange(pkg, filePath) {
|
|
62
|
+
if (this.initialLinkDone) {
|
|
63
|
+
await (0, index_js_1.processPackagesLinking)(this.projectPath, [{ name: pkg.name }], {
|
|
64
|
+
devMode: true,
|
|
65
|
+
changedFiles: [filePath],
|
|
66
|
+
});
|
|
67
|
+
this.pushQueue?.schedule();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.DevCoordinator = DevCoordinator;
|