@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 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#L2) 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.
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 [main ] Start Server
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 debug [precompile ] Started
48
- 2029-03-14T04:00:02.762Z debug [precompile ] Skipped @travetto/manifest
49
- 2029-03-14T04:00:02.947Z debug [precompile ] Skipped @travetto/transformer
50
- 2029-03-14T04:00:03.093Z debug [precompile ] Skipped @travetto/compiler
51
- 2029-03-14T04:00:04.003Z debug [precompile ] Completed
52
- 2029-03-14T04:00:04.495Z debug [manifest ] Started
53
- 2029-03-14T04:00:05.066Z debug [manifest ] Completed
54
- 2029-03-14T04:00:05.307Z debug [transformers ] Started
55
- 2029-03-14T04:00:05.952Z debug [transformers ] Skipped @travetto/runtime
56
- 2029-03-14T04:00:06.859Z debug [transformers ] Skipped @travetto/schema
57
- 2029-03-14T04:00:07.720Z debug [transformers ] Completed
58
- 2029-03-14T04:00:08.179Z debug [delta ] Started
59
- 2029-03-14T04:00:08.588Z debug [delta ] Completed
60
- 2029-03-14T04:00:09.493Z debug [manifest ] Started
61
- 2029-03-14T04:00:10.395Z debug [manifest ] Wrote manifest @travetto-doc/compiler
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/types.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 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
+ });
@@ -0,0 +1,4 @@
1
+ // @ts-check
2
+ import './hook.js';
3
+ const { Compiler } = await import('../src/compiler.ts');
4
+ Compiler.main();
package/bin/trvc.js CHANGED
@@ -1,41 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { load } from './entry.common.js';
3
-
4
- const help = `
5
- npx trvc [command]
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.2",
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.1",
34
- "@travetto/manifest": "^7.0.1",
35
- "@travetto/transformer": "^7.0.2"
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.2"
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 { ManifestContext } from '@travetto/manifest';
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
- const all = [process.cwd(), ctx.workspace.path, ...args].map(toPosix);
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, ManifestModuleUtil } from '@travetto/manifest';
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 '../support/log.ts';
13
- import { CommonUtil } from '../support/util.ts';
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 [dirty, watch] = process.argv.slice(2);
27
- const state = await CompilerState.get(new ManifestIndex());
28
- log.debug('Running compiler with dirty file', dirty);
29
- const dirtyFiles = ManifestModuleUtil.getFileType(dirty) === 'ts' ? [dirty] :
30
- (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(line => !!line);
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, dirtyFiles: string[], watch?: boolean) {
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.#dirtyFiles.length) {
174
- for await (const event of this.emit(this.#dirtyFiles, emitter)) {
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 '../support/types.ts';
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
  }
@@ -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 '../util.ts';
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
- return this.#fetch('/clean', { timeout: 300 }).then(response => response.ok, () => false);
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 '../util.ts';
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 '../util.ts';
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, mode: CompilerMode) {
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
- mode,
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 mode(): CompilerMode {
63
- return this.info.mode;
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.mode === 'build' && this.mode === 'watch') ? 'retry' : 'running');
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.compilerFolder, this.#ctx.build.outputFolder]
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.mode === 'watch' && !this.info.compilerProcessId) {
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
  }