@travetto/compiler 4.0.2 → 4.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
@@ -31,6 +31,7 @@ The compiler cli supports the following operations:
31
31
  * `clean` - Clean out the output and compiler caches
32
32
  * `info` - Retrieve the compiler information, if running
33
33
  * `event <log|progress|state>` - Watch events in realtime as newline delimited JSON
34
+ * `exec <file> [...args]` - Allow for compiling and executing an entrypoint file
34
35
  * `manifest --prod [output]` - Generate the project manifest
35
36
  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`.
36
37
 
package/bin/trvc.js CHANGED
@@ -14,6 +14,7 @@ const help = () => [
14
14
  ' * clean - Clean out the output and compiler caches',
15
15
  ' * info - Retrieve the compiler information, if running',
16
16
  ' * event <log|progress|state> - Watch events in realtime as newline delimited JSON',
17
+ ' * exec <file> [...args] - Allow for compiling and executing an entrypoint file',
17
18
  ' * manifest --prod [output] - Generate the project manifest',
18
19
  ].join('\n');
19
20
 
@@ -35,6 +36,7 @@ getEntry().then(async (ops) => {
35
36
  });
36
37
  case 'clean': return ops.clean();
37
38
  case 'manifest': return ops.manifest(args[0], flags.some(x => x === '--prod'));
39
+ case 'exec': return ops.getLoader().then(v => v(args[0], all.slice(1)));
38
40
  case 'start':
39
41
  case 'watch': return ops.watch();
40
42
  case 'build': return ops.build();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/compiler",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "The compiler infrastructure for the Travetto framework",
5
5
  "keywords": [
6
6
  "compiler",
@@ -31,12 +31,12 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@parcel/watcher": "^2.4.0",
34
- "@travetto/manifest": "^4.0.0",
35
- "@travetto/transformer": "^4.0.1",
34
+ "@travetto/manifest": "^4.0.1",
35
+ "@travetto/transformer": "^4.0.2",
36
36
  "@types/node": "^20.11.16"
37
37
  },
38
38
  "peerDependencies": {
39
- "@travetto/cli": "^4.0.3"
39
+ "@travetto/cli": "^4.0.4"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,4 +1,3 @@
1
- import timers from 'node:timers/promises';
2
1
  import fs from 'node:fs/promises';
3
2
  import { setMaxListeners } from 'node:events';
4
3
 
@@ -11,6 +10,7 @@ import { CompileEmitEvent, CompileEmitter } from './types';
11
10
  import { EventUtil } from './event';
12
11
 
13
12
  import { IpcLogger } from '../support/log';
13
+ import { CommonUtil } from '../support/util';
14
14
 
15
15
  const log = new IpcLogger({ level: 'debug' });
16
16
 
@@ -82,7 +82,7 @@ export class Compiler {
82
82
  process.removeAllListeners('disconnect');
83
83
  process.removeAllListeners('message');
84
84
  this.#ctrl.abort();
85
- setTimeout(() => process.exit(), 1000).unref(); // Allow upto 1s to shutdown gracefully
85
+ CommonUtil.nonBlockingTimeout(1000).then(() => process.exit()); // Allow upto 1s to shutdown gracefully
86
86
  }
87
87
 
88
88
  /**
@@ -113,7 +113,7 @@ export class Compiler {
113
113
  }
114
114
  EventUtil.sendEvent('progress', { total: files.length, idx: files.length, message: 'Complete', operation: 'compile', complete: true });
115
115
 
116
- await timers.setTimeout(1);
116
+ await CommonUtil.queueMacroTask();
117
117
 
118
118
  log.debug(`Compiled ${i} files`);
119
119
  }
@@ -91,7 +91,7 @@ export const main = (ctx: ManifestContext) => {
91
91
  },
92
92
 
93
93
  /** Build and return a loader */
94
- async getLoader(): Promise<(mod: string) => Promise<unknown>> {
94
+ async getLoader(): Promise<(mod: string, args?: string[]) => Promise<unknown>> {
95
95
  Log.initLevel('none');
96
96
  if (!(await client.isWatching())) { // Short circuit if we can
97
97
  Log.initLevel('error');
@@ -6,6 +6,7 @@ import { ManifestContext } from '@travetto/manifest';
6
6
 
7
7
  import type { CompilerEvent, CompilerEventType, CompilerServerInfo, CompilerStateType } from '../types';
8
8
  import type { LogShape } from '../log';
9
+ import { CommonUtil } from '../util';
9
10
  import { ProcessHandle } from './process-handle';
10
11
 
11
12
  type FetchEventsConfig<T> = {
@@ -14,6 +15,12 @@ type FetchEventsConfig<T> = {
14
15
  enforceIteration?: boolean;
15
16
  };
16
17
 
18
+ const streamAgent = new Agent({
19
+ keepAlive: true,
20
+ keepAliveMsecs: 10000,
21
+ timeout: 1000 * 60 * 60 * 24
22
+ });
23
+
17
24
  /**
18
25
  * Compiler Client Operations
19
26
  */
@@ -37,23 +44,26 @@ export class CompilerClient {
37
44
  return this.#url;
38
45
  }
39
46
 
40
- async #fetch(rel: string, opts?: RequestInit & { timeout?: number }, logTimeout = true): Promise<Response> {
47
+ async #fetch(rel: string, opts?: RequestInit & { timeout?: number }, logTimeout = true): Promise<{ ok: boolean, text: string }> {
41
48
  const ctrl = new AbortController();
49
+ const timeoutCtrl = new AbortController();
50
+
42
51
  opts?.signal?.addEventListener('abort', () => ctrl.abort());
43
- const timeoutId = setTimeout(() => {
44
- logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
45
- ctrl.abort('TIMEOUT');
46
- }, opts?.timeout ?? 100).unref();
47
- try {
48
- return await fetch(`${this.#url}${rel}`, { ...opts, signal: ctrl.signal });
49
- } finally {
50
- clearTimeout(timeoutId);
51
- }
52
+ timers.setTimeout(opts?.timeout ?? 100, undefined, { ref: false, signal: timeoutCtrl.signal })
53
+ .then(() => {
54
+ logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
55
+ ctrl.abort('TIMEOUT');
56
+ })
57
+ .catch(() => { });
58
+ const res = await fetch(`${this.#url}${rel}`, { ...opts, signal: ctrl.signal });
59
+ const out = { ok: res.ok, text: await res.text() };
60
+ timeoutCtrl.abort();
61
+ return out;
52
62
  }
