@travetto/compiler 7.0.2 → 7.0.4
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 +17 -30
- package/__index__.ts +8 -1
- package/bin/hook.js +23 -0
- package/bin/trvc-target.js +4 -0
- package/bin/trvc.js +4 -40
- package/package.json +5 -5
- package/{support/util.ts → src/common.ts} +11 -36
- package/src/compiler.ts +27 -18
- package/src/event.ts +10 -1
- package/{support → src}/log.ts +1 -1
- package/{support → src}/server/client.ts +15 -4
- package/src/server/manager.ts +90 -0
- package/{support → src}/server/process-handle.ts +2 -2
- package/{support → src}/server/server.ts +11 -13
- package/src/state.ts +60 -30
- package/src/ts-proxy.ts +11 -0
- package/src/types.ts +34 -2
- package/src/util.ts +18 -8
- package/src/watch.ts +56 -74
- package/support/invoke.ts +96 -0
- package/bin/entry.common.js +0 -88
- package/bin/gen.context.js +0 -72
- package/support/entry.compiler.ts +0 -3
- package/support/entry.main.ts +0 -118
- package/support/server/runner.ts +0 -80
- package/support/setup.ts +0 -246
- package/support/ts-util.ts +0 -42
- package/support/types.ts +0 -37
- /package/{support → src}/queue.ts +0 -0
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ This module expands upon the [Typescript](https://typescriptlang.org) compiler,
|
|
|
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
|
|
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
28
|
* `start|watch` - Run the compiler in watch mode
|
|
@@ -42,32 +42,23 @@ In addition to the normal output, the compiler supports an environment variable
|
|
|
42
42
|
$ TRV_BUILD=debug trvc build
|
|
43
43
|
|
|
44
44
|
2029-03-14T04:00:00.618Z info [server ] Starting server http://127.0.0.1:25539
|
|
45
|
-
2029-03-14T04:00:00.837Z debug [
|
|
45
|
+
2029-03-14T04:00:00.837Z debug [compiler-exec ] Start Server
|
|
46
46
|
2029-03-14T04:00:01.510Z debug [event-stream ] Started event stream
|
|
47
|
-
2029-03-14T04:00:02.450Z
|
|
48
|
-
2029-03-14T04:00:02.762Z debug [
|
|
49
|
-
2029-03-14T04:00:02.947Z
|
|
50
|
-
2029-03-14T04:00:03.093Z debug [
|
|
51
|
-
2029-03-14T04:00:04.003Z
|
|
52
|
-
2029-03-14T04:00:04.495Z
|
|
53
|
-
2029-03-14T04:00:05.066Z debug [
|
|
54
|
-
2029-03-14T04:00:05.307Z debug [
|
|
55
|
-
2029-03-14T04:00:05.952Z debug [
|
|
56
|
-
2029-03-14T04:00:06.859Z debug [
|
|
57
|
-
2029-03-14T04:00:07.720Z
|
|
58
|
-
2029-03-14T04:00:08.179Z debug [
|
|
59
|
-
2029-03-14T04:00:08.588Z
|
|
60
|
-
2029-03-14T04:00:09.493Z debug [
|
|
61
|
-
2029-03-14T04:00:10.395Z debug [
|
|
62
|
-
2029-03-14T04:00:10.407Z debug [manifest ] Completed
|
|
63
|
-
2029-03-14T04:00:10.799Z info [server ] State changed: compile-end
|
|
64
|
-
2029-03-14T04:00:11.013Z debug [compiler-exec ] Skipped
|
|
65
|
-
2029-03-14T04:00:11.827Z debug [event-stream ] Finished event stream
|
|
66
|
-
2029-03-14T04:00:11.894Z info [server ] Closing down server
|
|
67
|
-
2029-03-14T04:00:12.133Z debug [server ] Server close event
|
|
68
|
-
2029-03-14T04:00:13.123Z info [server ] Closed down server
|
|
69
|
-
2029-03-14T04:00:14.014Z debug [server ] Finished processing events
|
|
70
|
-
2029-03-14T04:00:14.924Z debug [main ] End Server
|
|
47
|
+
2029-03-14T04:00:02.450Z info [compiler-exec ] Launching compiler
|
|
48
|
+
2029-03-14T04:00:02.762Z debug [server ] Compilation started
|
|
49
|
+
2029-03-14T04:00:02.947Z info [server ] State changed: init
|
|
50
|
+
2029-03-14T04:00:03.093Z debug [server ] Compiler loaded
|
|
51
|
+
2029-03-14T04:00:04.003Z info [server ] State changed: compile-start
|
|
52
|
+
2029-03-14T04:00:04.495Z info [server ] State changed: compile-end
|
|
53
|
+
2029-03-14T04:00:05.066Z debug [server ] Compiler process shutdown
|
|
54
|
+
2029-03-14T04:00:05.307Z debug [compiler-exec ] Finished
|
|
55
|
+
2029-03-14T04:00:05.952Z debug [event-stream ] Finished event stream
|
|
56
|
+
2029-03-14T04:00:06.859Z debug [compiler-exec ] Shutting down process
|
|
57
|
+
2029-03-14T04:00:07.720Z info [server ] Closing down server
|
|
58
|
+
2029-03-14T04:00:08.179Z debug [server ] Server close event
|
|
59
|
+
2029-03-14T04:00:08.588Z info [server ] Closed down server
|
|
60
|
+
2029-03-14T04:00:09.493Z debug [server ] Finished processing events
|
|
61
|
+
2029-03-14T04:00:10.395Z debug [compiler-exec ] End Server
|
|
71
62
|
```
|
|
72
63
|
|
|
73
64
|
**Terminal: Sample trv output with default log level**
|
|
@@ -77,7 +68,6 @@ $ trvc build
|
|
|
77
68
|
|
|
78
69
|
## Compilation Architecture
|
|
79
70
|
The compiler will move through the following phases on a given compilation execution:
|
|
80
|
-
* `Bootstrapping` - Initial compilation of [Compiler](https://github.com/travetto/travetto/tree/main/module/compiler#readme "The compiler infrastructure for the Travetto framework")'s `support/*.ts` files
|
|
81
71
|
* `Compiler Server` - Provides a simple HTTP interface to watching compiler file and state changes, and synchronizing multiple processes
|
|
82
72
|
* `Build Compiler` - Leverages [Typescript](https://typescriptlang.org) to build files needed to execute compiler
|
|
83
73
|
* `Build Manifest` - Produces the manifest for the given execution
|
|
@@ -86,6 +76,3 @@ The compiler will move through the following phases on a given compilation execu
|
|
|
86
76
|
* `Clear all output if needed` - When the compiler source or transformers change, invalidate the entire output
|
|
87
77
|
* `Persist Manifest(s)` - Ensure the manifest is available for the compiler to leverage. Multiple will be written if in a monorepo
|
|
88
78
|
* `Invoke Compiler` - Run [Typescript](https://typescriptlang.org) compiler with the aforementioned enhancements
|
|
89
|
-
|
|
90
|
-
### Bootstrapping
|
|
91
|
-
Given that the framework is distributed as [Typescript](https://typescriptlang.org) only files, there is a bootstrapping problem that needs to be mitigated. The [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L2) entrypoint, along with a small context utility in [Manifest](https://github.com/travetto/travetto/tree/main/module/manifest#readme "Support for project indexing, manifesting, along with file watching") are the only [Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) files needed to run the project. The [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L2) entry point will compile `@travetto/compiler/support/*` files as the set that is used at startup. These files are also accessible to the compiler as they get re-compiled after the fact.
|
package/__index__.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
export * from './src/common.ts';
|
|
1
2
|
export * from './src/compiler.ts';
|
|
3
|
+
export * from './src/event.ts';
|
|
4
|
+
export * from './src/log.ts';
|
|
2
5
|
export * from './src/state.ts';
|
|
6
|
+
export * from './src/types.ts';
|
|
3
7
|
export * from './src/util.ts';
|
|
4
8
|
export * from './src/watch.ts';
|
|
5
|
-
export * from './src/
|
|
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
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { registerHooks, stripTypeScriptTypes } from 'node:module';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
process.setSourceMapsEnabled(true); // Ensure source map during compilation/development
|
|
6
|
+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ''} --enable-source-maps`; // Ensure it passes to children
|
|
7
|
+
const ogEmitWarning = process.emitWarning;
|
|
8
|
+
|
|
9
|
+
registerHooks({
|
|
10
|
+
load: (url, context, nextLoad) => {
|
|
11
|
+
if (/[.]tsx?$/.test(url)) {
|
|
12
|
+
try {
|
|
13
|
+
process.emitWarning = () => { }; // Suppress ts-node experimental warnings
|
|
14
|
+
const source = readFileSync(fileURLToPath(url), 'utf8');
|
|
15
|
+
return { format: 'module', source: stripTypeScriptTypes(source), shortCircuit: true };
|
|
16
|
+
} finally {
|
|
17
|
+
process.emitWarning = ogEmitWarning;
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
return nextLoad(url, context);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
package/bin/trvc.js
CHANGED
|
@@ -1,41 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Available Commands:
|
|
8
|
-
* start|watch - Run the compiler in watch mode
|
|
9
|
-
* stop - Stop the compiler if running
|
|
10
|
-
* restart - Restart the compiler in watch mode
|
|
11
|
-
* build - Ensure the project is built and upto date
|
|
12
|
-
* clean - Clean out the output and compiler caches
|
|
13
|
-
* info - Retrieve the compiler information, if running
|
|
14
|
-
* event <log|progress|state> - Watch events in realtime as newline delimited JSON
|
|
15
|
-
* exec <file> [...args] - Allow for compiling and executing an entrypoint file
|
|
16
|
-
* manifest --prod [output] - Generate the project manifest
|
|
17
|
-
`;
|
|
18
|
-
|
|
19
|
-
const toJson = (/** @type {number} */ depth) => value => process.stdout.write(`${JSON.stringify(value, undefined, depth)}\n`) ||
|
|
20
|
-
new Promise(resolve => process.stdout.once('drain', resolve));
|
|
21
|
-
|
|
22
|
-
load(operations => {
|
|
23
|
-
const [operation, ...all] = process.argv.slice(2);
|
|
24
|
-
const args = all.filter(arg => !arg.startsWith('-'));
|
|
25
|
-
|
|
26
|
-
switch (operation) {
|
|
27
|
-
case undefined:
|
|
28
|
-
case 'help': return console.log(help);
|
|
29
|
-
case 'info': return operations.info().then(toJson(2));
|
|
30
|
-
case 'event': return operations.events(args[0], toJson(0));
|
|
31
|
-
case 'manifest': return operations.manifest(args[0], all.some(arg => arg === '--prod'));
|
|
32
|
-
case 'exec': return operations.exec(args[0], all.slice(1));
|
|
33
|
-
case 'build': return operations.build();
|
|
34
|
-
case 'clean': return operations.clean();
|
|
35
|
-
case 'start':
|
|
36
|
-
case 'watch': return operations.watch();
|
|
37
|
-
case 'stop': return operations.stop();
|
|
38
|
-
case 'restart': return operations.restart();
|
|
39
|
-
default: console.error(`\nUnknown trvc operation: ${operation}\n${help}`);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
2
|
+
// @ts-check
|
|
3
|
+
import './hook.js';
|
|
4
|
+
const { invoke } = await import('@travetto/compiler/support/invoke.ts');
|
|
5
|
+
invoke();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
6
6
|
"keywords": [
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"directory": "module/compiler"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@parcel/watcher": "^2.5.
|
|
34
|
-
"@travetto/manifest": "^7.0.
|
|
35
|
-
"@travetto/transformer": "^7.0.
|
|
33
|
+
"@parcel/watcher": "^2.5.4",
|
|
34
|
+
"@travetto/manifest": "^7.0.3",
|
|
35
|
+
"@travetto/transformer": "^7.0.4"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@travetto/cli": "^7.0.
|
|
38
|
+
"@travetto/cli": "^7.0.6"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/cli": {
|
|
@@ -1,30 +1,11 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { setMaxListeners } from 'node:events';
|
|
3
2
|
import timers from 'node:timers/promises';
|
|
4
|
-
import posix from 'node:path/posix';
|
|
5
|
-
import native from 'node:path';
|
|
6
3
|
|
|
7
|
-
import type
|
|
4
|
+
import { type ManifestContext, path } from '@travetto/manifest';
|
|
8
5
|
|
|
9
6
|
import { Log } from './log.ts';
|
|
10
7
|
|
|
11
|
-
const toPosix = (file: string): string => file.replaceAll('\\', '/');
|
|
12
|
-
|
|
13
8
|
export class CommonUtil {
|
|
14
|
-
/**
|
|
15
|
-
* Determine file type
|
|
16
|
-
*/
|
|
17
|
-
static getFileType(file: string): 'ts' | 'js' | 'package-json' | 'typings' | undefined {
|
|
18
|
-
return file.endsWith('package.json') ? 'package-json' :
|
|
19
|
-
(file.endsWith('.js') ? 'js' :
|
|
20
|
-
(file.endsWith('.d.ts') ? 'typings' : (/[.][cm]?tsx?$/.test(file) ? 'ts' : undefined)));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Write text file, and ensure folder exists
|
|
25
|
-
*/
|
|
26
|
-
static writeTextFile = (file: string, content: string): Promise<void> =>
|
|
27
|
-
fs.mkdir(native.dirname(file), { recursive: true }).then(() => fs.writeFile(file, content, 'utf8'));
|
|
28
9
|
|
|
29
10
|
/**
|
|
30
11
|
* Restartable Event Stream
|
|
@@ -63,20 +44,6 @@ export class CommonUtil {
|
|
|
63
44
|
}
|
|
64
45
|
}
|
|
65
46
|
|
|
66
|
-
/**
|
|
67
|
-
* Naive hashing
|
|
68
|
-
*/
|
|
69
|
-
static naiveHash(text: string): number {
|
|
70
|
-
let hash = 5381;
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < text.length; i++) {
|
|
73
|
-
// eslint-disable-next-line no-bitwise
|
|
74
|
-
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return Math.abs(hash);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
47
|
/**
|
|
81
48
|
* Non-blocking timeout
|
|
82
49
|
*/
|
|
@@ -102,7 +69,15 @@ export class CommonUtil {
|
|
|
102
69
|
* Resolve path for workspace, ensuring posix compliant slashes
|
|
103
70
|
*/
|
|
104
71
|
static resolveWorkspace(ctx: ManifestContext, ...args: string[]): string {
|
|
105
|
-
|
|
106
|
-
return process.platform === 'win32' ? toPosix(native.resolve(...all)) : posix.resolve(...all);
|
|
72
|
+
return path.resolve(ctx.workspace.path, ...args);
|
|
107
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Write to stdout with backpressure handling
|
|
77
|
+
*/
|
|
78
|
+
static async writeStdout(level: number, data: unknown): Promise<void> {
|
|
79
|
+
if (data === undefined) { return; }
|
|
80
|
+
process.stdout.write(`${JSON.stringify(data, undefined, level)}\n`) ||
|
|
81
|
+
await new Promise(resolve => process.stdout.once('drain', resolve));
|
|
82
|
+
};
|
|
108
83
|
}
|
package/src/compiler.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import { setMaxListeners } from 'node:events';
|
|
3
3
|
|
|
4
|
-
import { ManifestIndex,
|
|
4
|
+
import { getManifestContext, ManifestDeltaUtil, ManifestIndex, ManifestUtil, type DeltaEvent } from '@travetto/manifest';
|
|
5
5
|
|
|
6
6
|
import { CompilerUtil } from './util.ts';
|
|
7
7
|
import { CompilerState } from './state.ts';
|
|
8
8
|
import { CompilerWatcher } from './watch.ts';
|
|
9
|
-
import { CompileEmitEvent, CompileEmitter, CompilerReset } from './types.ts';
|
|
9
|
+
import { type CompileEmitEvent, type CompileEmitter, CompilerReset } from './types.ts';
|
|
10
10
|
import { EventUtil } from './event.ts';
|
|
11
11
|
|
|
12
|
-
import { IpcLogger } from '
|
|
13
|
-
import { CommonUtil } from '
|
|
12
|
+
import { IpcLogger } from './log.ts';
|
|
13
|
+
import { CommonUtil } from './common.ts';
|
|
14
14
|
|
|
15
15
|
const log = new IpcLogger({ level: 'debug' });
|
|
16
16
|
|
|
@@ -23,28 +23,24 @@ export class Compiler {
|
|
|
23
23
|
* Run compiler as a main entry point
|
|
24
24
|
*/
|
|
25
25
|
static async main(): Promise<void> {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
log.debug('Running compiler with dirty file', dirtyFiles);
|
|
32
|
-
await new Compiler(state, dirtyFiles, watch === 'true').run();
|
|
26
|
+
const ctx = ManifestUtil.getWorkspaceContext(getManifestContext());
|
|
27
|
+
const manifest = await ManifestUtil.buildManifest(ctx);
|
|
28
|
+
const delta = await ManifestDeltaUtil.produceDelta(manifest);
|
|
29
|
+
const state = await CompilerState.get(new ManifestIndex(manifest));
|
|
30
|
+
await new Compiler(state, delta, process.env.TRV_COMPILER_WATCH === 'true').run();
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
#state: CompilerState;
|
|
36
|
-
#dirtyFiles: string[];
|
|
37
34
|
#watch?: boolean;
|
|
38
35
|
#controller: AbortController;
|
|
39
36
|
#signal: AbortSignal;
|
|
40
37
|
#shuttingDown = false;
|
|
38
|
+
#deltaEvents: DeltaEvent[];
|
|
41
39
|
|
|
42
|
-
constructor(state: CompilerState,
|
|
40
|
+
constructor(state: CompilerState, deltaEvents: DeltaEvent[], watch?: boolean) {
|
|
43
41
|
this.#state = state;
|
|
44
|
-
this.#dirtyFiles = dirtyFiles[0] === '*' ?
|
|
45
|
-
this.#state.getAllFiles() :
|
|
46
|
-
dirtyFiles.map(file => this.#state.getBySource(file)!.sourceFile);
|
|
47
42
|
this.#watch = watch;
|
|
43
|
+
this.#deltaEvents = deltaEvents;
|
|
48
44
|
|
|
49
45
|
this.#controller = new AbortController();
|
|
50
46
|
this.#signal = this.#controller.signal;
|
|
@@ -169,9 +165,16 @@ export class Compiler {
|
|
|
169
165
|
EventUtil.sendEvent('state', { state: 'compile-start' });
|
|
170
166
|
|
|
171
167
|
const metrics: CompileEmitEvent[] = [];
|
|
168
|
+
const isCompilerChanged = this.#deltaEvents.some(event => this.#state.isCompilerFile(event.sourceFile));
|
|
169
|
+
const changedFiles = (isCompilerChanged ? this.#state.getAllFiles() : this.#deltaEvents.map(event => event.sourceFile));
|
|
172
170
|
|
|
173
|
-
if (this.#
|
|
174
|
-
|
|
171
|
+
if (this.#watch || changedFiles.length) {
|
|
172
|
+
await ManifestUtil.writeManifest(this.#state.manifestIndex.manifest);
|
|
173
|
+
await this.#state.initializeTypescript();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (changedFiles.length) {
|
|
177
|
+
for await (const event of this.emit(changedFiles, emitter)) {
|
|
175
178
|
if (event.error) {
|
|
176
179
|
const compileError = CompilerUtil.buildTranspileError(event.file, event.error);
|
|
177
180
|
failure ??= compileError;
|
|
@@ -187,6 +190,12 @@ export class Compiler {
|
|
|
187
190
|
} else {
|
|
188
191
|
log.debug('Compilation succeeded');
|
|
189
192
|
}
|
|
193
|
+
|
|
194
|
+
// Rebuild manifests if dirty
|
|
195
|
+
const manifest = await ManifestUtil.buildManifest(this.#state.manifestIndex.manifest);
|
|
196
|
+
await ManifestUtil.writeManifest(manifest);
|
|
197
|
+
await ManifestUtil.writeDependentManifests(manifest);
|
|
198
|
+
this.#state.manifestIndex.reinitForModule(this.#state.manifest.main.name); // Reload
|
|
190
199
|
} else if (this.#watch) {
|
|
191
200
|
// Prime compiler before complete
|
|
192
201
|
const resolved = this.#state.getArbitraryInputFile();
|
package/src/event.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { CompilerEventPayload, CompilerEventType } from '
|
|
1
|
+
import type { CompilerEvent, CompilerEventPayload, CompilerEventType } from './types.ts';
|
|
2
|
+
|
|
3
|
+
const VALID_EVENT_TYPES = new Set<CompilerEventType>(['change', 'log', 'progress', 'state', 'all', 'file']);
|
|
2
4
|
|
|
3
5
|
export class EventUtil {
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
8
|
+
static isComplilerEventType = (value: string): value is CompilerEventType => VALID_EVENT_TYPES.has(value as CompilerEventType);
|
|
9
|
+
|
|
10
|
+
static isCompilerEvent = (value: unknown): value is CompilerEvent =>
|
|
11
|
+
typeof value === 'object' && value !== null && 'type' in value && typeof value.type === 'string' && EventUtil.isComplilerEventType(value.type);
|
|
12
|
+
|
|
4
13
|
static sendEvent<K extends CompilerEventType, T extends CompilerEventPayload<K>>(type: K, payload: T): void {
|
|
5
14
|
process.connected && process.send!({ type, payload }, undefined, undefined, () => { });
|
|
6
15
|
}
|
package/{support → src}/log.ts
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types.ts';
|
|
1
|
+
import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types.ts';
|
|
2
2
|
|
|
3
3
|
const LEVEL_TO_PRIORITY: Record<CompilerLogLevel | 'none', number> = { debug: 1, info: 2, warn: 3, error: 4, none: 5 };
|
|
4
4
|
const SCOPE_MAX = 15;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import rl from 'node:readline/promises';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
3
4
|
import http, { Agent } from 'node:http';
|
|
4
5
|
|
|
5
|
-
import { ManifestContext } from '@travetto/manifest';
|
|
6
|
+
import type { ManifestContext } from '@travetto/manifest';
|
|
6
7
|
|
|
7
8
|
import type { CompilerEventPayload, CompilerEventType, CompilerServerInfo, CompilerStateType } from '../types.ts';
|
|
8
9
|
import type { LogShape } from '../log.ts';
|
|
9
|
-
import { CommonUtil } from '../
|
|
10
|
+
import { CommonUtil } from '../common.ts';
|
|
10
11
|
import { ProcessHandle } from './process-handle.ts';
|
|
11
12
|
|
|
12
13
|
type FetchEventsConfig<T> = {
|
|
@@ -29,8 +30,10 @@ export class CompilerClient {
|
|
|
29
30
|
#url: string;
|
|
30
31
|
#log: LogShape;
|
|
31
32
|
#handle: Record<'compiler' | 'server', ProcessHandle>;
|
|
33
|
+
#ctx: ManifestContext;
|
|
32
34
|
|
|
33
35
|
constructor(ctx: ManifestContext, log: LogShape) {
|
|
36
|
+
this.#ctx = ctx;
|
|
34
37
|
this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
|
|
35
38
|
this.#log = log;
|
|
36
39
|
this.#handle = { compiler: new ProcessHandle(ctx, 'compiler'), server: new ProcessHandle(ctx, 'server') };
|
|
@@ -71,8 +74,16 @@ export class CompilerClient {
|
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/** Clean the server */
|
|
74
|
-
clean(): Promise<boolean> {
|
|
75
|
-
|
|
77
|
+
async clean(forceOnFailure?: boolean): Promise<boolean> {
|
|
78
|
+
const result = await this.#fetch('/clean', { timeout: 300 }).then(response => response.ok, () => false);
|
|
79
|
+
if (!result && forceOnFailure) {
|
|
80
|
+
this.#log.warn('Clean request failed, forcing cleanup');
|
|
81
|
+
try {
|
|
82
|
+
await Promise.all([this.#ctx.build.outputFolder, this.#ctx.build.typesFolder]
|
|
83
|
+
.map(file => fs.rm(CommonUtil.resolveWorkspace(this.#ctx, file), { force: true, recursive: true })));
|
|
84
|
+
} catch { }
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
/** Stop server and wait for shutdown */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
import type { ManifestContext } from '@travetto/manifest';
|
|
4
|
+
|
|
5
|
+
import type { CompilerEvent, CompilerLogLevel } from '../types.ts';
|
|
6
|
+
import { AsyncQueue } from '../queue.ts';
|
|
7
|
+
import { Log } from '../log.ts';
|
|
8
|
+
import { CommonUtil } from '../common.ts';
|
|
9
|
+
import { EventUtil } from '../event.ts';
|
|
10
|
+
import type { CompilerClient } from './client.ts';
|
|
11
|
+
import { CompilerServer } from './server.ts';
|
|
12
|
+
|
|
13
|
+
const log = Log.scoped('compiler-exec');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Running the compiler
|
|
17
|
+
*/
|
|
18
|
+
export class CompilerManager {
|
|
19
|
+
/** Run compile process */
|
|
20
|
+
static async * #runTarget(ctx: ManifestContext, watching: boolean, signal: AbortSignal): AsyncIterable<CompilerEvent> {
|
|
21
|
+
if (signal.aborted) {
|
|
22
|
+
log.debug('Skipping, shutting down');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const queue = new AsyncQueue<CompilerEvent>();
|
|
27
|
+
|
|
28
|
+
log.info('Launching compiler');
|
|
29
|
+
const subProcess = spawn(process.argv0, ['-e', 'import("@travetto/compiler/bin/trvc-target.js")'], {
|
|
30
|
+
env: {
|
|
31
|
+
...process.env,
|
|
32
|
+
TRV_COMPILER_WATCH: String(watching),
|
|
33
|
+
TRV_MANIFEST: CommonUtil.resolveWorkspace(ctx, ctx.build.outputFolder, 'node_modules', ctx.workspace.name),
|
|
34
|
+
},
|
|
35
|
+
detached: true,
|
|
36
|
+
stdio: ['pipe', 1, 2, 'ipc'],
|
|
37
|
+
})
|
|
38
|
+
.on('message', message => EventUtil.isCompilerEvent(message) && queue.add(message))
|
|
39
|
+
.on('exit', () => queue.close());
|
|
40
|
+
|
|
41
|
+
const kill = (): unknown => {
|
|
42
|
+
log.debug('Shutting down process');
|
|
43
|
+
return (subProcess.connected ? subProcess.send('shutdown', () => subProcess.kill()) : subProcess.kill());
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
process.once('SIGINT', kill);
|
|
47
|
+
signal.addEventListener('abort', kill);
|
|
48
|
+
|
|
49
|
+
yield* queue;
|
|
50
|
+
|
|
51
|
+
if (subProcess.exitCode !== 0) {
|
|
52
|
+
log.error(`Terminated during compilation, code=${subProcess.exitCode}, killed=${subProcess.killed}`);
|
|
53
|
+
}
|
|
54
|
+
process.off('SIGINT', kill);
|
|
55
|
+
|
|
56
|
+
log.debug('Finished');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Main entry point for compilation */
|
|
60
|
+
static async compile(ctx: ManifestContext, client: CompilerClient, config: { watch?: boolean, logLevel?: CompilerLogLevel, forceRestart?: boolean }): Promise<void> {
|
|
61
|
+
Log.initLevel(config.logLevel ?? 'info');
|
|
62
|
+
const watch = !!config.watch;
|
|
63
|
+
|
|
64
|
+
if (config.forceRestart && await client.stop()) {
|
|
65
|
+
log.info('Stopped existing server');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const server = await new CompilerServer(ctx, watch).listen();
|
|
69
|
+
|
|
70
|
+
// Wait for build to be ready
|
|
71
|
+
if (server) {
|
|
72
|
+
log.debug('Start Server');
|
|
73
|
+
await server.processEvents(signal => this.#runTarget(ctx, watch, signal));
|
|
74
|
+
log.debug('End Server');
|
|
75
|
+
} else {
|
|
76
|
+
log.info('Server already running, waiting for initial compile to complete');
|
|
77
|
+
const controller = new AbortController();
|
|
78
|
+
Log.consumeProgressEvents(() => client.fetchEvents('progress', { until: event => !!event.complete, signal: controller.signal }));
|
|
79
|
+
await client.waitForState(['compile-end', 'watch-start'], 'Successfully built');
|
|
80
|
+
controller.abort();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Compile only if necessary */
|
|
85
|
+
static async compileIfNecessary(ctx: ManifestContext, client: CompilerClient): Promise<void> {
|
|
86
|
+
if (!(await client.isWatching())) { // Short circuit if we can
|
|
87
|
+
await this.compile(ctx, client, { watch: false, logLevel: 'error' });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -3,8 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import type { ManifestContext } from '@travetto/manifest';
|
|
5
5
|
|
|
6
|
-
import { Log, Logger } from '../log.ts';
|
|
7
|
-
import { CommonUtil } from '../
|
|
6
|
+
import { Log, type Logger } from '../log.ts';
|
|
7
|
+
import { CommonUtil } from '../common.ts';
|
|
8
8
|
|
|
9
9
|
export class ProcessHandle {
|
|
10
10
|
|
|
@@ -4,14 +4,12 @@ import { setMaxListeners } from 'node:events';
|
|
|
4
4
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
type CompilerMode, type CompilerProgressEvent, type CompilerEvent,
|
|
9
|
-
type CompilerEventType, type CompilerServerInfo, isComplilerEventType
|
|
10
|
-
} from '../types.ts';
|
|
7
|
+
import type { CompilerProgressEvent, CompilerEvent, CompilerEventType, CompilerServerInfo } from '../types.ts';
|
|
11
8
|
import { Log } from '../log.ts';
|
|
12
|
-
import { CommonUtil } from '../
|
|
9
|
+
import { CommonUtil } from '../common.ts';
|
|
13
10
|
import { CompilerClient } from './client.ts';
|
|
14
11
|
import { ProcessHandle } from './process-handle.ts';
|
|
12
|
+
import { EventUtil } from '../event.ts';
|
|
15
13
|
|
|
16
14
|
const log = Log.scoped('server');
|
|
17
15
|
|
|
@@ -30,7 +28,7 @@ export class CompilerServer {
|
|
|
30
28
|
#url: string;
|
|
31
29
|
#handle: Record<'compiler' | 'server', ProcessHandle>;
|
|
32
30
|
|
|
33
|
-
constructor(ctx: ManifestContext,
|
|
31
|
+
constructor(ctx: ManifestContext, watching: boolean) {
|
|
34
32
|
this.#ctx = ctx;
|
|
35
33
|
this.#client = new CompilerClient(ctx, Log.scoped('server.client'));
|
|
36
34
|
this.#url = this.#client.url;
|
|
@@ -39,7 +37,7 @@ export class CompilerServer {
|
|
|
39
37
|
this.info = {
|
|
40
38
|
state: 'startup',
|
|
41
39
|
iteration: Date.now(),
|
|
42
|
-
|
|
40
|
+
watching,
|
|
43
41
|
serverProcessId: process.pid,
|
|
44
42
|
compilerProcessId: -1,
|
|
45
43
|
path: ctx.workspace.path,
|
|
@@ -59,8 +57,8 @@ export class CompilerServer {
|
|
|
59
57
|
return this.#shutdown.signal;
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
get
|
|
63
|
-
return this.info.
|
|
60
|
+
get watching(): boolean {
|
|
61
|
+
return this.info.watching;
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
isResetEvent(event: CompilerEvent): boolean {
|
|
@@ -74,7 +72,7 @@ export class CompilerServer {
|
|
|
74
72
|
.on('error', async error => {
|
|
75
73
|
if ('code' in error && error.code === 'EADDRINUSE') {
|
|
76
74
|
const info = await this.#client.info();
|
|
77
|
-
resolve((info && info.
|
|
75
|
+
resolve((info && !info.watching && this.watching) ? 'retry' : 'running');
|
|
78
76
|
} else {
|
|
79
77
|
log.warn('Failed in running server', error);
|
|
80
78
|
reject(error);
|
|
@@ -153,7 +151,7 @@ export class CompilerServer {
|
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
async #clean(): Promise<{ clean: boolean }> {
|
|
156
|
-
await Promise.all([this.#ctx.build.
|
|
154
|
+
await Promise.all([this.#ctx.build.outputFolder, this.#ctx.build.typesFolder]
|
|
157
155
|
.map(folder => fs.rm(CommonUtil.resolveWorkspace(this.#ctx, folder), { recursive: true, force: true })));
|
|
158
156
|
return { clean: true };
|
|
159
157
|
}
|
|
@@ -170,7 +168,7 @@ export class CompilerServer {
|
|
|
170
168
|
let close = false;
|
|
171
169
|
switch (action) {
|
|
172
170
|
case 'event': {
|
|
173
|
-
if (isComplilerEventType(subAction)) {
|
|
171
|
+
if (EventUtil.isComplilerEventType(subAction)) {
|
|
174
172
|
this.#addListener(subAction, response);
|
|
175
173
|
}
|
|
176
174
|
return;
|
|
@@ -200,7 +198,7 @@ export class CompilerServer {
|
|
|
200
198
|
if (event.type === 'state') {
|
|
201
199
|
this.info.state = event.payload.state;
|
|
202
200
|
if (event.payload.state === 'init' && event.payload.extra && 'processId' in event.payload.extra && typeof event.payload.extra.processId === 'number') {
|
|
203
|
-
if (this.info.
|
|
201
|
+
if (this.info.watching && !this.info.compilerProcessId) {
|
|
204
202
|
// Ensure we are killing in watch mode on first set
|
|
205
203
|
await this.#handle.compiler.kill();
|
|
206
204
|
}
|