@travetto/compiler 3.4.2 → 4.0.0-rc.0

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
@@ -21,7 +21,7 @@ This module expands upon the [Typescript](https://typescriptlang.org) compiler,
21
21
  Beyond the [Typescript](https://typescriptlang.org) compiler functionality, the module provides the primary entry point into the development process.
22
22
 
23
23
  ## CLI
24
- The compiler cli, [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L4) 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.
24
+ The compiler cli, [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L5) 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
25
 
26
26
  The compiler cli supports the following operations:
27
27
  * `start|watch` - Run the compiler in watch mode
@@ -30,7 +30,8 @@ The compiler cli supports the following operations:
30
30
  * `build` - Ensure the project is built and upto date
31
31
  * `clean` - Clean out the output and compiler caches
32
32
  * `info` - Retrieve the compiler information, if running
33
- * `manifest [output] [prod]` - Generate the project manifest
33
+ * `event <log|progress|state>` - Watch events in realtime as newline delimited JSON
34
+ * `manifest --prod [output]` - Generate the project manifest
34
35
  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`.
35
36
 
36
37
  **Terminal: Sample trv output with debug logging**
@@ -38,34 +39,35 @@ In addition to the normal output, the compiler supports an environment variable
38
39
  $ TRV_BUILD=debug trvc build
39
40
 
40
41
  2029-03-14T04:00:00.618Z info [compiler-server] Starting server http://127.0.0.1:25539
41
- 2029-03-14T04:00:00.837Z debug [compiler-client] Starting watch for events of type "log"
42
+ 2029-03-14T04:00:00.837Z debug [compiler ] Started, streaming logs
42
43
  2029-03-14T04:00:01.510Z debug [event-stream ] Started event stream
43
44
  2029-03-14T04:00:02.450Z debug [precompile ] Started
44
- 2029-03-14T04:00:02.762Z debug [compiler-server] Receive request { action: 'event', subAction: 'log' }
45
- 2029-03-14T04:00:02.947Z debug [precompile ] Skipped @travetto/terminal
46
- 2029-03-14T04:00:03.093Z debug [precompile ] Skipped @travetto/manifest
47
- 2029-03-14T04:00:04.003Z debug [precompile ] Skipped @travetto/transformer
48
- 2029-03-14T04:00:04.495Z debug [precompile ] Skipped @travetto/compiler
49
- 2029-03-14T04:00:05.066Z debug [precompile ] Completed
50
- 2029-03-14T04:00:05.307Z debug [manifest ] Started
51
- 2029-03-14T04:00:05.952Z debug [manifest ] Completed
52
- 2029-03-14T04:00:06.859Z debug [transformers ] Started
53
- 2029-03-14T04:00:07.720Z debug [transformers ] Skipped @travetto/base
54
- 2029-03-14T04:00:08.179Z debug [transformers ] Skipped @travetto/cli
55
- 2029-03-14T04:00:08.588Z debug [transformers ] Skipped @travetto/manifest
56
- 2029-03-14T04:00:09.493Z debug [transformers ] Skipped @travetto/registry
57
- 2029-03-14T04:00:10.395Z debug [transformers ] Skipped @travetto/schema
58
- 2029-03-14T04:00:10.407Z debug [transformers ] Completed
59
- 2029-03-14T04:00:10.799Z debug [delta ] Started
60
- 2029-03-14T04:00:11.013Z debug [delta ] Completed
61
- 2029-03-14T04:00:11.827Z debug [manifest ] Started
62
- 2029-03-14T04:00:11.894Z debug [manifest ] Wrote manifest @travetto-doc/compiler
63
- 2029-03-14T04:00:12.133Z debug [manifest ] Completed
64
- 2029-03-14T04:00:13.123Z info [compiler-server] State changed: compile-end
65
- 2029-03-14T04:00:14.014Z debug [compiler-exec ] Skipped
66
- 2029-03-14T04:00:14.924Z debug [event-stream ] Finished event stream
67
- 2029-03-14T04:00:15.690Z info [compiler-server] Closing down server
68
- 2029-03-14T04:00:15.865Z debug [compiler-client] Stopping watch for events of type "log"
45
+ 2029-03-14T04:00:02.762Z debug [compiler-server] Receive request { action: 'info', subAction: undefined }
46
+ 2029-03-14T04:00:02.947Z debug [client.server ] Starting watch for events of type "log"
47
+ 2029-03-14T04:00:03.093Z debug [compiler-server] Receive request { action: 'event', subAction: 'log' }
48
+ 2029-03-14T04:00:04.003Z debug [precompile ] Skipped @travetto/manifest
49
+ 2029-03-14T04:00:04.495Z debug [precompile ] Skipped @travetto/transformer
50
+ 2029-03-14T04:00:05.066Z debug [precompile ] Skipped @travetto/compiler
51
+ 2029-03-14T04:00:05.307Z debug [precompile ] Completed
52
+ 2029-03-14T04:00:05.952Z debug [manifest ] Started
53
+ 2029-03-14T04:00:06.859Z debug [manifest ] Completed
54
+ 2029-03-14T04:00:07.720Z debug [transformers ] Started
55
+ 2029-03-14T04:00:08.179Z debug [transformers ] Skipped @travetto/base
56
+ 2029-03-14T04:00:08.588Z debug [transformers ] Skipped @travetto/cli
57
+ 2029-03-14T04:00:09.493Z debug [transformers ] Skipped @travetto/manifest
58
+ 2029-03-14T04:00:10.395Z debug [transformers ] Skipped @travetto/registry
59
+ 2029-03-14T04:00:10.407Z debug [transformers ] Skipped @travetto/schema
60
+ 2029-03-14T04:00:10.799Z debug [transformers ] Completed
61
+ 2029-03-14T04:00:11.013Z debug [delta ] Started
62
+ 2029-03-14T04:00:11.827Z debug [delta ] Completed
63
+ 2029-03-14T04:00:11.894Z debug [manifest ] Started
64
+ 2029-03-14T04:00:12.133Z debug [manifest ] Wrote manifest @travetto-doc/compiler
65
+ 2029-03-14T04:00:13.123Z debug [manifest ] Completed
66
+ 2029-03-14T04:00:14.014Z info [compiler-server] State changed: compile-end
67
+ 2029-03-14T04:00:14.924Z debug [compiler-exec ] Skipped
68
+ 2029-03-14T04:00:15.690Z debug [event-stream ] Finished event stream
69
+ 2029-03-14T04:00:15.865Z info [compiler-server] Closing down server
70
+ 2029-03-14T04:00:16.757Z debug [client.server ] Stopping watch for events of type "log"
69
71
  ```
70
72
 
71
73
  **Terminal: Sample trv output with default log level**
@@ -86,4 +88,4 @@ The compiler will move through the following phases on a given compilation execu
86
88
  * `Invoke Compiler` - Run [Typescript](https://typescriptlang.org) compiler with the aforementioned enhancements
87
89
 
88
90
  ### Bootstrapping
89
- 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#L4) 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#L4) 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.
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#L5) 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#L5) 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/bin/common.js CHANGED
@@ -1,55 +1,47 @@
1
1
  // @ts-check
2
-
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
- import { createRequire } from 'module';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
6
4
 
7
5
  import { getManifestContext } from '@travetto/manifest/bin/context.js';
8
6
 
9
- const COMPILER_FILES = [...['entry.trvc', 'log', 'queue', 'server/client', 'server/runner', 'server/server', 'setup', 'util'].map(x => `support/${x}.ts`), 'package.json'];
7
+ /** @typedef {import('@travetto/manifest').ManifestContext} Ctx */
8
+ /** @typedef {(ctx: Ctx, content:string) => (string | Promise<string>)} Transform */
9
+
10
+ const stat = (/** @type {string}*/ file) => fs.stat(file).then(s => Math.max(s.mtimeMs, s.ctimeMs)).catch(() => 0);
11
+ const TS_EXT = /[.]tsx?$/;
12
+
13
+ const /** @type {Transform} */ transpile = async (ctx, content, tsconfig = path.resolve(ctx.workspace.path, 'tsconfig.json')) => {
14
+ await fs.stat(tsconfig).catch(() => fs.writeFile(tsconfig, JSON.stringify({ extends: '@travetto/compiler/tsconfig.trv.json' }), 'utf8'));
15
+ const ts = (await import('typescript')).default;
16
+ const module = ctx.workspace.type === 'module' ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS;
17
+ return ts.transpile(content, { target: ts.ScriptTarget.ES2022, module, esModuleInterop: true, allowSyntheticDefaultImports: true });
18
+ };
19
+
20
+ const /** @type {Transform} */ rewritePackage = (ctx, content) =>
21
+ JSON.stringify(Object.assign(JSON.parse(content), { type: ctx.workspace.type }), null, 2);
22
+
23
+ async function outputIfChanged(/** @type {Ctx} */ ctx, /** @type {string} */ file, /** @type {Transform} */ transform) {
24
+ const target = path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules', '@travetto/compiler', file).replace(TS_EXT, '.js');
25
+ const src = path.resolve(ctx.workspace.path, ctx.build.compilerModuleFolder, file);
26
+
27
+ if (await stat(src) > await stat(target)) {
28
+ await fs.mkdir(path.dirname(target), { recursive: true });
29
+ const content = await fs.readFile(src, 'utf8');
30
+ await fs.writeFile(target, await transform(ctx, content), 'utf8');
31
+ }
32
+ return target;
33
+ }
10
34
 
11
- /** @return {Promise<ReturnType<import('@travetto/compiler/support/entry.trvc').main>>} */
12
35
  export const getEntry = async () => {
13
36
  const ctx = await getManifestContext();
14
- const tsconfigFile = path.resolve(ctx.workspacePath, 'tsconfig.json');
15
- if (!(await fs.stat(tsconfigFile).catch(() => undefined))) {
16
- await fs.writeFile(tsconfigFile, JSON.stringify({ extends: '@travetto/compiler/tsconfig.trv.json' }), 'utf8');
17
- }
18
- const compMod = path.dirname(createRequire(path.resolve(ctx.workspacePath, 'node_modules')).resolve('@travetto/compiler/package.json'));
19
- const files = [];
20
-
21
- for (const file of COMPILER_FILES) {
22
- const target = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler', file).replace(/[.]tsx?$/, '.js');
23
- const src = path.resolve(compMod, file);
24
-
25
- const targetTime = await fs.stat(target).then(s => Math.max(s.mtimeMs, s.ctimeMs)).catch(() => 0);
26
- const srcTime = await fs.stat(src).then(s => Math.max(s.mtimeMs, s.ctimeMs));
27
- // If stale
28
- if (srcTime > targetTime) {
29
- const ts = (await import('typescript')).default;
30
- const module = ctx.moduleType === 'module' ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS;
31
- await fs.mkdir(path.dirname(target), { recursive: true });
32
- const text = await fs.readFile(src, 'utf8');
33
- if (/[.]tsx?$/.test(file)) {
34
- const content = ts.transpile(
35
- text,
36
- { target: ts.ScriptTarget.ES2022, module, esModuleInterop: true, allowSyntheticDefaultImports: true }
37
- )
38
- .replace(/^((?:im|ex)port .*from '[.][^']+)(')/mg, (_, a, b) => `${a}.js${b}`)
39
- .replace(/^(import [^\n]*from '[^.][^\n/]+[/][^\n/]+[/][^\n']+)(')/mg, (_, a, b) => `${a}.js${b}`);
40
- await fs.writeFile(target, content, 'utf8');
41
- } else {
42
- const pkg = JSON.parse(text);
43
- pkg.type = ctx.moduleType;
44
- await fs.writeFile(target, JSON.stringify(pkg, null, 2), 'utf8');
45
- }
46
- // Compile
47
- }
48
- files.push(target);
49
- }
37
+ const entry = await outputIfChanged(ctx, 'support/entry.trvc.ts', transpile);
38
+ const run = (/** @type {import('@travetto/compiler/support/entry.trvc')} */ mod) => mod.main(ctx);
39
+
40
+ await outputIfChanged(ctx, 'package.json', rewritePackage);
50
41
 
51
- const rootCtx = await (ctx.monoRepo ? getManifestContext(ctx.workspacePath) : ctx);
42
+ await fs.readdir(path.resolve(ctx.workspace.path, ctx.build.compilerModuleFolder, 'support'), { recursive: true }).then(files =>
43
+ Promise.all(files.filter(x => TS_EXT.test(x)).map(f => outputIfChanged(ctx, `support/${f}`, transpile))));
52
44
 
53
- try { return require(files[0]).main(rootCtx, ctx); }
54
- catch { return import(files[0]).then(v => v.main(rootCtx, ctx)); }
45
+ try { return run(require(entry)); }
46
+ catch { return import(entry).then(run); }
55
47
  };
package/bin/trvc.js CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env -S node --disable-proto=delete --enable-source-maps
2
+ process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ''} --enable-source-maps --disable-proto=delete`;
2
3
 
3
4
  // @ts-check
4
5
  import { getEntry } from './common.js';
@@ -7,18 +8,20 @@ const help = () => [
7
8
  'npx trvc [command]',
8
9
  '',
9
10
  'Available Commands:',
10
- ' * start|watch - Run the compiler in watch mode',
11
- ' * stop - Stop the compiler if running',
12
- ' * restart - Restart the compiler in watch mode',
13
- ' * build - Ensure the project is built and upto date',
14
- ' * clean - Clean out the output and compiler caches',
15
- ' * info - Retrieve the compiler information, if running',
16
- ' * manifest [output] [prod] - Generate the project manifest',
11
+ ' * start|watch - Run the compiler in watch mode',
12
+ ' * stop - Stop the compiler if running',
13
+ ' * restart - Restart the compiler in watch mode',
14
+ ' * build - Ensure the project is built and upto date',
15
+ ' * clean - Clean out the output and compiler caches',
16
+ ' * info - Retrieve the compiler information, if running',
17
+ ' * event <log|progress|state> - Watch events in realtime as newline delimited JSON',
18
+ ' * manifest --prod [output] - Generate the project manifest',
17
19
  ].join('\n');
18
20
 
19
21
  getEntry().then(async (ops) => {
20
- const [op, ...args] = process.argv.slice(2);
21
- const baseArgs = args.filter(x => !x.startsWith('-'));
22
+ const [op, ...all] = process.argv.slice(2);
23
+ const args = all.filter(x => !x.startsWith('-'));
24
+ const flags = all.filter(x => x.startsWith('-'));
22
25
 
23
26
  switch (op) {
24
27
  case undefined:
@@ -26,8 +29,13 @@ getEntry().then(async (ops) => {
26
29
  case 'restart': return ops.stop().then(() => ops.compile('watch'));
27
30
  case 'stop': return ops.stop();
28
31
  case 'info': return ops.info().then(v => console.log(JSON.stringify(v, null, 2)));
32
+ case 'event': return ops.events(args[0], v => {
33
+ if (!process.stdout.write(`${JSON.stringify(v)}\n`)) {
34
+ return new Promise(r => process.stdout.once('drain', r));
35
+ }
36
+ });
29
37
  case 'clean': return ops.clean();
30
- case 'manifest': return ops.manifest(baseArgs[0], /prod/i.test(baseArgs[1] ?? ''));
38
+ case 'manifest': return ops.manifest(args[0], flags.some(x => x === '--prod'));
31
39
  case 'start': return ops.compile('watch');
32
40
  case 'watch':
33
41
  case 'build': return ops.compile(op);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/compiler",
3
- "version": "3.4.2",
3
+ "version": "4.0.0-rc.0",
4
4
  "description": "The compiler infrastructure for the Travetto framework",
5
5
  "keywords": [
6
6
  "compiler",
@@ -31,13 +31,12 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@parcel/watcher": "^2.3.0",
34
- "@travetto/manifest": "^3.4.0",
35
- "@travetto/terminal": "^3.4.0",
36
- "@travetto/transformer": "^3.4.1",
37
- "@types/node": "^20.9.2"
34
+ "@travetto/manifest": "^4.0.0-rc.0",
35
+ "@travetto/transformer": "^4.0.0-rc.0",
36
+ "@types/node": "^20.10.3"
38
37
  },
39
38
  "peerDependencies": {
40
- "@travetto/cli": "^3.4.4"
39
+ "@travetto/cli": "^4.0.0-rc.0"
41
40
  },
42
41
  "peerDependenciesMeta": {
43
42
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { install } from 'source-map-support';
2
1
  import ts from 'typescript';
3
- import fs from 'fs/promises';
2
+ import timers from 'node:timers/promises';
3
+ import fs from 'node:fs/promises';
4
+ import { setMaxListeners } from 'node:events';
4
5
 
5
- import { ManifestModuleUtil, RootIndex } from '@travetto/manifest';
6
+ import { ManifestModuleUtil, RuntimeIndex } from '@travetto/manifest';
6
7
 
7
8
  import { CompilerUtil } from './util';
8
9
  import { CompilerState } from './state';
@@ -21,15 +22,17 @@ export class Compiler {
21
22
  */
22
23
  static async main(): Promise<void> {
23
24
  const [dirty, watch] = process.argv.slice(2);
24
- const state = await CompilerState.get(RootIndex);
25
+ const state = await CompilerState.get(RuntimeIndex);
26
+ Log.debug('Running compiler with dirty file', dirty);
25
27
  const dirtyFiles = ManifestModuleUtil.getFileType(dirty) === 'ts' ? [dirty] : (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
26
28
  await new Compiler(state, dirtyFiles, watch === 'true').run();
27
- process.exit();
28
29
  }
29
30
 
30
31
  #state: CompilerState;
31
32
  #dirtyFiles: string[];
32
33
  #watch?: boolean;
34
+ #ctrl: AbortController;
35
+ #signal: AbortSignal;
33
36
 
34
37
  constructor(state: CompilerState, dirtyFiles: string[], watch?: boolean) {
35
38
  this.#state = state;
@@ -37,6 +40,11 @@ export class Compiler {
37
40
  this.#state.getAllFiles() :
38
41
  dirtyFiles.map(f => this.#state.getBySource(f)!.input);
39
42
  this.#watch = watch;
43
+
44
+ this.#ctrl = new AbortController();
45
+ this.#signal = this.#ctrl.signal;
46
+ setMaxListeners(1000, this.#signal);
47
+ process.once('disconnect', () => this.#ctrl.abort()).once('SIGINT', () => this.#ctrl.abort());
40
48
  }
41
49
 
42
50
  /**
@@ -50,6 +58,7 @@ export class Compiler {
50
58
  if (needsNewProgram) {
51
59
  program = this.#state.createProgram(program);
52
60
  }
61
+ await timers.setImmediate();
53
62
  const result = this.#state.writeInputFile(program, inputFile);
54
63
  if (result?.diagnostics?.length) {
55
64
  return result.diagnostics;
@@ -72,6 +81,7 @@ export class Compiler {
72
81
  async * emit(files: string[], emitter: CompileEmitter): AsyncIterable<CompileEmitEvent> {
73
82
  let i = 0;
74
83
  let lastSent = Date.now();
84
+
75
85
  for (const file of files) {
76
86
  const err = await emitter(file);
77
87
  const imp = file.replace(/.*node_modules\//, '');
@@ -80,8 +90,14 @@ export class Compiler {
80
90
  lastSent = Date.now();
81
91
  EventUtil.sendEvent('progress', { total: files.length, idx: i, message: imp, operation: 'compile' });
82
92
  }
93
+ if (this.#signal.aborted) {
94
+ break;
95
+ }
83
96
  }
84
97
  EventUtil.sendEvent('progress', { total: files.length, idx: files.length, message: 'Complete', operation: 'compile', complete: true });
98
+
99
+ await timers.setTimeout(1);
100
+
85
101
  Log.debug(`Compiled ${i} files`);
86
102
  }
87
103
 
@@ -89,16 +105,10 @@ export class Compiler {
89
105
  * Run the compiler
90
106
  */
91
107
  async run(): Promise<void> {
92
- install();
93
-
94
108
  Log.debug('Compilation started');
95
109
 
96
110
  EventUtil.sendEvent('state', { state: 'init', extra: { pid: process.pid } });
97
111
 
98
- if (process.send) {
99
- process.on('disconnect', () => process.exit(0));
100
- }
101
-
102
112
  const emitter = await this.getCompiler();
103
113
  let failed = false;
104
114
 
@@ -114,9 +124,12 @@ export class Compiler {
114
124
  EventUtil.sendEvent('log', { level: 'error', message: compileError.toString(), time: Date.now() });
115
125
  }
116
126
  }
117
- if (failed) {
127
+ if (this.#signal.aborted) {
128
+ Log.debug('Compilation aborted');
129
+ } else if (failed) {
118
130
  Log.debug('Compilation failed');
119
- process.exit(1);
131
+ process.exitCode = 1;
132
+ return;
120
133
  } else {
121
134
  Log.debug('Compilation succeeded');
122
135
  }
@@ -128,15 +141,16 @@ export class Compiler {
128
141
 
129
142
  EventUtil.sendEvent('state', { state: 'compile-end' });
130
143
 
131
- if (this.#watch) {
144
+ if (this.#watch && !this.#signal.aborted) {
132
145
  Log.info('Watch is ready');
133
146
 
134
147
  EventUtil.sendEvent('state', { state: 'watch-start' });
135
148
 
136
- for await (const ev of CompilerWatcher.watch(this.#state)) {
149
+ for await (const ev of new CompilerWatcher(this.#state, this.#signal).watchChanges()) {
137
150
  if (ev.action === 'reset') {
138
151
  Log.info(`Triggering reset due to change in ${ev.file}`);
139
152
  EventUtil.sendEvent('state', { state: 'reset' });
153
+ this.#ctrl.abort();
140
154
  return;
141
155
  }
142
156
  const { action, entry } = ev;
@@ -166,5 +180,13 @@ export class Compiler {
166
180
 
167
181
  EventUtil.sendEvent('state', { state: 'watch-end' });
168
182
  }
183
+
184
+ // No longer listen to disconnect
185
+ process.removeAllListeners('disconnect');
186
+
187
+ if (this.#ctrl.signal.aborted) {
188
+ process.exitCode = 2;
189
+ Log.error('Shutting down manually');
190
+ }
169
191
  }
170
192
  }
package/src/event.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { CompilerServerEvent, CompilerServerEventType } from '../support/types';
1
+ import type { CompilerEvent, CompilerEventType } from '../support/types';
2
2
 
3
3
  export class EventUtil {
4
- static sendEvent<K extends CompilerServerEventType, T extends CompilerServerEvent & { type: K }>(type: K, payload: T['payload']): void {
5
- process.send?.({ type, payload });
4
+ static sendEvent<K extends CompilerEventType, T extends CompilerEvent & { type: K }>(type: K, payload: T['payload']): void {
5
+ process.connected && process.send!({ type, payload });
6
6
  }
7
7
  }
@@ -1,6 +1,6 @@
1
- import fs from 'fs/promises';
1
+ import fs from 'node:fs/promises';
2
2
 
3
- import { IndexedModule, ManifestContext, ManifestModuleUtil, RootIndex, path } from '@travetto/manifest';
3
+ import { IndexedModule, ManifestContext, ManifestModuleUtil, path } from '@travetto/manifest';
4
4
 
5
5
  import { AsyncQueue } from '../../support/queue';
6
6
 
@@ -13,7 +13,7 @@ const VALID_TYPES = new Set(['ts', 'typings', 'js', 'package-json']);
13
13
  type ToWatch = { file: string, actions: string[] };
14
14
 
15
15
  /** Watch file for reset */
16
- async function watchForReset(q: AsyncQueue<WatchEvent>, root: string, files: ToWatch[], signal: AbortSignal): Promise<void> {
16
+ function watchForReset(q: AsyncQueue<WatchEvent>, root: string, files: ToWatch[], signal: AbortSignal): void {
17
17
  const watchers: Record<string, { folder: string, files: Map<string, (ToWatch & { name: string, actionSet: Set<string> })> }> = {};
18
18
  // Group by base path
19
19
  for (const el of files) {
@@ -26,15 +26,19 @@ async function watchForReset(q: AsyncQueue<WatchEvent>, root: string, files: ToW
26
26
 
27
27
  // Fire them all off
28
28
  Object.values(watchers).map(async (watcher) => {
29
- for await (const ev of fs.watch(watcher.folder, { persistent: true, encoding: 'utf8', signal })) {
30
- const toWatch = watcher.files.get(ev.filename!);
31
- if (toWatch) {
32
- const stat = await fs.stat(path.resolve(root, ev.filename!)).catch(() => undefined);
33
- const action = !stat ? 'delete' : ((Date.now() - stat.ctimeMs) < CREATE_THRESHOLD) ? 'create' : 'update';
34
- if (toWatch.actionSet.has(action)) {
35
- q.add({ action: 'reset', file: ev.filename! });
29
+ try {
30
+ for await (const ev of fs.watch(watcher.folder, { persistent: true, encoding: 'utf8', signal })) {
31
+ const toWatch = watcher.files.get(ev.filename!);
32
+ if (toWatch) {
33
+ const stat = await fs.stat(path.resolve(root, ev.filename!)).catch(() => undefined);
34
+ const action = !stat ? 'delete' : ((Date.now() - stat.ctimeMs) < CREATE_THRESHOLD) ? 'create' : 'update';
35
+ if (toWatch.actionSet.has(action)) {
36
+ q.add({ action: 'reset', file: ev.filename! });
37
+ }
36
38
  }
37
39
  }
40
+ } catch (err) {
41
+ // Ignore
38
42
  }
39
43
  });
40
44
  }
@@ -44,7 +48,7 @@ async function watchFolder(ctx: ManifestContext, q: AsyncQueue<WatchEvent>, src:
44
48
  const lib = await import('@parcel/watcher');
45
49
  const ignore = [
46
50
  'node_modules', '**/.trv',
47
- ...((!ctx.monoRepo || src === ctx.workspacePath) ? [ctx.compilerFolder, ctx.outputFolder, ctx.toolFolder] : []),
51
+ ...((!ctx.workspace.mono || src === ctx.workspace.path) ? [ctx.build.compilerFolder, ctx.build.outputFolder] : []),
48
52
  ...(await fs.readdir(src)).filter(x => x.startsWith('.'))
49
53
  ];
50
54
 
@@ -78,24 +82,23 @@ async function watchFolder(ctx: ManifestContext, q: AsyncQueue<WatchEvent>, src:
78
82
  export async function* fileWatchEvents(manifest: ManifestContext, modules: IndexedModule[], signal: AbortSignal): AsyncIterable<WatchEvent> {
79
83
  const q = new AsyncQueue<WatchEvent>(signal);
80
84
 
81
- for (const m of modules.filter(x => !manifest.monoRepo || x.sourcePath !== manifest.workspacePath)) {
82
- watchFolder(manifest, q, m.sourcePath, m.sourcePath, signal);
85
+ for (const m of modules.filter(x => !manifest.workspace.mono || x.sourcePath !== manifest.workspace.path)) {
86
+ await watchFolder(manifest, q, m.sourcePath, m.sourcePath, signal);
83
87
  }
84
88
 
85
89
  // Add monorepo folders
86
- if (manifest.monoRepo) {
87
- const mono = modules.find(x => x.sourcePath === manifest.workspacePath)!;
90
+ if (manifest.workspace.mono) {
91
+ const mono = modules.find(x => x.sourcePath === manifest.workspace.path)!;
88
92
  for (const folder of Object.keys(mono.files)) {
89
93
  if (!folder.startsWith('$')) {
90
- watchFolder(manifest, q, path.resolve(mono.sourcePath, folder), mono.sourcePath, signal);
94
+ await watchFolder(manifest, q, path.resolve(mono.sourcePath, folder), mono.sourcePath, signal);
91
95
  }
92
96
  }
93
97
  }
94
98
 
95
- watchForReset(q, RootIndex.manifest.workspacePath, [
96
- { file: RootIndex.manifest.outputFolder, actions: ['delete'] },
97
- { file: RootIndex.manifest.compilerFolder, actions: ['delete'] },
98
- { file: RootIndex.manifest.toolFolder, actions: ['delete'] },
99
+ watchForReset(q, manifest.workspace.path, [
100
+ { file: manifest.build.outputFolder, actions: ['delete'] },
101
+ { file: manifest.build.compilerFolder, actions: ['delete'] },
99
102
  { file: 'package-lock.json', actions: ['delete', 'update', 'create'] },
100
103
  { file: 'package.json', actions: ['delete', 'update', 'create'] }
101
104
  ], signal);
package/src/log.ts CHANGED
@@ -3,7 +3,7 @@ import { EventUtil } from './event';
3
3
 
4
4
  function log(level: CompilerLogLevel, message: string, ...args: unknown[]): void {
5
5
  EventUtil.sendEvent('log', { level, message, args, time: Date.now(), scope: 'compiler-exec' });
6
- if (!process.send) {
6
+ if (!process.connected) {
7
7
  // eslint-disable-next-line no-console
8
8
  console[level](message, ...args);
9
9
  }
package/src/state.ts CHANGED
@@ -46,11 +46,11 @@ export class CompilerState implements ts.CompilerHost {
46
46
  async init(idx: ManifestIndex): Promise<this> {
47
47
  this.#manifestIndex = idx;
48
48
  this.#manifest = idx.manifest;
49
- const mapper = folderMapper(this.#manifest.workspacePath, '##');
49
+ const mapper = folderMapper(this.#manifest.workspace.path, '##');
50
50
  this.#rootDir = mapper.dir;
51
51
  this.#inputPathToSource = mapper.translate;
52
52
 
53
- this.#outputPath = path.resolve(this.#manifest.workspacePath, this.#manifest.outputFolder);
53
+ this.#outputPath = path.resolve(this.#manifest.workspace.path, this.#manifest.build.outputFolder);
54
54
  this.#modules = Object.values(this.#manifest.modules);
55
55
 
56
56
  // Register all inputs
@@ -91,7 +91,7 @@ export class CompilerState implements ts.CompilerHost {
91
91
  }
92
92
 
93
93
  resolveOutputFile(file: string): string {
94
- return path.resolve(this.#manifest.workspacePath, this.#manifest.outputFolder, file);
94
+ return path.resolve(this.#manifest.workspace.path, this.#manifest.build.outputFolder, file);
95
95
  }
96
96
 
97
97
  getArbitraryInputFile(): string {
@@ -125,7 +125,7 @@ export class CompilerState implements ts.CompilerHost {
125
125
 
126
126
  registerInput(module: ManifestModule, moduleFile: string): CompileStateEntry {
127
127
  const relativeInput = `${module.outputFolder}/${moduleFile}`;
128
- const sourceFile = path.resolve(this.#manifest.workspacePath, module.sourceFolder, moduleFile);
128
+ const sourceFile = path.resolve(this.#manifest.workspace.path, module.sourceFolder, moduleFile);
129
129
  const sourceFolder = path.dirname(sourceFile);
130
130
  const inputFile = path.resolve(this.#rootDir, relativeInput); // Ensure input is isolated
131
131
  const inputFolder = path.dirname(inputFile);
package/src/util.ts CHANGED
@@ -31,7 +31,7 @@ export class CompilerUtil {
31
31
  */
32
32
  static rewriteSourceMap(ctx: ManifestContext, text: string, outputToSource: OutputToSource): string {
33
33
  const data: { sourceRoot?: string, sources: string[] } = JSON.parse(text);
34
- const output = ManifestModuleUtil.sourceToOutputExt(path.resolve(ctx.workspacePath, ctx.outputFolder, data.sources[0]));
34
+ const output = ManifestModuleUtil.sourceToOutputExt(path.resolve(ctx.workspace.path, ctx.build.outputFolder, data.sources[0]));
35
35
  const { source: file } = outputToSource(output) ?? {};
36
36
 
37
37
  if (file) {
@@ -81,7 +81,7 @@ export class CompilerUtil {
81
81
  if (pkg.main) {
82
82
  pkg.main = ManifestModuleUtil.sourceToOutputExt(pkg.main);
83
83
  }
84
- pkg.type = manifest.moduleType;
84
+ pkg.type = manifest.workspace.type;
85
85
  for (const key of ['devDependencies', 'dependencies', 'peerDependencies'] as const) {
86
86
  if (key in pkg) {
87
87
  for (const dep of Object.keys(pkg[key] ?? {})) {