@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.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/build/builder/config.js +88 -0
  4. package/build/cli/commands/artifact.js +14 -0
  5. package/build/cli/commands/build.js +25 -0
  6. package/build/cli/commands/dev.js +190 -0
  7. package/build/cli/commands/index.js +18 -0
  8. package/build/cli/commands/init.js +15 -0
  9. package/build/cli/commands/link.js +18 -0
  10. package/build/cli/commands/push.js +28 -0
  11. package/build/cli/index.js +14 -0
  12. package/build/cli/types.js +2 -0
  13. package/build/core/artifacting/context.js +54 -0
  14. package/build/core/artifacting/index.js +37 -0
  15. package/build/core/artifacting/stages/index.js +10 -0
  16. package/build/core/artifacting/stages/main-archive.js +72 -0
  17. package/build/core/artifacting/stages/validate.js +70 -0
  18. package/build/core/artifacting/types.js +6 -0
  19. package/build/core/artifacting/utils/index.js +10 -0
  20. package/build/core/artifacting/utils/zip.js +94 -0
  21. package/build/core/babel.js +96 -0
  22. package/build/core/btconfig.types.js +6 -0
  23. package/build/core/build.js +280 -0
  24. package/build/core/building/compile-mode.js +146 -0
  25. package/build/core/building/compiler.js +281 -0
  26. package/build/core/building/coordinator.js +71 -0
  27. package/build/core/building/files.js +290 -0
  28. package/build/core/building/index.js +102 -0
  29. package/build/core/building/output.js +92 -0
  30. package/build/core/building/transformers.js +110 -0
  31. package/build/core/building/types.js +19 -0
  32. package/build/core/config.js +157 -0
  33. package/build/core/dependencies.js +223 -0
  34. package/build/core/linking/cache.js +260 -0
  35. package/build/core/linking/context.js +149 -0
  36. package/build/core/linking/dependencies.js +240 -0
  37. package/build/core/linking/executables.js +61 -0
  38. package/build/core/linking/generators/api-ext.js +57 -0
  39. package/build/core/linking/generators/component.js +83 -0
  40. package/build/core/linking/generators/filemap.js +53 -0
  41. package/build/core/linking/generators/index.js +21 -0
  42. package/build/core/linking/generators/init-xml.js +37 -0
  43. package/build/core/linking/generators/package-json.js +50 -0
  44. package/build/core/linking/index.js +213 -0
  45. package/build/core/linking/linkers/component.js +175 -0
  46. package/build/core/linking/linkers/index.js +69 -0
  47. package/build/core/linking/linkers/standalone.js +144 -0
  48. package/build/core/linking/linkers/system.js +86 -0
  49. package/build/core/linking/parsers.js +278 -0
  50. package/build/core/linking/types.js +6 -0
  51. package/build/core/linking/utils/copy.js +101 -0
  52. package/build/core/linking/utils/index.js +26 -0
  53. package/build/core/linking/utils/node-modules.js +226 -0
  54. package/build/core/linking/utils/package-type.js +101 -0
  55. package/build/core/linking/utils/url.js +73 -0
  56. package/build/core/linking/utils/write.js +91 -0
  57. package/build/core/logger.js +10 -0
  58. package/build/core/pushing/config.js +90 -0
  59. package/build/core/pushing/index.js +96 -0
  60. package/build/core/pushing/init-scripts.js +173 -0
  61. package/build/core/pushing/queue.js +95 -0
  62. package/build/core/pushing/reinit.js +61 -0
  63. package/build/core/pushing/session.js +167 -0
  64. package/build/core/pushing/types.js +6 -0
  65. package/build/core/pushing/upload.js +35 -0
  66. package/build/core/tsconfig.js +78 -0
  67. package/build/core/utils/index.js +17 -0
  68. package/build/core/utils/logger.js +46 -0
  69. package/build/core/utils/properties.js +81 -0
  70. package/build/core/utils/xml.js +44 -0
  71. package/build/core/utils.js +59 -0
  72. package/build/index.js +76 -0
  73. package/build/plugins/destructuring.js +83 -0
  74. package/build/plugins/forOfToForIn.js +14 -0
  75. package/build/plugins/loopHoistVariables.js +160 -0
  76. package/build/plugins/precedence.js +172 -0
  77. package/build/plugins/removeImportExport.js +42 -0
  78. package/build/plugins/replaceDollar.js +16 -0
  79. package/build/plugins/spreadArray.js +42 -0
  80. package/build/plugins/spreadObject.js +91 -0
  81. package/build/transformers/arrayFunctional.js +467 -0
  82. package/build/transformers/arrayGeneral.js +222 -0
  83. package/build/transformers/blockScoping.js +212 -0
  84. package/build/transformers/destructuring.js +133 -0
  85. package/build/transformers/dirname.js +79 -0
  86. package/build/transformers/enumsToObjects.js +25 -0
  87. package/build/transformers/execObj.js +220 -0
  88. package/build/transformers/forOfToForIn.js +45 -0
  89. package/build/transformers/funcSemantic.js +113 -0
  90. package/build/transformers/functions.js +270 -0
  91. package/build/transformers/globalCache.js +34 -0
  92. package/build/transformers/loopHoistVariables.js +352 -0
  93. package/build/transformers/math.js +39 -0
  94. package/build/transformers/namespaces.js +22 -0
  95. package/build/transformers/numericSeparator.js +46 -0
  96. package/build/transformers/objectProperties.js +54 -0
  97. package/build/transformers/precedence.js +192 -0
  98. package/build/transformers/propSemantic.js +467 -0
  99. package/build/transformers/remodule.js +620 -0
  100. package/build/transformers/removeImportExport.js +135 -0
  101. package/build/transformers/replaceDollar.js +46 -0
  102. package/build/transformers/shorthandProperties.js +34 -0
  103. package/build/transformers/spreadArray.js +68 -0
  104. package/build/transformers/spreadObject.js +134 -0
  105. package/build/transformers/string.js +138 -0
  106. package/build/transformers/templateLiterals.js +104 -0
  107. package/build/transformers/tocodelibrary.js +178 -0
  108. package/build/transformers/utils.js +202 -0
  109. package/build/wshcm/client.js +193 -0
  110. package/build/wshcm/evaluator.js +111 -0
  111. package/build/wshcm/exceptions.js +25 -0
  112. package/build/wshcm/index.js +20 -0
  113. package/build/wshcm/soap-utils.js +228 -0
  114. package/build/wshcm/types.js +2 -0
  115. package/build/wshcm/uploader.js +320 -0
  116. 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;