@travetto/compiler 3.3.1 → 3.4.0-rc.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.
@@ -1,160 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
-
4
- import type { ManifestContext } from '@travetto/manifest';
5
-
6
- import { TranspileUtil, CompileResult, BuildEvent } from './transpile';
7
- import { LockManager } from './lock';
8
- import { LogUtil } from './log';
9
-
10
- const SOURCE_SEED = ['package.json', 'index.ts', '__index__.ts', 'src', 'support', 'bin'];
11
- const PRECOMPILE_MODS = ['@travetto/terminal', '@travetto/manifest', '@travetto/transformer', '@travetto/compiler'];
12
-
13
- const importManifest = (ctx: ManifestContext): Promise<typeof import('@travetto/manifest')> =>
14
- import(path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/manifest/__index__.js'));
15
-
16
- /**
17
- * Run the compiler
18
- */
19
- async function compile(ctx: ManifestContext, op: 'watch' | 'build' | undefined, onMessage: (msg: BuildEvent) => void): Promise<CompileResult> {
20
- let changes = 0;
21
-
22
- await LogUtil.withLogger('precompile', async () => {
23
- for (const mod of PRECOMPILE_MODS) {
24
- const count = (await TranspileUtil.compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
25
- if (mod !== '@travetto/terminal') {
26
- changes += count;
27
- }
28
- }
29
- });
30
-
31
- const { ManifestUtil, ManifestDeltaUtil, PackageUtil } = await importManifest(ctx);
32
-
33
- PackageUtil.clearCache();
34
-
35
- const manifest = await LogUtil.withLogger('manifest', async () => ManifestUtil.buildManifest(ctx));
36
-
37
- await LogUtil.withLogger('transformers', async () => {
38
- for (const mod of Object.values(manifest.modules).filter(m => m.files.$transformer?.length)) {
39
- changes += (await TranspileUtil.compileIfStale(ctx, 'transformers', mod.name, ['package.json', ...mod.files.$transformer!.map(x => x[0])])).length;
40
- }
41
- });
42
-
43
- const delta = await LogUtil.withLogger('delta', async log => {
44
- if (changes) {
45
- log('debug', 'Skipping, everything changed');
46
- return [{ type: 'changed', file: '*', module: ctx.mainModule } as const];
47
- } else {
48
- return ManifestDeltaUtil.produceDelta(ctx, manifest);
49
- }
50
- });
51
-
52
- if (changes) {
53
- await LogUtil.withLogger('reset', async log => {
54
- await fs.rm(path.resolve(ctx.workspacePath, ctx.outputFolder), { recursive: true, force: true });
55
- log('info', 'Clearing output due to compiler changes');
56
- }, false);
57
- }
58
-
59
- // Write manifest
60
- await LogUtil.withLogger('manifest', async log => {
61
- await ManifestUtil.writeManifest(ctx, manifest);
62
- log('debug', `Wrote manifest ${ctx.mainModule}`);
63
-
64
- // Update all manifests
65
- if (delta.length && ctx.monoRepo && !ctx.mainFolder) {
66
- const names: string[] = [];
67
- const mods = Object.values(manifest.modules).filter(x => x.local && x.name !== ctx.mainModule);
68
- for (const mod of mods) {
69
- await ManifestUtil.rewriteManifest(path.resolve(ctx.workspacePath, mod.sourceFolder));
70
- names.push(mod.name);
71
- }
72
- log('debug', `Changes triggered ${delta.slice(0, 10).map(x => `${x.type}:${x.module}:${x.file}`)}`);
73
- log('debug', `Rewrote monorepo manifests [changes=${delta.length}] ${names.slice(0, 10).join(', ')}`);
74
- }
75
- });
76
-
77
- return await LogUtil.withLogger('compile', async log => {
78
- const changed = delta.filter(x => x.type === 'added' || x.type === 'changed');
79
- log('debug', `Started action=${op} changed=${changed.slice(0, 10).map(x => `${x.module}/${x.file}`)}`);
80
- if (changed.length || op === 'watch') {
81
- const res = await TranspileUtil.runCompiler(ctx, manifest, changed, op === 'watch', onMessage);
82
- log('debug', 'Finished');
83
- return res;
84
- } else {
85
- log('debug', 'Skipped');
86
- return 'skipped';
87
- }
88
- }, false);
89
- }
90
-
91
- /**
92
- * Export manifests
93
- */
94
- async function exportManifest(ctx: ManifestContext, output?: string, env = 'dev'): Promise<void> {
95
- const { ManifestUtil } = await importManifest(ctx);
96
- let manifest = await ManifestUtil.buildManifest(ctx);
97
-
98
- // If in prod mode, only include std modules
99
- if (/^prod/i.test(env)) {
100
- manifest = ManifestUtil.createProductionManifest(manifest);
101
- }
102
- if (output) {
103
- output = await ManifestUtil.writeManifestToFile(output, manifest);
104
- LogUtil.log('manifest', [], 'info', `Wrote manifest ${output}`);
105
- } else {
106
- console.log(JSON.stringify(manifest, null, 2));
107
- }
108
- }
109
-
110
- /**
111
- * Launch
112
- */
113
- export async function launch(ctx: ManifestContext, root: ManifestContext, op?: 'build' | 'watch' | 'manifest', args: (string | undefined)[] = []): Promise<void> {
114
- // If quiet enabled, turn off all output by default
115
- LogUtil.level = process.env.TRV_BUILD ?? (process.env.TRV_QUIET ? 'none' : (!op ? 'warn' : 'info'));
116
-
117
- if (op !== 'manifest' && await LockManager.getCompileAction(root, op) === 'build') {
118
-
119
- // Ready signal
120
- if (process.send) {
121
- process.send('ready');
122
- process.on('disconnect', () => process.exit(0));
123
- }
124
-
125
- await LockManager.withLocks(root, async (acquire, release) => {
126
- let action: CompileResult;
127
- do {
128
- acquire(op ?? 'build');
129
- if (op === 'watch') {
130
- acquire('build');
131
- }
132
- action = await compile(root, op, msg => {
133
- switch (msg.type) {
134
- case 'complete': {
135
- release('build');
136
- break;
137
- }
138
- }
139
- });
140
- } while (action === 'restart');
141
- });
142
- }
143
-
144
- // Disconnect for non-cli operations
145
- if (op && process.send) {
146
- process.disconnect();
147
- }
148
-
149
- switch (op) {
150
- case 'manifest': return exportManifest(ctx, ...args);
151
- case 'build': return LogUtil.log('build', [], 'info', 'Successfully built');
152
- case undefined: {
153
- // TODO: Externalize somehow?
154
- const outputPath = path.resolve(ctx.workspacePath, ctx.outputFolder);
155
- process.env.TRV_MANIFEST = path.resolve(outputPath, 'node_modules', ctx.mainModule);
156
- const cliMain = path.join(outputPath, 'node_modules', '@travetto/cli/support/entry.cli.js');
157
- return import(cliMain);
158
- }
159
- }
160
- }
@@ -1,25 +0,0 @@
1
- import { workerData, parentPort } from 'worker_threads';
2
- import { utimesSync } from 'fs';
3
-
4
- const data: { files: string[], interval: number } = workerData;
5
- const files = data.files;
6
-
7
- const interval = setInterval(() => {
8
- const now = Date.now() / 1000;
9
- for (const file of files) {
10
- try {
11
- utimesSync(file, now, now);
12
- } catch { }
13
- }
14
- }, data.interval);
15
-
16
-
17
- parentPort?.on('message', val => {
18
- if (val === 'stop') {
19
- files.splice(0, files.length);
20
- clearInterval(interval);
21
- } else if (val && typeof val === 'object' && 'files' in val && Array.isArray(val.files)) {
22
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
23
- files.splice(0, files.length, ...val.files as string[]);
24
- }
25
- });
package/support/lock.ts DELETED
@@ -1,234 +0,0 @@
1
- import { Stats, watchFile, unwatchFile, rmSync, mkdirSync, writeFileSync, existsSync } from 'fs';
2
- import fs from 'fs/promises';
3
- import { Worker } from 'worker_threads';
4
- import path from 'path';
5
-
6
- import type { ManifestContext } from '@travetto/manifest';
7
-
8
- import { CompilerLogger, LogUtil } from './log';
9
-
10
- type LockStatus = 'complete' | 'stale';
11
- type LockDetails = {
12
- pid: number | undefined;
13
- file: string;
14
- };
15
-
16
- export type LockType = 'build' | 'watch';
17
- export type LockCompileAction = 'skip' | 'build';
18
- type LockAction = LockCompileAction | 'retry';
19
-
20
- const STALE_THRESHOLD = 1000;
21
-
22
- /**
23
- * Manager for all lock activity
24
- */
25
- export class LockManager {
26
-
27
- /**
28
- * Get the lock file name
29
- */
30
- static #getFileName(ctx: ManifestContext, type: LockType): string {
31
- return path.resolve(ctx.workspacePath, ctx.toolFolder, `${type}.lock`);
32
- }
33
-
34
- /**
35
- * Determine if the given stats are stale for modification time
36
- */
37
- static #isStale(stat?: Stats): boolean {
38
- return !!stat && stat.mtimeMs < (Date.now() - STALE_THRESHOLD * 1.1);
39
- }
40
-
41
- /**
42
- * Get the lock file details
43
- */
44
- static async #getDetails(ctx: ManifestContext, type: LockType): Promise<LockDetails> {
45
- const file = this.#getFileName(ctx, type);
46
- const stat = await fs.stat(file).catch(() => undefined);
47
- const stale = this.#isStale(stat);
48
- let pid: number | undefined;
49
- if (stat) {
50
- const { pid: filePid } = JSON.parse(await fs.readFile(file, 'utf8'));
51
- if (stale) {
52
- LogUtil.log('lock', [], 'debug', `${type} file is stale: ${stat.mtimeMs} vs ${Date.now()}`);
53
- } else {
54
- pid = filePid;
55
- }
56
- }
57
- return { pid, file };
58
- }
59
-
60
- /**
61
- * Acquire the lock file, and register a cleanup on exit
62
- */
63
- static #acquireFile(ctx: ManifestContext, type: LockType): void {
64
- const file = this.#getFileName(ctx, type);
65
- mkdirSync(path.dirname(file), { recursive: true });
66
- LogUtil.log('lock', [], 'debug', `Acquiring ${type}`);
67
- writeFileSync(file, JSON.stringify({ pid: process.pid }), 'utf8');
68
- }
69
-
70
- /**
71
- * Release the lock file (i.e. deleting)
72
- */
73
- static #releaseFile(ctx: ManifestContext, type: LockType): void {
74
- const file = this.#getFileName(ctx, type);
75
- if (existsSync(file)) {
76
- rmSync(file, { force: true });
77
- LogUtil.log('lock', [], 'debug', `Releasing ${type}`);
78
- }
79
- }
80
-
81
- /**
82
- * Wait until a lock file is released, or it becomes stale
83
- */
84
- static async #waitForRelease(ctx: ManifestContext, type: LockType): Promise<LockStatus> {
85
- const file = this.#getFileName(ctx, type);
86
- let remove: (() => void) | undefined = undefined;
87
-
88
- const prom = new Promise<LockStatus>(resolve => {
89
- let timer: NodeJS.Timeout | undefined = undefined;
90
- const handler = async (): Promise<void> => {
91
- if (timer) {
92
- clearTimeout(timer);
93
- }
94
- const stats = await fs.stat(file).catch(() => undefined);
95
- if (!stats) {
96
- resolve('complete');
97
- } else if (this.#isStale(stats)) {
98
- resolve('stale');
99
- } else {
100
- timer = setTimeout(handler, STALE_THRESHOLD * 1.1);
101
- }
102
- };
103
-
104
- watchFile(file, handler);
105
- handler();
106
-
107
- remove = (): void => {
108
- clearTimeout(timer);
109
- unwatchFile(file, handler);
110
- };
111
- });
112
-
113
- return prom.finally(remove);
114
- }
115
-
116
- /**
117
- * Read the watch lock file and determine its result, communicating with the user as necessary
118
- */
119
- static async #getWatchAction(ctx: ManifestContext, log: CompilerLogger, lockType: LockType | undefined, buildState: LockDetails): Promise<LockAction> {
120
- if (lockType === 'watch') {
121
- log('info', 'Already running');
122
- return 'skip';
123
- } else {
124
- if (buildState.pid) {
125
- log('warn', 'Already running, waiting for build to finish');
126
- switch (await this.#waitForRelease(ctx, 'build')) {
127
- case 'complete': {
128
- log('info', 'Completed build');
129
- return 'skip';
130
- }
131
- case 'stale': {
132
- log('info', 'Became stale, retrying');
133
- return 'retry';
134
- }
135
- }
136
- } else {
137
- log('info', 'Already running, and has built');
138
- return 'skip';
139
- }
140
- }
141
- }
142
-
143
- /**
144
- * Read the build lock file and determine its result, communicating with the user as necessary
145
- */
146
- static async #getBuildAction(ctx: ManifestContext, log: CompilerLogger, lockType: LockType | undefined): Promise<LockAction> {
147
- if (lockType === 'watch') {
148
- log('warn', 'Build already running, waiting to begin watch');
149
- const res = await this.#waitForRelease(ctx, 'build');
150
- log('info', `Finished with status of ${res}, retrying`);
151
- return 'retry';
152
- } else {
153
- log('warn', 'Already running, waiting for completion');
154
- switch (await this.#waitForRelease(ctx, lockType ?? 'build')) {
155
- case 'complete': {
156
- log('info', 'Completed');
157
- return 'skip';
158
- }
159
- case 'stale': {
160
- log('info', 'Became stale, retrying');
161
- return 'retry';
162
- }
163
- }
164
- }
165
- }
166
-
167
- /**
168
- * Run code with support for lock acquire and release
169
- */
170
- static async withLocks(ctx: ManifestContext, fn: (acquire: (type: LockType) => void, release: (type: LockType) => void) => Promise<unknown>): Promise<void> {
171
- const activeLockTypes = new Set<LockType>();
172
-
173
- const pinger = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler/support/lock-pinger.js');
174
- const worker = new Worker(pinger, {
175
- workerData: {
176
- interval: STALE_THRESHOLD,
177
- files: []
178
- }
179
- });
180
-
181
- const notify = (): void => worker.postMessage({ files: [...activeLockTypes].map(t => this.#getFileName(ctx, t)) });
182
-
183
- const stop = (): void => {
184
- worker.postMessage('stop');
185
- for (const type of activeLockTypes) {
186
- this.#releaseFile(ctx, type);
187
- }
188
- worker.terminate().then(() => { });
189
- };
190
-
191
- process.on('SIGINT', stop);
192
- process.on('exit', stop);
193
-
194
- try {
195
- await new Promise(r => worker.on('online', r));
196
- await fn(
197
- type => {
198
- if (!activeLockTypes.has(type)) {
199
- activeLockTypes.add(type);
200
- this.#acquireFile(ctx, type);
201
- notify();
202
- }
203
- },
204
- type => {
205
- if (activeLockTypes.has(type)) {
206
- activeLockTypes.delete(type);
207
- this.#releaseFile(ctx, type);
208
- notify();
209
- }
210
- }
211
- );
212
- } finally {
213
- stop();
214
- }
215
- }
216
-
217
- /**
218
- * Reads the lock file states (build + watch) to determine what action should be taken for the compiler
219
- */
220
- static async getCompileAction(ctx: ManifestContext, lockType: LockType | undefined): Promise<LockCompileAction> {
221
- let result: LockAction;
222
- do {
223
- result = 'build';
224
- const buildState = await this.#getDetails(ctx, 'build');
225
- const watchState = await this.#getDetails(ctx, 'watch');
226
- if (watchState.pid) { // Existing watch operation
227
- result = await LogUtil.withLogger('lock', log => this.#getWatchAction(ctx, log, lockType, buildState), true, ['watch', `pid=${watchState.pid}`]);
228
- } else if (buildState.pid) { // Existing build operation
229
- result = await LogUtil.withLogger('lock', log => this.#getBuildAction(ctx, log, lockType), true, ['build', `pid=${buildState.pid}`]);
230
- }
231
- } while (result === 'retry');
232
- return result;
233
- }
234
- }
@@ -1,256 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs/promises';
3
- import os from 'os';
4
- import timers from 'timers/promises';
5
- import cp from 'child_process';
6
- import { createRequire } from 'module';
7
-
8
- import { DeltaEvent, ManifestContext, ManifestRoot, Package } from '@travetto/manifest';
9
-
10
- import { LogUtil } from './log';
11
-
12
- type ModFile = { input: string, output: string, stale: boolean };
13
- export type CompileResult = 'restart' | 'complete' | 'skipped';
14
- export type BuildEvent = { type: 'restart' | 'start' | 'complete' } | { type: 'status', idx: number, total: number };
15
-
16
- const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
17
- const SRC_REQ = createRequire(path.resolve('node_modules'));
18
- const RECENT_STAT = (stat: { ctimeMs: number, mtimeMs: number }): number => Math.max(stat.ctimeMs, stat.mtimeMs);
19
-
20
- const isBuildEvent = (ev: unknown): ev is BuildEvent =>
21
- ev !== undefined && ev !== null && typeof ev === 'object' && 'type' in ev && typeof ev.type === 'string';
22
-
23
- /**
24
- * Transpile utilities for launching
25
- */
26
- export class TranspileUtil {
27
- /**
28
- * Determine file type
29
- */
30
- static getFileType(file: string): 'ts' | 'js' | 'package-json' | 'typings' | undefined {
31
- return file.endsWith('package.json') ? 'package-json' :
32
- (file.endsWith('.js') ? 'js' :
33
- (file.endsWith('.d.ts') ? 'typings' : (/[.]tsx?$/.test(file) ? 'ts' : undefined)));
34
- }
35
-
36
- /** Convert a file to a given ext */
37
- static #sourceToExtension(inputFile: string, ext: string): string {
38
- return inputFile.replace(/[.][tj]sx?$/, ext);
39
- }
40
-
41
- /**
42
- * Get the output file name for a given input
43
- */
44
- static sourceToOutputExt(inputFile: string): string {
45
- return this.#sourceToExtension(inputFile, '.js');
46
- }
47
-
48
- /**
49
- * Write text file, and ensure folder exists
50
- */
51
- static writeTextFile = (file: string, content: string): Promise<void> =>
52
- fs.mkdir(path.dirname(file), { recursive: true }).then(() => fs.writeFile(file, content, 'utf8'));
53
-
54
- /**
55
- * Returns the compiler options
56
- */
57
- static async getCompilerOptions(ctx: ManifestContext): Promise<{}> {
58
- if (!(ctx.workspacePath in OPT_CACHE)) {
59
- let tsconfig = path.resolve(ctx.workspacePath, 'tsconfig.json');
60
-
61
- if (!await fs.stat(tsconfig).then(_ => true, _ => false)) {
62
- tsconfig = SRC_REQ.resolve('@travetto/compiler/tsconfig.trv.json');
63
- }
64
-
65
- const ts = (await import('typescript')).default;
66
-
67
- const { options } = ts.parseJsonSourceFileConfigFileContent(
68
- ts.readJsonConfigFile(tsconfig, ts.sys.readFile), ts.sys, ctx.workspacePath
69
- );
70
-
71
- OPT_CACHE[ctx.workspacePath] = {
72
- ...options,
73
- allowJs: true,
74
- resolveJsonModule: true,
75
- sourceRoot: ctx.workspacePath,
76
- rootDir: ctx.workspacePath,
77
- outDir: path.resolve(ctx.workspacePath),
78
- module: ctx.moduleType === 'commonjs' ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext,
79
- };
80
- }
81
- return OPT_CACHE[ctx.workspacePath];
82
- }
83
-
84
- /**
85
- * Output a file, support for ts, js, and package.json
86
- */
87
- static async transpileFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void> {
88
- const type = this.getFileType(inputFile);
89
- if (type === 'js' || type === 'ts') {
90
- const compilerOut = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules');
91
-
92
- const text = (await fs.readFile(inputFile, 'utf8'))
93
- .replace(/from '([.][^']+)'/g, (_, i) => `from '${i.replace(/[.]js$/, '')}.js'`)
94
- .replace(/from '(@travetto\/(.*?))'/g, (_, i, s) => `from '${path.resolve(compilerOut, `${i}${s.includes('/') ? '.js' : '/__index__.js'}`)}'`);
95
-
96
- const ts = (await import('typescript')).default;
97
- const content = ts.transpile(text, {
98
- ...await this.getCompilerOptions(ctx),
99
- sourceMap: false,
100
- inlineSourceMap: true,
101
- }, inputFile);
102
- await this.writeTextFile(outputFile, content);
103
- } else if (type === 'package-json') {
104
- const pkg: Package = JSON.parse(await fs.readFile(inputFile, 'utf8'));
105
- const main = pkg.main ? this.sourceToOutputExt(pkg.main) : undefined;
106
- const files = pkg.files?.map(x => this.sourceToOutputExt(x));
107
-
108
- const content = JSON.stringify({ ...pkg, main, type: ctx.moduleType, files }, null, 2);
109
- await this.writeTextFile(outputFile, content);
110
- }
111
- }
112
-
113
- /**
114
- * Scan directory to find all project sources for comparison
115
- */
116
- static async getModuleSources(ctx: ManifestContext, module: string, seed: string[]): Promise<ModFile[]> {
117
- const inputFolder = (ctx.mainModule === module) ?
118
- process.cwd() :
119
- path.dirname(SRC_REQ.resolve(`${module}/package.json`));
120
-
121
- const folders = seed.filter(x => !/[.]/.test(x)).map(x => path.resolve(inputFolder, x));
122
- const files = seed.filter(x => /[.]/.test(x)).map(x => path.resolve(inputFolder, x));
123
-
124
- while (folders.length) {
125
- const sub = folders.pop();
126
- if (!sub) {
127
- continue;
128
- }
129
-
130
- for (const file of await fs.readdir(sub).catch(() => [])) {
131
- if (file.startsWith('.')) {
132
- continue;
133
- }
134
- const resolvedInput = path.resolve(sub, file);
135
- const stat = await fs.stat(resolvedInput);
136
-
137
- if (stat.isDirectory()) {
138
- folders.push(resolvedInput);
139
- } else {
140
- switch (this.getFileType(file)) {
141
- case 'js':
142
- case 'ts':
143
- files.push(resolvedInput);
144
- }
145
- }
146
- }
147
- }
148
-
149
- const outputFolder = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', module);
150
- const out: ModFile[] = [];
151
- for (const input of files) {
152
- const output = this.sourceToOutputExt(input.replace(inputFolder, outputFolder));
153
- const inputTs = await fs.stat(input).then(RECENT_STAT, () => 0);
154
- if (inputTs) {
155
- const outputTs = await fs.stat(output).then(RECENT_STAT, () => 0);
156
- await fs.mkdir(path.dirname(output), { recursive: true, });
157
- out.push({ input, output, stale: inputTs > outputTs });
158
- }
159
- }
160
-
161
- return out;
162
- }
163
-
164
- /**
165
- * Recompile folder if stale
166
- */
167
- static async compileIfStale(ctx: ManifestContext, scope: string, mod: string, seed: string[]): Promise<string[]> {
168
- const files = await this.getModuleSources(ctx, mod, seed);
169
- const changes = files.filter(x => x.stale).map(x => x.input);
170
- const out: string[] = [];
171
-
172
- try {
173
- await LogUtil.withLogger(scope, async log => {
174
- if (files.some(f => f.stale)) {
175
- log('debug', 'Starting');
176
- for (const file of files.filter(x => x.stale)) {
177
- await this.transpileFile(ctx, file.input, file.output);
178
- }
179
- if (changes.length) {
180
- out.push(...changes.map(x => `${mod}/${x}`));
181
- log('debug', `Source changed: ${changes.join(', ')}`);
182
- }
183
- log('debug', 'Completed');
184
- } else {
185
- log('debug', 'Skipped');
186
- }
187
- }, false, [mod]);
188
- } catch (err) {
189
- console.error(err);
190
- }
191
- return out;
192
- }
193
-
194
- /**
195
- * Run compiler
196
- */
197
- static async runCompiler(ctx: ManifestContext, manifest: ManifestRoot, changed: DeltaEvent[], watch: boolean, onMessage: (msg: BuildEvent) => void): Promise<CompileResult> {
198
- const compiler = path.resolve(ctx.workspacePath, ctx.compilerFolder);
199
- const main = path.resolve(compiler, 'node_modules', '@travetto/compiler/support/compiler-entry.js');
200
- const deltaFile = path.resolve(os.tmpdir(), `manifest-delta.${process.pid}.${process.ppid}.${Date.now()}.json`);
201
-
202
- const changedFiles = changed[0]?.file === '*' ? ['*'] : changed.map(ev =>
203
- path.resolve(manifest.workspacePath, manifest.modules[ev.module].sourceFolder, ev.file)
204
- );
205
-
206
- let proc: cp.ChildProcess | undefined;
207
- let kill: (() => void) | undefined;
208
-
209
- try {
210
- await this.writeTextFile(deltaFile, changedFiles.join('\n'));
211
-
212
- const result = await LogUtil.withLogger('compiler-exec', log => new Promise<CompileResult>((res, rej) => {
213
- proc = cp.spawn(process.argv0, [main, deltaFile, `${watch}`], {
214
- env: {
215
- ...process.env,
216
- TRV_MANIFEST: path.resolve(ctx.workspacePath, ctx.outputFolder, 'node_modules', ctx.mainModule),
217
- },
218
- stdio: [0, 1, 2, 'ipc'],
219
- })
220
- .on('message', msg => {
221
- if (LogUtil.isLogEvent(msg)) {
222
- log(...msg);
223
- } else if (isBuildEvent(msg)) {
224
- // Send to parent if exists
225
- process.send?.(msg);
226
- if (msg.type === 'restart') {
227
- res('restart');
228
- } else {
229
- onMessage(msg);
230
- }
231
- }
232
- })
233
- .on('exit', code => (code !== null && code > 0) ? rej(new Error('Failed during compilation')) : res('complete'));
234
- kill = (): void => { proc?.kill('SIGKILL'); };
235
- process.on('exit', kill);
236
- }));
237
-
238
- if (result === 'restart') {
239
- await timers.setTimeout(150 + 100 * Math.random());
240
- }
241
-
242
- LogUtil.log('compiler-exec', [], 'info', `Result ${result}, exit code: ${proc?.exitCode}`);
243
-
244
- return result;
245
- } finally {
246
- if (proc?.killed === false) { proc.kill('SIGKILL'); }
247
- if (kill) {
248
- process.off('exit', kill);
249
- }
250
- if (process.stdout.isTTY) {
251
- process.stdout.write('\x1b[s\x1b[?25h\x1b[r\x1b[u');
252
- }
253
- await fs.rm(deltaFile, { force: true });
254
- }
255
- }
256
- }