@travetto/compiler 8.0.0-alpha.2 → 8.0.0-alpha.21
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 +5 -5
- package/__index__.ts +4 -4
- package/bin/hook.js +6 -9
- package/bin/trvc-target.js +3 -1
- package/bin/trvc.js +3 -1
- package/package.json +4 -4
- package/src/common.ts +15 -9
- package/src/compiler.ts +61 -66
- package/src/event.ts +7 -5
- package/src/log.ts +57 -22
- package/src/queue.ts +4 -2
- package/src/server/client.ts +38 -24
- package/src/server/manager.ts +18 -12
- package/src/server/process-handle.ts +14 -10
- package/src/server/server.ts +49 -25
- package/src/state.ts +145 -74
- package/src/ts-proxy.ts +6 -3
- package/src/types.ts +22 -19
- package/src/util.ts +1 -34
- package/src/watch.ts +81 -69
- package/support/invoke.ts +65 -29
- package/tsconfig.trv.json +6 -15
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
|
|
|
@@ -195,7 +205,7 @@ export class CompilerWatcher {
|
|
|
195
205
|
return;
|
|
196
206
|
}
|
|
197
207
|
const full = path.resolve(toolRootFolder, file);
|
|
198
|
-
const stat = await fs.stat(full
|
|
208
|
+
const stat = await fs.stat(full, { throwIfNoEntry: false });
|
|
199
209
|
if (toolFolders.has(full) && !stat) {
|
|
200
210
|
this.#queue.throw(new CompilerReset(`Tooling folder removal ${full}`));
|
|
201
211
|
}
|
|
@@ -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
|
|
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
|
+
}
|
package/support/invoke.ts
CHANGED
|
@@ -1,48 +1,85 @@
|
|
|
1
1
|
// @trv-no-transform
|
|
2
2
|
import { getManifestContext, ManifestUtil } from '@travetto/manifest';
|
|
3
3
|
|
|
4
|
-
import { Log } from '../src/log.ts';
|
|
5
|
-
import { CompilerManager } from '../src/server/manager.ts';
|
|
6
|
-
import { CompilerClient } from '../src/server/client.ts';
|
|
7
4
|
import { CommonUtil } from '../src/common.ts';
|
|
8
5
|
import { EventUtil } from '../src/event.ts';
|
|
6
|
+
import { Log } from '../src/log.ts';
|
|
7
|
+
import { CompilerClient } from '../src/server/client.ts';
|
|
8
|
+
import { CompilerManager } from '../src/server/manager.ts';
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const hasColor = (process.stdout.isTTY && /^(0)*$/.test(process.env.NO_COLOR ?? '')) || /1\d*/.test(process.env.FORCE_COLOR ?? '');
|
|
11
|
+
const color =
|
|
12
|
+
(code: number) =>
|
|
13
|
+
(value: string): string =>
|
|
14
|
+
hasColor ? `\x1b[${code}m${value}\x1b[0m` : `${value}`;
|
|
15
|
+
const STYLE = { error: color(91), title: color(36), main: color(92), command: color(35), arg: color(37), description: color(33) };
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const COMMANDS = {
|
|
18
|
+
start: { description: 'Run the compiler in watch mode' },
|
|
19
|
+
stop: { description: 'Stop the compiler if running' },
|
|
20
|
+
restart: { description: 'Restart the compiler in watch mode' },
|
|
21
|
+
build: { description: 'Ensure the project is built and upto date' },
|
|
22
|
+
clean: { description: 'Clean out the output and compiler caches' },
|
|
23
|
+
info: { description: 'Retrieve the compiler information, if running' },
|
|
24
|
+
event: { args: ['<log|progress|state>'], description: 'Watch events in realtime as newline delimited JSON' },
|
|
25
|
+
exec: { args: ['<file>', '[...args]'], description: 'Allow for compiling and executing an entrypoint file' },
|
|
26
|
+
manifest: { args: ['[output]'], description: 'Generate the project manifest' },
|
|
27
|
+
'manifest:production': { args: ['[output]'], description: 'Generate the production project manifest' }
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
function showHelp(errorMessage?: string): void {
|
|
31
|
+
const PREPARED = Object.entries(COMMANDS)
|
|
32
|
+
.map(([name, config]) => ({ args: [], ...config, name }))
|
|
33
|
+
.map(config => ({ ...config, commandLength: [config.name, ...config.args].join(' ').length }));
|
|
34
|
+
|
|
35
|
+
const commandWidth = Math.max(...PREPARED.map(config => config.commandLength));
|
|
36
|
+
|
|
37
|
+
console.log(
|
|
38
|
+
[
|
|
39
|
+
...(errorMessage ? ['', STYLE.error(errorMessage)] : []),
|
|
40
|
+
'',
|
|
41
|
+
`${STYLE.main('trvc')} ${STYLE.command('[command]')}`,
|
|
42
|
+
'',
|
|
43
|
+
STYLE.title('Available Commands'),
|
|
44
|
+
...PREPARED.map(({ name, args, description, commandLength }) =>
|
|
45
|
+
[
|
|
46
|
+
'*',
|
|
47
|
+
STYLE.command(name),
|
|
48
|
+
...args.map(arg => STYLE.arg(arg)),
|
|
49
|
+
' '.repeat(commandWidth - commandLength),
|
|
50
|
+
'-',
|
|
51
|
+
STYLE.description(description)
|
|
52
|
+
].join(' ')
|
|
53
|
+
),
|
|
54
|
+
''
|
|
55
|
+
].join('\n')
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const validateInputs = (value: string[]): value is [keyof typeof COMMANDS, ...string[]] => !!value.length && value[0] in COMMANDS;
|
|
25
60
|
|
|
26
61
|
/**
|
|
27
62
|
* Invoke the compiler
|
|
28
63
|
*/
|
|
29
|
-
export async function invoke(
|
|
30
|
-
if (
|
|
31
|
-
[
|
|
64
|
+
export async function invoke(...input: string[]): Promise<unknown> {
|
|
65
|
+
if (!validateInputs(input)) {
|
|
66
|
+
return showHelp(input[0] ? `Unknown trvc command: ${input[0]}` : undefined);
|
|
32
67
|
}
|
|
68
|
+
|
|
69
|
+
const [command, ...args] = input;
|
|
33
70
|
const ctx = getManifestContext();
|
|
34
71
|
const client = new CompilerClient(ctx, Log.scoped('client'));
|
|
35
72
|
|
|
36
73
|
Log.initLevel('error');
|
|
37
74
|
Log.root = ctx.workspace.path;
|
|
38
75
|
|
|
39
|
-
switch (
|
|
40
|
-
case undefined:
|
|
41
|
-
case 'help': console.log(HELP); break;
|
|
76
|
+
switch (command) {
|
|
42
77
|
case 'start':
|
|
43
|
-
|
|
44
|
-
case 'build':
|
|
45
|
-
|
|
78
|
+
return CompilerManager.compile(ctx, client, { watch: true });
|
|
79
|
+
case 'build':
|
|
80
|
+
return CompilerManager.compile(ctx, client, { watch: false });
|
|
81
|
+
case 'restart':
|
|
82
|
+
return CompilerManager.compile(ctx, client, { watch: true, forceRestart: true });
|
|
46
83
|
case 'info': {
|
|
47
84
|
const info = await client.info();
|
|
48
85
|
return CommonUtil.writeStdout(2, info);
|
|
@@ -59,7 +96,7 @@ export async function invoke(operation?: string, args: string[] = []): Promise<u
|
|
|
59
96
|
case 'manifest:production':
|
|
60
97
|
case 'manifest': {
|
|
61
98
|
let manifest = await ManifestUtil.buildManifest(ctx);
|
|
62
|
-
if (
|
|
99
|
+
if (command === 'manifest:production') {
|
|
63
100
|
manifest = ManifestUtil.createProductionManifest(manifest);
|
|
64
101
|
}
|
|
65
102
|
if (args[0]) {
|
|
@@ -92,6 +129,5 @@ export async function invoke(operation?: string, args: string[] = []): Promise<u
|
|
|
92
129
|
// Return function to run import on a module
|
|
93
130
|
return import(importTarget);
|
|
94
131
|
}
|
|
95
|
-
default: console.error(`\nUnknown trvc operation: ${operation}\n${HELP}`);
|
|
96
132
|
}
|
|
97
|
-
}
|
|
133
|
+
}
|
package/tsconfig.trv.json
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"module": "esnext",
|
|
4
|
-
"target": "esnext",
|
|
5
3
|
"moduleResolution": "bundler",
|
|
6
4
|
"allowImportingTsExtensions": true,
|
|
7
|
-
"lib": [
|
|
8
|
-
|
|
9
|
-
],
|
|
5
|
+
"lib": ["ESNext"],
|
|
6
|
+
"types": ["node"],
|
|
10
7
|
"jsx": "react-jsx",
|
|
11
|
-
"stableTypeOrdering": true,
|
|
12
|
-
"strict": true,
|
|
13
8
|
"declaration": true,
|
|
14
9
|
"declarationMap": true,
|
|
15
|
-
"
|
|
16
|
-
"libReplacement": false,
|
|
10
|
+
"emitDeclarationOnly": false,
|
|
17
11
|
"strictPropertyInitialization": false,
|
|
18
12
|
"erasableSyntaxOnly": true,
|
|
19
13
|
"experimentalDecorators": true,
|
|
20
14
|
"noEmitOnError": false,
|
|
15
|
+
"allowJs": true,
|
|
21
16
|
"noErrorTruncation": true,
|
|
22
17
|
"resolveJsonModule": true,
|
|
23
18
|
"sourceMap": true,
|
|
@@ -29,10 +24,6 @@
|
|
|
29
24
|
"noEmit": true
|
|
30
25
|
},
|
|
31
26
|
"watchOptions": {
|
|
32
|
-
"excludeDirectories": [
|
|
33
|
-
"**/node_modules",
|
|
34
|
-
"**/.trv",
|
|
35
|
-
"**/.git",
|
|
36
|
-
]
|
|
27
|
+
"excludeDirectories": ["**/node_modules", "**/.trv", "**/.git"]
|
|
37
28
|
}
|
|
38
|
-
}
|
|
29
|
+
}
|