@travetto/compiler 3.0.0-rc.27 → 3.0.0-rc.29

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": "3.0.0-rc.27",
3
+ "version": "3.0.0-rc.29",
4
4
  "description": "Compiler",
5
5
  "keywords": [
6
6
  "compiler",
@@ -32,11 +32,11 @@
32
32
  "dependencies": {
33
33
  "@parcel/watcher": "^2.1.0",
34
34
  "@travetto/manifest": "^3.0.0-rc.15",
35
- "@travetto/terminal": "^3.0.0-rc.8",
35
+ "@travetto/terminal": "^3.0.0-rc.10",
36
36
  "@travetto/transformer": "^3.0.0-rc.19"
37
37
  },
38
38
  "peerDependencies": {
39
- "@travetto/cli": "^3.0.0-rc.18"
39
+ "@travetto/cli": "^3.0.0-rc.20"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { install } from 'source-map-support';
2
- import timers from 'timers/promises';
3
2
  import ts from 'typescript';
4
3
  import fs from 'fs/promises';
5
4
 
@@ -12,8 +11,6 @@ import { CompilerWatcher } from './watch';
12
11
  import { Log } from './log';
13
12
  import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
14
13
 
15
- const PING_THRESHOLD = 1000;
16
-
17
14
  /**
18
15
  * Compilation support
19
16
  */
@@ -24,21 +21,22 @@ export class Compiler {
24
21
  */
25
22
  static async main(): Promise<void> {
26
23
  const [dirty, watch] = process.argv.slice(2);
27
- install();
24
+ const state = await CompilerState.get(RootIndex);
28
25
  const dirtyFiles = (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
29
- return new Compiler().init(dirtyFiles).then(c => c.run(watch === 'true'));
26
+ await new Compiler(state, dirtyFiles, watch === 'true').run();
27
+ process.exit(0);
30
28
  }
31
29
 
32
30
  #state: CompilerState;
33
31
  #dirtyFiles: string[];
32
+ #watch?: boolean;
34
33
 
35
- async init(dirtyFiles: string[]): Promise<this> {
36
- this.#state = await CompilerState.get(RootIndex);
34
+ constructor(state: CompilerState, dirtyFiles: string[], watch?: boolean) {
35
+ this.#state = state;
37
36
  this.#dirtyFiles = dirtyFiles[0] === '*' ?
38
37
  this.#state.getAllFiles() :
39
38
  dirtyFiles.map(f => this.#state.getBySource(f)!.input);
40
-
41
- return this;
39
+ this.#watch = watch;
42
40
  }
43
41
 
44
42
  /**
@@ -99,9 +97,14 @@ export class Compiler {
99
97
  /**
100
98
  * Run the compiler
101
99
  */
102
- async run(watch?: boolean): Promise<void> {
100
+ async run(): Promise<void> {
101
+ await GlobalTerminal.init();
102
+ install();
103
+
103
104
  Log.debug('Compilation started');
104
105
 
106
+ process.on('disconnect', () => process.exit(0));
107
+
105
108
  const emitter = await this.getCompiler();
106
109
  let failed = false;
107
110
 
@@ -123,7 +126,7 @@ export class Compiler {
123
126
  } else {
124
127
  Log.debug('Compilation succeeded');
125
128
  }
126
- } else if (watch) {
129
+ } else if (this.#watch) {
127
130
  // Prime compiler before complete
128
131
  const resolved = this.#state.getArbitraryInputFile();
129
132
  await emitter(resolved, true);
@@ -131,16 +134,15 @@ export class Compiler {
131
134
 
132
135
  process.send?.('build-complete');
133
136
 
134
- if (watch) {
137
+ if (this.#watch) {
135
138
  Log.info('Watch is ready');
136
139
  await this.#watchLocalModules(emitter);
137
- for await (const _ of timers.setInterval(PING_THRESHOLD)) {
138
- if (!await fs.stat(this.#state.resolveOutputFile('.')).catch(() => false)) { // Output removed
140
+ const output = this.#state.resolveOutputFile('.');
141
+ for await (const _ of fs.watch(output)) {
142
+ if (!await fs.stat(output).catch(() => false)) {
139
143
  process.send?.('restart');
140
- } else {
141
- process.send?.('ping');
142
144
  }
143
145
  }
144
146
  }
145
147
  }
146
- }
148
+ }
@@ -21,7 +21,10 @@ async function compile(ctx: ManifestContext, op: 'watch' | 'build' | undefined,
21
21
 
22
22
  await LogUtil.withLogger('precompile', async () => {
23
23
  for (const mod of PRECOMPILE_MODS) {
24
- changes += (await TranspileUtil.compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
24
+ const count = (await TranspileUtil.compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
25
+ if (mod !== '@travetto/terminal') {
26
+ changes += count;
27
+ }
25
28
  }
26
29
  });
27
30
 
@@ -108,7 +111,14 @@ async function exportManifest(ctx: ManifestContext, output?: string, env = 'dev'
108
111
  * Launch
109
112
  */
110
113
  export async function launch(ctx: ManifestContext, root: ManifestContext, op?: 'build' | 'watch' | 'manifest', args: (string | undefined)[] = []): Promise<void> {
114
+ // If quiet enabled, turn off all output by default
115
+ LogUtil.level = process.env.TRV_BUILD ?? (process.env.TRV_QUIET ? 'none' : (!op ? 'warn' : 'info'));
116
+
111
117
  if (op !== 'manifest' && await LockManager.getCompileAction(root, op) === 'build') {
118
+
119
+ // Ready signal
120
+ process.send?.('ready');
121
+
112
122
  await LockManager.withLocks(root, async (acquire, release) => {
113
123
  let action: CompileResult;
114
124
  do {
@@ -118,7 +128,10 @@ export async function launch(ctx: ManifestContext, root: ManifestContext, op?: '
118
128
  }
119
129
  action = await compile(root, op, msg => {
120
130
  switch (msg) {
121
- case 'build-complete': release('build'); break;
131
+ case 'build-complete': {
132
+ release('build');
133
+ break;
134
+ }
122
135
  }
123
136
  });
124
137
  } while (action === 'restart');
package/support/lock.ts CHANGED
@@ -28,7 +28,7 @@ export class LockManager {
28
28
  * Get the lock file name
29
29
  */
30
30
  static #getFileName(ctx: ManifestContext, type: LockType): string {
31
- return path.resolve(ctx.workspacePath, ctx.toolFolder, `${type}.pid`);
31
+ return path.resolve(ctx.workspacePath, ctx.toolFolder, `${type}.lock`);
32
32
  }
33
33
 
34
34
  /**
@@ -47,10 +47,9 @@ export class LockManager {
47
47
  const stale = this.#isStale(stat);
48
48
  let pid: number | undefined;
49
49
  if (stat) {
50
- const content = await fs.readFile(file, 'utf8');
51
- const filePid = parseInt(content, 10);
50
+ const { pid: filePid } = JSON.parse(await fs.readFile(file, 'utf8'));
52
51
  if (stale) {
53
- LogUtil.log('lock', [], 'info', `${type} file is stale: ${stat.mtimeMs} vs ${Date.now()}`);
52
+ LogUtil.log('lock', [], 'debug', `${type} file is stale: ${stat.mtimeMs} vs ${Date.now()}`);
54
53
  } else {
55
54
  pid = filePid;
56
55
  }
@@ -65,7 +64,7 @@ export class LockManager {
65
64
  const file = this.#getFileName(ctx, type);
66
65
  mkdirSync(path.dirname(file), { recursive: true });
67
66
  LogUtil.log('lock', [], 'debug', `Acquiring ${type}`);
68
- writeFileSync(file, `${process.pid}`, 'utf8');
67
+ writeFileSync(file, JSON.stringify({ pid: process.pid }), 'utf8');
69
68
  }
70
69
 
71
70
  /**
@@ -118,25 +117,24 @@ export class LockManager {
118
117
  * Read the watch lock file and determine its result, communicating with the user as necessary
119
118
  */
120
119
  static async #getWatchAction(ctx: ManifestContext, log: CompilerLogger, lockType: LockType | undefined, buildState: LockDetails): Promise<LockAction> {
121
- const level = lockType ? 'info' : 'debug';
122
120
  if (lockType === 'watch') {
123
- log(level, 'Already running');
121
+ log('info', 'Already running');
124
122
  return 'skip';
125
123
  } else {
126
124
  if (buildState.pid) {
127
- log('info', 'Already running, waiting for build to finish');
125
+ log('warn', 'Already running, waiting for build to finish');
128
126
  switch (await this.#waitForRelease(ctx, 'build')) {
129
127
  case 'complete': {
130
- log(level, 'Completed build');
128
+ log('info', 'Completed build');
131
129
  return 'skip';
132
130
  }
133
131
  case 'stale': {
134
- log(level, 'Became stale, retrying');
132
+ log('info', 'Became stale, retrying');
135
133
  return 'retry';
136
134
  }
137
135
  }
138
136
  } else {
139
- log(level, 'Already running, and has built');
137
+ log('info', 'Already running, and has built');
140
138
  return 'skip';
141
139
  }
142
140
  }
@@ -146,21 +144,20 @@ export class LockManager {
146
144
  * Read the build lock file and determine its result, communicating with the user as necessary
147
145
  */
148
146
  static async #getBuildAction(ctx: ManifestContext, log: CompilerLogger, lockType: LockType | undefined): Promise<LockAction> {
149
- const level = lockType ? 'info' : 'debug';
150
147
  if (lockType === 'watch') {
151
- log('info', 'Build already running, waiting to begin watch');
148
+ log('warn', 'Build already running, waiting to begin watch');
152
149
  const res = await this.#waitForRelease(ctx, 'build');
153
- log(level, `Finished with status of ${res}, retrying`);
150
+ log('info', `Finished with status of ${res}, retrying`);
154
151
  return 'retry';
155
152
  } else {
156
- log('info', 'Already running, waiting for completion');
153
+ log('warn', 'Already running, waiting for completion');
157
154
  switch (await this.#waitForRelease(ctx, lockType ?? 'build')) {
158
155
  case 'complete': {
159
- log(level, 'Completed');
156
+ log('info', 'Completed');
160
157
  return 'skip';
161
158
  }
162
159
  case 'stale': {
163
- log(level, 'Became stale, retrying');
160
+ log('info', 'Became stale, retrying');
164
161
  return 'retry';
165
162
  }
166
163
  }
package/support/log.ts CHANGED
@@ -2,16 +2,24 @@ export type CompilerLogEvent = [level: 'info' | 'debug' | 'warn', message: strin
2
2
  export type CompilerLogger = (...args: CompilerLogEvent) => void;
3
3
  export type WithLogger<T> = (log: CompilerLogger) => Promise<T>;
4
4
 
5
- const ENV_LEVEL = process.env.TRV_BUILD ?? 'info';
6
- const LEVELS = {
7
- warn: /^(debug|info|warn)$/.test(ENV_LEVEL),
8
- info: /^(debug|info)$/.test(ENV_LEVEL),
9
- debug: /^debug$/.test(ENV_LEVEL),
10
- };
11
5
  const SCOPE_MAX = 15;
12
6
 
13
7
  export class LogUtil {
14
8
 
9
+ static levels: {
10
+ debug: boolean;
11
+ info: boolean;
12
+ warn: boolean;
13
+ }
14
+
15
+ static set level(value: string) {
16
+ this.levels = {
17
+ warn: /^(debug|info|warn)$/.test(value),
18
+ info: /^(debug|info)$/.test(value),
19
+ debug: /^debug$/.test(value),
20
+ };
21
+ }
22
+
15
23
  /**
16
24
  * Is object a log event
17
25
  */
@@ -22,7 +30,14 @@ export class LogUtil {
22
30
  */
23
31
  static log(scope: string, args: string[], ...[level, msg]: CompilerLogEvent): void {
24
32
  const message = msg.replaceAll(process.cwd(), '.');
25
- LEVELS[level] && console.debug(new Date().toISOString(), `[${scope.padEnd(SCOPE_MAX, ' ')}]`, ...args, message);
33
+ if (LogUtil.levels[level]) {
34
+ const params = [`[${scope.padEnd(SCOPE_MAX, ' ')}]`, ...args, message];
35
+ if (!/(0|false|off|no)$/i.test(process.env.TRV_LOG_TIME ?? '')) {
36
+ params.unshift(new Date().toISOString());
37
+ }
38
+ // eslint-disable-next-line no-console
39
+ console[level]!(...params);
40
+ }
26
41
  }
27
42
 
28
43
  /**
@@ -131,8 +131,8 @@ export class TranspileUtil {
131
131
  }
132
132
 
133
133
  /**
134
- * Recompile folder if stale
135
- */
134
+ * Recompile folder if stale
135
+ */
136
136
  static async compileIfStale(ctx: ManifestContext, scope: string, mod: string, seed: string[]): Promise<string[]> {
137
137
  const files = await this.getModuleSources(ctx, mod, seed);
138
138
  const changes = files.filter(x => x.stale).map(x => x.input);
@@ -166,13 +166,14 @@ export class TranspileUtil {
166
166
  static async runCompiler(ctx: ManifestContext, manifest: ManifestRoot, changed: DeltaEvent[], watch: boolean, onMessage: (msg: unknown) => void): Promise<CompileResult> {
167
167
  const compiler = path.resolve(ctx.workspacePath, ctx.compilerFolder);
168
168
  const main = path.resolve(compiler, 'node_modules', '@travetto/compiler/support/compiler-entry.js');
169
- const deltaFile = path.resolve(os.tmpdir(), `manifest-delta.${Date.now()}.${Math.random()}.json`);
169
+ const deltaFile = path.resolve(os.tmpdir(), `manifest-delta.${process.pid}.${process.ppid}.${Date.now()}.json`);
170
170
 
171
171
  const changedFiles = changed[0]?.file === '*' ? ['*'] : changed.map(ev =>
172
172
  path.resolve(manifest.workspacePath, manifest.modules[ev.module].sourceFolder, ev.file)
173
173
  );
174
174
 
175
175
  let proc: cp.ChildProcess | undefined;
176
+ let kill: (() => void) | undefined;
176
177
 
177
178
  try {
178
179
  await this.writeTextFile(deltaFile, changedFiles.join('\n'));
@@ -195,9 +196,14 @@ export class TranspileUtil {
195
196
  }
196
197
  })
197
198
  .on('exit', code => (code !== null && code > 0) ? rej(new Error('Failed during compilation')) : res('complete'));
199
+ kill = (): void => { proc?.kill('SIGKILL'); };
200
+ process.on('exit', kill);
198
201
  }));
199
202
  } finally {
200
203
  if (proc?.killed === false) { proc.kill('SIGKILL'); }
204
+ if (kill) {
205
+ process.removeListener('exit', kill);
206
+ }
201
207
  await fs.rm(deltaFile, { force: true });
202
208
  }
203
209
  }