@travetto/compiler 4.0.0-rc.5 → 4.0.0-rc.7
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/README.md +8 -8
- package/__index__.ts +0 -1
- package/package.json +4 -4
- package/src/compiler.ts +22 -41
- package/src/state.ts +37 -19
- package/support/entry.trvc.ts +24 -21
- package/support/log.ts +96 -83
- package/support/server/client.ts +13 -13
- package/support/server/process-handle.ts +9 -9
- package/support/server/runner.ts +9 -9
- package/support/server/server.ts +15 -17
- package/support/setup.ts +17 -17
- package/support/types.ts +1 -1
- package/support/util.ts +5 -5
- package/src/log.ts +0 -17
package/README.md
CHANGED
|
@@ -38,8 +38,8 @@ In addition to the normal output, the compiler supports an environment variable
|
|
|
38
38
|
```bash
|
|
39
39
|
$ TRV_BUILD=debug trvc build
|
|
40
40
|
|
|
41
|
-
2029-03-14T04:00:00.618Z info [
|
|
42
|
-
2029-03-14T04:00:00.837Z debug [
|
|
41
|
+
2029-03-14T04:00:00.618Z info [server ] Starting server http://127.0.0.1:25539
|
|
42
|
+
2029-03-14T04:00:00.837Z debug [main ] Start Server
|
|
43
43
|
2029-03-14T04:00:01.510Z debug [event-stream ] Started event stream
|
|
44
44
|
2029-03-14T04:00:02.450Z debug [precompile ] Started
|
|
45
45
|
2029-03-14T04:00:02.762Z debug [precompile ] Skipped @travetto/manifest
|
|
@@ -60,14 +60,14 @@ $ TRV_BUILD=debug trvc build
|
|
|
60
60
|
2029-03-14T04:00:10.799Z debug [manifest ] Started
|
|
61
61
|
2029-03-14T04:00:11.013Z debug [manifest ] Wrote manifest @travetto-doc/compiler
|
|
62
62
|
2029-03-14T04:00:11.827Z debug [manifest ] Completed
|
|
63
|
-
2029-03-14T04:00:11.894Z info [
|
|
63
|
+
2029-03-14T04:00:11.894Z info [server ] State changed: compile-end
|
|
64
64
|
2029-03-14T04:00:12.133Z debug [compiler-exec ] Skipped
|
|
65
65
|
2029-03-14T04:00:13.123Z debug [event-stream ] Finished event stream
|
|
66
|
-
2029-03-14T04:00:14.014Z info [
|
|
67
|
-
2029-03-14T04:00:14.924Z debug [
|
|
68
|
-
2029-03-14T04:00:15.690Z info [
|
|
69
|
-
2029-03-14T04:00:15.865Z debug [
|
|
70
|
-
2029-03-14T04:00:16.757Z debug [
|
|
66
|
+
2029-03-14T04:00:14.014Z info [server ] Closing down server
|
|
67
|
+
2029-03-14T04:00:14.924Z debug [server ] Server close event
|
|
68
|
+
2029-03-14T04:00:15.690Z info [server ] Closed down server
|
|
69
|
+
2029-03-14T04:00:15.865Z debug [server ] Finished processing events
|
|
70
|
+
2029-03-14T04:00:16.757Z debug [main ] End Server
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**Terminal: Sample trv output with default log level**
|
package/__index__.ts
CHANGED
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.7",
|
|
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.7",
|
|
35
|
+
"@travetto/transformer": "^4.0.0-rc.7",
|
|
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.8"
|
|
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';
|
|
@@ -8,10 +7,13 @@ import { ManifestModuleUtil, RuntimeIndex } from '@travetto/manifest';
|
|
|
8
7
|
import { CompilerUtil } from './util';
|
|
9
8
|
import { CompilerState } from './state';
|
|
10
9
|
import { CompilerWatcher } from './watch';
|
|
11
|
-
import {
|
|
12
|
-
import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
|
|
10
|
+
import { CompileEmitEvent, CompileEmitter } from './types';
|
|
13
11
|
import { EventUtil } from './event';
|
|
14
12
|
|
|
13
|
+
import { IpcLogger } from '../support/log';
|
|
14
|
+
|
|
15
|
+
const log = new IpcLogger({ level: 'debug' });
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* Compilation support
|
|
17
19
|
*/
|
|
@@ -23,7 +25,7 @@ export class Compiler {
|
|
|
23
25
|
static async main(): Promise<void> {
|
|
24
26
|
const [dirty, watch] = process.argv.slice(2);
|
|
25
27
|
const state = await CompilerState.get(RuntimeIndex);
|
|
26
|
-
|
|
28
|
+
log.debug('Running compiler with dirty file', dirty);
|
|
27
29
|
const dirtyFiles = ManifestModuleUtil.getFileType(dirty) === 'ts' ? [dirty] : (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
|
|
28
30
|
await new Compiler(state, dirtyFiles, watch === 'true').run();
|
|
29
31
|
}
|
|
@@ -58,19 +60,19 @@ export class Compiler {
|
|
|
58
60
|
this.#shuttingDown = true;
|
|
59
61
|
switch (mode) {
|
|
60
62
|
case 'manual': {
|
|
61
|
-
|
|
63
|
+
log.error('Shutting down manually');
|
|
62
64
|
process.exitCode = 2;
|
|
63
65
|
break;
|
|
64
66
|
}
|
|
65
67
|
case 'error': {
|
|
66
68
|
process.exitCode = 1;
|
|
67
69
|
if (err) {
|
|
68
|
-
|
|
70
|
+
log.error('Shutting down due to failure', err.message);
|
|
69
71
|
}
|
|
70
72
|
break;
|
|
71
73
|
}
|
|
72
74
|
case 'reset': {
|
|
73
|
-
|
|
75
|
+
log.info('Triggering reset due to change in core files', err?.cause);
|
|
74
76
|
EventUtil.sendEvent('state', { state: 'reset' });
|
|
75
77
|
process.exitCode = 0;
|
|
76
78
|
break;
|
|
@@ -86,29 +88,8 @@ export class Compiler {
|
|
|
86
88
|
/**
|
|
87
89
|
* Compile in a single pass, only emitting dirty files
|
|
88
90
|
*/
|
|
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;
|
|
91
|
+
getCompiler(): CompileEmitter {
|
|
92
|
+
return (inputFile: string, needsNewProgram?: boolean) => this.#state.writeInputFile(inputFile, needsNewProgram);
|
|
112
93
|
}
|
|
113
94
|
|
|
114
95
|
/**
|
|
@@ -134,21 +115,21 @@ export class Compiler {
|
|
|
134
115
|
|
|
135
116
|
await timers.setTimeout(1);
|
|
136
117
|
|
|
137
|
-
|
|
118
|
+
log.debug(`Compiled ${i} files`);
|
|
138
119
|
}
|
|
139
120
|
|
|
140
121
|
/**
|
|
141
122
|
* Run the compiler
|
|
142
123
|
*/
|
|
143
124
|
async run(): Promise<void> {
|
|
144
|
-
|
|
125
|
+
log.debug('Compilation started');
|
|
145
126
|
|
|
146
127
|
EventUtil.sendEvent('state', { state: 'init', extra: { pid: process.pid } });
|
|
147
128
|
|
|
148
129
|
const emitter = await this.getCompiler();
|
|
149
130
|
let failed = false;
|
|
150
131
|
|
|
151
|
-
|
|
132
|
+
log.debug('Compiler loaded');
|
|
152
133
|
|
|
153
134
|
EventUtil.sendEvent('state', { state: 'compile-start' });
|
|
154
135
|
|
|
@@ -161,13 +142,13 @@ export class Compiler {
|
|
|
161
142
|
}
|
|
162
143
|
}
|
|
163
144
|
if (this.#signal.aborted) {
|
|
164
|
-
|
|
145
|
+
log.debug('Compilation aborted');
|
|
165
146
|
} else if (failed) {
|
|
166
|
-
|
|
147
|
+
log.debug('Compilation failed');
|
|
167
148
|
process.exitCode = 1;
|
|
168
149
|
return;
|
|
169
150
|
} else {
|
|
170
|
-
|
|
151
|
+
log.debug('Compilation succeeded');
|
|
171
152
|
}
|
|
172
153
|
} else if (this.#watch) {
|
|
173
154
|
// Prime compiler before complete
|
|
@@ -178,7 +159,7 @@ export class Compiler {
|
|
|
178
159
|
EventUtil.sendEvent('state', { state: 'compile-end' });
|
|
179
160
|
|
|
180
161
|
if (this.#watch && !this.#signal.aborted) {
|
|
181
|
-
|
|
162
|
+
log.info('Watch is ready');
|
|
182
163
|
|
|
183
164
|
EventUtil.sendEvent('state', { state: 'watch-start' });
|
|
184
165
|
try {
|
|
@@ -186,14 +167,14 @@ export class Compiler {
|
|
|
186
167
|
if (ev.action !== 'delete') {
|
|
187
168
|
const err = await emitter(ev.entry.inputFile, true);
|
|
188
169
|
if (err) {
|
|
189
|
-
|
|
170
|
+
log.info('Compilation Error', CompilerUtil.buildTranspileError(ev.entry.inputFile, err));
|
|
190
171
|
} else {
|
|
191
|
-
|
|
172
|
+
log.info(`Compiled ${ev.entry.sourceFile} on ${ev.action}`);
|
|
192
173
|
}
|
|
193
174
|
} else {
|
|
194
175
|
if (ev.entry.outputFile) {
|
|
195
176
|
// Remove output
|
|
196
|
-
|
|
177
|
+
log.info(`Removed ${ev.entry.sourceFile}, ${ev.entry.outputFile}`);
|
|
197
178
|
await fs.rm(ev.entry.outputFile, { force: true }); // Ensure output is deleted
|
|
198
179
|
}
|
|
199
180
|
}
|
|
@@ -216,7 +197,7 @@ export class Compiler {
|
|
|
216
197
|
}
|
|
217
198
|
}
|
|
218
199
|
|
|
219
|
-
|
|
200
|
+
log.debug('Compiler process shutdown');
|
|
220
201
|
|
|
221
202
|
this.#shutdown('complete');
|
|
222
203
|
}
|
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/support/entry.trvc.ts
CHANGED
|
@@ -4,8 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
|
-
import type {
|
|
8
|
-
import {
|
|
7
|
+
import type { CompilerMode, CompilerServerInfo } from './types';
|
|
8
|
+
import { Log } from './log';
|
|
9
9
|
import { CommonUtil } from './util';
|
|
10
10
|
import { CompilerSetup } from './setup';
|
|
11
11
|
import { CompilerServer } from './server/server';
|
|
@@ -14,40 +14,38 @@ 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, Log.scoped('client'));
|
|
19
18
|
const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
|
|
19
|
+
Log.root = ctx.workspace.path;
|
|
20
|
+
Log.initLevel('error');
|
|
20
21
|
|
|
21
22
|
/** Main entry point for compilation */
|
|
22
|
-
const compile = async (op: CompilerMode,
|
|
23
|
-
LogUtil.initLogs(ctx, logLevel ?? 'info');
|
|
24
|
-
|
|
23
|
+
const compile = async (op: CompilerMode, setupOnly = false): Promise<void> => {
|
|
25
24
|
const server = await new CompilerServer(ctx, op).listen();
|
|
25
|
+
const log = Log.scoped('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> {
|
|
50
|
-
LogUtil.initLogs(ctx);
|
|
51
49
|
if (await client.stop()) {
|
|
52
50
|
console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
|
|
53
51
|
} else {
|
|
@@ -63,7 +61,6 @@ export const main = (ctx: ManifestContext) => {
|
|
|
63
61
|
|
|
64
62
|
/** Clean the server */
|
|
65
63
|
async clean(): Promise<void> {
|
|
66
|
-
LogUtil.initLogs(ctx);
|
|
67
64
|
if (await client.clean()) {
|
|
68
65
|
return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
|
|
69
66
|
} else {
|
|
@@ -74,7 +71,6 @@ export const main = (ctx: ManifestContext) => {
|
|
|
74
71
|
|
|
75
72
|
/** Stream events */
|
|
76
73
|
events: async (type: string, handler: (ev: unknown) => unknown): Promise<void> => {
|
|
77
|
-
LogUtil.initLogs(ctx, 'error');
|
|
78
74
|
if (type === 'change' || type === 'log' || type === 'progress' || type === 'state') {
|
|
79
75
|
for await (const ev of client.fetchEvents(type)) { await handler(ev); }
|
|
80
76
|
} else {
|
|
@@ -83,23 +79,30 @@ export const main = (ctx: ManifestContext) => {
|
|
|
83
79
|
},
|
|
84
80
|
|
|
85
81
|
/** Build the project */
|
|
86
|
-
async build(): Promise<void> {
|
|
82
|
+
async build(): Promise<void> {
|
|
83
|
+
Log.initLevel('info');
|
|
84
|
+
await compile('build');
|
|
85
|
+
},
|
|
87
86
|
|
|
88
87
|
/** Build and watch the project */
|
|
89
|
-
async watch(): Promise<void> {
|
|
88
|
+
async watch(): Promise<void> {
|
|
89
|
+
Log.initLevel('info');
|
|
90
|
+
await compile('watch');
|
|
91
|
+
},
|
|
90
92
|
|
|
91
93
|
/** Build and return a loader */
|
|
92
94
|
async getLoader(): Promise<(mod: string) => Promise<unknown>> {
|
|
93
|
-
|
|
94
|
-
if (!(await client.isWatching())) {
|
|
95
|
-
|
|
95
|
+
Log.initLevel('none');
|
|
96
|
+
if (!(await client.isWatching())) { // Short circuit if we can
|
|
97
|
+
Log.initLevel('error');
|
|
98
|
+
await compile('build');
|
|
96
99
|
}
|
|
97
100
|
return CommonUtil.moduleLoader(ctx);
|
|
98
101
|
},
|
|
99
102
|
|
|
100
103
|
/** Manifest entry point */
|
|
101
104
|
async manifest(output?: string, prod?: boolean): Promise<void> {
|
|
102
|
-
await compile('build',
|
|
105
|
+
await compile('build', true);
|
|
103
106
|
await CompilerSetup.exportManifest(ctx, output, prod); return;
|
|
104
107
|
}
|
|
105
108
|
};
|
package/support/log.ts
CHANGED
|
@@ -1,117 +1,130 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
|
|
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
|
-
const LEVEL_TO_PRI: Record<CompilerLogLevel, number> = { debug: 1, info: 2, warn: 3, error: 4 };
|
|
1
|
+
import { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
|
|
10
2
|
|
|
3
|
+
const LEVEL_TO_PRI: Record<CompilerLogLevel | 'none', number> = { debug: 1, info: 2, warn: 3, error: 4, none: 5 };
|
|
11
4
|
const SCOPE_MAX = 15;
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
type LogConfig = {
|
|
7
|
+
level?: CompilerLogLevel | 'none';
|
|
8
|
+
root?: string;
|
|
9
|
+
scope?: string;
|
|
10
|
+
parent?: Logger;
|
|
11
|
+
};
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
export type LogShape = Record<'info' | 'debug' | 'warn' | 'error', (message: string, ...args: unknown[]) => void>;
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
const ESC = '\x1b[';
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
export class Logger implements LogConfig, LogShape {
|
|
22
18
|
|
|
23
|
-
static linePartial
|
|
19
|
+
static #linePartial: boolean | undefined;
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
/** Rewrite text line, tracking cleanup as necessary */
|
|
22
|
+
static rewriteLine(text: string): Promise<void> | void {
|
|
23
|
+
if ((!text && !this.#linePartial) || !process.stdout.isTTY) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (this.#linePartial === undefined) { // First time
|
|
27
|
+
process.stdout.write(`${ESC}?25l`); // Hide cursor
|
|
28
|
+
process.on('exit', () => this.reset());
|
|
29
|
+
}
|
|
26
30
|
// Move to 1st position, and clear after text
|
|
27
31
|
const done = process.stdout.write(`${ESC}1G${text}${ESC}0K`);
|
|
28
|
-
this
|
|
32
|
+
this.#linePartial = !!text;
|
|
29
33
|
if (!done) {
|
|
30
34
|
return new Promise<void>(r => process.stdout.once('drain', r));
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
static reset(): void { process.stdout.write(`${ESC}!p${ESC}?25h`); }
|
|
39
|
+
|
|
40
|
+
level?: CompilerLogLevel | 'none';
|
|
41
|
+
root: string = process.cwd();
|
|
42
|
+
scope?: string;
|
|
43
|
+
parent?: Logger;
|
|
44
|
+
|
|
45
|
+
constructor(cfg: LogConfig = {}) {
|
|
46
|
+
Object.assign(this, cfg);
|
|
37
47
|
}
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// If we are in info or a terminal and also in a tty
|
|
50
|
-
this.logProgress = (this.isLevelActive('info') && process.stdout.isTTY) ? this.#logProgressEvent : undefined;
|
|
51
|
-
if (this.logProgress) {
|
|
52
|
-
process.stdout.write(`${ESC}?25l`); // Hide cursor
|
|
49
|
+
valid(ev: CompilerLogEvent): boolean {
|
|
50
|
+
return LEVEL_TO_PRI[this.level ?? this.parent?.level!] <= LEVEL_TO_PRI[ev.level];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Log event with filtering by level */
|
|
54
|
+
render(ev: CompilerLogEvent): void {
|
|
55
|
+
if (!this.valid(ev)) { return; }
|
|
56
|
+
const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root ?? this.parent?.root!, '.') : x);
|
|
57
|
+
if (ev.scope ?? this.scope) {
|
|
58
|
+
params.unshift(`[${(ev.scope ?? this.scope!).padEnd(SCOPE_MAX, ' ')}]`);
|
|
53
59
|
}
|
|
54
|
-
|
|
60
|
+
params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
|
|
61
|
+
Logger.rewriteLine(''); // Clear out progress line, if active
|
|
62
|
+
// eslint-disable-next-line no-console
|
|
63
|
+
console[ev.level]!(...params);
|
|
55
64
|
}
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
info(message: string, ...args: unknown[]): void { return this.render({ level: 'info', message, args }); }
|
|
67
|
+
debug(message: string, ...args: unknown[]): void { return this.render({ level: 'debug', message, args }); }
|
|
68
|
+
warn(message: string, ...args: unknown[]): void { return this.render({ level: 'warn', message, args }); }
|
|
69
|
+
error(message: string, ...args: unknown[]): void { return this.render({ level: 'error', message, args }); }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class $RootLogger extends Logger {
|
|
74
|
+
#logProgress?: boolean;
|
|
75
|
+
|
|
76
|
+
/** Get if we should log progress */
|
|
77
|
+
get logProgress(): boolean {
|
|
78
|
+
if (this.#logProgress === undefined) {
|
|
79
|
+
this.#logProgress = !!process.env.PS1 && process.stdout.isTTY && process.env.TRV_BUILD !== 'none' && process.env.TRV_QUIET !== 'true';
|
|
60
80
|
}
|
|
81
|
+
return this.#logProgress;
|
|
61
82
|
}
|
|
62
83
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
/** Set level for operation */
|
|
85
|
+
initLevel(defaultLevel: CompilerLogLevel | 'none'): void {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
87
|
+
const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
|
|
88
|
+
this.level = (build !== 'none' && process.env.TRV_QUIET !== 'true') ? (build || defaultLevel) : 'none';
|
|
67
89
|
}
|
|
68
90
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
static isLevelActive(lvl: CompilerLogLevel): boolean {
|
|
73
|
-
return LEVEL_TO_PRI[this.logLevel] <= LEVEL_TO_PRI[lvl];
|
|
91
|
+
/** Produce a scoped logger */
|
|
92
|
+
scoped(name: string): Logger {
|
|
93
|
+
return new Logger({ parent: this, scope: name });
|
|
74
94
|
}
|
|
75
95
|
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (this.isLevelActive(ev.level)) {
|
|
81
|
-
const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root, '.') : x);
|
|
82
|
-
if (ev.scope) {
|
|
83
|
-
params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
|
|
84
|
-
}
|
|
85
|
-
params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
|
|
86
|
-
if (this.linePartial) {
|
|
87
|
-
this.#rewriteLine(''); // Clear out progress line
|
|
88
|
-
}
|
|
89
|
-
// eslint-disable-next-line no-console
|
|
90
|
-
console[ev.level]!(...params);
|
|
91
|
-
}
|
|
96
|
+
/** Scope and provide a callback pattern for access to a logger */
|
|
97
|
+
wrap<T = unknown>(scope: string, op: (log: Logger) => Promise<T>, basic = true): Promise<T> {
|
|
98
|
+
const l = this.scoped(scope);
|
|
99
|
+
return basic ? (l.debug('Started'), op(l).finally(() => l.debug('Completed'))) : op(l);
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
return op(log).finally(() => basic && log('debug', 'Completed'));
|
|
102
|
+
/** Write progress event, if active */
|
|
103
|
+
onProgressEvent(ev: CompilerProgressEvent): void | Promise<void> {
|
|
104
|
+
if (!(this.logProgress)) { return; }
|
|
105
|
+
const pct = Math.trunc(ev.idx * 100 / ev.total);
|
|
106
|
+
const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
|
|
107
|
+
return Logger.rewriteLine(text);
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
/** Write all progress events if active */
|
|
111
|
+
async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
|
|
112
|
+
if (!(this.logProgress)) { return; }
|
|
113
|
+
for await (const ev of src()) { this.onProgressEvent(ev); }
|
|
114
|
+
Logger.reset();
|
|
108
115
|
}
|
|
116
|
+
}
|
|
109
117
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (!this.
|
|
115
|
-
|
|
118
|
+
export const Log = new $RootLogger();
|
|
119
|
+
|
|
120
|
+
export class IpcLogger extends Logger {
|
|
121
|
+
render(ev: CompilerLogEvent): void {
|
|
122
|
+
if (!this.valid(ev)) { return; }
|
|
123
|
+
if (process.connected && process.send) {
|
|
124
|
+
process.send({ type: 'log', payload: ev });
|
|
125
|
+
}
|
|
126
|
+
if (!process.connected) {
|
|
127
|
+
super.render(ev);
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
|
-
}
|
|
130
|
+
}
|
package/support/server/client.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Readable } from 'node:stream';
|
|
|
5
5
|
import { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
7
|
import type { CompilerEvent, CompilerEventType, CompilerServerInfo, CompilerStateType } from '../types';
|
|
8
|
-
import type {
|
|
8
|
+
import type { LogShape } from '../log';
|
|
9
9
|
import { ProcessHandle } from './process-handle';
|
|
10
10
|
|
|
11
11
|
type FetchEventsConfig<T> = {
|
|
@@ -20,10 +20,10 @@ type FetchEventsConfig<T> = {
|
|
|
20
20
|
export class CompilerClient {
|
|
21
21
|
|
|
22
22
|
#url: string;
|
|
23
|
-
#log:
|
|
23
|
+
#log: LogShape;
|
|
24
24
|
#handle: Record<'compiler' | 'server', ProcessHandle>;
|
|
25
25
|
|
|
26
|
-
constructor(ctx: ManifestContext, log:
|
|
26
|
+
constructor(ctx: ManifestContext, log: LogShape) {
|
|
27
27
|
this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
|
|
28
28
|
this.#log = log;
|
|
29
29
|
this.#handle = { compiler: new ProcessHandle(ctx, 'compiler'), server: new ProcessHandle(ctx, 'server') };
|
|
@@ -41,7 +41,7 @@ export class CompilerClient {
|
|
|
41
41
|
const ctrl = new AbortController();
|
|
42
42
|
opts?.signal?.addEventListener('abort', () => ctrl.abort());
|
|
43
43
|
const timeoutId = setTimeout(() => {
|
|
44
|
-
logTimeout && this.#log(
|
|
44
|
+
logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
|
|
45
45
|
ctrl.abort('TIMEOUT');
|
|
46
46
|
}, opts?.timeout ?? 100).unref();
|
|
47
47
|
try {
|
|
@@ -71,13 +71,13 @@ export class CompilerClient {
|
|
|
71
71
|
async stop(): Promise<boolean> {
|
|
72
72
|
const info = await this.info();
|
|
73
73
|
if (!info) {
|
|
74
|
-
this.#log('
|
|
74
|
+
this.#log.debug('Stopping server, info not found, manual killing');
|
|
75
75
|
return Promise.all([this.#handle.server.kill(), this.#handle.compiler.kill()])
|
|
76
76
|
.then(v => v.some(x => x));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
await this.#fetch('/stop').catch(() => { }); // Trigger
|
|
80
|
-
this.#log('
|
|
80
|
+
this.#log.debug('Waiting for compiler to exit');
|
|
81
81
|
await this.#handle.compiler.ensureKilled();
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
@@ -92,7 +92,7 @@ export class CompilerClient {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
this.#log(
|
|
95
|
+
this.#log.debug(`Starting watch for events of type "${type}"`);
|
|
96
96
|
|
|
97
97
|
let signal = cfg.signal;
|
|
98
98
|
|
|
@@ -132,15 +132,15 @@ export class CompilerClient {
|
|
|
132
132
|
info = await this.info();
|
|
133
133
|
|
|
134
134
|
if (ctrl.signal.reason === 'TIMEOUT') {
|
|
135
|
-
this.#log('
|
|
135
|
+
this.#log.debug('Failed due to timeout');
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
if (ctrl.signal.aborted || !info || (cfg.enforceIteration && info.iteration !== iteration)) { // If health check fails, or aborted
|
|
140
|
-
this.#log(
|
|
140
|
+
this.#log.debug(`Stopping watch for events of type "${type}"`);
|
|
141
141
|
return;
|
|
142
142
|
} else {
|
|
143
|
-
this.#log(
|
|
143
|
+
this.#log.debug(`Restarting watch for events of type "${type}"`);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -149,11 +149,11 @@ export class CompilerClient {
|
|
|
149
149
|
async waitForState(states: CompilerStateType[], message?: string, signal?: AbortSignal): Promise<void> {
|
|
150
150
|
const set = new Set(states);
|
|
151
151
|
// Loop until
|
|
152
|
-
this.#log(
|
|
152
|
+
this.#log.debug(`Waiting for states, ${states.join(', ')}`);
|
|
153
153
|
for await (const _ of this.fetchEvents('state', { signal, until: s => set.has(s.state) })) { }
|
|
154
|
-
this.#log(
|
|
154
|
+
this.#log.debug(`Found state, one of ${states.join(', ')} `);
|
|
155
155
|
if (message) {
|
|
156
|
-
this.#log(
|
|
156
|
+
this.#log.info(message);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
|
@@ -3,16 +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 {
|
|
6
|
+
import { Log, Logger } from '../log';
|
|
7
7
|
|
|
8
8
|
export class ProcessHandle {
|
|
9
9
|
|
|
10
10
|
#file: string;
|
|
11
|
-
#log:
|
|
11
|
+
#log: Logger;
|
|
12
12
|
|
|
13
13
|
constructor(ctx: ManifestContext, name: string) {
|
|
14
14
|
this.#file = path.resolve(ctx.workspace.path, ctx.build.toolFolder, `${name}.pid`);
|
|
15
|
-
this.#log =
|
|
15
|
+
this.#log = Log.scoped(`process-handle.${name}`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async writePid(pid: number): Promise<void> {
|
|
@@ -29,10 +29,10 @@ export class ProcessHandle {
|
|
|
29
29
|
if (!pid) { return false; }
|
|
30
30
|
try {
|
|
31
31
|
process.kill(pid, 0); // See if process is still running
|
|
32
|
-
this.#log('
|
|
32
|
+
this.#log.debug('Is running', pid);
|
|
33
33
|
return true;
|
|
34
34
|
} catch {
|
|
35
|
-
this.#log('
|
|
35
|
+
this.#log.debug('Is not running', pid);
|
|
36
36
|
}
|
|
37
37
|
return false; // Not running
|
|
38
38
|
}
|
|
@@ -41,7 +41,7 @@ export class ProcessHandle {
|
|
|
41
41
|
const pid = await this.getPid();
|
|
42
42
|
if (pid && await this.isRunning()) {
|
|
43
43
|
try {
|
|
44
|
-
this.#log('
|
|
44
|
+
this.#log.debug('Killing', pid);
|
|
45
45
|
return process.kill(pid);
|
|
46
46
|
} catch { }
|
|
47
47
|
}
|
|
@@ -51,7 +51,7 @@ export class ProcessHandle {
|
|
|
51
51
|
async ensureKilled(gracePeriod: number = 3000): Promise<boolean> {
|
|
52
52
|
const start = Date.now();
|
|
53
53
|
const pid = await this.getPid();
|
|
54
|
-
this.#log('
|
|
54
|
+
this.#log.debug('Ensuring Killed', pid);
|
|
55
55
|
while (pid && (Date.now() - start) < gracePeriod) { // Ensure its done
|
|
56
56
|
if (!await this.isRunning()) {
|
|
57
57
|
return true;
|
|
@@ -59,10 +59,10 @@ export class ProcessHandle {
|
|
|
59
59
|
await timers.setTimeout(100);
|
|
60
60
|
}
|
|
61
61
|
try {
|
|
62
|
-
this.#log('
|
|
62
|
+
this.#log.debug('Force Killing', pid);
|
|
63
63
|
pid && process.kill(pid); // Force kill
|
|
64
64
|
} catch { }
|
|
65
|
-
this.#log('
|
|
65
|
+
this.#log.debug('Did Kill', this.#file, !!pid);
|
|
66
66
|
return pid !== undefined;
|
|
67
67
|
}
|
|
68
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 { Log } from '../log';
|
|
10
10
|
import { CommonUtil } from '../util';
|
|
11
11
|
|
|
12
|
-
const log =
|
|
12
|
+
const log = Log.scoped('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 {
|
|
9
|
+
import { Log } from '../log';
|
|
10
10
|
import { CommonUtil } from '../util';
|
|
11
11
|
import { CompilerClient } from './client';
|
|
12
12
|
import { ProcessHandle } from './process-handle';
|
|
13
13
|
|
|
14
|
-
const log =
|
|
14
|
+
const log = Log.scoped('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, Log.scoped('server.client'));
|
|
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,7 +119,7 @@ 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)) {
|
|
@@ -142,8 +142,6 @@ export class CompilerServer {
|
|
|
142
142
|
|
|
143
143
|
const [, action, subAction] = new URL(`${this.#url}${req.url}`).pathname.split('/');
|
|
144
144
|
|
|
145
|
-
log('debug', 'Receive request', { action, subAction });
|
|
146
|
-
|
|
147
145
|
let out: unknown;
|
|
148
146
|
let close = false;
|
|
149
147
|
switch (action) {
|
|
@@ -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.render(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
|
|
|
@@ -220,7 +218,7 @@ export class CompilerServer {
|
|
|
220
218
|
await this.#handle.compiler.kill();
|
|
221
219
|
}
|
|
222
220
|
|
|
223
|
-
log('
|
|
221
|
+
log.info('Closed down server');
|
|
224
222
|
}
|
|
225
223
|
|
|
226
224
|
/**
|
|
@@ -228,7 +226,7 @@ export class CompilerServer {
|
|
|
228
226
|
*/
|
|
229
227
|
async listen(): Promise<CompilerServer | undefined> {
|
|
230
228
|
const running = await this.#tryListen() === 'ok';
|
|
231
|
-
log(
|
|
229
|
+
log.info(running ? 'Starting server' : 'Server already running under a different process', this.#url);
|
|
232
230
|
return running ? this : undefined;
|
|
233
231
|
}
|
|
234
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 { Log } 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 Log.wrap(scope, 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 Log.wrap('precompile', 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 Log.wrap('manifest', () =>
|
|
185
185
|
ManifestUtil.buildManifest(ManifestUtil.getWorkspaceContext(ctx)));
|
|
186
186
|
|
|
187
|
-
await
|
|
187
|
+
await Log.wrap('transformers', 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 Log.wrap('delta', 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,16 +200,16 @@ export class CompilerSetup {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
if (changes) {
|
|
203
|
-
await
|
|
203
|
+
await Log.wrap('reset', 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 Log.wrap('manifest', 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) {
|
|
@@ -221,8 +221,8 @@ export class CompilerSetup {
|
|
|
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/types.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type CompilerMode = 'build' | 'watch';
|
|
|
3
3
|
export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'close';
|
|
4
4
|
export type CompilerChangeEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
|
|
5
5
|
export type CompilerLogLevel = 'info' | 'debug' | 'warn' | 'error';
|
|
6
|
-
export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time
|
|
6
|
+
export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time?: number, args?: unknown[], scope?: string };
|
|
7
7
|
export type CompilerProgressEvent = { idx: number, total: number, message: string, operation: 'compile', complete?: boolean };
|
|
8
8
|
export type CompilerStateEvent = { state: CompilerStateType, extra?: Record<string, unknown> };
|
|
9
9
|
|
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 { Log } from './log';
|
|
8
8
|
|
|
9
9
|
const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
|
|
10
10
|
|
|
@@ -58,7 +58,7 @@ 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 = Log.scoped('event-stream');
|
|
62
62
|
outer: while (!parent.aborted) {
|
|
63
63
|
const controller = new AbortController();
|
|
64
64
|
setMaxListeners(1000, controller.signal);
|
|
@@ -68,20 +68,20 @@ export class CommonUtil {
|
|
|
68
68
|
|
|
69
69
|
const comp = src(controller.signal);
|
|
70
70
|
|
|
71
|
-
log('
|
|
71
|
+
log.debug('Started event stream');
|
|
72
72
|
|
|
73
73
|
// Wait for all events, close at the end
|
|
74
74
|
for await (const ev of comp) {
|
|
75
75
|
yield ev;
|
|
76
76
|
if (shouldRestart(ev)) {
|
|
77
|
-
log('
|
|
77
|
+
log.debug('Restarting stream');
|
|
78
78
|
controller.abort(); // Ensure terminated of process
|
|
79
79
|
parent.removeEventListener('abort', kill);
|
|
80
80
|
continue outer;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
log('
|
|
84
|
+
log.debug('Finished event stream');
|
|
85
85
|
|
|
86
86
|
// Natural exit, we done
|
|
87
87
|
if (!controller.signal.aborted) { // Shutdown source if still running
|
package/src/log.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { CompilerLogLevel } from '../support/types';
|
|
2
|
-
import { EventUtil } from './event';
|
|
3
|
-
|
|
4
|
-
function log(level: CompilerLogLevel, message: string, ...args: unknown[]): void {
|
|
5
|
-
EventUtil.sendEvent('log', { level, message, args, time: Date.now(), scope: 'compiler-exec' });
|
|
6
|
-
if (!process.connected) {
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
|
-
console[level](message, ...args);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const Log = {
|
|
13
|
-
warn: log.bind(null, 'warn'),
|
|
14
|
-
debug: log.bind(null, 'debug'),
|
|
15
|
-
info: log.bind(null, 'info'),
|
|
16
|
-
error: log.bind(null, 'error'),
|
|
17
|
-
};
|