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

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/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.6",
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.6",
35
+ "@travetto/transformer": "^4.0.0-rc.6",
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.7"
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';
@@ -9,7 +8,7 @@ import { CompilerUtil } from './util';
9
8
  import { CompilerState } from './state';
10
9
  import { CompilerWatcher } from './watch';
11
10
  import { Log } from './log';
12
- import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
11
+ import { CompileEmitEvent, CompileEmitter } from './types';
13
12
  import { EventUtil } from './event';
14
13
 
15
14
  /**
@@ -86,29 +85,8 @@ export class Compiler {
86
85
  /**
87
86
  * Compile in a single pass, only emitting dirty files
88
87
  */
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;
88
+ getCompiler(): CompileEmitter {
89
+ return (inputFile: string, needsNewProgram?: boolean) => this.#state.writeInputFile(inputFile, needsNewProgram);
112
90
  }
113
91
 
114
92
  /**
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
 
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import type { ManifestContext } from '@travetto/manifest';
6
6
 
7
7
  import type { CompilerLogLevel, CompilerMode, CompilerServerInfo } from './types';
8
- import { LogUtil } from './log';
8
+ import { CompilerLogger } from './log';
9
9
  import { CommonUtil } from './util';
10
10
  import { CompilerSetup } from './setup';
11
11
  import { CompilerServer } from './server/server';
@@ -14,40 +14,39 @@ 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, new CompilerLogger('client'));
19
18
  const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
20
19
 
21
20
  /** Main entry point for compilation */
