@travetto/compiler 4.0.0-rc.4 → 4.0.0-rc.6
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/package.json +4 -4
- package/src/compiler.ts +4 -26
- package/src/state.ts +37 -19
- package/src/watch.ts +1 -1
- package/support/entry.trvc.ts +11 -10
- package/support/log.ts +66 -71
- package/support/server/client.ts +16 -14
- package/support/server/process-handle.ts +13 -3
- package/support/server/runner.ts +9 -9
- package/support/server/server.ts +24 -30
- package/support/setup.ts +18 -18
- package/support/util.ts +8 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.6",
|
|
4
4
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@parcel/watcher": "^2.4.0",
|
|
34
|
-
"@travetto/manifest": "^4.0.0-rc.
|
|
35
|
-
"@travetto/transformer": "^4.0.0-rc.
|
|
34
|
+
"@travetto/manifest": "^4.0.0-rc.6",
|
|
35
|
+
"@travetto/transformer": "^4.0.0-rc.6",
|
|
36
36
|
"@types/node": "^20.11.16"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@travetto/cli": "^4.0.0-rc.
|
|
39
|
+
"@travetto/cli": "^4.0.0-rc.7"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import ts from 'typescript';
|
|
2
1
|
import timers from 'node:timers/promises';
|
|
3
2
|
import fs from 'node:fs/promises';
|
|
4
3
|
import { setMaxListeners } from 'node:events';
|
|
@@ -9,7 +8,7 @@ import { CompilerUtil } from './util';
|
|
|
9
8
|
import { CompilerState } from './state';
|
|
10
9
|
import { CompilerWatcher } from './watch';
|
|
11
10
|
import { Log } from './log';
|
|
12
|
-
import {
|
|
11
|
+
import { CompileEmitEvent, CompileEmitter } from './types';
|
|
13
12
|
import { EventUtil } from './event';
|
|
14
13
|
|
|
15
14
|
/**
|
|
@@ -70,7 +69,7 @@ export class Compiler {
|
|
|
70
69
|
break;
|
|
71
70
|
}
|
|
72
71
|
case 'reset': {
|
|
73
|
-
Log.info('Triggering reset due to change in core files');
|
|
72
|
+
Log.info('Triggering reset due to change in core files', err?.cause);
|
|
74
73
|
EventUtil.sendEvent('state', { state: 'reset' });
|
|
75
74
|
process.exitCode = 0;
|
|
76
75
|
break;
|
|
@@ -86,29 +85,8 @@ export class Compiler {
|
|
|
86
85
|
/**
|
|
87
86
|
* Compile in a single pass, only emitting dirty files
|
|
88
87
|
*/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const emit = async (inputFile: string, needsNewProgram = program === undefined): Promise<CompileEmitError | undefined> => {
|
|
93
|
-
try {
|
|
94
|
-
if (needsNewProgram) {
|
|
95
|
-
program = this.#state.createProgram(program);
|
|
96
|
-
}
|
|
97
|
-
await timers.setImmediate();
|
|
98
|
-
const result = this.#state.writeInputFile(program, inputFile);
|
|
99
|
-
if (result?.diagnostics?.length) {
|
|
100
|
-
return result.diagnostics;
|
|
101
|
-
}
|
|
102
|
-
} catch (err) {
|
|
103
|
-
if (err instanceof Error) {
|
|
104
|
-
return err;
|
|
105
|
-
} else {
|
|
106
|
-
throw err;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
return emit;
|
|
88
|
+
getCompiler(): CompileEmitter {
|
|
89
|
+
return (inputFile: string, needsNewProgram?: boolean) => this.#state.writeInputFile(inputFile, needsNewProgram);
|
|
112
90
|
}
|
|
113
91
|
|
|
114
92
|
/**
|
package/src/state.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
+
import timers from 'node:timers/promises';
|
|
2
3
|
|
|
3
4
|
import { path, ManifestModuleUtil, ManifestModule, ManifestRoot, ManifestIndex } from '@travetto/manifest';
|
|
4
5
|
import { TransformerManager } from '@travetto/transformer';
|
|
@@ -6,7 +7,7 @@ import { TransformerManager } from '@travetto/transformer';
|
|
|
6
7
|
import { CommonUtil } from '../support/util';
|
|
7
8
|
|
|
8
9
|
import { CompilerUtil } from './util';
|
|
9
|
-
import { CompileStateEntry } from './types';
|
|
10
|
+
import { CompileEmitError, CompileStateEntry } from './types';
|
|
10
11
|
|
|
11
12
|
function folderMapper(root: string, prefix: string): { dir: string, translate: (val: string) => string } {
|
|
12
13
|
let matched: string = '~~';
|
|
@@ -44,6 +45,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
44
45
|
#modules: ManifestModule[];
|
|
45
46
|
#transformerManager: TransformerManager;
|
|
46
47
|
#compilerOptions: ts.CompilerOptions;
|
|
48
|
+
#program: ts.Program;
|
|
47
49
|
|
|
48
50
|
#readFile(inputFile: string): string | undefined {
|
|
49
51
|
return ts.sys.readFile(this.#inputToEntry.get(inputFile)?.sourceFile ?? this.#inputPathToSourcePath(inputFile));
|
|
@@ -104,24 +106,40 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
104
106
|
return this.getBySource(this.#manifestIndex.getModule('@travetto/manifest')!.files.src[0].sourceFile)!.inputFile;
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
createProgram(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
109
|
+
async createProgram(force = false): Promise<ts.Program> {
|
|
110
|
+
if (force || !this.#program) {
|
|
111
|
+
this.#program = ts.createProgram({ rootNames: this.getAllFiles(), host: this, options: this.#compilerOptions, oldProgram: this.#program });
|
|
112
|
+
this.#transformerManager.init(this.#program.getTypeChecker());
|
|
113
|
+
await timers.setImmediate();
|
|
114
|
+
}
|
|
115
|
+
return this.#program;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async writeInputFile(inputFile: string, needsNewProgram = false): Promise<CompileEmitError | undefined> {
|
|
119
|
+
const program = await this.createProgram(needsNewProgram);
|
|
120
|
+
try {
|
|
121
|
+
switch (ManifestModuleUtil.getFileType(inputFile)) {
|
|
122
|
+
case 'package-json':
|
|
123
|
+
this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, this.readFile(inputFile)!, false), undefined;
|
|
124
|
+
break;
|
|
125
|
+
case 'js':
|
|
126
|
+
this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, ts.transpile(this.readFile(inputFile)!, this.#compilerOptions), false);
|
|
127
|
+
break;
|
|
128
|
+
case 'ts': {
|
|
129
|
+
const result = program.emit(
|
|
130
|
+
program.getSourceFile(inputFile)!,
|
|
131
|
+
(...args) => this.writeFile(...args), undefined, false,
|
|
132
|
+
this.#transformerManager.get()
|
|
133
|
+
);
|
|
134
|
+
return result?.diagnostics?.length ? result.diagnostics : undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err instanceof Error) {
|
|
139
|
+
return err;
|
|
140
|
+
} else {
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
125
143
|
}
|
|
126
144
|
}
|
|
127
145
|
|
package/src/watch.ts
CHANGED
|
@@ -128,7 +128,7 @@ export class CompilerWatcher {
|
|
|
128
128
|
sourceFile === ROOT_PKG ||
|
|
129
129
|
(action === 'delete' && (sourceFile === OUTPUT_PATH || sourceFile === COMPILER_PATH))
|
|
130
130
|
) {
|
|
131
|
-
throw new Error('RESET');
|
|
131
|
+
throw new Error('RESET', { cause: `${action}:${sourceFile}` });
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
const fileType = ManifestModuleUtil.getFileType(sourceFile);
|
package/support/entry.trvc.ts
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
7
|
import type { CompilerLogLevel, CompilerMode, CompilerServerInfo } from './types';
|
|
8
|
-
import {
|
|
8
|
+
import { CompilerLogger } from './log';
|
|
9
9
|
import { CommonUtil } from './util';
|
|
10
10
|
import { CompilerSetup } from './setup';
|
|
11
11
|
import { CompilerServer } from './server/server';
|
|
@@ -14,39 +14,39 @@ import { CompilerClient } from './server/client';
|
|
|
14
14
|
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
16
16
|
export const main = (ctx: ManifestContext) => {
|
|
17
|
-
const
|
|
18
|
-
const client = new CompilerClient(ctx, log);
|
|
17
|
+
const client = new CompilerClient(ctx, new CompilerLogger('client'));
|
|
19
18
|
const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
|
|
20
19
|
|
|
21
20
|
/** Main entry point for compilation */
|
|
22
21
|
const compile = async (op: CompilerMode, logLevel: CompilerLogLevel, setupOnly = false): Promise<void> => {
|
|
23
|
-
|
|
22
|
+
CompilerLogger.init(ctx, logLevel ?? 'info');
|
|
24
23
|
|
|
25
24
|
const server = await new CompilerServer(ctx, op).listen();
|
|
25
|
+
const log = new CompilerLogger('main');
|
|
26
26
|
|
|
27
27
|
// Wait for build to be ready
|
|
28
28
|
if (server) {
|
|
29
|
-
log('
|
|
29
|
+
log.debug('Start Server');
|
|
30
30
|
await server.processEvents(async function* (signal) {
|
|
31
31
|
const changed = await CompilerSetup.setup(ctx);
|
|
32
32
|
if (!setupOnly) {
|
|
33
33
|
yield* CompilerRunner.runProcess(ctx, changed, op, signal);
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
-
log('
|
|
36
|
+
log.debug('End Server');
|
|
37
37
|
} else {
|
|
38
|
-
log('
|
|
38
|
+
log.info('Server already running, waiting for initial compile to complete');
|
|
39
39
|
const ctrl = new AbortController();
|
|
40
|
-
|
|
40
|
+
log.consumeProgressEvents(() => client.fetchEvents('progress', { until: ev => !!ev.complete, signal: ctrl.signal }));
|
|
41
41
|
await client.waitForState(['compile-end', 'watch-start'], 'Successfully built');
|
|
42
42
|
ctrl.abort();
|
|
43
43
|
}
|
|
44
|
-
LogUtil.cleanup();
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
const ops = {
|
|
48
47
|
/** Stop the server */
|
|
49
48
|
async stop(): Promise<void> {
|
|
49
|
+
CompilerLogger.init(ctx);
|
|
50
50
|
if (await client.stop()) {
|
|
51
51
|
console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
|
|
52
52
|
} else {
|
|
@@ -62,6 +62,7 @@ export const main = (ctx: ManifestContext) => {
|
|
|
62
62
|
|
|
63
63
|
/** Clean the server */
|
|
64
64
|
async clean(): Promise<void> {
|
|
65
|
+
CompilerLogger.init(ctx);
|
|
65
66
|
if (await client.clean()) {
|
|
66
67
|
return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
|
|
67
68
|
} else {
|
|
@@ -72,7 +73,7 @@ export const main = (ctx: ManifestContext) => {
|
|
|
72
73
|
|
|
73
74
|
/** Stream events */
|
|
74
75
|
events: async (type: string, handler: (ev: unknown) => unknown): Promise<void> => {
|
|
75
|
-
|
|
76
|
+
CompilerLogger.init(ctx, 'error');
|
|
76
77
|
if (type === 'change' || type === 'log' || type === 'progress' || type === 'state') {
|
|
77
78
|
for await (const ev of client.fetchEvents(type)) { await handler(ev); }
|
|
78
79
|
} else {
|
package/support/log.ts
CHANGED
|
@@ -1,112 +1,107 @@
|
|
|
1
1
|
import type { ManifestContext } from '@travetto/manifest';
|
|
2
2
|
import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
|
|
3
3
|
|
|
4
|
-
export type CompilerLogger = (level: CompilerLogLevel, message: string, ...args: unknown[]) => void;
|
|
5
|
-
export type WithLogger<T> = (log: CompilerLogger) => Promise<T>;
|
|
6
|
-
|
|
7
|
-
type ProgressWriter = (ev: CompilerProgressEvent) => (unknown | Promise<unknown>);
|
|
8
|
-
|
|
9
4
|
const LEVEL_TO_PRI: Record<CompilerLogLevel, number> = { debug: 1, info: 2, warn: 3, error: 4 };
|
|
10
|
-
|
|
11
5
|
const SCOPE_MAX = 15;
|
|
12
|
-
|
|
13
6
|
const ESC = '\x1b[';
|
|
14
7
|
|
|
15
|
-
export class
|
|
8
|
+
export class CompilerLogger {
|
|
16
9
|
|
|
17
|
-
static root = process.cwd();
|
|
10
|
+
static #root = process.cwd();
|
|
11
|
+
static #logLevel: CompilerLogLevel = 'error';
|
|
12
|
+
static #linePartial: boolean | undefined;
|
|
18
13
|
|
|
19
|
-
static
|
|
14
|
+
static logProgress = false;
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
/** Rewrite text line, tracking cleanup as necessary */
|
|
17
|
+
static rewriteLine(text: string): Promise<void> | void {
|
|
18
|
+
if ((!text && !this.#linePartial) || !process.stdout.isTTY) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (this.#linePartial === undefined) { // First time
|
|
22
|
+
process.stdout.write(`${ESC}?25l`); // Hide cursor
|
|
23
|
+
process.on('exit', () => this.reset());
|
|
24
|
+
}
|
|
26
25
|
// Move to 1st position, and clear after text
|
|
27
26
|
const done = process.stdout.write(`${ESC}1G${text}${ESC}0K`);
|
|
28
|
-
this
|
|
27
|
+
this.#linePartial = !!text;
|
|
29
28
|
if (!done) {
|
|
30
29
|
return new Promise<void>(r => process.stdout.once('drain', r));
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
|
|
33
|
+
/** Are we in a shell that is interactive */
|
|
34
|
+
static get isInteractiveShell(): boolean {
|
|
35
|
+
return !!process.env.PS1 && process.stdout.isTTY;
|
|
36
|
+
}
|
|
37
|
+
|
|
34
38
|
/**
|
|
35
39
|
* Set level for operation
|
|
36
40
|
*/
|
|
37
|
-
static
|
|
41
|
+
static init(ctx: ManifestContext, defaultLevel?: CompilerLogLevel): void {
|
|
38
42
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
39
43
|
const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
|
|
40
44
|
if (build !== 'none' && process.env.TRV_QUIET !== 'true') {
|
|
41
|
-
this
|
|
45
|
+
this.#logLevel = build || defaultLevel;
|
|
46
|
+
this.logProgress = this.isInteractiveShell;
|
|
42
47
|
}
|
|
43
|
-
this
|
|
44
|
-
// If we are in info or a terminal and also in a tty
|
|
45
|
-
this.logProgress = ((this.isLevelActive('info') || process.env.PS1) && process.stdout.isTTY) ? this.#logProgressEvent : undefined;
|
|
46
|
-
if (this.logProgress) {
|
|
47
|
-
process.stdout.write(`${ESC}?25l`); // Hide cursor
|
|
48
|
-
}
|
|
49
|
-
process.on('exit', () => this.cleanup());
|
|
48
|
+
this.#root = ctx.workspace.path;
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/** Cleanup to restore behavior */
|
|
52
|
+
static reset(): void {
|
|
53
|
+
if (process.stdout.isTTY) {
|
|
54
|
+
process.stdout.write(`${ESC}!p${ESC}?25h`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
constructor(
|
|
59
|
+
private scope: string,
|
|
60
|
+
private level?: CompilerLogLevel,
|
|
61
|
+
private logProgress?: boolean,
|
|
62
|
+
private root = CompilerLogger.#root,
|
|
63
|
+
) { }
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*/
|
|
67
|
-
static isLevelActive(lvl: CompilerLogLevel): boolean {
|
|
68
|
-
return LEVEL_TO_PRI[this.logLevel] <= LEVEL_TO_PRI[lvl];
|
|
65
|
+
isActive(level: CompilerLogLevel): boolean {
|
|
66
|
+
return LEVEL_TO_PRI[this.level ?? CompilerLogger.#logLevel] <= LEVEL_TO_PRI[level];
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
if (ev.scope) {
|
|
78
|
-
params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
|
|
79
|
-
}
|
|
80
|
-
params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
|
|
81
|
-
if (this.linePartial) {
|
|
82
|
-
this.#rewriteLine(''); // Clear out progress line
|
|
83
|
-
}
|
|
84
|
-
// eslint-disable-next-line no-console
|
|
85
|
-
console[ev.level]!(...params);
|
|
69
|
+
/** Log event with filtering by level */
|
|
70
|
+
onLogEvent(ev: CompilerLogEvent): void {
|
|
71
|
+
if (!this.isActive(ev.level)) { return; }
|
|
72
|
+
const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root, '.') : x);
|
|
73
|
+
if (ev.scope) {
|
|
74
|
+
params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
|
|
86
75
|
}
|
|
76
|
+
params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
|
|
77
|
+
CompilerLogger.rewriteLine(''); // Clear out progress line, if active
|
|
78
|
+
// eslint-disable-next-line no-console
|
|
79
|
+
console[ev.level]!(...params);
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
return op(log).finally(() => basic && log('debug', 'Completed'));
|
|
82
|
+
/** Write progress event, if active */
|
|
83
|
+
onProgressEvent(ev: CompilerProgressEvent): void | Promise<void> {
|
|
84
|
+
if (!(this.logProgress ?? CompilerLogger.logProgress)) { return; }
|
|
85
|
+
const pct = Math.trunc(ev.idx * 100 / ev.total);
|
|
86
|
+
const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
|
|
87
|
+
return CompilerLogger.rewriteLine(text);
|
|
96
88
|
}
|
|
97
89
|
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
90
|
+
/** Write all progress events if active */
|
|
91
|
+
async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
|
|
92
|
+
if (!(this.logProgress ?? CompilerLogger.logProgress)) { return; }
|
|
93
|
+
for await (const ev of src()) { this.onProgressEvent(ev); }
|
|
94
|
+
await CompilerLogger.reset();
|
|
103
95
|
}
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
log(level: CompilerLogLevel, message: string, args: unknown[]): void {
|
|
98
|
+
this.onLogEvent({ scope: this.scope, message, level, args, time: Date.now() });
|
|
99
|
+
}
|
|
100
|
+
info(message: string, ...args: unknown[]): void { return this.log('info', message, args); }
|
|
101
|
+
debug(message: string, ...args: unknown[]): void { return this.log('debug', message, args); }
|
|
102
|
+
warn(message: string, ...args: unknown[]): void { return this.log('warn', message, args); }
|
|
103
|
+
error(message: string, ...args: unknown[]): void { return this.log('error', message, args); }
|
|
104
|
+
wrap<T = unknown>(op: (log: typeof this) => Promise<T>, basic = false): Promise<T> {
|
|
105
|
+
return basic ? (this.debug('Started'), op(this).finally(() => this.debug('Completed'))) : op(this);
|
|
111
106
|
}
|
|
112
107
|
}
|
package/support/server/client.ts
CHANGED
|
@@ -14,16 +14,18 @@ type FetchEventsConfig<T> = {
|
|
|
14
14
|
enforceIteration?: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
type SimpleLogger = Pick<CompilerLogger, 'error' | 'info' | 'debug'>;
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Compiler Client Operations
|
|
19
21
|
*/
|
|
20
22
|
export class CompilerClient {
|
|
21
23
|
|
|
22
24
|
#url: string;
|
|
23
|
-
#log:
|
|
25
|
+
#log: SimpleLogger;
|
|
24
26
|
#handle: Record<'compiler' | 'server', ProcessHandle>;
|
|
25
27
|
|
|
26
|
-
constructor(ctx: ManifestContext, log:
|
|
28
|
+
constructor(ctx: ManifestContext, log: SimpleLogger) {
|
|
27
29
|
this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
|
|
28
30
|
this.#log = log;
|
|
29
31
|
this.#handle = { compiler: new ProcessHandle(ctx, 'compiler'), server: new ProcessHandle(ctx, 'server') };
|
|
@@ -41,9 +43,9 @@ export class CompilerClient {
|
|
|
41
43
|
const ctrl = new AbortController();
|
|
42
44
|
opts?.signal?.addEventListener('abort', () => ctrl.abort());
|
|
43
45
|
const timeoutId = setTimeout(() => {
|
|
44
|
-
logTimeout && this.#log(
|
|
46
|
+
logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
|
|
45
47
|
ctrl.abort('TIMEOUT');
|
|
46
|
-
}, 100).unref();
|
|
48
|
+
}, opts?.timeout ?? 100).unref();
|
|
47
49
|
try {
|
|
48
50
|
return await fetch(`${this.#url}${rel}`, { ...opts, signal: ctrl.signal });
|
|
49
51
|
} finally {
|
|
@@ -53,7 +55,7 @@ export class CompilerClient {
|
|
|
53
55
|
|
|
54
56
|
/** Get server information, if server is running */
|
|
55
57
|
info(): Promise<CompilerServerInfo | undefined> {
|
|
56
|
-
return this.#fetch('/info', {}, false).then(v => v.json(), () => undefined)
|
|
58
|
+
return this.#fetch('/info', { timeout: 200 }, false).then(v => v.json(), () => undefined)
|
|
57
59
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
58
60
|
.then(v => v as CompilerServerInfo);
|
|
59
61
|
}
|
|
@@ -71,13 +73,13 @@ export class CompilerClient {
|
|
|
71
73
|
async stop(): Promise<boolean> {
|
|
72
74
|
const info = await this.info();
|
|
73
75
|
if (!info) {
|
|
74
|
-
this.#log('
|
|
76
|
+
this.#log.debug('Stopping server, info not found, manual killing');
|
|
75
77
|
return Promise.all([this.#handle.server.kill(), this.#handle.compiler.kill()])
|
|
76
78
|
.then(v => v.some(x => x));
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
await this.#fetch('/stop').catch(() => { }); // Trigger
|
|
80
|
-
this.#log('
|
|
82
|
+
this.#log.debug('Waiting for compiler to exit');
|
|
81
83
|
await this.#handle.compiler.ensureKilled();
|
|
82
84
|
return true;
|
|
83
85
|
}
|
|
@@ -92,7 +94,7 @@ export class CompilerClient {
|
|
|
92
94
|
return;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
this.#log(
|
|
97
|
+
this.#log.debug(`Starting watch for events of type "${type}"`);
|
|
96
98
|
|
|
97
99
|
let signal = cfg.signal;
|
|
98
100
|
|
|
@@ -132,15 +134,15 @@ export class CompilerClient {
|
|
|
132
134
|
info = await this.info();
|
|
133
135
|
|
|
134
136
|
if (ctrl.signal.reason === 'TIMEOUT') {
|
|
135
|
-
this.#log('
|
|
137
|
+
this.#log.debug('Failed due to timeout');
|
|
136
138
|
return;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
if (ctrl.signal.aborted || !info || (cfg.enforceIteration && info.iteration !== iteration)) { // If health check fails, or aborted
|
|
140
|
-
this.#log(
|
|
142
|
+
this.#log.debug(`Stopping watch for events of type "${type}"`);
|
|
141
143
|
return;
|
|
142
144
|
} else {
|
|
143
|
-
this.#log(
|
|
145
|
+
this.#log.debug(`Restarting watch for events of type "${type}"`);
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
}
|
|
@@ -149,11 +151,11 @@ export class CompilerClient {
|
|
|
149
151
|
async waitForState(states: CompilerStateType[], message?: string, signal?: AbortSignal): Promise<void> {
|
|
150
152
|
const set = new Set(states);
|
|
151
153
|
// Loop until
|
|
152
|
-
this.#log(
|
|
154
|
+
this.#log.debug(`Waiting for states, ${states.join(', ')}`);
|
|
153
155
|
for await (const _ of this.fetchEvents('state', { signal, until: s => set.has(s.state) })) { }
|
|
154
|
-
this.#log(
|
|
156
|
+
this.#log.debug(`Found state, one of ${states.join(', ')} `);
|
|
155
157
|
if (message) {
|
|
156
|
-
this.#log(
|
|
158
|
+
this.#log.info(message);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
}
|
|
@@ -3,13 +3,16 @@ import path from 'node:path';
|
|
|
3
3
|
import timers from 'node:timers/promises';
|
|
4
4
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
|
+
import { CompilerLogger } from '../log';
|
|
6
7
|
|
|
7
8
|
export class ProcessHandle {
|
|
8
9
|
|
|
9
10
|
#file: string;
|
|
11
|
+
#log: CompilerLogger;
|
|
10
12
|
|
|
11
13
|
constructor(ctx: ManifestContext, name: string) {
|
|
12
14
|
this.#file = path.resolve(ctx.workspace.path, ctx.build.toolFolder, `${name}.pid`);
|
|
15
|
+
this.#log = new CompilerLogger(`process-handle.${name}`);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
async writePid(pid: number): Promise<void> {
|
|
@@ -26,15 +29,19 @@ export class ProcessHandle {
|
|
|
26
29
|
if (!pid) { return false; }
|
|
27
30
|
try {
|
|
28
31
|
process.kill(pid, 0); // See if process is still running
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
this.#log.debug('Is running', pid);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
this.#log.debug('Is not running', pid);
|
|
36
|
+
}
|
|
37
|
+
return false; // Not running
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
async kill(): Promise<boolean> {
|
|
35
41
|
const pid = await this.getPid();
|
|
36
42
|
if (pid && await this.isRunning()) {
|
|
37
43
|
try {
|
|
44
|
+
this.#log.debug('Killing', pid);
|
|
38
45
|
return process.kill(pid);
|
|
39
46
|
} catch { }
|
|
40
47
|
}
|
|
@@ -44,6 +51,7 @@ export class ProcessHandle {
|
|
|
44
51
|
async ensureKilled(gracePeriod: number = 3000): Promise<boolean> {
|
|
45
52
|
const start = Date.now();
|
|
46
53
|
const pid = await this.getPid();
|
|
54
|
+
this.#log.debug('Ensuring Killed', pid);
|
|
47
55
|
while (pid && (Date.now() - start) < gracePeriod) { // Ensure its done
|
|
48
56
|
if (!await this.isRunning()) {
|
|
49
57
|
return true;
|
|
@@ -51,8 +59,10 @@ export class ProcessHandle {
|
|
|
51
59
|
await timers.setTimeout(100);
|
|
52
60
|
}
|
|
53
61
|
try {
|
|
62
|
+
this.#log.debug('Force Killing', pid);
|
|
54
63
|
pid && process.kill(pid); // Force kill
|
|
55
64
|
} catch { }
|
|
65
|
+
this.#log.debug('Did Kill', this.#file, !!pid);
|
|
56
66
|
return pid !== undefined;
|
|
57
67
|
}
|
|
58
68
|
}
|
package/support/server/runner.ts
CHANGED
|
@@ -6,10 +6,10 @@ import type { ManifestContext, DeltaEvent } from '@travetto/manifest';
|
|
|
6
6
|
|
|
7
7
|
import type { CompilerEvent, CompilerMode } from '../types';
|
|
8
8
|
import { AsyncQueue } from '../queue';
|
|
9
|
-
import {
|
|
9
|
+
import { CompilerLogger } from '../log';
|
|
10
10
|
import { CommonUtil } from '../util';
|
|
11
11
|
|
|
12
|
-
const log =
|
|
12
|
+
const log = new CompilerLogger('compiler-exec');
|
|
13
13
|
const isEvent = (msg: unknown): msg is CompilerEvent => !!msg && typeof msg === 'object' && 'type' in msg;
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -22,17 +22,17 @@ export class CompilerRunner {
|
|
|
22
22
|
*/
|
|
23
23
|
static async * runProcess(ctx: ManifestContext, changed: DeltaEvent[], mode: CompilerMode, signal: AbortSignal): AsyncIterable<CompilerEvent> {
|
|
24
24
|
if (signal.aborted) {
|
|
25
|
-
log('
|
|
25
|
+
log.debug('Skipping, shutting down');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const watch = mode === 'watch';
|
|
30
30
|
if (!changed.length && !watch) {
|
|
31
31
|
yield { type: 'state', payload: { state: 'compile-end' } };
|
|
32
|
-
log('
|
|
32
|
+
log.debug('Skipped');
|
|
33
33
|
return;
|
|
34
34
|
} else {
|
|
35
|
-
log(
|
|
35
|
+
log.debug(`Started watch=${watch} changed=${changed.slice(0, 10).map(x => `${x.module}/${x.file}`)}`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const main = path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules', '@travetto/compiler/support/entry.compiler.js');
|
|
@@ -45,7 +45,7 @@ export class CompilerRunner {
|
|
|
45
45
|
try {
|
|
46
46
|
await CommonUtil.writeTextFile(deltaFile, changedFiles.join('\n'));
|
|
47
47
|
|
|
48
|
-
log('
|
|
48
|
+
log.info('Launching compiler');
|
|
49
49
|
const proc = cp.spawn(process.argv0, [main, deltaFile, `${watch}`], {
|
|
50
50
|
env: {
|
|
51
51
|
...process.env,
|
|
@@ -58,7 +58,7 @@ export class CompilerRunner {
|
|
|
58
58
|
.on('exit', () => queue.close());
|
|
59
59
|
|
|
60
60
|
const kill = (): unknown => {
|
|
61
|
-
log('
|
|
61
|
+
log.debug('Shutting down process');
|
|
62
62
|
return (proc.connected ? proc.send('shutdown', (err) => proc.kill()) : proc.kill());
|
|
63
63
|
};
|
|
64
64
|
|
|
@@ -68,11 +68,11 @@ export class CompilerRunner {
|
|
|
68
68
|
yield* queue;
|
|
69
69
|
|
|
70
70
|
if (proc.exitCode !== 0) {
|
|
71
|
-
log(
|
|
71
|
+
log.error(`Terminated during compilation, code=${proc.exitCode}, killed=${proc.killed}`);
|
|
72
72
|
}
|
|
73
73
|
process.off('SIGINT', kill);
|
|
74
74
|
|
|
75
|
-
log('
|
|
75
|
+
log.debug('Finished');
|
|
76
76
|
} finally {
|
|
77
77
|
rmSync(deltaFile, { force: true });
|
|
78
78
|
}
|
package/support/server/server.ts
CHANGED
|
@@ -6,12 +6,12 @@ import { setMaxListeners } from 'node:events';
|
|
|
6
6
|
import type { ManifestContext } from '@travetto/manifest';
|
|
7
7
|
|
|
8
8
|
import type { CompilerMode, CompilerProgressEvent, CompilerEvent, CompilerEventType, CompilerServerInfo } from '../types';
|
|
9
|
-
import {
|
|
10
|
-
import { CompilerClient } from './client';
|
|
9
|
+
import { CompilerLogger } from '../log';
|
|
11
10
|
import { CommonUtil } from '../util';
|
|
11
|
+
import { CompilerClient } from './client';
|
|
12
12
|
import { ProcessHandle } from './process-handle';
|
|
13
13
|
|
|
14
|
-
const log =
|
|
14
|
+
const log = new CompilerLogger('compiler-server');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Compiler Server Class
|
|
@@ -30,7 +30,7 @@ export class CompilerServer {
|
|
|
30
30
|
|
|
31
31
|
constructor(ctx: ManifestContext, mode: CompilerMode) {
|
|
32
32
|
this.#ctx = ctx;
|
|
33
|
-
this.#client = new CompilerClient(ctx,
|
|
33
|
+
this.#client = new CompilerClient(ctx, new CompilerLogger('client.server'));
|
|
34
34
|
this.#url = this.#client.url;
|
|
35
35
|
this.#handle = { server: new ProcessHandle(ctx, 'server'), compiler: new ProcessHandle(ctx, 'compiler') };
|
|
36
36
|
|
|
@@ -70,11 +70,11 @@ export class CompilerServer {
|
|
|
70
70
|
const info = await this.#client.info();
|
|
71
71
|
resolve((info && info.mode === 'build' && this.mode === 'watch') ? 'retry' : 'running');
|
|
72
72
|
} else {
|
|
73
|
-
log('
|
|
73
|
+
log.warn('Failed in running server', err);
|
|
74
74
|
reject(err);
|
|
75
75
|
}
|
|
76
76
|
})
|
|
77
|
-
.on('close', () => log('
|
|
77
|
+
.on('close', () => log.debug('Server close event'));
|
|
78
78
|
|
|
79
79
|
const url = new URL(this.#url);
|
|
80
80
|
setTimeout(() => this.#server.listen(+url.port, url.hostname), 1); // Run async
|
|
@@ -84,7 +84,7 @@ export class CompilerServer {
|
|
|
84
84
|
if (attempt >= 5) {
|
|
85
85
|
throw new Error('Unable to verify compilation server');
|
|
86
86
|
}
|
|
87
|
-
log('
|
|
87
|
+
log.info('Waiting for build to finish, before retrying');
|
|
88
88
|
// Let the server finish
|
|
89
89
|
await this.#client.waitForState(['close'], 'Server closed', this.signal);
|
|
90
90
|
return this.#tryListen(attempt + 1);
|
|
@@ -119,12 +119,13 @@ export class CompilerServer {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
async #disconnectActive(): Promise<void> {
|
|
122
|
-
log('
|
|
122
|
+
log.info('Server disconnect requested');
|
|
123
123
|
this.info.iteration = Date.now();
|
|
124
124
|
await new Promise(r => setTimeout(r, 20));
|
|
125
125
|
for (const el of Object.values(this.#listeners)) {
|
|
126
|
-
el.res.end();
|
|
126
|
+
try { el.res.end(); } catch { }
|
|
127
127
|
}
|
|
128
|
+
this.#listeners = {}; // Ensure its empty
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
async #clean(): Promise<{ clean: boolean }> {
|
|
@@ -141,22 +142,19 @@ export class CompilerServer {
|
|
|
141
142
|
|
|
142
143
|
const [, action, subAction] = new URL(`${this.#url}${req.url}`).pathname.split('/');
|
|
143
144
|
|
|
144
|
-
log('debug', 'Receive request', { action, subAction });
|
|
145
|
-
|
|
146
145
|
let out: unknown;
|
|
146
|
+
let close = false;
|
|
147
147
|
switch (action) {
|
|
148
148
|
case 'event': return await this.#addListener(subAction, res);
|
|
149
149
|
case 'clean': out = await this.#clean(); break;
|
|
150
|
-
case 'stop': {
|
|
151
|
-
// Must send immediately
|
|
152
|
-
res.end(JSON.stringify({ closing: true }));
|
|
153
|
-
await this.close();
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
150
|
+
case 'stop': out = JSON.stringify({ closing: true }); close = true; break;
|
|
156
151
|
case 'info':
|
|
157
152
|
default: out = this.info ?? {}; break;
|
|
158
153
|
}
|
|
159
154
|
res.end(JSON.stringify(out));
|
|
155
|
+
if (close) {
|
|
156
|
+
await this.close();
|
|
157
|
+
}
|
|
160
158
|
}
|
|
161
159
|
|
|
162
160
|
/**
|
|
@@ -165,7 +163,7 @@ export class CompilerServer {
|
|
|
165
163
|
async processEvents(src: (signal: AbortSignal) => AsyncIterable<CompilerEvent>): Promise<void> {
|
|
166
164
|
for await (const ev of CommonUtil.restartableEvents(src, this.signal, this.isResetEvent)) {
|
|
167
165
|
if (ev.type === 'progress') {
|
|
168
|
-
await
|
|
166
|
+
await log.onProgressEvent(ev.payload);
|
|
169
167
|
}
|
|
170
168
|
|
|
171
169
|
this.#emitEvent(ev);
|
|
@@ -176,9 +174,9 @@ export class CompilerServer {
|
|
|
176
174
|
if (ev.payload.state === 'init' && ev.payload.extra && 'pid' in ev.payload.extra && typeof ev.payload.extra.pid === 'number') {
|
|
177
175
|
this.info.compilerPid = ev.payload.extra.pid;
|
|
178
176
|
}
|
|
179
|
-
log(
|
|
177
|
+
log.info(`State changed: ${this.info.state}`);
|
|
180
178
|
} else if (ev.type === 'log') {
|
|
181
|
-
|
|
179
|
+
log.onLogEvent(ev.payload);
|
|
182
180
|
}
|
|
183
181
|
if (this.isResetEvent(ev)) {
|
|
184
182
|
await this.#disconnectActive();
|
|
@@ -188,19 +186,19 @@ export class CompilerServer {
|
|
|
188
186
|
// Terminate, after letting all remaining events emit
|
|
189
187
|
await this.close();
|
|
190
188
|
|
|
191
|
-
log('
|
|
189
|
+
log.debug('Finished processing events');
|
|
192
190
|
}
|
|
193
191
|
|
|
194
192
|
/**
|
|
195
193
|
* Close server
|
|
196
194
|
*/
|
|
197
195
|
async close(): Promise<void> {
|
|
198
|
-
log('
|
|
196
|
+
log.info('Closing down server');
|
|
199
197
|
|
|
200
198
|
// If we are in a place where progress exists
|
|
201
199
|
if (this.info.state === 'compile-start') {
|
|
202
200
|
const cancel: CompilerProgressEvent = { complete: true, idx: 0, total: 0, message: 'Complete', operation: 'compile' };
|
|
203
|
-
|
|
201
|
+
await log.onProgressEvent(cancel);
|
|
204
202
|
this.#emitEvent({ type: 'progress', payload: cancel });
|
|
205
203
|
}
|
|
206
204
|
|
|
@@ -217,14 +215,10 @@ export class CompilerServer {
|
|
|
217
215
|
} catch { // Timeout or other error
|
|
218
216
|
// Force shutdown
|
|
219
217
|
this.#server.closeAllConnections();
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
process.kill(this.info.compilerPid);
|
|
223
|
-
} catch { }
|
|
224
|
-
}
|
|
218
|
+
await this.#handle.compiler.kill();
|
|
225
219
|
}
|
|
226
220
|
|
|
227
|
-
log('
|
|
221
|
+
log.info('Closed down server');
|
|
228
222
|
}
|
|
229
223
|
|
|
230
224
|
/**
|
|
@@ -232,7 +226,7 @@ export class CompilerServer {
|
|
|
232
226
|
*/
|
|
233
227
|
async listen(): Promise<CompilerServer | undefined> {
|
|
234
228
|
const running = await this.#tryListen() === 'ok';
|
|
235
|
-
log(
|
|
229
|
+
log.info(running ? 'Starting server' : 'Server already running under a different process', this.#url);
|
|
236
230
|
return running ? this : undefined;
|
|
237
231
|
}
|
|
238
232
|
}
|
package/support/setup.ts
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'node:fs/promises';
|
|
|
4
4
|
|
|
5
5
|
import { type DeltaEvent, type ManifestContext, type Package } from '@travetto/manifest';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { CompilerLogger } from './log';
|
|
8
8
|
import { CommonUtil } from './util';
|
|
9
9
|
|
|
10
10
|
type ModFile = { input: string, output: string, stale: boolean };
|
|
@@ -128,19 +128,19 @@ export class CompilerSetup {
|
|
|
128
128
|
const out: string[] = [];
|
|
129
129
|
|
|
130
130
|
try {
|
|
131
|
-
await
|
|
131
|
+
await new CompilerLogger(scope).wrap(async log => {
|
|
132
132
|
if (files.some(f => f.stale)) {
|
|
133
|
-
log('
|
|
133
|
+
log.debug('Starting', mod);
|
|
134
134
|
for (const file of files.filter(x => x.stale)) {
|
|
135
135
|
await this.#transpileFile(ctx, file.input, file.output);
|
|
136
136
|
}
|
|
137
137
|
if (changes.length) {
|
|
138
138
|
out.push(...changes.map(x => `${mod}/${x}`));
|
|
139
|
-
log(
|
|
139
|
+
log.debug(`Source changed: ${changes.join(', ')}`, mod);
|
|
140
140
|
}
|
|
141
|
-
log('
|
|
141
|
+
log.debug('Completed', mod);
|
|
142
142
|
} else {
|
|
143
|
-
log('
|
|
143
|
+
log.debug('Skipped', mod);
|
|
144
144
|
}
|
|
145
145
|
}, false);
|
|
146
146
|
} catch (err) {
|
|
@@ -173,7 +173,7 @@ export class CompilerSetup {
|
|
|
173
173
|
static async setup(ctx: ManifestContext): Promise<DeltaEvent[]> {
|
|
174
174
|
let changes = 0;
|
|
175
175
|
|
|
176
|
-
await
|
|
176
|
+
await new CompilerLogger('precompile').wrap(async () => {
|
|
177
177
|
for (const mod of PRECOMPILE_MODS) {
|
|
178
178
|
changes += (await this.#compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
|
|
179
179
|
}
|
|
@@ -181,18 +181,18 @@ export class CompilerSetup {
|
|
|
181
181
|
|
|
182
182
|
const { ManifestUtil, ManifestDeltaUtil } = await this.#importManifest(ctx);
|
|
183
183
|
|
|
184
|
-
const manifest = await
|
|
184
|
+
const manifest = await new CompilerLogger('manifest').wrap(() =>
|
|
185
185
|
ManifestUtil.buildManifest(ManifestUtil.getWorkspaceContext(ctx)));
|
|
186
186
|
|
|
187
|
-
await
|
|
187
|
+
await new CompilerLogger('transformers').wrap(async () => {
|
|
188
188
|
for (const mod of Object.values(manifest.modules).filter(m => m.files.$transformer?.length)) {
|
|
189
189
|
changes += (await this.#compileIfStale(ctx, 'transformers', mod.name, ['package.json', ...mod.files.$transformer!.map(x => x[0])])).length;
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
const delta = await
|
|
193
|
+
const delta = await new CompilerLogger('delta').wrap(async log => {
|
|
194
194
|
if (changes) {
|
|
195
|
-
log('
|
|
195
|
+
log.debug('Skipping, everything changed');
|
|
196
196
|
return [{ type: 'changed', file: '*', module: ctx.workspace.name, sourceFile: '' } as const];
|
|
197
197
|
} else {
|
|
198
198
|
return ManifestDeltaUtil.produceDelta(manifest);
|
|
@@ -200,29 +200,29 @@ export class CompilerSetup {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
if (changes) {
|
|
203
|
-
await
|
|
203
|
+
await new CompilerLogger('reset').wrap(async log => {
|
|
204
204
|
await fs.rm(path.resolve(ctx.workspace.path, ctx.build.outputFolder), { recursive: true, force: true });
|
|
205
|
-
log('
|
|
205
|
+
log.info('Clearing output due to compiler changes');
|
|
206
206
|
}, false);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
// Write manifest
|
|
210
|
-
await
|
|
210
|
+
await new CompilerLogger('manifest').wrap(async log => {
|
|
211
211
|
await ManifestUtil.writeManifest(manifest);
|
|
212
|
-
log(
|
|
212
|
+
log.debug(`Wrote manifest ${ctx.workspace.name}`);
|
|
213
213
|
|
|
214
214
|
// Update all manifests when in mono repo
|
|
215
215
|
if (delta.length && ctx.workspace.mono) {
|
|
216
216
|
const names: string[] = [];
|
|
217
217
|
const mods = Object.values(manifest.modules).filter(x => x.workspace && x.name !== ctx.workspace.name);
|
|
218
218
|
for (const mod of mods) {
|
|
219
|
-
const modCtx = ManifestUtil.getModuleContext(ctx, mod.sourceFolder);
|
|
219
|
+
const modCtx = ManifestUtil.getModuleContext(ctx, mod.sourceFolder, true);
|
|
220
220
|
const modManifest = await ManifestUtil.buildManifest(modCtx);
|
|
221
221
|
await ManifestUtil.writeManifest(modManifest);
|
|
222
222
|
names.push(mod.name);
|
|
223
223
|
}
|
|
224
|
-
log(
|
|
225
|
-
log(
|
|
224
|
+
log.debug(`Changes triggered ${delta.slice(0, 10).map(x => `${x.type}:${x.module}:${x.file}`)}`);
|
|
225
|
+
log.debug(`Rewrote monorepo manifests [changes=${delta.length}] ${names.slice(0, 10).join(', ')}`);
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
228
|
|
package/support/util.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { setMaxListeners } from 'node:events';
|
|
|
4
4
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { CompilerLogger } from './log';
|
|
8
8
|
|
|
9
9
|
const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
|
|
10
10
|
|
|
@@ -58,28 +58,30 @@ export class CommonUtil {
|
|
|
58
58
|
* Restartable Event Stream
|
|
59
59
|
*/
|
|
60
60
|
static async * restartableEvents<T>(src: (signal: AbortSignal) => AsyncIterable<T>, parent: AbortSignal, shouldRestart: (item: T) => boolean): AsyncIterable<T> {
|
|
61
|
-
const log =
|
|
61
|
+
const log = new CompilerLogger('event-stream');
|
|
62
62
|
outer: while (!parent.aborted) {
|
|
63
63
|
const controller = new AbortController();
|
|
64
64
|
setMaxListeners(1000, controller.signal);
|
|
65
65
|
// Chain
|
|
66
|
-
|
|
66
|
+
const kill = (): void => controller.abort();
|
|
67
|
+
parent.addEventListener('abort', kill);
|
|
67
68
|
|
|
68
69
|
const comp = src(controller.signal);
|
|
69
70
|
|
|
70
|
-
log('
|
|
71
|
+
log.debug('Started event stream');
|
|
71
72
|
|
|
72
73
|
// Wait for all events, close at the end
|
|
73
74
|
for await (const ev of comp) {
|
|
74
75
|
yield ev;
|
|
75
76
|
if (shouldRestart(ev)) {
|
|
76
|
-
log('
|
|
77
|
+
log.debug('Restarting stream');
|
|
77
78
|
controller.abort(); // Ensure terminated of process
|
|
79
|
+
parent.removeEventListener('abort', kill);
|
|
78
80
|
continue outer;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
log('
|
|
84
|
+
log.debug('Finished event stream');
|
|
83
85
|
|
|
84
86
|
// Natural exit, we done
|
|
85
87
|
if (!controller.signal.aborted) { // Shutdown source if still running
|