@travetto/compiler 8.0.0-alpha.9 → 8.0.1
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 +3 -3
- package/__index__.ts +4 -4
- package/bin/hook.js +10 -4
- package/bin/trvc-target.js +2 -1
- package/bin/trvc.js +2 -1
- package/package.json +18 -18
- package/src/common.ts +15 -9
- package/src/compiler.ts +26 -29
- package/src/event.ts +8 -6
- package/src/log.ts +57 -22
- package/src/queue.ts +4 -2
- package/src/server/client.ts +38 -24
- package/src/server/manager.ts +16 -11
- package/src/server/process-handle.ts +14 -10
- package/src/server/server.ts +50 -26
- package/src/state.ts +85 -52
- package/src/ts-proxy.ts +5 -3
- package/src/types.ts +22 -15
- package/src/util.ts +2 -4
- package/src/watch.ts +80 -68
- package/support/invoke.ts +37 -22
- package/tsconfig.trv.json +4 -18
package/src/state.ts
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import type { CompilerHost, SourceFile, CompilerOptions, Program, ScriptTarget } from 'typescript';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import type { CompilerHost, CompilerOptions, Program, ScriptTarget, SourceFile } from 'typescript';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type ManifestIndex,
|
|
7
|
+
type ManifestModule,
|
|
8
|
+
type ManifestModuleFolderType,
|
|
9
|
+
ManifestModuleUtil,
|
|
10
|
+
type ManifestRoot,
|
|
11
|
+
path
|
|
12
|
+
} from '@travetto/manifest';
|
|
5
13
|
import type { TransformerManager } from '@travetto/transformer';
|
|
6
14
|
|
|
7
|
-
import { CompilerUtil } from './util.ts';
|
|
8
|
-
import type { CompileStateEntry } from './types.ts';
|
|
9
15
|
import { CommonUtil } from './common.ts';
|
|
10
16
|
import { tsProxy as ts, tsProxyInit } from './ts-proxy.ts';
|
|
17
|
+
import type { CompileStateEntry } from './types.ts';
|
|
18
|
+
import { CompilerUtil } from './util.ts';
|
|
11
19
|
|
|
12
20
|
const TYPINGS_FOLDER_KEYS = new Set<ManifestModuleFolderType>(['$index', 'support', 'src', '$package']);
|
|
13
21
|
|
|
14
22
|
export class CompilerState implements CompilerHost {
|
|
15
|
-
|
|
16
23
|
static async get(idx: ManifestIndex): Promise<CompilerState> {
|
|
17
24
|
return new CompilerState().init(idx);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
/** @private */
|
|
21
|
-
constructor() { }
|
|
22
|
-
|
|
23
27
|
#outputPath: string;
|
|
24
28
|
#typingsPath: string;
|
|
25
29
|
#sourceFiles = new Set<string>();
|
|
26
30
|
#sourceDirectory = new Map<string, string>();
|
|
27
31
|
#sourceToEntry = new Map<string, CompileStateEntry>();
|
|
28
32
|
#outputToEntry = new Map<string, CompileStateEntry>();
|
|
29
|
-
#
|
|
33
|
+
#tscOutputFileToOutput = new Map<string, string>();
|
|
30
34
|
|
|
31
35
|
#sourceContents = new Map<string, string | undefined>();
|
|
32
36
|
#sourceFileObjects = new Map<string, SourceFile>();
|
|
@@ -44,7 +48,9 @@ export class CompilerState implements CompilerHost {
|
|
|
44
48
|
try {
|
|
45
49
|
return ts.sys.readFile(location, 'utf8');
|
|
46
50
|
} catch {
|
|
47
|
-
try {
|
|
51
|
+
try {
|
|
52
|
+
return fs.readFileSync(location, 'utf8');
|
|
53
|
+
} catch {}
|
|
48
54
|
}
|
|
49
55
|
return undefined;
|
|
50
56
|
}
|
|
@@ -61,13 +67,17 @@ export class CompilerState implements CompilerHost {
|
|
|
61
67
|
#fileExists(location: string): boolean {
|
|
62
68
|
try {
|
|
63
69
|
return ts.sys.fileExists(location);
|
|
64
|
-
} catch {
|
|
70
|
+
} catch {
|
|
71
|
+
return fs.existsSync(location);
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
#directoryExists(location: string): boolean {
|
|
68
76
|
try {
|
|
69
77
|
return ts.sys.directoryExists(location);
|
|
70
|
-
} catch {
|
|
78
|
+
} catch {
|
|
79
|
+
return fs.existsSync(location);
|
|
80
|
+
}
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
#writeExternalTypings(location: string, text: string, bom?: boolean): void {
|
|
@@ -120,14 +130,14 @@ export class CompilerState implements CompilerHost {
|
|
|
120
130
|
for (const module of this.#modules) {
|
|
121
131
|
const base = module?.files ?? {};
|
|
122
132
|
const files = [
|
|
123
|
-
...base.bin ?? [],
|
|
124
|
-
...base.src ?? [],
|
|
125
|
-
...base.support ?? [],
|
|
126
|
-
...base.doc ?? [],
|
|
127
|
-
...base.test ?? [],
|
|
128
|
-
...base.$transformer ?? [],
|
|
129
|
-
...base.$index ?? [],
|
|
130
|
-
...base.$package ?? []
|
|
133
|
+
...(base.bin ?? []),
|
|
134
|
+
...(base.src ?? []),
|
|
135
|
+
...(base.support ?? []),
|
|
136
|
+
...(base.doc ?? []),
|
|
137
|
+
...(base.test ?? []),
|
|
138
|
+
...(base.$transformer ?? []),
|
|
139
|
+
...(base.$index ?? []),
|
|
140
|
+
...(base.$package ?? [])
|
|
131
141
|
];
|
|
132
142
|
for (const [file, type] of files) {
|
|
133
143
|
if (ManifestModuleUtil.isSourceType(type)) {
|
|
@@ -158,9 +168,7 @@ export class CompilerState implements CompilerHost {
|
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
getArbitraryInputFile(): string {
|
|
161
|
-
const randomSource = this.#manifestIndex.getWorkspaceModules()
|
|
162
|
-
.filter(module => module.files.src?.length)[0]
|
|
163
|
-
.files.src[0].sourceFile;
|
|
171
|
+
const randomSource = this.#manifestIndex.getWorkspaceModules().filter(module => module.files.src?.length)[0].files.src[0].sourceFile;
|
|
164
172
|
|
|
165
173
|
return this.getBySource(randomSource)!.sourceFile;
|
|
166
174
|
}
|
|
@@ -168,7 +176,12 @@ export class CompilerState implements CompilerHost {
|
|
|
168
176
|
async getProgram(force = false): Promise<Program> {
|
|
169
177
|
if (force || !this.#program) {
|
|
170
178
|
await this.initializeTypescript();
|
|
171
|
-
this.#program = ts.createProgram({
|
|
179
|
+
this.#program = ts.createProgram({
|
|
180
|
+
rootNames: this.getAllFiles(),
|
|
181
|
+
host: this,
|
|
182
|
+
options: this.#compilerOptions,
|
|
183
|
+
oldProgram: this.#program
|
|
184
|
+
});
|
|
172
185
|
this.#transformerManager.init(this.#program.getTypeChecker());
|
|
173
186
|
await CommonUtil.queueMacroTask();
|
|
174
187
|
}
|
|
@@ -185,36 +198,40 @@ export class CompilerState implements CompilerHost {
|
|
|
185
198
|
case 'package-json': {
|
|
186
199
|
const text = this.readFile(sourceFile)!;
|
|
187
200
|
const finalText = CompilerUtil.rewritePackageJSON(this.#manifest, text);
|
|
188
|
-
const location = this.#
|
|
201
|
+
const location = this.#tscOutputFileToOutput.get(output) ?? output;
|
|
189
202
|
this.#writeFile(location, finalText);
|
|
190
203
|
this.#writeExternalTypings(location, finalText);
|
|
191
204
|
break;
|
|
192
205
|
}
|
|
193
206
|
case 'js':
|
|
194
|
-
case 'typings':
|
|
207
|
+
case 'typings':
|
|
208
|
+
this.writeFile(output, this.readFile(sourceFile)!);
|
|
209
|
+
break;
|
|
195
210
|
case 'ts': {
|
|
196
211
|
const program = await this.getProgram(needsNewProgram);
|
|
197
212
|
const tsSourceFile = program.getSourceFile(sourceFile)!;
|
|
198
213
|
program.emit(
|
|
199
214
|
tsSourceFile,
|
|
200
|
-
(...args) => this.writeFile(args[0], args[1], args[2]),
|
|
215
|
+
(...args) => this.writeFile(args[0], args[1], args[2]),
|
|
216
|
+
undefined,
|
|
217
|
+
false,
|
|
201
218
|
this.#transformerManager.get()
|
|
202
219
|
);
|
|
203
220
|
return [
|
|
204
221
|
...program.getSemanticDiagnostics(tsSourceFile),
|
|
205
222
|
...program.getSyntacticDiagnostics(tsSourceFile),
|
|
206
|
-
...program.getDeclarationDiagnostics(tsSourceFile)
|
|
223
|
+
...program.getDeclarationDiagnostics(tsSourceFile)
|
|
207
224
|
]
|
|
208
225
|
.filter(d => d.category === ts.DiagnosticCategory.Error)
|
|
209
226
|
.map(diag => {
|
|
210
227
|
let message = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
|
|
211
228
|
if (
|
|
212
|
-
message.includes("is not under 'rootDir'")
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
229
|
+
message.includes("is not under 'rootDir'") ||
|
|
230
|
+
message.includes("does not exist on type 'EnvDataCombinedType'") ||
|
|
231
|
+
message.startsWith('Could not find a declaration file for module') ||
|
|
232
|
+
message.startsWith("Cannot find module '@travetto") ||
|
|
233
|
+
message.startsWith("This JSX tag requires the module path '@travetto") ||
|
|
234
|
+
message.startsWith("JSX element implicitly has type 'any'")
|
|
218
235
|
) {
|
|
219
236
|
return '';
|
|
220
237
|
}
|
|
@@ -235,7 +252,11 @@ export class CompilerState implements CompilerHost {
|
|
|
235
252
|
|
|
236
253
|
isCompilerFile(file: string): boolean {
|
|
237
254
|
const entry = this.getBySource(file);
|
|
238
|
-
return (
|
|
255
|
+
return (
|
|
256
|
+
(entry?.moduleFile && ManifestModuleUtil.getFileRole(entry.moduleFile) === 'compile') ||
|
|
257
|
+
entry?.module.roles.includes('compile') ||
|
|
258
|
+
false
|
|
259
|
+
);
|
|
239
260
|
}
|
|
240
261
|
|
|
241
262
|
registerInput(module: ManifestModule, moduleFile: string): CompileStateEntry {
|
|
@@ -256,14 +277,14 @@ export class CompilerState implements CompilerHost {
|
|
|
256
277
|
this.#sourceToEntry.set(sourceFile, entry);
|
|
257
278
|
this.#sourceDirectory.set(sourceFolder, sourceFolder);
|
|
258
279
|
|
|
259
|
-
this.#
|
|
260
|
-
this.#
|
|
280
|
+
this.#tscOutputFileToOutput.set(tscOutputFile, outputFile);
|
|
281
|
+
this.#tscOutputFileToOutput.set(`${tscOutputFile}.map`, `${outputFile}.map`);
|
|
261
282
|
|
|
262
283
|
if (!isTypings) {
|
|
263
284
|
const srcBase = `${ManifestModuleUtil.withoutSourceExtension(tscOutputFile)}${ManifestModuleUtil.TYPINGS_EXT}`;
|
|
264
285
|
const outBase = `${ManifestModuleUtil.withoutSourceExtension(outputFile)}${ManifestModuleUtil.TYPINGS_EXT}`;
|
|
265
|
-
this.#
|
|
266
|
-
this.#
|
|
286
|
+
this.#tscOutputFileToOutput.set(`${srcBase}.map`, `${outBase}.map`);
|
|
287
|
+
this.#tscOutputFileToOutput.set(srcBase, outBase);
|
|
267
288
|
}
|
|
268
289
|
|
|
269
290
|
return entry;
|
|
@@ -298,10 +319,10 @@ export class CompilerState implements CompilerHost {
|
|
|
298
319
|
this.#sourceFiles.delete(sourceFile);
|
|
299
320
|
|
|
300
321
|
const tscOutputDts = `${ManifestModuleUtil.withoutSourceExtension(entry.tscOutputFile)}${ManifestModuleUtil.TYPINGS_EXT}`;
|
|
301
|
-
this.#
|
|
302
|
-
this.#
|
|
303
|
-
this.#
|
|
304
|
-
this.#
|
|
322
|
+
this.#tscOutputFileToOutput.delete(entry.tscOutputFile);
|
|
323
|
+
this.#tscOutputFileToOutput.delete(`${entry.tscOutputFile}.map`);
|
|
324
|
+
this.#tscOutputFileToOutput.delete(tscOutputDts);
|
|
325
|
+
this.#tscOutputFileToOutput.delete(`${tscOutputDts}.map`);
|
|
305
326
|
}
|
|
306
327
|
|
|
307
328
|
getAllFiles(): string[] {
|
|
@@ -309,12 +330,24 @@ export class CompilerState implements CompilerHost {
|
|
|
309
330
|
}
|
|
310
331
|
|
|
311
332
|
/* Start Compiler Host */
|
|
312
|
-
getCanonicalFileName(file: string): string {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
333
|
+
getCanonicalFileName(file: string): string {
|
|
334
|
+
return file;
|
|
335
|
+
}
|
|
336
|
+
getCurrentDirectory(): string {
|
|
337
|
+
return this.#manifest.workspace.path;
|
|
338
|
+
}
|
|
339
|
+
getDefaultLibFileName(options: CompilerOptions): string {
|
|
340
|
+
return ts.getDefaultLibFileName(options);
|
|
341
|
+
}
|
|
342
|
+
getNewLine(): string {
|
|
343
|
+
return ts.sys.newLine;
|
|
344
|
+
}
|
|
345
|
+
useCaseSensitiveFileNames(): boolean {
|
|
346
|
+
return ts.sys.useCaseSensitiveFileNames;
|
|
347
|
+
}
|
|
348
|
+
getDefaultLibLocation(): string {
|
|
349
|
+
return path.dirname(ts.getDefaultLibFilePath(this.#compilerOptions));
|
|
350
|
+
}
|
|
318
351
|
|
|
319
352
|
fileExists(sourceFile: string): boolean {
|
|
320
353
|
return this.#sourceToEntry.has(sourceFile) || this.#fileExists(sourceFile);
|
|
@@ -328,13 +361,13 @@ export class CompilerState implements CompilerHost {
|
|
|
328
361
|
// JSX runtime shenanigans
|
|
329
362
|
text = text.replace(/support\/jsx-runtime"/g, 'support/jsx-runtime.js"');
|
|
330
363
|
|
|
331
|
-
const location = this.#
|
|
364
|
+
const location = this.#tscOutputFileToOutput.get(outputFile) ?? outputFile;
|
|
332
365
|
|
|
333
366
|
if (ManifestModuleUtil.TYPINGS_WITH_MAP_EXT_REGEX.test(outputFile)) {
|
|
334
367
|
this.#writeExternalTypings(location, text, bom);
|
|
335
368
|
}
|
|
336
369
|
|
|
337
|
-
|
|
370
|
+
this.#writeFile(location, text, bom);
|
|
338
371
|
}
|
|
339
372
|
|
|
340
373
|
readFile(sourceFile: string): string | undefined {
|
|
@@ -349,4 +382,4 @@ export class CompilerState implements CompilerHost {
|
|
|
349
382
|
return ts.createSourceFile(sourceFile, content ?? '', language);
|
|
350
383
|
});
|
|
351
384
|
}
|
|
352
|
-
}
|
|
385
|
+
}
|
package/src/ts-proxy.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
2
1
|
import type ts from 'typescript';
|
|
3
2
|
|
|
4
3
|
let state: typeof ts | undefined;
|
|
5
4
|
let promise: Promise<unknown> | undefined;
|
|
6
|
-
export const tsProxyInit = (): Promise<unknown> =>
|
|
5
|
+
export const tsProxyInit = (): Promise<unknown> =>
|
|
6
|
+
(promise ??= import('typescript').then(module => {
|
|
7
|
+
state = module.default;
|
|
8
|
+
}));
|
|
7
9
|
|
|
8
10
|
export const tsProxy = new Proxy({}!, {
|
|
9
11
|
get(_, prop: string): unknown {
|
|
10
12
|
return state![prop as keyof typeof ts];
|
|
11
13
|
}
|
|
12
|
-
}) as typeof ts;
|
|
14
|
+
}) as typeof ts;
|
package/src/types.ts
CHANGED
|
@@ -3,23 +3,30 @@ import type { ChangeEventType, ManifestModule } from '@travetto/manifest';
|
|
|
3
3
|
export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'closed';
|
|
4
4
|
export type CompilerLogLevel = 'info' | 'debug' | 'warn' | 'error';
|
|
5
5
|
|
|
6
|
-
export type CompileEmitEvent = { file: string
|
|
7
|
-
export type CompileStateEntry = {
|
|
8
|
-
|
|
6
|
+
export type CompileEmitEvent = { file: string; i: number; total: number; errors?: string[]; duration: number };
|
|
7
|
+
export type CompileStateEntry = {
|
|
8
|
+
sourceFile: string;
|
|
9
|
+
tscOutputFile: string;
|
|
10
|
+
outputFile?: string;
|
|
11
|
+
module: ManifestModule;
|
|
12
|
+
import: string;
|
|
13
|
+
moduleFile: string;
|
|
14
|
+
};
|
|
15
|
+
export type CompilerWatchEvent = { action: ChangeEventType; file: string; entry: CompileStateEntry; moduleFile: string };
|
|
9
16
|
|
|
10
|
-
export type CompilerChangeEvent = { file: string
|
|
11
|
-
export type CompilerLogEvent = { level: CompilerLogLevel
|
|
12
|
-
export type CompilerProgressEvent = { idx: number
|
|
13
|
-
export type CompilerStateEvent = { state: CompilerStateType
|
|
14
|
-
export type FileChangeEvent = { files: { file: string
|
|
17
|
+
export type CompilerChangeEvent = { file: string; action: ChangeEventType; output: string; module: string; import: string; time: number };
|
|
18
|
+
export type CompilerLogEvent = { level: CompilerLogLevel; message: string; time?: number; args?: unknown[]; scope?: string };
|
|
19
|
+
export type CompilerProgressEvent = { idx: number; total: number; message: string; operation: 'compile'; complete?: boolean };
|
|
20
|
+
export type CompilerStateEvent = { state: CompilerStateType; extra?: Record<string, unknown> };
|
|
21
|
+
export type FileChangeEvent = { files: { file: string; action: ChangeEventType }[]; time: number };
|
|
15
22
|
|
|
16
23
|
export type CompilerEvent =
|
|
17
|
-
{ type: 'file'
|
|
18
|
-
{ type: 'change'
|
|
19
|
-
{ type: 'log'
|
|
20
|
-
{ type: 'progress'
|
|
21
|
-
{ type: 'state'
|
|
22
|
-
{ type: 'all'
|
|
24
|
+
| { type: 'file'; payload: FileChangeEvent }
|
|
25
|
+
| { type: 'change'; payload: CompilerChangeEvent }
|
|
26
|
+
| { type: 'log'; payload: CompilerLogEvent }
|
|
27
|
+
| { type: 'progress'; payload: CompilerProgressEvent }
|
|
28
|
+
| { type: 'state'; payload: CompilerStateEvent }
|
|
29
|
+
| { type: 'all'; payload: unknown };
|
|
23
30
|
|
|
24
31
|
export type CompilerEventType = CompilerEvent['type'];
|
|
25
32
|
export type CompilerEventPayload<V> = (CompilerEvent & { type: V })['payload'];
|
|
@@ -35,4 +42,4 @@ export type CompilerServerInfo = {
|
|
|
35
42
|
env?: Record<string, string>;
|
|
36
43
|
};
|
|
37
44
|
|
|
38
|
-
export class CompilerReset extends Error {
|
|
45
|
+
export class CompilerReset extends Error {}
|
package/src/util.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { ManifestModuleUtil, type ManifestRoot, type Package } from '@travetto/m
|
|
|
4
4
|
* Standard utilities for compiler
|
|
5
5
|
*/
|
|
6
6
|
export class CompilerUtil {
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* Rewrites the package.json to target output file names, and pins versions
|
|
10
9
|
* @param manifest
|
|
@@ -39,11 +38,10 @@ export class CompilerUtil {
|
|
|
39
38
|
static naiveHash(text: string): number {
|
|
40
39
|
let hash = 5381;
|
|
41
40
|
|
|
42
|
-
for (let i = 0; i < text.length; i
|
|
43
|
-
// eslint-disable-next-line no-bitwise
|
|
41
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
44
42
|
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
return Math.abs(hash);
|
|
48
46
|
}
|
|
49
|
-
}
|
|
47
|
+
}
|
package/src/watch.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { watch } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
3
|
|
|
4
4
|
import { ManifestFileUtil, ManifestModuleUtil, ManifestUtil, PACKAGE_MANAGERS, PackageUtil, path } from '@travetto/manifest';
|
|
5
5
|
|
|
6
|
-
import { CompilerReset, type CompilerWatchEvent, type CompileStateEntry } from './types.ts';
|
|
7
|
-
import type { CompilerState } from './state.ts';
|
|
8
|
-
|
|
9
|
-
import { AsyncQueue } from './queue.ts';
|
|
10
|
-
import { IpcLogger } from './log.ts';
|
|
11
6
|
import { EventUtil } from './event.ts';
|
|
7
|
+
import { IpcLogger } from './log.ts';
|
|
8
|
+
import { AsyncQueue } from './queue.ts';
|
|
9
|
+
import type { CompilerState } from './state.ts';
|
|
10
|
+
import { CompilerReset, type CompilerWatchEvent, type CompileStateEntry } from './types.ts';
|
|
12
11
|
|
|
13
12
|
const log = new IpcLogger({ level: 'debug' });
|
|
14
13
|
|
|
@@ -16,7 +15,7 @@ type CompilerWatchEventCandidate = Omit<CompilerWatchEvent, 'entry'> & { entry?:
|
|
|
16
15
|
|
|
17
16
|
export class CompilerWatcher {
|
|
18
17
|
#state: CompilerState;
|
|
19
|
-
#cleanup: Partial<Record<'tool' | 'workspace' | 'canary' | 'git', () =>
|
|
18
|
+
#cleanup: Partial<Record<'tool' | 'workspace' | 'canary' | 'git', () => void | Promise<void>>> = {};
|
|
20
19
|
#watchCanary: string = '.trv/canary.id';
|
|
21
20
|
#lastWorkspaceModified = Date.now();
|
|
22
21
|
#watchCanaryFrequency = 5;
|
|
@@ -27,22 +26,22 @@ export class CompilerWatcher {
|
|
|
27
26
|
this.#state = state;
|
|
28
27
|
this.#root = state.manifest.workspace.path;
|
|
29
28
|
this.#queue = new AsyncQueue(signal);
|
|
30
|
-
signal.addEventListener('abort', () =>
|
|
29
|
+
signal.addEventListener('abort', () =>
|
|
30
|
+
Object.values(this.#cleanup).forEach(fn => {
|
|
31
|
+
fn?.();
|
|
32
|
+
})
|
|
33
|
+
);
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
async #getWatchIgnores(): Promise<string[]> {
|
|
34
37
|
const pkg = PackageUtil.readPackage(this.#root);
|
|
35
|
-
const patterns = [
|
|
36
|
-
...pkg?.travetto?.build?.watchIgnores ?? [],
|
|
37
|
-
'**/node_modules',
|
|
38
|
-
'.*/**/node_modules'
|
|
39
|
-
];
|
|
38
|
+
const patterns = [...(pkg?.travetto?.build?.watchIgnores ?? []), '**/node_modules', '.*/**/node_modules'];
|
|
40
39
|
const ignores = new Set(['node_modules', '.git', this.#state.resolveOutputFile('.')]);
|
|
41
40
|
for (const item of patterns) {
|
|
42
41
|
if (item.includes('*')) {
|
|
43
42
|
for await (const sub of fs.glob(item, { cwd: this.#root })) {
|
|
44
43
|
if (sub.startsWith('node_modules')) {
|
|
45
|
-
|
|
44
|
+
// Continue
|
|
46
45
|
} else if (sub.endsWith('/node_modules')) {
|
|
47
46
|
ignores.add(sub.split('/node_modules')[0]);
|
|
48
47
|
} else {
|
|
@@ -53,7 +52,7 @@ export class CompilerWatcher {
|
|
|
53
52
|
ignores.add(item);
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
|
-
return [...ignores].toSorted().map(ignore => ignore.endsWith('/') ? ignore : `${ignore}/`);
|
|
55
|
+
return [...ignores].toSorted().map(ignore => (ignore.endsWith('/') ? ignore : `${ignore}/`));
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
#toCandidateEvent({ action, file }: Pick<CompilerWatchEvent, 'action' | 'file'>): CompilerWatchEventCandidate {
|
|
@@ -68,7 +67,7 @@ export class CompilerWatcher {
|
|
|
68
67
|
this.#state.removeSource(entry.sourceFile); // Ensure we remove it
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
return { entry, file: entry?.sourceFile ?? file, action, moduleFile: entry?.moduleFile
|
|
70
|
+
return { entry, file: entry?.sourceFile ?? file, action, moduleFile: entry?.moduleFile ?? '' };
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
#isValidFile(file: string): boolean {
|
|
@@ -97,8 +96,7 @@ export class CompilerWatcher {
|
|
|
97
96
|
|
|
98
97
|
async #updateManifestWithEvents(compilerEvents: CompilerWatchEvent[]): Promise<void> {
|
|
99
98
|
const eventsByModule = this.#state.manifestIndex.groupByLineage(
|
|
100
|
-
compilerEvents.map(event => ({ item: event, module: event.entry!.module.name }))
|
|
101
|
-
.filter(x => x.item.action !== 'update')
|
|
99
|
+
compilerEvents.map(event => ({ item: event, module: event.entry!.module.name })).filter(x => x.item.action !== 'update')
|
|
102
100
|
);
|
|
103
101
|
|
|
104
102
|
for (const [moduleName, events] of eventsByModule.entries()) {
|
|
@@ -116,66 +114,76 @@ export class CompilerWatcher {
|
|
|
116
114
|
async #listenWorkspace(): Promise<void> {
|
|
117
115
|
const lib = await import('@parcel/watcher');
|
|
118
116
|
const ignore = await this.#getWatchIgnores();
|
|
119
|
-
const packageFiles = new Set(
|
|
120
|
-
'package.json',
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const packageFiles = new Set(
|
|
118
|
+
['package.json', ...PACKAGE_MANAGERS.flatMap(x => [x.workspaceFile!, x.lock].filter(Boolean))].map(file =>
|
|
119
|
+
path.resolve(this.#root, file)
|
|
120
|
+
)
|
|
121
|
+
);
|
|
123
122
|
|
|
124
123
|
log.debug('Ignore Globs', ignore);
|
|
125
124
|
log.debug('Watching', this.#root);
|
|
126
125
|
|
|
127
126
|
await this.#cleanup.workspace?.();
|
|
128
127
|
|
|
129
|
-
const listener = await lib.subscribe(
|
|
130
|
-
this.#
|
|
128
|
+
const listener = await lib.subscribe(
|
|
129
|
+
this.#root,
|
|
130
|
+
async (error, events) => {
|
|
131
|
+
this.#lastWorkspaceModified = Date.now();
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
try {
|
|
134
|
+
if (error) {
|
|
135
|
+
throw error instanceof Error ? error : new Error(`${error}`);
|
|
136
|
+
} else if (events.length > 25) {
|
|
137
|
+
throw new CompilerReset(`Large influx of file changes: ${events.length}`);
|
|
138
|
+
} else if (events.some(event => packageFiles.has(path.toPosix(event.path)))) {
|
|
139
|
+
throw new CompilerReset('Package information changed');
|
|
140
|
+
}
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
// One event per file set
|
|
143
|
+
const filesChanged = events
|
|
144
|
+
.map(event => ({ file: path.toPosix(event.path), action: event.type }))
|
|
145
|
+
.filter(event => this.#isValidFile(event.file));
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
if (filesChanged.length) {
|
|
148
|
+
EventUtil.sendEvent('file', { time: Date.now(), files: filesChanged });
|
|
149
|
+
}
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
if (filesChanged.some(item => this.#state.isCompilerFile(item.file))) {
|
|
152
|
+
throw new CompilerReset('Compiler has changed, restarting');
|
|
153
|
+
}
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
.map(event => this.#toCandidateEvent(event))
|
|
156
|
-
.filter(event => this.#isValidEvent(event));
|
|
155
|
+
const items = filesChanged.map(event => this.#toCandidateEvent(event)).filter(event => this.#isValidEvent(event));
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
if (items.length === 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
try {
|
|
162
|
+
await this.#updateManifestWithEvents(items);
|
|
163
|
+
} catch (manifestError) {
|
|
164
|
+
log.info('Restarting due to manifest rebuild failure', manifestError);
|
|
165
|
+
throw new CompilerReset(`Manifest rebuild failure: ${manifestError} `);
|
|
166
|
+
}
|
|
168
167
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
this.#queue.add(item);
|
|
170
|
+
}
|
|
171
|
+
} catch (out) {
|
|
172
|
+
let error: Error;
|
|
173
|
+
if (out instanceof Error) {
|
|
174
|
+
if (out.message.includes('Events were dropped by the FSEvents client.')) {
|
|
175
|
+
error = new CompilerReset('FSEvents failure, requires restart');
|
|
176
|
+
} else {
|
|
177
|
+
error = out;
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
error = new Error(`${out}`);
|
|
181
|
+
}
|
|
182
|
+
return this.#queue.throw(error);
|
|
175
183
|
}
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
184
|
+
},
|
|
185
|
+
{ ignore }
|
|
186
|
+
);
|
|
179
187
|
|
|
180
188
|
this.#cleanup.workspace = (): Promise<void> => listener.unsubscribe();
|
|
181
189
|
}
|
|
@@ -183,10 +191,12 @@ export class CompilerWatcher {
|
|
|
183
191
|
async #listenToolFolder(): Promise<void> {
|
|
184
192
|
const build = this.#state.manifest.build;
|
|
185
193
|
const toolRootFolder = path.dirname(path.resolve(this.#root, build.outputFolder));
|
|
186
|
-
const toolFolders = new Set([toolRootFolder, build.typesFolder, build.outputFolder]
|
|
187
|
-
.map(folder => path.resolve(this.#root, folder)));
|
|
194
|
+
const toolFolders = new Set([toolRootFolder, build.typesFolder, build.outputFolder].map(folder => path.resolve(this.#root, folder)));
|
|
188
195
|
|
|
189
|
-
log.debug(
|
|
196
|
+
log.debug(
|
|
197
|
+
'Tooling Folders',
|
|
198
|
+
[...toolFolders].map(folder => folder.replace(`${this.#root}/`, ''))
|
|
199
|
+
);
|
|
190
200
|
|
|
191
201
|
await this.#cleanup.tool?.();
|
|
192
202
|
|
|
@@ -229,7 +239,9 @@ export class CompilerWatcher {
|
|
|
229
239
|
|
|
230
240
|
async #listenGitChanges(): Promise<void> {
|
|
231
241
|
const gitFolder = path.resolve(this.#root, '.git');
|
|
232
|
-
if (!await fs.stat(gitFolder, { throwIfNoEntry: false })) {
|
|
242
|
+
if (!(await fs.stat(gitFolder, { throwIfNoEntry: false }))) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
233
245
|
log.debug('Starting git canary');
|
|
234
246
|
const listener = watch(gitFolder, { encoding: 'utf8' }, async (event, file) => {
|
|
235
247
|
if (!file) {
|
|
@@ -251,4 +263,4 @@ export class CompilerWatcher {
|
|
|
251
263
|
}
|
|
252
264
|
return this.#queue[Symbol.asyncIterator]();
|
|
253
265
|
}
|
|
254
|
-
}
|
|
266
|
+
}
|