@travetto/compiler 8.0.0-alpha.2 → 8.0.0-alpha.21
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 +5 -5
- package/__index__.ts +4 -4
- package/bin/hook.js +6 -9
- package/bin/trvc-target.js +3 -1
- package/bin/trvc.js +3 -1
- package/package.json +4 -4
- package/src/common.ts +15 -9
- package/src/compiler.ts +61 -66
- package/src/event.ts +7 -5
- package/src/log.ts +57 -22
- package/src/queue.ts +4 -2
- package/src/server/client.ts +38 -24
- package/src/server/manager.ts +18 -12
- package/src/server/process-handle.ts +14 -10
- package/src/server/server.ts +49 -25
- package/src/state.ts +145 -74
- package/src/ts-proxy.ts +6 -3
- package/src/types.ts +22 -19
- package/src/util.ts +1 -34
- package/src/watch.ts +81 -69
- package/support/invoke.ts +65 -29
- package/tsconfig.trv.json +6 -15
package/README.md
CHANGED
|
@@ -17,15 +17,15 @@ This module expands upon the [Typescript](https://typescriptlang.org) compiler,
|
|
|
17
17
|
* Integration with the [Transformation](https://github.com/travetto/travetto/tree/main/module/transformer#readme "Functionality for AST transformations, with transformer registration, and general utils") module, allowing for rich, type-aware transformations
|
|
18
18
|
* Automatic conversion to either [Ecmascript Module](https://nodejs.org/api/esm.html) or [CommonJS](https://nodejs.org/api/modules.html) based on the [Package JSON](https://docs.npmjs.com/cli/v9/configuring-npm/package-json) `type` value
|
|
19
19
|
* Removal of type only imports which can break [Ecmascript Module](https://nodejs.org/api/esm.html)-style output
|
|
20
|
-
* Automatic addition of `.js` extension to imports to also support
|
|
20
|
+
* Automatic addition of `.js` extension to imports to also support [Ecmascript Module](https://nodejs.org/api/esm.html)-style output
|
|
21
21
|
|
|
22
22
|
Beyond the [Typescript](https://typescriptlang.org) compiler functionality, the module provides the primary entry point into the development process.
|
|
23
23
|
|
|
24
24
|
## CLI
|
|
25
|
-
The compiler cli, [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js) is the entry point for compilation-related operations. It has the ability to check for active builds, and ongoing watch operations to ensure only one process is building at a time.
|
|
25
|
+
The compiler cli, [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js) is the entry point for compilation-related operations. It has the ability to check for active builds, and ongoing watch operations to ensure only one process is building at a time. Within the framework, regardless of mono-repo or not, the compilation always targets the entire project. With the efficient caching behavior, this leads to generally a minimal overhead but allows for centralization of all operations.
|
|
26
26
|
|
|
27
27
|
The compiler cli supports the following operations:
|
|
28
|
-
* `start
|
|
28
|
+
* `start` - Run the compiler in watch mode
|
|
29
29
|
* `stop` - Stop the compiler if running
|
|
30
30
|
* `restart` - Restart the compiler in watch mode
|
|
31
31
|
* `build` - Ensure the project is built and upto date
|
|
@@ -36,7 +36,7 @@ The compiler cli supports the following operations:
|
|
|
36
36
|
* `manifest [output]` - Generate the project manifest
|
|
37
37
|
* `manifest:production [output]` - Generate the production project manifest
|
|
38
38
|
|
|
39
|
-
In addition to the normal output, the compiler supports an environment variable `TRV_BUILD` that supports the following values: `debug`, `info`, `warn` or `none`.
|
|
39
|
+
In addition to the normal output, the compiler supports an environment variable `TRV_BUILD` that supports the following values: `debug`, `info`, `warn` or `none`. This provides different level of logging during the build process which is helpful to diagnose any odd behaviors. When invoking an unknown command (e.g. `<other>` from above), the default level is `warn`. Otherwise the default logging level is `info`.
|
|
40
40
|
|
|
41
41
|
**Terminal: Sample trv output with debug logging**
|
|
42
42
|
```bash
|
|
@@ -48,7 +48,7 @@ $ TRV_BUILD=debug trvc build
|
|
|
48
48
|
2029-03-14T04:00:02.450Z info [compiler-exec ] Launching compiler
|
|
49
49
|
2029-03-14T04:00:02.762Z debug [server ] Compilation started
|
|
50
50
|
2029-03-14T04:00:02.947Z info [server ] State changed: init
|
|
51
|
-
2029-03-14T04:00:03.093Z debug [server ] Compiler loaded
|
|
51
|
+
2029-03-14T04:00:03.093Z debug [server ] Compiler loaded: 0 files changed
|
|
52
52
|
2029-03-14T04:00:04.003Z info [server ] State changed: compile-start
|
|
53
53
|
2029-03-14T04:00:04.495Z info [server ] State changed: compile-end
|
|
54
54
|
2029-03-14T04:00:05.066Z debug [server ] Compiler process shutdown
|
package/__index__.ts
CHANGED
|
@@ -2,11 +2,11 @@ export * from './src/common.ts';
|
|
|
2
2
|
export * from './src/compiler.ts';
|
|
3
3
|
export * from './src/event.ts';
|
|
4
4
|
export * from './src/log.ts';
|
|
5
|
+
export * from './src/server/client.ts';
|
|
6
|
+
export * from './src/server/manager.ts';
|
|
7
|
+
export * from './src/server/process-handle.ts';
|
|
8
|
+
export * from './src/server/server.ts';
|
|
5
9
|
export * from './src/state.ts';
|
|
6
10
|
export * from './src/types.ts';
|
|
7
11
|
export * from './src/util.ts';
|
|
8
12
|
export * from './src/watch.ts';
|
|
9
|
-
export * from './src/server/client.ts';
|
|
10
|
-
export * from './src/server/server.ts';
|
|
11
|
-
export * from './src/server/manager.ts';
|
|
12
|
-
export * from './src/server/process-handle.ts';
|
package/bin/hook.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
2
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
+
import module from 'node:module';
|
|
3
5
|
import { fileURLToPath } from 'node:url';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
process.setSourceMapsEnabled(true); // Ensure source map during compilation/development
|
|
8
|
-
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ''} --enable-source-maps`; // Ensure it passes to children
|
|
9
|
-
const ogEmitWarning = process.emitWarning;
|
|
10
|
-
Error.stackTraceLimit = 50;
|
|
7
|
+
const ogEmitWarning = process.emitWarning.bind(process);
|
|
11
8
|
|
|
12
9
|
module.registerHooks({
|
|
13
10
|
load: (url, context, nextLoad) => {
|
|
14
11
|
if (/[.]tsx?$/.test(url)) {
|
|
12
|
+
const source = readFileSync(fileURLToPath(url), 'utf8');
|
|
15
13
|
try {
|
|
16
|
-
process.emitWarning = () => {
|
|
17
|
-
const source = readFileSync(fileURLToPath(url), 'utf8');
|
|
14
|
+
process.emitWarning = () => {};
|
|
18
15
|
return { format: 'module', source: module.stripTypeScriptTypes(source), shortCircuit: true };
|
|
19
16
|
} finally {
|
|
20
17
|
process.emitWarning = ogEmitWarning;
|
package/bin/trvc-target.js
CHANGED
package/bin/trvc.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
6
6
|
"keywords": [
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@parcel/watcher": "^2.5.6",
|
|
34
|
-
"@travetto/manifest": "^8.0.0-alpha.
|
|
35
|
-
"@travetto/transformer": "^8.0.0-alpha.
|
|
34
|
+
"@travetto/manifest": "^8.0.0-alpha.11",
|
|
35
|
+
"@travetto/transformer": "^8.0.0-alpha.14"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
38
|
+
"@travetto/cli": "^8.0.0-alpha.28"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/cli": {
|
package/src/common.ts
CHANGED
|
@@ -6,11 +6,14 @@ import { type ManifestContext, path } from '@travetto/manifest';
|
|
|
6
6
|
import { Log } from './log.ts';
|
|
7
7
|
|
|
8
8
|
export class CommonUtil {
|
|
9
|
-
|
|
10
9
|
/**
|
|
11
10
|
* Restartable Event Stream
|
|
12
11
|
*/
|
|
13
|
-
static async *
|
|
12
|
+
static async *restartableEvents<T>(
|
|
13
|
+
input: (signal: AbortSignal) => AsyncIterable<T>,
|
|
14
|
+
parent: AbortSignal,
|
|
15
|
+
shouldRestart: (item: T) => boolean
|
|
16
|
+
): AsyncIterable<T> {
|
|
14
17
|
const log = Log.scoped('event-stream');
|
|
15
18
|
outer: while (!parent.aborted) {
|
|
16
19
|
const controller = new AbortController();
|
|
@@ -37,7 +40,8 @@ export class CommonUtil {
|
|
|
37
40
|
log.debug('Finished event stream');
|
|
38
41
|
|
|
39
42
|
// Natural exit, we done
|
|
40
|
-
if (!controller.signal.aborted) {
|
|
43
|
+
if (!controller.signal.aborted) {
|
|
44
|
+
// Shutdown source if still running
|
|
41
45
|
controller.abort();
|
|
42
46
|
}
|
|
43
47
|
return;
|
|
@@ -48,14 +52,14 @@ export class CommonUtil {
|
|
|
48
52
|
* Non-blocking timeout
|
|
49
53
|
*/
|
|
50
54
|
static nonBlockingTimeout(time: number): Promise<void> {
|
|
51
|
-
return timers.setTimeout(time, undefined, { ref: false }).catch(() => {
|
|
55
|
+
return timers.setTimeout(time, undefined, { ref: false }).catch(() => {});
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|
|
55
59
|
* Blocking timeout
|
|
56
60
|
*/
|
|
57
61
|
static blockingTimeout(time: number): Promise<void> {
|
|
58
|
-
return timers.setTimeout(time, undefined, { ref: true }).catch(() => {
|
|
62
|
+
return timers.setTimeout(time, undefined, { ref: true }).catch(() => {});
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
@@ -76,8 +80,10 @@ export class CommonUtil {
|
|
|
76
80
|
* Write to stdout with backpressure handling
|
|
77
81
|
*/
|
|
78
82
|
static async writeStdout(level: number, data: unknown): Promise<void> {
|
|
79
|
-
if (data === undefined) {
|
|
83
|
+
if (data === undefined) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
80
86
|
process.stdout.write(`${JSON.stringify(data, undefined, level)}\n`) ||
|
|
81
|
-
await new Promise(resolve => process.stdout.once('drain', resolve));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
87
|
+
(await new Promise(resolve => process.stdout.once('drain', resolve)));
|
|
88
|
+
}
|
|
89
|
+
}
|
package/src/compiler.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { setMaxListeners } from 'node:events';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
3
|
|
|
4
|
-
import { getManifestContext, ManifestDeltaUtil, ManifestIndex, ManifestUtil
|
|
4
|
+
import { type DeltaEvent, getManifestContext, ManifestDeltaUtil, ManifestIndex, ManifestUtil } from '@travetto/manifest';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { CompilerState } from './state.ts';
|
|
8
|
-
import { CompilerWatcher } from './watch.ts';
|
|
9
|
-
import { type CompileEmitEvent, type CompileEmitter, CompilerReset } from './types.ts';
|
|
6
|
+
import { CommonUtil } from './common.ts';
|
|
10
7
|
import { EventUtil } from './event.ts';
|
|
11
|
-
|
|
12
8
|
import { IpcLogger } from './log.ts';
|
|
13
|
-
import {
|
|
9
|
+
import { CompilerState } from './state.ts';
|
|
10
|
+
import { type CompileEmitEvent, CompilerReset } from './types.ts';
|
|
11
|
+
import { CompilerWatcher } from './watch.ts';
|
|
14
12
|
|
|
15
13
|
const log = new IpcLogger({ level: 'debug' });
|
|
16
14
|
|
|
@@ -18,7 +16,6 @@ const log = new IpcLogger({ level: 'debug' });
|
|
|
18
16
|
* Compilation support
|
|
19
17
|
*/
|
|
20
18
|
export class Compiler {
|
|
21
|
-
|
|
22
19
|
/**
|
|
23
20
|
* Run compiler as a main entry point
|
|
24
21
|
*/
|
|
@@ -32,8 +29,8 @@ export class Compiler {
|
|
|
32
29
|
|
|
33
30
|
#state: CompilerState;
|
|
34
31
|
#watch?: boolean;
|
|
35
|
-
#
|
|
36
|
-
#
|
|
32
|
+
#shutdownController: AbortController;
|
|
33
|
+
#shutdownSignal: AbortSignal;
|
|
37
34
|
#shuttingDown = false;
|
|
38
35
|
#deltaEvents: DeltaEvent[];
|
|
39
36
|
|
|
@@ -42,15 +39,13 @@ export class Compiler {
|
|
|
42
39
|
this.#watch = watch;
|
|
43
40
|
this.#deltaEvents = deltaEvents;
|
|
44
41
|
|
|
45
|
-
this.#
|
|
46
|
-
this.#
|
|
47
|
-
setMaxListeners(1000, this.#
|
|
48
|
-
process
|
|
49
|
-
.once('disconnect', () => this.#shutdown('manual'))
|
|
50
|
-
.on('message', event => (event === 'shutdown') && this.#shutdown('manual'));
|
|
42
|
+
this.#shutdownController = new AbortController();
|
|
43
|
+
this.#shutdownSignal = this.#shutdownController.signal;
|
|
44
|
+
setMaxListeners(1000, this.#shutdownSignal);
|
|
45
|
+
process.once('disconnect', () => this.#shutdown('manual')).on('message', event => event === 'shutdown' && this.#shutdown('manual'));
|
|
51
46
|
}
|
|
52
47
|
|
|
53
|
-
#shutdown(mode: 'error' | 'manual' | 'complete' | 'reset',
|
|
48
|
+
#shutdown(mode: 'error' | 'manual' | 'complete' | 'reset', errorMessage?: string): void {
|
|
54
49
|
if (this.#shuttingDown) {
|
|
55
50
|
return;
|
|
56
51
|
}
|
|
@@ -64,14 +59,13 @@ export class Compiler {
|
|
|
64
59
|
}
|
|
65
60
|
case 'error': {
|
|
66
61
|
process.exitCode = 1;
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
log.error('Shutting down due to failure', error.stack);
|
|
62
|
+
if (errorMessage) {
|
|
63
|
+
log.error('Shutting down due to failure', errorMessage);
|
|
70
64
|
}
|
|
71
65
|
break;
|
|
72
66
|
}
|
|
73
67
|
case 'reset': {
|
|
74
|
-
log.info('Reset due to',
|
|
68
|
+
log.info('Reset due to', errorMessage);
|
|
75
69
|
EventUtil.sendEvent('state', { state: 'reset' });
|
|
76
70
|
process.exitCode = 0;
|
|
77
71
|
break;
|
|
@@ -80,7 +74,7 @@ export class Compiler {
|
|
|
80
74
|
// No longer listen to disconnect
|
|
81
75
|
process.removeAllListeners('disconnect');
|
|
82
76
|
process.removeAllListeners('message');
|
|
83
|
-
this.#
|
|
77
|
+
this.#shutdownController.abort();
|
|
84
78
|
CommonUtil.nonBlockingTimeout(1000).then(() => process.exit()); // Allow upto 1s to shutdown gracefully
|
|
85
79
|
}
|
|
86
80
|
|
|
@@ -110,35 +104,27 @@ export class Compiler {
|
|
|
110
104
|
});
|
|
111
105
|
}
|
|
112
106
|
|
|
113
|
-
/**
|
|
114
|
-
* Compile in a single pass, only emitting dirty files
|
|
115
|
-
*/
|
|
116
|
-
getCompiler(): CompileEmitter {
|
|
117
|
-
return (sourceFile: string, needsNewProgram?: boolean) => this.#state.compileSourceFile(sourceFile, needsNewProgram);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
107
|
/**
|
|
121
108
|
* Emit all files as a stream
|
|
122
109
|
*/
|
|
123
|
-
async *
|
|
110
|
+
async *emit(files: string[]): AsyncIterable<CompileEmitEvent> {
|
|
124
111
|
let i = 0;
|
|
125
112
|
let lastSent = Date.now();
|
|
126
113
|
|
|
127
|
-
await emitter(files[0]); // Prime
|
|
128
|
-
|
|
129
114
|
for (const file of files) {
|
|
130
115
|
const start = Date.now();
|
|
131
|
-
const
|
|
116
|
+
const errors = await this.#state.compileSourceFile(file);
|
|
132
117
|
const duration = Date.now() - start;
|
|
133
118
|
const nodeModSeparator = 'node_modules/';
|
|
134
119
|
const nodeModIdx = file.lastIndexOf(nodeModSeparator);
|
|
135
120
|
const imp = nodeModIdx >= 0 ? file.substring(nodeModIdx + nodeModSeparator.length) : file;
|
|
136
|
-
yield { file: imp, i: i += 1,
|
|
137
|
-
if (
|
|
121
|
+
yield { file: imp, i: (i += 1), errors, total: files.length, duration };
|
|
122
|
+
if (Date.now() - lastSent > 50) {
|
|
123
|
+
// Limit to 1 every 50ms
|
|
138
124
|
lastSent = Date.now();
|
|
139
125
|
EventUtil.sendEvent('progress', { total: files.length, idx: i, message: imp, operation: 'compile' });
|
|
140
126
|
}
|
|
141
|
-
if (this.#
|
|
127
|
+
if (this.#shutdownSignal.aborted) {
|
|
142
128
|
break;
|
|
143
129
|
}
|
|
144
130
|
}
|
|
@@ -157,49 +143,52 @@ export class Compiler {
|
|
|
157
143
|
|
|
158
144
|
EventUtil.sendEvent('state', { state: 'init', extra: { processId: process.pid } });
|
|
159
145
|
|
|
160
|
-
const
|
|
161
|
-
let failure: Error | undefined;
|
|
146
|
+
const failures = new Map<string, number>();
|
|
162
147
|
|
|
163
|
-
log.debug(
|
|
148
|
+
log.debug(`Compiler loaded: ${this.#deltaEvents.length} files changed`);
|
|
164
149
|
|
|
165
150
|
EventUtil.sendEvent('state', { state: 'compile-start' });
|
|
166
151
|
|
|
167
152
|
const metrics: CompileEmitEvent[] = [];
|
|
168
153
|
const isCompilerChanged = this.#deltaEvents.some(event => this.#state.isCompilerFile(event.sourceFile));
|
|
169
|
-
const changedFiles =
|
|
170
|
-
|
|
171
|
-
if (this.#watch || changedFiles.length) {
|
|
172
|
-
await ManifestUtil.writeManifest(this.#state.manifestIndex.manifest);
|
|
173
|
-
await this.#state.initializeTypescript();
|
|
174
|
-
}
|
|
154
|
+
const changedFiles = isCompilerChanged ? this.#state.getAllFiles() : this.#deltaEvents.map(event => event.sourceFile);
|
|
175
155
|
|
|
176
156
|
if (changedFiles.length) {
|
|
177
|
-
for await (const event of this.emit(changedFiles
|
|
178
|
-
if (event.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
157
|
+
for await (const event of this.emit(changedFiles)) {
|
|
158
|
+
if (event.errors?.length) {
|
|
159
|
+
failures.set(event.file, event.errors.length);
|
|
160
|
+
for (const error of event.errors) {
|
|
161
|
+
log.error(`ERROR ${event.file}:${error}`);
|
|
162
|
+
}
|
|
163
|
+
// Touch file to ensure recompilation later
|
|
164
|
+
if (await fs.stat(event.file, { throwIfNoEntry: false })) {
|
|
165
|
+
await fs.utimes(event.file, new Date(), new Date());
|
|
166
|
+
}
|
|
182
167
|
}
|
|
183
168
|
metrics.push(event);
|
|
184
169
|
}
|
|
185
|
-
if (this.#
|
|
170
|
+
if (this.#shutdownSignal.aborted) {
|
|
186
171
|
log.debug('Compilation aborted');
|
|
187
|
-
} else if (
|
|
188
|
-
|
|
189
|
-
|
|
172
|
+
} else if (failures.size) {
|
|
173
|
+
const sortedFailures = [...failures.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
174
|
+
log.error(
|
|
175
|
+
'Compilation failed',
|
|
176
|
+
['', sortedFailures.flatMap(([file, count]) => `- ${file}: ${count} errors found`)].flat(3).join('\n')
|
|
177
|
+
);
|
|
190
178
|
} else {
|
|
191
179
|
log.debug('Compilation succeeded');
|
|
192
180
|
}
|
|
193
181
|
|
|
194
|
-
// Rebuild manifests
|
|
182
|
+
// Rebuild manifests
|
|
195
183
|
const manifest = await ManifestUtil.buildManifest(this.#state.manifestIndex.manifest);
|
|
196
184
|
await ManifestUtil.writeManifest(manifest);
|
|
197
185
|
await ManifestUtil.writeDependentManifests(manifest);
|
|
186
|
+
|
|
187
|
+
if (!this.#watch && failures.size) {
|
|
188
|
+
return this.#shutdown('error');
|
|
189
|
+
}
|
|
190
|
+
|
|
198
191
|
this.#state.manifestIndex.reinitForModule(this.#state.manifest.main.name); // Reload
|
|
199
|
-
} else if (this.#watch) {
|
|
200
|
-
// Prime compiler before complete
|
|
201
|
-
const resolved = this.#state.getArbitraryInputFile();
|
|
202
|
-
await emitter(resolved, true);
|
|
203
192
|
}
|
|
204
193
|
|
|
205
194
|
EventUtil.sendEvent('state', { state: 'compile-end' });
|
|
@@ -208,16 +197,22 @@ export class Compiler {
|
|
|
208
197
|
this.logStatistics(metrics);
|
|
209
198
|
}
|
|
210
199
|
|
|
211
|
-
if (this.#watch && !this.#
|
|
200
|
+
if (this.#watch && !this.#shutdownSignal.aborted) {
|
|
201
|
+
const resolved = this.#state.getArbitraryInputFile();
|
|
202
|
+
await this.#state.compileSourceFile(resolved);
|
|
203
|
+
|
|
212
204
|
log.info('Watch is ready');
|
|
213
205
|
|
|
214
206
|
EventUtil.sendEvent('state', { state: 'watch-start' });
|
|
215
207
|
try {
|
|
216
|
-
for await (const event of new CompilerWatcher(this.#state, this.#
|
|
208
|
+
for await (const event of new CompilerWatcher(this.#state, this.#shutdownSignal)) {
|
|
217
209
|
if (event.action !== 'delete') {
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
220
|
-
log.
|
|
210
|
+
const errors = await this.#state.compileSourceFile(event.entry.sourceFile, true);
|
|
211
|
+
if (errors?.length) {
|
|
212
|
+
log.error('Compilation failed', `${event.entry.sourceFile}: ${errors.length} errors found`);
|
|
213
|
+
for (const error of errors) {
|
|
214
|
+
log.error(`ERROR ${event.file}:${error}`);
|
|
215
|
+
}
|
|
221
216
|
} else {
|
|
222
217
|
log.info(`Compiled ${event.entry.sourceFile} on ${event.action}`);
|
|
223
218
|
}
|
|
@@ -242,7 +237,7 @@ export class Compiler {
|
|
|
242
237
|
EventUtil.sendEvent('state', { state: 'watch-end' });
|
|
243
238
|
} catch (error) {
|
|
244
239
|
if (error instanceof Error) {
|
|
245
|
-
this.#shutdown(error instanceof CompilerReset ? 'reset' : 'error', error);
|
|
240
|
+
this.#shutdown(error instanceof CompilerReset ? 'reset' : 'error', error.message);
|
|
246
241
|
}
|
|
247
242
|
}
|
|
248
243
|
}
|
package/src/event.ts
CHANGED
|
@@ -3,14 +3,16 @@ import type { CompilerEvent, CompilerEventPayload, CompilerEventType } from './t
|
|
|
3
3
|
const VALID_EVENT_TYPES = new Set<CompilerEventType>(['change', 'log', 'progress', 'state', 'all', 'file']);
|
|
4
4
|
|
|
5
5
|
export class EventUtil {
|
|
6
|
-
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
8
6
|
static isComplilerEventType = (value: string): value is CompilerEventType => VALID_EVENT_TYPES.has(value as CompilerEventType);
|
|
9
7
|
|
|
10
8
|
static isCompilerEvent = (value: unknown): value is CompilerEvent =>
|
|
11
|
-
typeof value === 'object' &&
|
|
9
|
+
typeof value === 'object' &&
|
|
10
|
+
value !== null &&
|
|
11
|
+
'type' in value &&
|
|
12
|
+
typeof value.type === 'string' &&
|
|
13
|
+
EventUtil.isComplilerEventType(value.type);
|
|
12
14
|
|
|
13
15
|
static sendEvent<K extends CompilerEventType, T extends CompilerEventPayload<K>>(type: K, payload: T): void {
|
|
14
|
-
process.connected && process.send!({ type, payload }, undefined, undefined, () => {
|
|
16
|
+
process.connected && process.send!({ type, payload }, undefined, undefined, () => {});
|
|
15
17
|
}
|
|
16
|
-
}
|
|
18
|
+
}
|
package/src/log.ts
CHANGED
|
@@ -15,7 +15,6 @@ export type LogShape = Record<'info' | 'debug' | 'warn' | 'error', (message: str
|
|
|
15
15
|
const ESC = '\x1b[';
|
|
16
16
|
|
|
17
17
|
export class Logger implements LogConfig, LogShape {
|
|
18
|
-
|
|
19
18
|
static #linePartial: boolean | undefined;
|
|
20
19
|
|
|
21
20
|
/** Rewrite text line, tracking cleanup as necessary */
|
|
@@ -23,7 +22,8 @@ export class Logger implements LogConfig, LogShape {
|
|
|
23
22
|
if ((!text && !this.#linePartial) || !process.stdout.isTTY) {
|
|
24
23
|
return;
|
|
25
24
|
}
|
|
26
|
-
if (this.#linePartial === undefined) {
|
|
25
|
+
if (this.#linePartial === undefined) {
|
|
26
|
+
// First time
|
|
27
27
|
process.stdout.write(`${ESC}?25l`); // Hide cursor
|
|
28
28
|
process.on('exit', () => this.reset());
|
|
29
29
|
}
|
|
@@ -35,7 +35,9 @@ export class Logger implements LogConfig, LogShape {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
static reset(): void {
|
|
38
|
+
static reset(): void {
|
|
39
|
+
process.stdout.write(`${ESC}!p${ESC}?25h`);
|
|
40
|
+
}
|
|
39
41
|
|
|
40
42
|
level?: CompilerLogLevel | 'none';
|
|
41
43
|
root: string = process.cwd();
|
|
@@ -47,28 +49,38 @@ export class Logger implements LogConfig, LogShape {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
valid(event: CompilerLogEvent): boolean {
|
|
50
|
-
return LEVEL_TO_PRIORITY[this.level ?? this.parent?.level
|
|
52
|
+
return LEVEL_TO_PRIORITY[this.level ?? this.parent?.level ?? 'none'] <= LEVEL_TO_PRIORITY[event.level];
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/** Log event with filtering by level */
|
|
54
56
|
render(event: CompilerLogEvent): void {
|
|
55
|
-
if (!this.valid(event)) {
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
if (!this.valid(event)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const params = [event.message, ...(event.args ?? [])].map(arg =>
|
|
61
|
+
typeof arg === 'string' ? arg.replaceAll(this.root ?? this.parent?.root, '.') : arg
|
|
62
|
+
);
|
|
58
63
|
|
|
59
64
|
if (event.scope ?? this.scope) {
|
|
60
65
|
params.unshift(`[${(event.scope ?? this.scope!).padEnd(SCOPE_MAX, ' ')}]`);
|
|
61
66
|
}
|
|
62
67
|
params.unshift(new Date().toISOString(), `${event.level.padEnd(5)}`);
|
|
63
68
|
Logger.rewriteLine(''); // Clear out progress line, if active
|
|
64
|
-
// eslint-disable-next-line no-console
|
|
65
69
|
console[event.level]!(...params);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
info(message: string, ...args: unknown[]): void {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
info(message: string, ...args: unknown[]): void {
|
|
73
|
+
this.render({ level: 'info', message, args });
|
|
74
|
+
}
|
|
75
|
+
debug(message: string, ...args: unknown[]): void {
|
|
76
|
+
this.render({ level: 'debug', message, args });
|
|
77
|
+
}
|
|
78
|
+
warn(message: string, ...args: unknown[]): void {
|
|
79
|
+
this.render({ level: 'warn', message, args });
|
|
80
|
+
}
|
|
81
|
+
error(message: string, ...args: unknown[]): void {
|
|
82
|
+
this.render({ level: 'error', message, args });
|
|
83
|
+
}
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
class $RootLogger extends Logger {
|
|
@@ -86,9 +98,17 @@ class $RootLogger extends Logger {
|
|
|
86
98
|
initLevel(defaultLevel: CompilerLogLevel | 'none'): void {
|
|
87
99
|
const value = process.env.TRV_QUIET !== 'true' ? process.env.TRV_BUILD : 'none';
|
|
88
100
|
switch (value) {
|
|
89
|
-
case 'debug':
|
|
90
|
-
case
|
|
91
|
-
case '
|
|
101
|
+
case 'debug':
|
|
102
|
+
case 'warn':
|
|
103
|
+
case 'error':
|
|
104
|
+
case 'info':
|
|
105
|
+
this.level = value;
|
|
106
|
+
break;
|
|
107
|
+
case undefined:
|
|
108
|
+
this.level = defaultLevel;
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
this.level = 'none';
|
|
92
112
|
}
|
|
93
113
|
}
|
|
94
114
|
|
|
@@ -100,21 +120,34 @@ class $RootLogger extends Logger {
|
|
|
100
120
|
/** Scope and provide a callback pattern for access to a logger */
|
|
101
121
|
wrap<T = unknown>(scope: string, operation: (log: Logger) => Promise<T>, basic = true): Promise<T> {
|
|
102
122
|
const logger = this.scoped(scope);
|
|
103
|
-
|
|
123
|
+
if (basic) {
|
|
124
|
+
logger.debug('Started');
|
|
125
|
+
return operation(logger).finally(() => logger.debug('Completed'));
|
|
126
|
+
} else {
|
|
127
|
+
return operation(logger);
|
|
128
|
+
}
|
|
104
129
|
}
|
|
105
130
|
|
|
106
131
|
/** Write progress event, if active */
|
|
107
132
|
onProgressEvent(event: CompilerProgressEvent): void | Promise<void> {
|
|
108
|
-
if (!
|
|
109
|
-
|
|
110
|
-
|
|
133
|
+
if (!this.logProgress) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const progress = Math.trunc((event.idx * 100) / event.total);
|
|
137
|
+
const text = event.complete
|
|
138
|
+
? ''
|
|
139
|
+
: `Compiling [${'#'.repeat(Math.trunc(progress / 10)).padEnd(10, ' ')}] [${event.idx}/${event.total}] ${event.message}`;
|
|
111
140
|
return Logger.rewriteLine(text);
|
|
112
141
|
}
|
|
113
142
|
|
|
114
143
|
/** Write all progress events if active */
|
|
115
144
|
async consumeProgressEvents(input: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
|
|
116
|
-
if (!
|
|
117
|
-
|
|
145
|
+
if (!this.logProgress) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
for await (const event of input()) {
|
|
149
|
+
this.onProgressEvent(event);
|
|
150
|
+
}
|
|
118
151
|
Logger.reset();
|
|
119
152
|
}
|
|
120
153
|
}
|
|
@@ -123,7 +156,9 @@ export const Log = new $RootLogger();
|
|
|
123
156
|
|
|
124
157
|
export class IpcLogger extends Logger {
|
|
125
158
|
render(event: CompilerLogEvent): void {
|
|
126
|
-
if (!this.valid(event)) {
|
|
159
|
+
if (!this.valid(event)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
127
162
|
if (process.connected && process.send) {
|
|
128
163
|
process.send({ type: 'log', payload: event });
|
|
129
164
|
}
|
package/src/queue.ts
CHANGED
|
@@ -10,7 +10,9 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
[Symbol.asyncIterator](): AsyncIterator<X> {
|
|
13
|
+
[Symbol.asyncIterator](): AsyncIterator<X> {
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
async next(): Promise<IteratorResult<X>> {
|
|
16
18
|
while (!this.#done && !this.#queue.length) {
|
|
@@ -35,4 +37,4 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
35
37
|
this.#done = true;
|
|
36
38
|
this.#ready.resolve();
|
|
37
39
|
}
|
|
38
|
-
}
|
|
40
|
+
}
|