@travetto/compiler 4.0.0-rc.5 → 4.0.0-rc.7

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
@@ -38,8 +38,8 @@ In addition to the normal output, the compiler supports an environment variable
38
38
  ```bash
39
39
  $ TRV_BUILD=debug trvc build
40
40
 
41
- 2029-03-14T04:00:00.618Z info [compiler-server] Starting server http://127.0.0.1:25539
42
- 2029-03-14T04:00:00.837Z debug [client.main ] Start Server
41
+ 2029-03-14T04:00:00.618Z info [server ] Starting server http://127.0.0.1:25539
42
+ 2029-03-14T04:00:00.837Z debug [main ] Start Server
43
43
  2029-03-14T04:00:01.510Z debug [event-stream ] Started event stream
44
44
  2029-03-14T04:00:02.450Z debug [precompile ] Started
45
45
  2029-03-14T04:00:02.762Z debug [precompile ] Skipped @travetto/manifest
@@ -60,14 +60,14 @@ $ TRV_BUILD=debug trvc build
60
60
  2029-03-14T04:00:10.799Z debug [manifest ] Started
61
61
  2029-03-14T04:00:11.013Z debug [manifest ] Wrote manifest @travetto-doc/compiler
62
62
  2029-03-14T04:00:11.827Z debug [manifest ] Completed
63
- 2029-03-14T04:00:11.894Z info [compiler-server] State changed: compile-end
63
+ 2029-03-14T04:00:11.894Z info [server ] State changed: compile-end
64
64
  2029-03-14T04:00:12.133Z debug [compiler-exec ] Skipped
65
65
  2029-03-14T04:00:13.123Z debug [event-stream ] Finished event stream
66
- 2029-03-14T04:00:14.014Z info [compiler-server] Closing down server
67
- 2029-03-14T04:00:14.924Z debug [compiler-server] Server close event
68
- 2029-03-14T04:00:15.690Z info [compiler-server] Closed down server
69
- 2029-03-14T04:00:15.865Z debug [compiler-server] Finished processing events
70
- 2029-03-14T04:00:16.757Z debug [client.main ] End Server
66
+ 2029-03-14T04:00:14.014Z info [server ] Closing down server
67
+ 2029-03-14T04:00:14.924Z debug [server ] Server close event
68
+ 2029-03-14T04:00:15.690Z info [server ] Closed down server
69
+ 2029-03-14T04:00:15.865Z debug [server ] Finished processing events
70
+ 2029-03-14T04:00:16.757Z debug [main ] End Server
71
71
  ```
72
72
 
73
73
  **Terminal: Sample trv output with default log level**
package/__index__.ts CHANGED
@@ -2,5 +2,4 @@ export * from './src/compiler';
2
2
  export * from './src/state';
3
3
  export * from './src/util';
4
4
  export * from './src/watch';
5
- export * from './src/log';
6
5
  export * from './src/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/compiler",
3
- "version": "4.0.0-rc.5",
3
+ "version": "4.0.0-rc.7",
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-rc.5",
35
- "@travetto/transformer": "^4.0.0-rc.5",
34
+ "@travetto/manifest": "^4.0.0-rc.7",
35
+ "@travetto/transformer": "^4.0.0-rc.7",
36
36
  "@types/node": "^20.11.16"
37
37
  },