22
21
  const compile = async (op: CompilerMode, logLevel: CompilerLogLevel, setupOnly = false): Promise<void> => {
23
- LogUtil.initLogs(ctx, logLevel ?? 'info');
22
+ CompilerLogger.init(ctx, logLevel ?? 'info');
24
23
 
25
24
  const server = await new CompilerServer(ctx, op).listen();
25
+ const log = new CompilerLogger('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);
49
+ CompilerLogger.init(ctx);
51
50
  if (await client.stop()) {
52
51
  console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
53
52
  } else {
@@ -63,7 +62,7 @@ export const main = (ctx: ManifestContext) => {
63
62
 
64
63
  /** Clean the server */
65
64
  async clean(): Promise<void> {
66
- LogUtil.initLogs(ctx);
65
+ CompilerLogger.init(ctx);
67
66
  if (await client.clean()) {
68
67
  return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
69
68
  } else {
@@ -74,7 +73,7 @@ export const main = (ctx: ManifestContext) => {
74
73
 
75
74
  /** Stream events */
76
75
  events: async (type: string, handler: (ev: unknown) => unknown): Promise<void> => {
77
- LogUtil.initLogs(ctx, 'error');
76
+ CompilerLogger.init(ctx, 'error');
78
77
  if (type === 'change' || type === 'log' || type === 'progress' || type === 'state') {
79
78
  for await (const ev of client.fetchEvents(type)) { await handler(ev); }
80
79
  } else {
@@ -92,7 +91,7 @@ export const main = (ctx: ManifestContext) => {
92
91
  async getLoader(): Promise<(mod: string) => Promise<unknown>> {
93
92
  // Short circuit if we can
94
93
  if (!(await client.isWatching())) {
95
- await compile('build', LogUtil.isInteractiveShell ? 'info' : 'error');
94
+ await compile('build', 'error');
96
95
  }
97
96
  return CommonUtil.moduleLoader(ctx);
98
97
  },
package/support/log.ts CHANGED
@@ -1,31 +1,30 @@
1
1
  import type { ManifestContext } from '@travetto/manifest';
2
2
  import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
3
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
4
  const LEVEL_TO_PRI: Record<CompilerLogLevel, number> = { debug: 1, info: 2, warn: 3, error: 4 };
10
-
11
5
  const SCOPE_MAX = 15;
12
-
13
6
  const ESC = '\x1b[';
14
7
 
15
- export class LogUtil {
8
+ export class CompilerLogger {
16
9
 
17
- static root = process.cwd();
10
+ static #root = process.cwd();
11
+ static #logLevel: CompilerLogLevel = 'error';
12
+ static #linePartial: boolean | undefined;
18
13
 
19
- static logLevel: CompilerLogLevel = 'error';
14
+ static logProgress = false;
20
15
 
21
- static logProgress?: ProgressWriter;
22
-
23
- static linePartial = false;
24
-
25
- static #rewriteLine(text: string): Promise<void> | void {
16
+ /** Rewrite text line, tracking cleanup as necessary */
17
+ static rewriteLine(text: string): Promise<void> | void {
18
+ if ((!text && !this.#linePartial) || !process.stdout.isTTY) {
19
+ return;
20
+ }
21
+ if (this.#linePartial === undefined) { // First time
22
+ process.stdout.write(`${ESC}?25l`); // Hide cursor
23
+ process.on('exit', () => this.reset());
24
+ }
26
25
  // Move to 1st position, and clear after text
27
26
  const done = process.stdout.write(`${ESC}1G${text}${ESC}0K`);
28
- this.linePartial = !!text;
27
+ this.#linePartial = !!text;
29
28
  if (!done) {
30
29
  return new Promise<void>(r => process.stdout.once('drain', r));
31
30
  }
@@ -39,79 +38,70 @@ export class LogUtil {
39
38
  /**
40
39
  * Set level for operation
41
40
  */
42
- static initLogs(ctx: ManifestContext, defaultLevel?: CompilerLogLevel): void {
41
+ static init(ctx: ManifestContext, defaultLevel?: CompilerLogLevel): void {
43
42
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
43
  const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
45
44
  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
45
+ this.#logLevel = build || defaultLevel;
46
+ this.logProgress = this.isInteractiveShell;
53
47
  }
54
- process.on('exit', () => this.cleanup());
48
+ this.#root = ctx.workspace.path;
55
49
  }
56
50
 
57
- static cleanup(): void {
58
- if (this.logProgress) {
59
- process.stdout.write(`${ESC}!p`); // Reset
51
+ /** Cleanup to restore behavior */
52
+ static reset(): void {
53
+ if (process.stdout.isTTY) {
54
+ process.stdout.write(`${ESC}!p${ESC}?25h`);
60
55
  }
61
56
  }
62
57
 
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);
67
- }
58
+ constructor(
59
+ private scope: string,
60
+ private level?: CompilerLogLevel,
61
+ private logProgress?: boolean,
62
+ private root = CompilerLogger.#root,
63
+ ) { }
68
64
 
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];
65
+ isActive(level: CompilerLogLevel): boolean {
66
+ return LEVEL_TO_PRI[this.level ?? CompilerLogger.#logLevel] <= LEVEL_TO_PRI[level];
74
67
  }
75
68
 
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);
69
+ /** Log event with filtering by level */
70
+ onLogEvent(ev: CompilerLogEvent): void {
71
+ if (!this.isActive(ev.level)) { return; }
72
+ const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root, '.') : x);
73
+ if (ev.scope) {
74
+ params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
91
75
  }
76
+ params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
77
+ CompilerLogger.rewriteLine(''); // Clear out progress line, if active
78
+ // eslint-disable-next-line no-console
79
+ console[ev.level]!(...params);
92
80
  }
93
81
 
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'));
82
+ /** Write progress event, if active */
83
+ onProgressEvent(ev: CompilerProgressEvent): void | Promise<void> {
84
+ if (!(this.logProgress ?? CompilerLogger.logProgress)) { return; }
85
+ const pct = Math.trunc(ev.idx * 100 / ev.total);
86
+ const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
87
+ return CompilerLogger.rewriteLine(text);
101
88
  }
102
89
 
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() });
90
+ /** Write all progress events if active */
91
+ async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
92
+ if (!(this.logProgress ?? CompilerLogger.logProgress)) { return; }
93
+ for await (const ev of src()) { this.onProgressEvent(ev); }
94
+ await CompilerLogger.reset();
108
95
  }
109
96
 
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); }
97
+ log(level: CompilerLogLevel, message: string, args: unknown[]): void {
98
+ this.onLogEvent({ scope: this.scope, message, level, args, time: Date.now() });
99
+ }
100
+ info(message: string, ...args: unknown[]): void { return this.log('info', message, args); }
101
+ debug(message: string, ...args: unknown[]): void { return this.log('debug', message, args); }
102
+ warn(message: string, ...args: unknown[]): void { return this.log('warn', message, args); }
103
+ error(message: string, ...args: unknown[]): void { return this.log('error', message, args); }
104
+ wrap<T = unknown>(op: (log: typeof this) => Promise<T>, basic = false): Promise<T> {
105
+ return basic ? (this.debug('Started'), op(this).finally(() => this.debug('Completed'))) : op(this);
116
106
  }
117
107
  }
@@ -14,16 +14,18 @@ type FetchEventsConfig<T> = {
14
14
  enforceIteration?: boolean;
15
15
  };
16
16
 
17
+ type SimpleLogger = Pick<CompilerLogger, 'error' | 'info' | 'debug'>;
18
+
17
19
  /**
18
20
  * Compiler Client Operations
19
21
  */
20
22
  export class CompilerClient {
21
23
 
22
24
  #url: string;
23
- #log: CompilerLogger;
25
+ #log: SimpleLogger;
24
26
  #handle: Record<'compiler' | 'server', ProcessHandle>;
25
27
 
26
- constructor(ctx: ManifestContext, log: CompilerLogger) {
28
+ constructor(ctx: ManifestContext, log: SimpleLogger) {
27
29
  this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
28
30
  this.#log = log;
29
31
  this.#handle = { compiler: new ProcessHandle(ctx, 'compiler'), server: new ProcessHandle(ctx, 'server') };
@@ -41,7 +43,7 @@ export class CompilerClient {
41
43
  const ctrl = new AbortController();
42
44
  opts?.signal?.addEventListener('abort', () => ctrl.abort());
43
45
  const timeoutId = setTimeout(() => {
44
- logTimeout && this.#log('error', `Timeout on request to ${this.#url}${rel}`);
46
+ logTimeout && this.#log.error(`Timeout on request to ${this.#url}${rel}`);
45
47
  ctrl.abort('TIMEOUT');
46
48
  }, opts?.timeout ?? 100).unref();
47
49
  try {
@@ -71,13 +73,13 @@ export class CompilerClient {
71
73
  async stop(): Promise<boolean> {
72
74
  const info = await this.info();
73
75
  if (!info) {
74
- this.#log('debug', 'Stopping server, info not found, manual killing');
76
+ this.#log.debug('Stopping server, info not found, manual killing');
75
77
  return Promise.all([this.#handle.server.kill(), this.#handle.compiler.kill()])
76
78
  .then(v => v.some(x => x));
77
79
  }
78
80
 
79
81
  await this.#fetch('/stop').catch(() => { }); // Trigger
80
- this.#log('debug', 'Waiting for compiler to exit');
82
+ this.#log.debug('Waiting for compiler to exit');
81
83
  await this.#handle.compiler.ensureKilled();
82
84
  return true;
83
85
  }
@@ -92,7 +94,7 @@ export class CompilerClient {
92
94
  return;
93
95
  }
94
96
 
95
- this.#log('debug', `Starting watch for events of type "${type}"`);
97
+ this.#log.debug(`Starting watch for events of type "${type}"`);
96
98
 
97
99
  let signal = cfg.signal;
98
100
 
@@ -132,15 +134,15 @@ export class CompilerClient {
132
134
  info = await this.info();
133
135
 
134
136
  if (ctrl.signal.reason === 'TIMEOUT') {
135
- this.#log('debug', 'Failed due to timeout');
137
+ this.#log.debug('Failed due to timeout');
136
138
  return;
137
139
  }
138
140
 
139
141
  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}"`);
142
+ this.#log.debug(`Stopping watch for events of type "${type}"`);
141
143
  return;
142
144
  } else {
143
- this.#log('debug', `Restarting watch for events of type "${type}"`);
145
+ this.#log.debug(`Restarting watch for events of type "${type}"`);
144
146
  }
145
147
  }
146
148
  }
@@ -149,11 +151,11 @@ export class CompilerClient {
149
151
  async waitForState(states: CompilerStateType[], message?: string, signal?: AbortSignal): Promise<void> {
150
152
  const set = new Set(states);
151
153
  // Loop until
152
- this.#log('debug', `Waiting for states, ${states.join(', ')}`);
154
+ this.#log.debug(`Waiting for states, ${states.join(', ')}`);
153
155
  for await (const _ of this.fetchEvents('state', { signal, until: s => set.has(s.state) })) { }
154
- this.#log('debug', `Found state, one of ${states.join(', ')} `);
156
+ this.#log.debug(`Found state, one of ${states.join(', ')} `);
155
157
  if (message) {
156
- this.#log('info', message);
158
+ this.#log.info(message);
157
159
  }
158
160
  }
159
161
  }
@@ -3,7 +3,7 @@ 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 { CompilerLogger } from '../log';
7
7
 
8
8
  export class ProcessHandle {
9
9
 
@@ -12,7 +12,7 @@ export class ProcessHandle {
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 = new CompilerLogger(`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 { CompilerLogger } from '../log';
10
10
  import { CommonUtil } from '../util';
11
11
 
12
- const log = LogUtil.logger('compiler-exec');
12
+ const log = new CompilerLogger('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 { CompilerLogger } 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 = new CompilerLogger('compiler-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, new CompilerLogger('client.server'));
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.onLogEvent(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 { CompilerLogger } 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 new CompilerLogger(scope).wrap(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 new CompilerLogger('precompile').wrap(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 new CompilerLogger('manifest').wrap(() =>
185
185
  ManifestUtil.buildManifest(ManifestUtil.getWorkspaceContext(ctx)));
186
186
 
187
- await LogUtil.withLogger('transformers', async () => {
187
+ await new CompilerLogger('transformers').wrap(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 new CompilerLogger('delta').wrap(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 new CompilerLogger('reset').wrap(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 new CompilerLogger('manifest').wrap(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/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 { CompilerLogger } 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 = new CompilerLogger('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