53
63
 
54
64
  /** Get server information, if server is running */
55
65
  info(): Promise<CompilerServerInfo | undefined> {
56
- return this.#fetch('/info', { timeout: 200 }, false).then(v => v.json(), () => undefined)
66
+ return this.#fetch('/info', { timeout: 200 }, false).then(v => JSON.parse(v.text), () => undefined)
57
67
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
58
68
  .then(v => v as CompilerServerInfo);
59
69
  }
@@ -110,22 +120,16 @@ export class CompilerClient {
110
120
  const quit = (): void => ctrl.abort();
111
121
  try {
112
122
  signal.addEventListener('abort', quit);
113
- const res = await new Promise<http.IncomingMessage>(resolve => {
114
- http.get(`${this.#url}/event/${type}`, {
115
- agent: new Agent({
116
- keepAlive: true,
117
- keepAliveMsecs: 10000,
118
- timeout: 1000 * 60 * 60 * 24
119
- }),
120
- signal: ctrl.signal
121
- }, res => resolve(res))
122
- });
123
-
124
- for await (const line of rl.createInterface(res)) {
123
+ const response = await new Promise<http.IncomingMessage>((resolve, reject) =>
124
+ http.get(`${this.#url}/event/${type}`, { agent: streamAgent, signal: ctrl.signal }, resolve)
125
+ .on('error', reject)
126
+ );
127
+
128
+ for await (const line of rl.createInterface(response)) {
125
129
  if (line.trim().charAt(0) === '{') {
126
130
  const val = JSON.parse(line);
127
131
  if (cfg.until?.(val)) {
128
- await timers.setTimeout(1);
132
+ await CommonUtil.queueMacroTask();
129
133
  ctrl.abort();
130
134
  }
131
135
  yield val;
@@ -136,7 +140,7 @@ export class CompilerClient {
136
140
  }
137
141
  signal.removeEventListener('abort', quit);
138
142
 
139
- await timers.setTimeout(1);
143
+ await CommonUtil.queueMacroTask();
140
144
 
141
145
  info = await this.info();
142
146
 
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import timers from 'node:timers/promises';
4
3
 
5
4
  import type { ManifestContext } from '@travetto/manifest';
6
5
  import { Log, Logger } from '../log';
6
+ import { CommonUtil } from '../util';
7
7
 
8
8
  export class ProcessHandle {
9
9
 
@@ -56,7 +56,7 @@ export class ProcessHandle {
56
56
  if (!await this.isRunning()) {
57
57
  return true;
58
58
  }
59
- await timers.setTimeout(100);
59
+ await CommonUtil.nonBlockingTimeout(100);
60
60
  }
61
61
  try {
62
62
  this.#log.debug('Force Killing', pid);
@@ -77,7 +77,7 @@ export class CompilerServer {
77
77
  .on('close', () => log.debug('Server close event'));
78
78
 
79
79
  const url = new URL(this.#url);
80
- setTimeout(() => this.#server.listen(+url.port, url.hostname), 1); // Run async
80
+ CommonUtil.queueMacroTask().then(() => this.#server.listen(+url.port, url.hostname)); // Run async
81
81
  });
82
82
 
83
83
  if (output === 'retry') {
@@ -86,7 +86,7 @@ export class CompilerServer {
86
86
  }
87
87
  log.info('Waiting for build to finish, before retrying');
88
88
  // Let the server finish
89
- await this.#client.waitForState(['close'], 'Server closed', this.signal);
89
+ await this.#client.waitForState(['closed'], 'Server closed', this.signal);
90
90
  return this.#tryListen(attempt + 1);
91
91
  } else if (output === 'ok') {
92
92
  await this.#handle.server.writePid(this.info.serverPid);
@@ -121,7 +121,7 @@ export class CompilerServer {
121
121
  async #disconnectActive(): Promise<void> {
122
122
  log.info('Server disconnect requested');
123
123
  this.info.iteration = Date.now();
124
- await new Promise(r => setTimeout(r, 20));
124
+ await CommonUtil.nonBlockingTimeout(20);
125
125
  for (const el of Object.values(this.#listeners)) {
126
126
  try { el.res.end(); } catch { }
127
127
  }
@@ -204,9 +204,9 @@ export class CompilerServer {
204
204
 
205
205
  try {
206
206
  await new Promise((resolve, reject) => {
207
- setTimeout(reject, 2000).unref(); // 2s max wait
207
+ CommonUtil.nonBlockingTimeout(2000).then(reject); // Wait 2s max
208
208
  this.#server.close(resolve);
209
- this.#emitEvent({ type: 'state', payload: { state: 'close' } });
209
+ this.#emitEvent({ type: 'state', payload: { state: 'closed' } });
210
210
  setImmediate(() => {
211
211
  this.#server.closeAllConnections();
212
212
  this.#shutdown.abort();
package/support/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type CompilerMode = 'build' | 'watch';
2
2
 
3
- export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'close';
3
+ export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'closed';
4
4
  export type CompilerChangeEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
5
5
  export type CompilerLogLevel = 'info' | 'debug' | 'warn' | 'error';
6
6
  export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time?: number, args?: unknown[], scope?: string };
package/support/util.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import timers from 'node:timers/promises';
3
4
  import { setMaxListeners } from 'node:events';
4
5
 
5
6
  import type { ManifestContext } from '@travetto/manifest';
@@ -95,11 +96,28 @@ export class CommonUtil {
95
96
  * Create a module loader given a context, and assuming build is complete
96
97
  * @param ctx
97
98
  */
98
- static moduleLoader(ctx: ManifestContext): (mod: string) => Promise<unknown> {
99
- return (mod) => {
99
+ static moduleLoader(ctx: ManifestContext): (mod: string, args?: string[]) => Promise<unknown> {
100
+ return (mod, args) => {
100
101
  const outputRoot = path.resolve(ctx.workspace.path, ctx.build.outputFolder);
101
102
  process.env.TRV_MANIFEST = path.resolve(outputRoot, 'node_modules', ctx.main.name); // Setup for running
103
+ if (args) {
104
+ process.argv = [process.argv0, mod, ...args];
105
+ }
102
106
  return import(path.join(outputRoot, 'node_modules', mod)); // Return function to run import on a module
103
107
  };
104
108
  }
109
+
110
+ /**
111
+ * Non-blocking timeout, that is cancellable
112
+ */
113
+ static nonBlockingTimeout(time: number): Promise<void> {
114
+ return timers.setTimeout(time, undefined, { ref: false }).catch(() => { });
115
+ }
116
+
117
+ /**
118
+ * Queue new macrotask
119
+ */
120
+ static queueMacroTask(): Promise<void> {
121
+ return timers.setImmediate(undefined);
122
+ }
105
123
  }