38
38
  "peerDependencies": {
39
- "@travetto/cli": "^4.0.0-rc.6"
39
+ "@travetto/cli": "^4.0.0-rc.8"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,4 +1,3 @@
1
- import ts from 'typescript';
2
1
  import timers from 'node:timers/promises';
3
2
  import fs from 'node:fs/promises';
4
3
  import { setMaxListeners } from 'node:events';
@@ -8,10 +7,13 @@ import { ManifestModuleUtil, RuntimeIndex } from '@travetto/manifest';
8
7
  import { CompilerUtil } from './util';
9
8
  import { CompilerState } from './state';
10
9
  import { CompilerWatcher } from './watch';
11
- import { Log } from './log';
12
- import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
10
+ import { CompileEmitEvent, CompileEmitter } from './types';
13
11
  import { EventUtil } from './event';
14
12
 
13
+ import { IpcLogger } from '../support/log';
14
+
15
+ const log = new IpcLogger({ level: 'debug' });
16
+
15
17
  /**
16
18
  * Compilation support
17
19
  */
@@ -23,7 +25,7 @@ export class Compiler {
23
25
  static async main(): Promise<void> {
24
26
  const [dirty, watch] = process.argv.slice(2);
25
27
  const state = await CompilerState.get(RuntimeIndex);
26
- Log.debug('Running compiler with dirty file', dirty);
28
+ log.debug('Running compiler with dirty file', dirty);
27
29
  const dirtyFiles = ManifestModuleUtil.getFileType(dirty) === 'ts' ? [dirty] : (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
28
30
  await new Compiler(state, dirtyFiles, watch === 'true').run();
29
31
  }
@@ -58,19 +60,19 @@ export class Compiler {
58
60
  this.#shuttingDown = true;
59
61
  switch (mode) {
60
62
  case 'manual': {
61
- Log.error('Shutting down manually');
63
+ log.error('Shutting down manually');
62
64
  process.exitCode = 2;
63
65
  break;
64
66
  }
65
67
  case 'error': {
66
68
  process.exitCode = 1;
67
69
  if (err) {
68
- Log.error('Shutting down due to failure', err.message);
70
+ log.error('Shutting down due to failure', err.message);
69
71
  }
70
72
  break;
71
73
  }
72
74
  case 'reset': {
73
- Log.info('Triggering reset due to change in core files', err?.cause);
75
+ log.info('Triggering reset due to change in core files', err?.cause);
74
76
  EventUtil.sendEvent('state', { state: 'reset' });
75
77
  process.exitCode = 0;
76
78
  break;
@@ -86,29 +88,8 @@ export class Compiler {
86
88
  /**
87
89
  * Compile in a single pass, only emitting dirty files
88
90
  */
89
- async getCompiler(): Promise<CompileEmitter> {
90
- let program: ts.Program;
91
-
92
- const emit = async (inputFile: string, needsNewProgram = program === undefined): Promise<CompileEmitError | undefined> => {
93
- try {
94
- if (needsNewProgram) {
95
- program = this.#state.createProgram(program);
96
- }
97
- await timers.setImmediate();
98
- const result = this.#state.writeInputFile(program, inputFile);
99
- if (result?.diagnostics?.length) {
100
- return result.diagnostics;
101
- }
102
- } catch (err) {
103
- if (err instanceof Error) {
104
- return err;
105
- } else {
106
- throw err;
107
- }
108
- }
109
- };
110
-
111
- return emit;
91
+ getCompiler(): CompileEmitter {
92
+ return (inputFile: string, needsNewProgram?: boolean) => this.#state.writeInputFile(inputFile, needsNewProgram);
112
93
  }
113
94
 
114
95
  /**
@@ -134,21 +115,21 @@ export class Compiler {
134
115
 
135
116
  await timers.setTimeout(1);
136
117
 
137
- Log.debug(`Compiled ${i} files`);
118
+ log.debug(`Compiled ${i} files`);
138
119
  }
139
120
 
140
121
  /**
141
122
  * Run the compiler
142
123
  */
143
124
  async run(): Promise<void> {
144
- Log.debug('Compilation started');
125
+ log.debug('Compilation started');
145
126
 
146
127
  EventUtil.sendEvent('state', { state: 'init', extra: { pid: process.pid } });
147
128
 
148
129
  const emitter = await this.getCompiler();
149
130
  let failed = false;
150
131
 
151
- Log.debug('Compiler loaded');
132
+ log.debug('Compiler loaded');
152
133
 
153
134
  EventUtil.sendEvent('state', { state: 'compile-start' });
154
135
 
@@ -161,13 +142,13 @@ export class Compiler {
161
142
  }
162
143
  }
163
144
  if (this.#signal.aborted) {
164
- Log.debug('Compilation aborted');
145
+ log.debug('Compilation aborted');
165
146
  } else if (failed) {
166
- Log.debug('Compilation failed');
147
+ log.debug('Compilation failed');
167
148
  process.exitCode = 1;
168
149
  return;
169
150
  } else {
170
- Log.debug('Compilation succeeded');
151
+ log.debug('Compilation succeeded');
171
152
  }
172
153
  } else if (this.#watch) {
173
154
  // Prime compiler before complete
@@ -178,7 +159,7 @@ export class Compiler {
178
159
  EventUtil.sendEvent('state', { state: 'compile-end' });
179
160
 
180
161
  if (this.#watch && !this.#signal.aborted) {
181
- Log.info('Watch is ready');
162
+ log.info('Watch is ready');
182
163
 
183
164
  EventUtil.sendEvent('state', { state: 'watch-start' });
184
165
  try {
@@ -186,14 +167,14 @@ export class Compiler {
186
167
  if (ev.action !== 'delete') {
187
168
  const err = await emitter(ev.entry.inputFile, true);
188
169
  if (err) {
189
- Log.info('Compilation Error', CompilerUtil.buildTranspileError(ev.entry.inputFile, err));
170
+ log.info('Compilation Error', CompilerUtil.buildTranspileError(ev.entry.inputFile, err));
190
171
  } else {
191
- Log.info(`Compiled ${ev.entry.sourceFile} on ${ev.action}`);
172
+ log.info(`Compiled ${ev.entry.sourceFile} on ${ev.action}`);
192
173
  }
193
174
  } else {
194
175
  if (ev.entry.outputFile) {
195
176
  // Remove output
196
- Log.info(`Removed ${ev.entry.sourceFile}, ${ev.entry.outputFile}`);
177
+ log.info(`Removed ${ev.entry.sourceFile}, ${ev.entry.outputFile}`);
197
178
  await fs.rm(ev.entry.outputFile, { force: true }); // Ensure output is deleted
198
179
  }
199
180
  }
@@ -216,7 +197,7 @@ export class Compiler {
216
197
  }
217
198
  }
218
199
 
219
- Log.debug('Compiler process shutdown');
200
+ log.debug('Compiler process shutdown');
220
201
 
221
202
  this.#shutdown('complete');
222
203
  }
package/src/state.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import ts from 'typescript';
2
+ import timers from 'node:timers/promises';
2
3
 
3
4
  import { path, ManifestModuleUtil, ManifestModule, ManifestRoot, ManifestIndex } from '@travetto/manifest';
4
5
  import { TransformerManager } from '@travetto/transformer';
@@ -6,7 +7,7 @@ import { TransformerManager } from '@travetto/transformer';
6
7
  import { CommonUtil } from '../support/util';
7
8
 
8
9
  import { CompilerUtil } from './util';
9
- import { CompileStateEntry } from './types';
10
+ import { CompileEmitError, CompileStateEntry } from './types';
10
11
 
11
12
  function folderMapper(root: string, prefix: string): { dir: string, translate: (val: string) => string } {
12
13
  let matched: string = '~~';
@@ -44,6 +45,7 @@ export class CompilerState implements ts.CompilerHost {
44
45
  #modules: ManifestModule[];
45
46
  #transformerManager: TransformerManager;
46
47
  #compilerOptions: ts.CompilerOptions;
48
+ #program: ts.Program;
47
49
 
48
50
  #readFile(inputFile: string): string | undefined {
49
51
  return ts.sys.readFile(this.#inputToEntry.get(inputFile)?.sourceFile ?? this.#inputPathToSourcePath(inputFile));
@@ -104,24 +106,40 @@ export class CompilerState implements ts.CompilerHost {
104
106
  return this.getBySource(this.#manifestIndex.getModule('@travetto/manifest')!.files.src[0].sourceFile)!.inputFile;
105
107
  }
106
108
 
107
- createProgram(oldProgram?: ts.Program): ts.Program {
108
- const prog = ts.createProgram({ rootNames: this.getAllFiles(), host: this, options: this.#compilerOptions, oldProgram });
109
- this.#transformerManager.init(prog.getTypeChecker());
110
- return prog;
111
- }
112
-
113
- writeInputFile(program: ts.Program, inputFile: string): ts.EmitResult | undefined | void {
114
- switch (ManifestModuleUtil.getFileType(inputFile)) {
115
- case 'package-json':
116
- return this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, this.readFile(inputFile)!, false);
117
- case 'js':
118
- return this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, ts.transpile(this.readFile(inputFile)!, this.#compilerOptions), false);
119
- case 'ts':
120
- return program.emit(
121
- program.getSourceFile(inputFile)!,
122
- (...args) => this.writeFile(...args), undefined, false,
123
- this.#transformerManager.get()
124
- );
109
+ async createProgram(force = false): Promise<ts.Program> {
110
+ if (force || !this.#program) {
111
+ this.#program = ts.createProgram({ rootNames: this.getAllFiles(), host: this, options: this.#compilerOptions, oldProgram: this.#program });
112
+ this.#transformerManager.init(this.#program.getTypeChecker());
113
+ await timers.setImmediate();
114
+ }
115
+ return this.#program;
116
+ }
117
+
118
+ async writeInputFile(inputFile: string, needsNewProgram = false): Promise<CompileEmitError | undefined> {
119
+ const program = await this.createProgram(needsNewProgram);
120
+ try {
121
+ switch (ManifestModuleUtil.getFileType(inputFile)) {
122
+ case 'package-json':
123
+ this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, this.readFile(inputFile)!, false), undefined;
124
+ break;
125
+ case 'js':
126
+ this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, ts.transpile(this.readFile(inputFile)!, this.#compilerOptions), false);
127
+ break;
128
+ case 'ts': {
129
+ const result = program.emit(
130
+ program.getSourceFile(inputFile)!,
131
+ (...args) => this.writeFile(...args), undefined, false,
132
+ this.#transformerManager.get()
133
+ );
134
+ return result?.diagnostics?.length ? result.diagnostics : undefined;
135
+ }
136
+ }
137
+ } catch (err) {
138
+ if (err instanceof Error) {
139
+ return err;
140
+ } else {
141
+ throw err;
142
+ }
125
143
  }
126
144
  }
127
145
 
@@ -4,8 +4,8 @@ import path from 'node:path';
4
4
 
5
5
  import type { ManifestContext } from '@travetto/manifest';
6
6
 
7
- import type { CompilerLogLevel, CompilerMode, CompilerServerInfo } from './types';
8
- import { LogUtil } from './log';
7
+ import type { CompilerMode, CompilerServerInfo } from './types';
8
+ import { Log } from './log';
9
9
  import { CommonUtil } from './util';
10
10
  import { CompilerSetup } from './setup';
11
11
  import { CompilerServer } from './server/server';
@@ -14,40 +14,38 @@ import { CompilerClient } from './server/client';
14
14
 
15
15
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
16
16
  export const main = (ctx: ManifestContext) => {
17
- const log = LogUtil.logger('client.main');
18
- const client = new CompilerClient(ctx, log);
17
+ const client = new CompilerClient(ctx, Log.scoped('client'));
19
18
  const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
19
+ Log.root = ctx.workspace.path;
20
+ Log.initLevel('error');
20
21
 
21
22
  /** Main entry point for compilation */
22
- const compile = async (op: CompilerMode, logLevel: CompilerLogLevel, setupOnly = false): Promise<void> => {
23
- LogUtil.initLogs(ctx, logLevel ?? 'info');
24
-
23
+ const compile = async (op: CompilerMode, setupOnly = false): Promise<void> => {
25
24
  const server = await new CompilerServer(ctx, op).listen();
25
+ const log = Log.scoped('main');
26
26
 
27
27
  // Wait for build to be ready
28
28
  if (server) {
29
- log('debug', 'Start Server');
29
+ log.debug('Start Server');
30
30
  await server.processEvents(async function* (signal) {
31
31
  const changed = await CompilerSetup.setup(ctx);
32
32
  if (!setupOnly) {
33
33
  yield* CompilerRunner.runProcess(ctx, changed, op, signal);
34
34
  }
35
35
  });
36
- log('debug', 'End Server');
36
+ log.debug('End Server');
37
37
  } else {
38
- log('info', 'Server already running, waiting for initial compile to complete');
38
+ log.info('Server already running, waiting for initial compile to complete');
39
39
  const ctrl = new AbortController();
40
- LogUtil.consumeProgressEvents(() => client.fetchEvents('progress', { until: ev => !!ev.complete, signal: ctrl.signal }));
40
+ Log.consumeProgressEvents(() => client.fetchEvents('progress', { until: ev => !!ev.complete, signal: ctrl.signal }));
41
41
  await client.waitForState(['compile-end', 'watch-start'], 'Successfully built');
42
42
  ctrl.abort();
43
43
  }
44
- LogUtil.cleanup();
45
44
  };
46
45
 
47
46
  const ops = {
48
47
  /** Stop the server */
49
48
  async stop(): Promise<void> {
50
- LogUtil.initLogs(ctx);
51
49
  if (await client.stop()) {
52
50
  console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
53
51
  } else {
@@ -63,7 +61,6 @@ export const main = (ctx: ManifestContext) => {
63
61
 
64
62
  /** Clean the server */
65
63
  async clean(): Promise<void> {
66
- LogUtil.initLogs(ctx);
67
64
  if (await client.clean()) {
68
65
  return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
69
66
  } else {
@@ -74,7 +71,6 @@ export const main = (ctx: ManifestContext) => {
74
71
 
75
72
  /** Stream events */
76
73
  events: async (type: string, handler: (ev: unknown) => unknown): Promise<void> => {
77
- LogUtil.initLogs(ctx, 'error');
78
74
  if (type === 'change' || type === 'log' || type === 'progress' || type === 'state') {
79
75
  for await (const ev of client.fetchEvents(type)) { await handler(ev); }
80
76
  } else {
@@ -83,23 +79,30 @@ export const main = (ctx: ManifestContext) => {
83
79
  },
84
80
 
85
81
  /** Build the project */
86
- async build(): Promise<void> { await compile('build', 'info'); },
82
+ async build(): Promise<void> {
83
+ Log.initLevel('info');
84
+ await compile('build');
85
+ },
87
86
 
88
87
  /** Build and watch the project */
89
- async watch(): Promise<void> { await compile('watch', 'info'); },
88
+ async watch(): Promise<void> {
89
+ Log.initLevel('info');
90
+ await compile('watch');
91
+ },
90
92
 
91
93
  /** Build and return a loader */
92
94
  async getLoader(): Promise<(mod: string) => Promise<unknown>> {
93
- // Short circuit if we can
94
- if (!(await client.isWatching())) {
95
- await compile('build', LogUtil.isInteractiveShell ? 'info' : 'error');
95
+ Log.initLevel('none');
96
+ if (!(await client.isWatching())) { // Short circuit if we can
97
+ Log.initLevel('error');
98
+ await compile('build');
96
99
  }
97
100
  return CommonUtil.moduleLoader(ctx);
98
101
  },
99
102
 
100
103
  /** Manifest entry point */
101
104
  async manifest(output?: string, prod?: boolean): Promise<void> {
102
- await compile('build', 'error', true);
105
+ await compile('build', true);
103
106
  await CompilerSetup.exportManifest(ctx, output, prod); return;
104
107
  }
105
108
  };
package/support/log.ts CHANGED
@@ -1,117 +1,130 @@
1
- import type { ManifestContext } from '@travetto/manifest';
2
- import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
3
-
4
- export type CompilerLogger = (level: CompilerLogLevel, message: string, ...args: unknown[]) => void;
5
- export type WithLogger<T> = (log: CompilerLogger) => Promise<T>;
6
-
7
- type ProgressWriter = (ev: CompilerProgressEvent) => (unknown | Promise<unknown>);
8
-
9
- const LEVEL_TO_PRI: Record<CompilerLogLevel, number> = { debug: 1, info: 2, warn: 3, error: 4 };
1
+ import { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
10
2
 
3
+ const LEVEL_TO_PRI: Record<CompilerLogLevel | 'none', number> = { debug: 1, info: 2, warn: 3, error: 4, none: 5 };
11
4
  const SCOPE_MAX = 15;
12
5
 
13
- const ESC = '\x1b[';
14
-
15
- export class LogUtil {
6
+ type LogConfig = {
7
+ level?: CompilerLogLevel | 'none';
8
+ root?: string;
9
+ scope?: string;
10
+ parent?: Logger;
11
+ };
16
12
 
17
- static root = process.cwd();
13
+ export type LogShape = Record<'info' | 'debug' | 'warn' | 'error', (message: string, ...args: unknown[]) => void>;
18
14
 
19
- static logLevel: CompilerLogLevel = 'error';
15
+ const ESC = '\x1b[';
20
16
 
21
- static logProgress?: ProgressWriter;
17
+ export class Logger implements LogConfig, LogShape {
22
18
 
23
- static linePartial = false;
19
+ static #linePartial: boolean | undefined;
24
20
 
25
- static #rewriteLine(text: string): Promise<void> | void {
21
+ /** Rewrite text line, tracking cleanup as necessary */
22
+ static rewriteLine(text: string): Promise<void> | void {
23
+ if ((!text && !this.#linePartial) || !process.stdout.isTTY) {
24
+ return;
25
+ }
26
+ if (this.#linePartial === undefined) { // First time
27
+ process.stdout.write(`${ESC}?25l`); // Hide cursor
28
+ process.on('exit', () => this.reset());
29
+ }
26
30
  // Move to 1st position, and clear after text
27
31
  const done = process.stdout.write(`${ESC}1G${text}${ESC}0K`);
28
- this.linePartial = !!text;
32
+ this.#linePartial = !!text;
29
33
  if (!done) {
30
34
  return new Promise<void>(r => process.stdout.once('drain', r));
31
35
  }
32
36
  }
33
37
 
34
- /** Are we in a shell that is interactive */
35
- static get isInteractiveShell(): boolean {
36
- return !!process.env.PS1 && process.stdout.isTTY;
38
+ static reset(): void { process.stdout.write(`${ESC}!p${ESC}?25h`); }
39
+
40
+ level?: CompilerLogLevel | 'none';
41
+ root: string = process.cwd();
42
+ scope?: string;
43
+ parent?: Logger;
44
+
45
+ constructor(cfg: LogConfig = {}) {
46
+ Object.assign(this, cfg);
37
47
  }
38
48
 
39
- /**
40
- * Set level for operation
41
- */
42
- static initLogs(ctx: ManifestContext, defaultLevel?: CompilerLogLevel): void {
43
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
- const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
45
- if (build !== 'none' && process.env.TRV_QUIET !== 'true') {
46
- this.logLevel = build || defaultLevel;
47
- }
48
- this.root = ctx.workspace.path;
49
- // If we are in info or a terminal and also in a tty
50
- this.logProgress = (this.isLevelActive('info') && process.stdout.isTTY) ? this.#logProgressEvent : undefined;
51
- if (this.logProgress) {
52
- process.stdout.write(`${ESC}?25l`); // Hide cursor
49
+ valid(ev: CompilerLogEvent): boolean {
50
+ return LEVEL_TO_PRI[this.level ?? this.parent?.level!] <= LEVEL_TO_PRI[ev.level];
51
+ }
52
+
53
+ /** Log event with filtering by level */
54
+ render(ev: CompilerLogEvent): void {
55
+ if (!this.valid(ev)) { return; }
56
+ const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root ?? this.parent?.root!, '.') : x);
57
+ if (ev.scope ?? this.scope) {
58
+ params.unshift(`[${(ev.scope ?? this.scope!).padEnd(SCOPE_MAX, ' ')}]`);
53
59
  }
54
- process.on('exit', () => this.cleanup());
60
+ params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
61
+ Logger.rewriteLine(''); // Clear out progress line, if active
62
+ // eslint-disable-next-line no-console
63
+ console[ev.level]!(...params);
55
64
  }
56
65
 
57
- static cleanup(): void {
58
- if (this.logProgress) {
59
- process.stdout.write(`${ESC}!p`); // Reset
66
+ info(message: string, ...args: unknown[]): void { return this.render({ level: 'info', message, args }); }
67
+ debug(message: string, ...args: unknown[]): void { return this.render({ level: 'debug', message, args }); }
68
+ warn(message: string, ...args: unknown[]): void { return this.render({ level: 'warn', message, args }); }
69
+ error(message: string, ...args: unknown[]): void { return this.render({ level: 'error', message, args }); }
70
+ }
71
+
72
+
73
+ class $RootLogger extends Logger {
74
+ #logProgress?: boolean;
75
+
76
+ /** Get if we should log progress */
77
+ get logProgress(): boolean {
78
+ if (this.#logProgress === undefined) {
79
+ this.#logProgress = !!process.env.PS1 && process.stdout.isTTY && process.env.TRV_BUILD !== 'none' && process.env.TRV_QUIET !== 'true';
60
80
  }
81
+ return this.#logProgress;
61
82
  }
62
83
 
63
- static #logProgressEvent(ev: CompilerProgressEvent): Promise<void> | void {
64
- const pct = Math.trunc(ev.idx * 100 / ev.total);
65
- const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
66
- return this.#rewriteLine(text);
84
+ /** Set level for operation */
85
+ initLevel(defaultLevel: CompilerLogLevel | 'none'): void {
86
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
87
+ const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
88
+ this.level = (build !== 'none' && process.env.TRV_QUIET !== 'true') ? (build || defaultLevel) : 'none';
67
89
  }
68
90
 
69
- /**
70
- * Is the log level active?
71
- */
72
- static isLevelActive(lvl: CompilerLogLevel): boolean {
73
- return LEVEL_TO_PRI[this.logLevel] <= LEVEL_TO_PRI[lvl];
91
+ /** Produce a scoped logger */
92
+ scoped(name: string): Logger {
93
+ return new Logger({ parent: this, scope: name });
74
94
  }
75
95
 
76
- /**
77
- * Log event with filtering by level
78
- */
79
- static logEvent(ev: CompilerLogEvent): void {
80
- if (this.isLevelActive(ev.level)) {
81
- const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root, '.') : x);
82
- if (ev.scope) {
83
- params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
84
- }
85
- params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
86
- if (this.linePartial) {
87
- this.#rewriteLine(''); // Clear out progress line
88
- }
89
- // eslint-disable-next-line no-console
90
- console[ev.level]!(...params);
91
- }
96
+ /** Scope and provide a callback pattern for access to a logger */
97
+ wrap<T = unknown>(scope: string, op: (log: Logger) => Promise<T>, basic = true): Promise<T> {
98
+ const l = this.scoped(scope);
99
+ return basic ? (l.debug('Started'), op(l).finally(() => l.debug('Completed'))) : op(l);
92
100
  }
93
101
 
94
- /**
95
- * With logger
96
- */
97
- static withLogger<T>(scope: string, op: WithLogger<T>, basic = true): Promise<T> {
98
- const log = this.logger(scope);
99
- basic && log('debug', 'Started');
100
- return op(log).finally(() => basic && log('debug', 'Completed'));
102
+ /** Write progress event, if active */
103
+ onProgressEvent(ev: CompilerProgressEvent): void | Promise<void> {
104
+ if (!(this.logProgress)) { return; }
105
+ const pct = Math.trunc(ev.idx * 100 / ev.total);
106
+ const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
107
+ return Logger.rewriteLine(text);
101
108
  }
102
109
 
103
- /**
104
- * With scope
105
- */
106
- static logger(scope: string): CompilerLogger {
107
- return (level, message, ...args) => this.logEvent({ scope, message, level, args, time: Date.now() });
110
+ /** Write all progress events if active */
111
+ async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
112
+ if (!(this.logProgress)) { return; }
113
+ for await (const ev of src()) { this.onProgressEvent(ev); }
114
+ Logger.reset();
108
115
  }
116
+ }
109
117
 
110
- /**
111
- * Write all progress events if active
112
- */
113
- static async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
114
- if (!this.logProgress) { return; }
115
- for await (const item of src()) { await this.logProgress?.(item); }
118
+ export const Log = new $RootLogger();
119
+
120
+ export class IpcLogger extends Logger {
121
+ render(ev: CompilerLogEvent): void {
122
+ if (!this.valid(ev)) { return; }
123
+ if (process.connected && process.send) {
124
+ process.send({ type: 'log', payload: ev });
125
+ }
126
+ if (!process.connected) {
127
+ super.render(ev);
128
+ }
116
129
  }
117
- }
130
+ }
@@ -5,7 +5,7 @@ import { Readable } from 'node:stream';
5
5
  import { ManifestContext } from '@travetto/manifest';
6
6
 
7
7
  import type { CompilerEvent, CompilerEventType, CompilerServerInfo, CompilerStateType } from '../types';
8
- import type { CompilerLogger } from '../log';
8
+ import type { LogShape } from '../log';
9
9
  import { ProcessHandle } from './process-handle';
10
10
 
11
11
  type FetchEventsConfig<T> = {
@@ -20,10 +20,10 @@ type FetchEventsConfig<T> = {
20
20
  export class CompilerClient {
21
21
 
22
22
  #url: string;
23
- #log: CompilerLogger;
23
+ #log: LogShape;
24
24
  #handle: Record<'compiler' | 'server', ProcessHandle>;
25
25
 
26
- constructor(ctx: ManifestContext, log: CompilerLogger) {
26
+ constructor(ctx: ManifestContext, log: LogShape) {
27
27
  this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
28
28
  this.#log = log;
29
29
  this.#handle = { compiler: new ProcessHandle(ctx, 'compiler'), server: new ProcessHandle(ctx, 'server') };
@@ -41,7 +41,7 @@ export class CompilerClient {
41
41
  const ctrl = new AbortController();
42
42
  opts?.signal?.addEventListener('abort', () => ctrl.abort());
43
43
  const timeoutId = setTimeout(() => {
44
- logTimeout && this.#log('error', `Timeout on request to ${this.#url}${rel}`);
44
+ logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
45
45
  ctrl.abort('TIMEOUT');
46
46
  }, opts?.timeout ?? 100).unref();
47
47
  try {
@@ -71,13 +71,13 @@ export class CompilerClient {
71
71
  async stop(): Promise<boolean> {
72
72
  const info = await this.info();
73
73
  if (!info) {
74
- this.#log('debug', 'Stopping server, info not found, manual killing');
74
+ this.#log.debug('Stopping server, info not found, manual killing');
75
75
  return Promise.all([this.#handle.server.kill(), this.#handle.compiler.kill()])
76
76
  .then(v => v.some(x => x));
77
77
  }
78
78
 
79
79
  await this.#fetch('/stop').catch(() => { }); // Trigger
80
- this.#log('debug', 'Waiting for compiler to exit');
80
+ this.#log.debug('Waiting for compiler to exit');
81
81
  await this.#handle.compiler.ensureKilled();
82
82
  return true;
83
83
  }
@@ -92,7 +92,7 @@ export class CompilerClient {
92
92
  return;
93
93
  }
94
94
 
95
- this.#log('debug', `Starting watch for events of type "${type}"`);
95
+ this.#log.debug(`Starting watch for events of type "${type}"`);
96
96
 
97
97
  let signal = cfg.signal;
98
98
 
@@ -132,15 +132,15 @@ export class CompilerClient {
132
132
  info = await this.info();
133
133
 
134
134
  if (ctrl.signal.reason === 'TIMEOUT') {
135
- this.#log('debug', 'Failed due to timeout');
135
+ this.#log.debug('Failed due to timeout');
136
136
  return;
137
137
  }
138
138
 
139
139
  if (ctrl.signal.aborted || !info || (cfg.enforceIteration && info.iteration !== iteration)) { // If health check fails, or aborted
140
- this.#log('debug', `Stopping watch for events of type "${type}"`);
140
+ this.#log.debug(`Stopping watch for events of type "${type}"`);
141
141
  return;
142
142
  } else {
143
- this.#log('debug', `Restarting watch for events of type "${type}"`);
143
+ this.#log.debug(`Restarting watch for events of type "${type}"`);
144
144
  }
145
145
  }
146
146
  }
@@ -149,11 +149,11 @@ export class CompilerClient {
149
149
  async waitForState(states: CompilerStateType[], message?: string, signal?: AbortSignal): Promise<void> {
150
150
  const set = new Set(states);
151
151
  // Loop until
152
- this.#log('debug', `Waiting for states, ${states.join(', ')}`);
152
+ this.#log.debug(`Waiting for states, ${states.join(', ')}`);
153
153
  for await (const _ of this.fetchEvents('state', { signal, until: s => set.has(s.state) })) { }
154
- this.#log('debug', `Found state, one of ${states.join(', ')} `);
154
+ this.#log.debug(`Found state, one of ${states.join(', ')} `);
155
155
  if (message) {
156
- this.#log('info', message);
156
+ this.#log.info(message);
157
157
  }
158
158
  }
159
159
  }
@@ -3,16 +3,16 @@ import path from 'node:path';
3
3
  import timers from 'node:timers/promises';
4
4
 
5
5
  import type { ManifestContext } from '@travetto/manifest';
6
- import { CompilerLogger, LogUtil } from '../log';
6
+ import { Log, Logger } from '../log';
7
7
 
8
8
  export class ProcessHandle {
9
9
 
10
10
  #file: string;
11
- #log: CompilerLogger;
11
+ #log: Logger;
12
12
 
13
13
  constructor(ctx: ManifestContext, name: string) {
14
14
  this.#file = path.resolve(ctx.workspace.path, ctx.build.toolFolder, `${name}.pid`);
15
- this.#log = LogUtil.logger(`process-handle.${name}`);
15
+ this.#log = Log.scoped(`process-handle.${name}`);
16
16
  }
17
17
 
18
18
  async writePid(pid: number): Promise<void> {
@@ -29,10 +29,10 @@ export class ProcessHandle {
29
29
  if (!pid) { return false; }
30
30
  try {
31
31
  process.kill(pid, 0); // See if process is still running
32
- this.#log('debug', 'Is running', pid);
32
+ this.#log.debug('Is running', pid);
33
33
  return true;
34
34
  } catch {
35
- this.#log('debug', 'Is not running', pid);
35
+ this.#log.debug('Is not running', pid);
36
36
  }
37
37
  return false; // Not running
38
38
  }
@@ -41,7 +41,7 @@ export class ProcessHandle {
41
41
  const pid = await this.getPid();
42
42
  if (pid && await this.isRunning()) {
43
43
  try {
44
- this.#log('debug', 'Killing', pid);
44
+ this.#log.debug('Killing', pid);
45
45
  return process.kill(pid);
46
46
  } catch { }
47
47
  }
@@ -51,7 +51,7 @@ export class ProcessHandle {
51
51
  async ensureKilled(gracePeriod: number = 3000): Promise<boolean> {
52
52
  const start = Date.now();
53
53
  const pid = await this.getPid();
54
- this.#log('debug', 'Ensuring Killed', pid);
54
+ this.#log.debug('Ensuring Killed', pid);
55
55
  while (pid && (Date.now() - start) < gracePeriod) { // Ensure its done
56
56
  if (!await this.isRunning()) {
57
57
  return true;
@@ -59,10 +59,10 @@ export class ProcessHandle {
59
59
  await timers.setTimeout(100);
60
60
  }
61
61
  try {
62
- this.#log('debug', 'Force Killing', pid);
62
+ this.#log.debug('Force Killing', pid);
63
63
  pid && process.kill(pid); // Force kill
64
64
  } catch { }
65
- this.#log('debug', 'Did Kill', this.#file, !!pid);
65
+ this.#log.debug('Did Kill', this.#file, !!pid);
66
66
  return pid !== undefined;
67
67
  }
68
68
  }
@@ -6,10 +6,10 @@ import type { ManifestContext, DeltaEvent } from '@travetto/manifest';
6
6
 
7
7
  import type { CompilerEvent, CompilerMode } from '../types';
8
8
  import { AsyncQueue } from '../queue';
9
- import { LogUtil } from '../log';
9
+ import { Log } from '../log';
10
10
  import { CommonUtil } from '../util';
11
11
 
12
- const log = LogUtil.logger('compiler-exec');
12
+ const log = Log.scoped('compiler-exec');
13
13
  const isEvent = (msg: unknown): msg is CompilerEvent => !!msg && typeof msg === 'object' && 'type' in msg;
14
14
 
15
15
  /**
@@ -22,17 +22,17 @@ export class CompilerRunner {
22
22
  */
23
23
  static async * runProcess(ctx: ManifestContext, changed: DeltaEvent[], mode: CompilerMode, signal: AbortSignal): AsyncIterable<CompilerEvent> {
24
24
  if (signal.aborted) {
25
- log('debug', 'Skipping, shutting down');
25
+ log.debug('Skipping, shutting down');
26
26
  return;
27
27
  }
28
28
 
29
29
  const watch = mode === 'watch';
30
30
  if (!changed.length && !watch) {
31
31
  yield { type: 'state', payload: { state: 'compile-end' } };
32
- log('debug', 'Skipped');
32
+ log.debug('Skipped');
33
33
  return;
34
34
  } else {
35
- log('debug', `Started watch=${watch} changed=${changed.slice(0, 10).map(x => `${x.module}/${x.file}`)}`);
35
+ log.debug(`Started watch=${watch} changed=${changed.slice(0, 10).map(x => `${x.module}/${x.file}`)}`);
36
36
  }
37
37
 
38
38
  const main = path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules', '@travetto/compiler/support/entry.compiler.js');
@@ -45,7 +45,7 @@ export class CompilerRunner {
45
45
  try {
46
46
  await CommonUtil.writeTextFile(deltaFile, changedFiles.join('\n'));
47
47
 
48
- log('info', 'Launching compiler');
48
+ log.info('Launching compiler');
49
49
  const proc = cp.spawn(process.argv0, [main, deltaFile, `${watch}`], {
50
50
  env: {
51
51
  ...process.env,
@@ -58,7 +58,7 @@ export class CompilerRunner {
58
58
  .on('exit', () => queue.close());
59
59
 
60
60
  const kill = (): unknown => {
61
- log('debug', 'Shutting down process');
61
+ log.debug('Shutting down process');
62
62
  return (proc.connected ? proc.send('shutdown', (err) => proc.kill()) : proc.kill());
63
63
  };
64
64
 
@@ -68,11 +68,11 @@ export class CompilerRunner {
68
68
  yield* queue;
69
69
 
70
70
  if (proc.exitCode !== 0) {
71
- log('error', `Terminated during compilation, code=${proc.exitCode}, killed=${proc.killed}`);
71
+ log.error(`Terminated during compilation, code=${proc.exitCode}, killed=${proc.killed}`);
72
72
  }
73
73
  process.off('SIGINT', kill);
74
74
 
75
- log('debug', 'Finished');
75
+ log.debug('Finished');
76
76
  } finally {
77
77
  rmSync(deltaFile, { force: true });
78
78
  }
@@ -6,12 +6,12 @@ import { setMaxListeners } from 'node:events';
6
6
  import type { ManifestContext } from '@travetto/manifest';
7
7
 
8
8
  import type { CompilerMode, CompilerProgressEvent, CompilerEvent, CompilerEventType, CompilerServerInfo } from '../types';
9
- import { LogUtil } from '../log';
9
+ import { Log } from '../log';
10
10
  import { CommonUtil } from '../util';
11
11
  import { CompilerClient } from './client';
12
12
  import { ProcessHandle } from './process-handle';
13
13
 
14
- const log = LogUtil.logger('compiler-server');
14
+ const log = Log.scoped('server');
15
15
 
16
16
  /**
17
17
  * Compiler Server Class
@@ -30,7 +30,7 @@ export class CompilerServer {
30
30
 
31
31
  constructor(ctx: ManifestContext, mode: CompilerMode) {
32
32
  this.#ctx = ctx;
33
- this.#client = new CompilerClient(ctx, LogUtil.logger('client.server'));
33
+ this.#client = new CompilerClient(ctx, Log.scoped('server.client'));
34
34
  this.#url = this.#client.url;
35
35
  this.#handle = { server: new ProcessHandle(ctx, 'server'), compiler: new ProcessHandle(ctx, 'compiler') };
36
36
 
@@ -70,11 +70,11 @@ export class CompilerServer {
70
70
  const info = await this.#client.info();
71
71
  resolve((info && info.mode === 'build' && this.mode === 'watch') ? 'retry' : 'running');
72
72
  } else {
73
- log('warn', 'Failed in running server', err);
73
+ log.warn('Failed in running server', err);
74
74
  reject(err);
75
75
  }
76
76
  })
77
- .on('close', () => log('debug', 'Server close event'));
77
+ .on('close', () => log.debug('Server close event'));
78
78
 
79
79
  const url = new URL(this.#url);
80
80
  setTimeout(() => this.#server.listen(+url.port, url.hostname), 1); // Run async
@@ -84,7 +84,7 @@ export class CompilerServer {
84
84
  if (attempt >= 5) {
85
85
  throw new Error('Unable to verify compilation server');
86
86
  }
87
- log('info', 'Waiting for build to finish, before retrying');
87
+ log.info('Waiting for build to finish, before retrying');
88
88
  // Let the server finish
89
89
  await this.#client.waitForState(['close'], 'Server closed', this.signal);
90
90
  return this.#tryListen(attempt + 1);
@@ -119,7 +119,7 @@ export class CompilerServer {
119
119
  }
120
120
 
121
121
  async #disconnectActive(): Promise<void> {
122
- log('info', 'Server disconnect requested');
122
+ log.info('Server disconnect requested');
123
123
  this.info.iteration = Date.now();
124
124
  await new Promise(r => setTimeout(r, 20));
125
125
  for (const el of Object.values(this.#listeners)) {
@@ -142,8 +142,6 @@ export class CompilerServer {
142
142
 
143
143
  const [, action, subAction] = new URL(`${this.#url}${req.url}`).pathname.split('/');
144
144
 
145
- log('debug', 'Receive request', { action, subAction });
146
-
147
145
  let out: unknown;
148
146
  let close = false;
149
147
  switch (action) {
@@ -165,7 +163,7 @@ export class CompilerServer {
165
163
  async processEvents(src: (signal: AbortSignal) => AsyncIterable<CompilerEvent>): Promise<void> {
166
164
  for await (const ev of CommonUtil.restartableEvents(src, this.signal, this.isResetEvent)) {
167
165
  if (ev.type === 'progress') {
168
- await LogUtil.logProgress?.(ev.payload);
166
+ await Log.onProgressEvent(ev.payload);
169
167
  }
170
168
 
171
169
  this.#emitEvent(ev);
@@ -176,9 +174,9 @@ export class CompilerServer {
176
174
  if (ev.payload.state === 'init' && ev.payload.extra && 'pid' in ev.payload.extra && typeof ev.payload.extra.pid === 'number') {
177
175
  this.info.compilerPid = ev.payload.extra.pid;
178
176
  }
179
- log('info', `State changed: ${this.info.state}`);
177
+ log.info(`State changed: ${this.info.state}`);
180
178
  } else if (ev.type === 'log') {
181
- LogUtil.logEvent(ev.payload);
179
+ log.render(ev.payload);
182
180
  }
183
181
  if (this.isResetEvent(ev)) {
184
182
  await this.#disconnectActive();
@@ -188,19 +186,19 @@ export class CompilerServer {
188
186
  // Terminate, after letting all remaining events emit
189
187
  await this.close();
190
188
 
191
- log('debug', 'Finished processing events');
189
+ log.debug('Finished processing events');
192
190
  }
193
191
 
194
192
  /**
195
193
  * Close server
196
194
  */
197
195
  async close(): Promise<void> {
198
- log('info', 'Closing down server');
196
+ log.info('Closing down server');
199
197
 
200
198
  // If we are in a place where progress exists
201
199
  if (this.info.state === 'compile-start') {
202
200
  const cancel: CompilerProgressEvent = { complete: true, idx: 0, total: 0, message: 'Complete', operation: 'compile' };
203
- LogUtil.logProgress?.(cancel);
201
+ await Log.onProgressEvent(cancel);
204
202
  this.#emitEvent({ type: 'progress', payload: cancel });
205
203
  }
206
204
 
@@ -220,7 +218,7 @@ export class CompilerServer {
220
218
  await this.#handle.compiler.kill();
221
219
  }
222
220
 
223
- log('info', 'Closed down server');
221
+ log.info('Closed down server');
224
222
  }
225
223
 
226
224
  /**
@@ -228,7 +226,7 @@ export class CompilerServer {
228
226
  */
229
227
  async listen(): Promise<CompilerServer | undefined> {
230
228
  const running = await this.#tryListen() === 'ok';
231
- log('info', running ? 'Starting server' : 'Server already running under a different process', this.#url);
229
+ log.info(running ? 'Starting server' : 'Server already running under a different process', this.#url);
232
230
  return running ? this : undefined;
233
231
  }
234
232
  }
package/support/setup.ts CHANGED
@@ -4,7 +4,7 @@ import fs from 'node:fs/promises';
4
4
 
5
5
  import { type DeltaEvent, type ManifestContext, type Package } from '@travetto/manifest';
6
6
 
7
- import { LogUtil } from './log';
7
+ import { Log } from './log';
8
8
  import { CommonUtil } from './util';
9
9
 
10
10
  type ModFile = { input: string, output: string, stale: boolean };
@@ -128,19 +128,19 @@ export class CompilerSetup {
128
128
  const out: string[] = [];
129
129
 
130
130
  try {
131
- await LogUtil.withLogger(scope, async log => {
131
+ await Log.wrap(scope, async log => {
132
132
  if (files.some(f => f.stale)) {
133
- log('debug', 'Starting', mod);
133
+ log.debug('Starting', mod);
134
134
  for (const file of files.filter(x => x.stale)) {
135
135
  await this.#transpileFile(ctx, file.input, file.output);
136
136
  }
137
137
  if (changes.length) {
138
138
  out.push(...changes.map(x => `${mod}/${x}`));
139
- log('debug', `Source changed: ${changes.join(', ')}`, mod);
139
+ log.debug(`Source changed: ${changes.join(', ')}`, mod);
140
140
  }
141
- log('debug', 'Completed', mod);
141
+ log.debug('Completed', mod);
142
142
  } else {
143
- log('debug', 'Skipped', mod);
143
+ log.debug('Skipped', mod);
144
144
  }
145
145
  }, false);
146
146
  } catch (err) {
@@ -173,7 +173,7 @@ export class CompilerSetup {
173
173
  static async setup(ctx: ManifestContext): Promise<DeltaEvent[]> {
174
174
  let changes = 0;
175
175
 
176
- await LogUtil.withLogger('precompile', async () => {
176
+ await Log.wrap('precompile', async () => {
177
177
  for (const mod of PRECOMPILE_MODS) {
178
178
  changes += (await this.#compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
179
179
  }
@@ -181,18 +181,18 @@ export class CompilerSetup {
181
181
 
182
182
  const { ManifestUtil, ManifestDeltaUtil } = await this.#importManifest(ctx);
183
183
 
184
- const manifest = await LogUtil.withLogger('manifest', () =>
184
+ const manifest = await Log.wrap('manifest', () =>
185
185
  ManifestUtil.buildManifest(ManifestUtil.getWorkspaceContext(ctx)));
186
186
 
187
- await LogUtil.withLogger('transformers', async () => {
187
+ await Log.wrap('transformers', async () => {
188
188
  for (const mod of Object.values(manifest.modules).filter(m => m.files.$transformer?.length)) {
189
189
  changes += (await this.#compileIfStale(ctx, 'transformers', mod.name, ['package.json', ...mod.files.$transformer!.map(x => x[0])])).length;
190
190
  }
191
191
  });
192
192
 
193
- const delta = await LogUtil.withLogger('delta', async log => {
193
+ const delta = await Log.wrap('delta', async log => {
194
194
  if (changes) {
195
- log('debug', 'Skipping, everything changed');
195
+ log.debug('Skipping, everything changed');
196
196
  return [{ type: 'changed', file: '*', module: ctx.workspace.name, sourceFile: '' } as const];
197
197
  } else {
198
198
  return ManifestDeltaUtil.produceDelta(manifest);
@@ -200,16 +200,16 @@ export class CompilerSetup {
200
200
  });
201
201
 
202
202
  if (changes) {
203
- await LogUtil.withLogger('reset', async log => {
203
+ await Log.wrap('reset', async log => {
204
204
  await fs.rm(path.resolve(ctx.workspace.path, ctx.build.outputFolder), { recursive: true, force: true });
205
- log('info', 'Clearing output due to compiler changes');
205
+ log.info('Clearing output due to compiler changes');
206
206
  }, false);
207
207
  }
208
208
 
209
209
  // Write manifest
210
- await LogUtil.withLogger('manifest', async log => {
210
+ await Log.wrap('manifest', async log => {
211
211
  await ManifestUtil.writeManifest(manifest);
212
- log('debug', `Wrote manifest ${ctx.workspace.name}`);
212
+ log.debug(`Wrote manifest ${ctx.workspace.name}`);
213
213
 
214
214
  // Update all manifests when in mono repo
215
215
  if (delta.length && ctx.workspace.mono) {
@@ -221,8 +221,8 @@ export class CompilerSetup {
221
221
  await ManifestUtil.writeManifest(modManifest);
222
222
  names.push(mod.name);
223
223
  }
224
- log('debug', `Changes triggered ${delta.slice(0, 10).map(x => `${x.type}:${x.module}:${x.file}`)}`);
225
- log('debug', `Rewrote monorepo manifests [changes=${delta.length}] ${names.slice(0, 10).join(', ')}`);
224
+ log.debug(`Changes triggered ${delta.slice(0, 10).map(x => `${x.type}:${x.module}:${x.file}`)}`);
225
+ log.debug(`Rewrote monorepo manifests [changes=${delta.length}] ${names.slice(0, 10).join(', ')}`);
226
226
  }
227
227
  });
228
228
 
package/support/types.ts CHANGED
@@ -3,7 +3,7 @@ export type CompilerMode = 'build' | 'watch';
3
3
  export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'close';
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
- export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time: number, args?: unknown[], scope?: string };
6
+ export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time?: number, args?: unknown[], scope?: string };
7
7
  export type CompilerProgressEvent = { idx: number, total: number, message: string, operation: 'compile', complete?: boolean };
8
8
  export type CompilerStateEvent = { state: CompilerStateType, extra?: Record<string, unknown> };
9
9
 
package/support/util.ts CHANGED
@@ -4,7 +4,7 @@ import { setMaxListeners } from 'node:events';
4
4
 
5
5
  import type { ManifestContext } from '@travetto/manifest';
6
6
 
7
- import { LogUtil } from './log';
7
+ import { Log } from './log';
8
8
 
9
9
  const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
10
10
 
@@ -58,7 +58,7 @@ export class CommonUtil {
58
58
  * Restartable Event Stream
59
59
  */
60
60
  static async * restartableEvents<T>(src: (signal: AbortSignal) => AsyncIterable<T>, parent: AbortSignal, shouldRestart: (item: T) => boolean): AsyncIterable<T> {
61
- const log = LogUtil.logger('event-stream');
61
+ const log = Log.scoped('event-stream');
62
62
  outer: while (!parent.aborted) {
63
63
  const controller = new AbortController();
64
64
  setMaxListeners(1000, controller.signal);
@@ -68,20 +68,20 @@ export class CommonUtil {
68
68
 
69
69
  const comp = src(controller.signal);
70
70
 
71
- log('debug', 'Started event stream');
71
+ log.debug('Started event stream');
72
72
 
73
73
  // Wait for all events, close at the end
74
74
  for await (const ev of comp) {
75
75
  yield ev;
76
76
  if (shouldRestart(ev)) {
77
- log('debug', 'Restarting stream');
77
+ log.debug('Restarting stream');
78
78
  controller.abort(); // Ensure terminated of process
79
79
  parent.removeEventListener('abort', kill);
80
80
  continue outer;
81
81
  }
82
82
  }
83
83
 
84
- log('debug', 'Finished event stream');
84
+ log.debug('Finished event stream');
85
85
 
86
86
  // Natural exit, we done
87
87
  if (!controller.signal.aborted) { // Shutdown source if still running
package/src/log.ts DELETED
@@ -1,17 +0,0 @@
1
- import type { CompilerLogLevel } from '../support/types';
2
- import { EventUtil } from './event';
3
-
4
- function log(level: CompilerLogLevel, message: string, ...args: unknown[]): void {
5
- EventUtil.sendEvent('log', { level, message, args, time: Date.now(), scope: 'compiler-exec' });
6
- if (!process.connected) {
7
- // eslint-disable-next-line no-console
8
- console[level](message, ...args);
9
- }
10
- }
11
-
12
- export const Log = {
13
- warn: log.bind(null, 'warn'),
14
- debug: log.bind(null, 'debug'),
15
- info: log.bind(null, 'info'),
16
- error: log.bind(null, 'error'),
17
- };