@travetto/compiler 4.0.8 → 4.1.0
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 +5 -5
- package/src/compiler.ts +5 -5
- package/src/state.ts +16 -11
- package/src/util.ts +0 -15
- package/src/watch.ts +93 -54
- package/support/server/server.ts +1 -1
- package/support/util.ts +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
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.1",
|
|
34
|
-
"@travetto/manifest": "^4.0
|
|
35
|
-
"@travetto/transformer": "^4.0
|
|
36
|
-
"@types/node": "^20.
|
|
34
|
+
"@travetto/manifest": "^4.1.0",
|
|
35
|
+
"@travetto/transformer": "^4.1.0",
|
|
36
|
+
"@types/node": "^20.12.12"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@travetto/cli": "^4.0
|
|
39
|
+
"@travetto/cli": "^4.1.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -67,6 +67,7 @@ export class Compiler {
|
|
|
67
67
|
case 'error': {
|
|
68
68
|
process.exitCode = 1;
|
|
69
69
|
if (err) {
|
|
70
|
+
EventUtil.sendEvent('log', { level: 'error', message: err.toString(), time: Date.now() });
|
|
70
71
|
log.error('Shutting down due to failure', err.message);
|
|
71
72
|
}
|
|
72
73
|
break;
|
|
@@ -127,7 +128,7 @@ export class Compiler {
|
|
|
127
128
|
EventUtil.sendEvent('state', { state: 'init', extra: { pid: process.pid } });
|
|
128
129
|
|
|
129
130
|
const emitter = await this.getCompiler();
|
|
130
|
-
let
|
|
131
|
+
let failure: Error | undefined;
|
|
131
132
|
|
|
132
133
|
log.debug('Compiler loaded');
|
|
133
134
|
|
|
@@ -136,17 +137,16 @@ export class Compiler {
|
|
|
136
137
|
if (this.#dirtyFiles.length) {
|
|
137
138
|
for await (const ev of this.emit(this.#dirtyFiles, emitter)) {
|
|
138
139
|
if (ev.err) {
|
|
139
|
-
failed = true;
|
|
140
140
|
const compileError = CompilerUtil.buildTranspileError(ev.file, ev.err);
|
|
141
|
+
failure ??= compileError;
|
|
141
142
|
EventUtil.sendEvent('log', { level: 'error', message: compileError.toString(), time: Date.now() });
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
if (this.#signal.aborted) {
|
|
145
146
|
log.debug('Compilation aborted');
|
|
146
|
-
} else if (
|
|
147
|
+
} else if (failure) {
|
|
147
148
|
log.debug('Compilation failed');
|
|
148
|
-
|
|
149
|
-
return;
|
|
149
|
+
return this.#shutdown('error', failure);
|
|
150
150
|
} else {
|
|
151
151
|
log.debug('Compilation succeeded');
|
|
152
152
|
}
|
package/src/state.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import timers from 'node:timers/promises';
|
|
3
2
|
|
|
4
3
|
import { path, ManifestModuleUtil, ManifestModule, ManifestRoot, ManifestIndex } from '@travetto/manifest';
|
|
5
4
|
import { TransformerManager } from '@travetto/transformer';
|
|
@@ -8,6 +7,7 @@ import { TypescriptUtil } from '../support/ts-util';
|
|
|
8
7
|
|
|
9
8
|
import { CompilerUtil } from './util';
|
|
10
9
|
import { CompileEmitError, CompileStateEntry } from './types';
|
|
10
|
+
import { CommonUtil } from '../support/util';
|
|
11
11
|
|
|
12
12
|
function folderMapper(root: string, prefix: string): { dir: string, translate: (val: string) => string } {
|
|
13
13
|
let matched: string = '~~';
|
|
@@ -63,14 +63,15 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
63
63
|
|
|
64
64
|
// Register all inputs
|
|
65
65
|
for (const x of this.#modules) {
|
|
66
|
+
const base = x?.files ?? {};
|
|
66
67
|
const files = [
|
|
67
|
-
...
|
|
68
|
-
...
|
|
69
|
-
...
|
|
70
|
-
...
|
|
71
|
-
...
|
|
72
|
-
...
|
|
73
|
-
...
|
|
68
|
+
...base.bin ?? [],
|
|
69
|
+
...base.src ?? [],
|
|
70
|
+
...base.support ?? [],
|
|
71
|
+
...base.doc ?? [],
|
|
72
|
+
...base.test ?? [],
|
|
73
|
+
...base.$index ?? [],
|
|
74
|
+
...base.$package ?? []
|
|
74
75
|
];
|
|
75
76
|
for (const [file, type] of files) {
|
|
76
77
|
if (CompilerUtil.validFile(type) || type === 'typings') {
|
|
@@ -103,14 +104,18 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
getArbitraryInputFile(): string {
|
|
106
|
-
|
|
107
|
+
const randomSource = this.#manifestIndex.getWorkspaceModules()
|
|
108
|
+
.filter(x => x.files.src?.length)[0]
|
|
109
|
+
.files.src[0].sourceFile;
|
|
110
|
+
|
|
111
|
+
return this.getBySource(randomSource)!.inputFile;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
async createProgram(force = false): Promise<ts.Program> {
|
|
110
115
|
if (force || !this.#program) {
|
|
111
116
|
this.#program = ts.createProgram({ rootNames: this.getAllFiles(), host: this, options: this.#compilerOptions, oldProgram: this.#program });
|
|
112
117
|
this.#transformerManager.init(this.#program.getTypeChecker());
|
|
113
|
-
await
|
|
118
|
+
await CommonUtil.queueMacroTask();
|
|
114
119
|
}
|
|
115
120
|
return this.#program;
|
|
116
121
|
}
|
|
@@ -179,7 +184,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
179
184
|
if (!contents || (contents.length === 0 && prevHash)) {
|
|
180
185
|
return false; // Ignore empty file
|
|
181
186
|
}
|
|
182
|
-
const currentHash =
|
|
187
|
+
const currentHash = CommonUtil.naiveHash(contents);
|
|
183
188
|
const changed = prevHash !== currentHash;
|
|
184
189
|
if (changed) {
|
|
185
190
|
this.#sourceHashes.set(inputFile, currentHash);
|
package/src/util.ts
CHANGED
|
@@ -105,7 +105,6 @@ export class CompilerUtil {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
const errors: string[] = diagnostics.slice(0, 5).map(diag => {
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
109
108
|
const message = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
|
|
110
109
|
if (diag.file) {
|
|
111
110
|
const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start!);
|
|
@@ -120,18 +119,4 @@ export class CompilerUtil {
|
|
|
120
119
|
}
|
|
121
120
|
return new Error(`Transpiling ${filename.replace(nativeCwd, '.')} failed: \n${errors.join('\n')}`);
|
|
122
121
|
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Naive hashing
|
|
126
|
-
*/
|
|
127
|
-
static naiveHash(text: string): number {
|
|
128
|
-
let hash = 5381;
|
|
129
|
-
|
|
130
|
-
for (let i = 0; i < text.length; i++) {
|
|
131
|
-
// eslint-disable-next-line no-bitwise
|
|
132
|
-
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return Math.abs(hash);
|
|
136
|
-
}
|
|
137
122
|
}
|
package/src/watch.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ManifestModuleFileType, ManifestModuleFolderType, ManifestModuleUtil, ManifestUtil, PackageUtil, RuntimeIndex, path } from '@travetto/manifest';
|
|
2
2
|
|
|
3
3
|
import type { CompileStateEntry } from './types';
|
|
4
4
|
import { CompilerState } from './state';
|
|
@@ -9,6 +9,14 @@ import { AsyncQueue } from '../support/queue';
|
|
|
9
9
|
type WatchAction = 'create' | 'update' | 'delete';
|
|
10
10
|
type WatchEvent = { action: WatchAction, file: string };
|
|
11
11
|
type CompilerWatchEvent = WatchEvent & { entry: CompileStateEntry };
|
|
12
|
+
type FileShape = {
|
|
13
|
+
mod: string;
|
|
14
|
+
folderKey: ManifestModuleFolderType;
|
|
15
|
+
fileType: ManifestModuleFileType;
|
|
16
|
+
moduleFile: string;
|
|
17
|
+
action: WatchAction;
|
|
18
|
+
};
|
|
19
|
+
|
|
12
20
|
|
|
13
21
|
/**
|
|
14
22
|
* Watch support, based on compiler state and manifest details
|
|
@@ -22,6 +30,10 @@ export class CompilerWatcher {
|
|
|
22
30
|
this.#signal = signal;
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
#reset(ev: WatchEvent): never {
|
|
34
|
+
throw new Error('RESET', { cause: `${ev.action}:${ev.file}` });
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
#getIgnores(): string[] {
|
|
26
38
|
// TODO: Read .gitignore?
|
|
27
39
|
let ignores = PackageUtil.readPackage(this.#state.manifest.workspace.path)?.travetto?.build?.watchIgnores;
|
|
@@ -40,8 +52,8 @@ export class CompilerWatcher {
|
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
/** Watch files */
|
|
43
|
-
async * #watchFolder(rootPath: string): AsyncIterable<WatchEvent> {
|
|
44
|
-
const q = new AsyncQueue<WatchEvent>(this.#signal);
|
|
55
|
+
async * #watchFolder(rootPath: string): AsyncIterable<WatchEvent[]> {
|
|
56
|
+
const q = new AsyncQueue<WatchEvent[]>(this.#signal);
|
|
45
57
|
const lib = await import('@parcel/watcher');
|
|
46
58
|
const ignore = this.#getIgnores();
|
|
47
59
|
|
|
@@ -51,9 +63,7 @@ export class CompilerWatcher {
|
|
|
51
63
|
q.throw(err instanceof Error ? err : new Error((err as Error).message));
|
|
52
64
|
return;
|
|
53
65
|
}
|
|
54
|
-
|
|
55
|
-
q.add({ action: ev.type, file: path.toPosix(ev.path) });
|
|
56
|
-
}
|
|
66
|
+
q.add(events.map(ev => ({ action: ev.type, file: path.toPosix(ev.path) })));
|
|
57
67
|
}, { ignore });
|
|
58
68
|
|
|
59
69
|
if (this.#signal.aborted) { // If already aborted, can happen async
|
|
@@ -66,30 +76,53 @@ export class CompilerWatcher {
|
|
|
66
76
|
yield* q;
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
async #rebuildManifestsIfNeeded(
|
|
70
|
-
|
|
79
|
+
async #rebuildManifestsIfNeeded(events: CompilerWatchEvent[]): Promise<void> {
|
|
80
|
+
events = events.filter(x => x.entry.outputFile && x.action !== 'update');
|
|
81
|
+
|
|
82
|
+
if (!events.length) {
|
|
71
83
|
return;
|
|
72
84
|
}
|
|
73
85
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
const mods = [...new Set(events.map(v => v.entry.module.name))];
|
|
87
|
+
|
|
88
|
+
const moduleToFiles = new Map(mods.map(m => [m, {
|
|
89
|
+
context: ManifestUtil.getModuleContext(this.#state.manifest, RuntimeIndex.getManifestModule(m)!.sourceFolder),
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
91
|
+
files: [] as FileShape[]
|
|
92
|
+
}] as const));
|
|
93
|
+
|
|
94
|
+
const parents = new Map<string, string[]>(
|
|
95
|
+
mods.map(m => [m, RuntimeIndex.getDependentModules(m, 'parents').map(x => x.name)])
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const allFiles = events.map(ev => {
|
|
99
|
+
const modRoot = ev.entry.module.sourceFolder || this.#state.manifest.workspace.path;
|
|
100
|
+
const moduleFile = ev.file.includes(modRoot) ? ev.file.split(`${modRoot}/`)[1] : ev.file;
|
|
101
|
+
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
102
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
103
|
+
return { mod: ev.entry.module.name, action: ev.action, moduleFile, folderKey, fileType };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (const file of allFiles) {
|
|
107
|
+
for (const parent of parents.get(file.mod)!) {
|
|
108
|
+
const mod = moduleToFiles.get(parent);
|
|
109
|
+
if (!mod || !mod.files) {
|
|
110
|
+
this.#reset({ action: file.action, file: file.moduleFile });
|
|
111
|
+
}
|
|
112
|
+
mod.files.push(file);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
82
115
|
|
|
83
|
-
for (const
|
|
84
|
-
const newManifest = await ManifestUtil.buildManifest(
|
|
85
|
-
|
|
86
|
-
const modFiles = newManifest.modules[mod
|
|
116
|
+
for (const { context, files } of moduleToFiles.values()) {
|
|
117
|
+
const newManifest = await ManifestUtil.buildManifest(context);
|
|
118
|
+
for (const { action, mod, fileType, moduleFile, folderKey } of files) {
|
|
119
|
+
const modFiles = newManifest.modules[mod].files[folderKey] ??= [];
|
|
87
120
|
const idx = modFiles.findIndex(x => x[0] === moduleFile);
|
|
88
121
|
|
|
89
|
-
if (
|
|
122
|
+
if (action === 'create' && idx < 0) {
|
|
90
123
|
modFiles.push([moduleFile, fileType, Date.now()]);
|
|
91
124
|
} else if (idx >= 0) {
|
|
92
|
-
if (
|
|
125
|
+
if (action === 'delete') {
|
|
93
126
|
modFiles.splice(idx, 1);
|
|
94
127
|
} else {
|
|
95
128
|
modFiles[idx] = [moduleFile, fileType, Date.now()];
|
|
@@ -120,45 +153,51 @@ export class CompilerWatcher {
|
|
|
120
153
|
const OUTPUT_PATH = path.resolve(manifest.workspace.path, manifest.build.outputFolder);
|
|
121
154
|
const COMPILER_PATH = path.resolve(manifest.workspace.path, manifest.build.compilerFolder);
|
|
122
155
|
|
|
123
|
-
for await (const
|
|
124
|
-
const { action, file: sourceFile } = ev;
|
|
156
|
+
for await (const events of this.#watchFolder(this.#state.manifest.workspace.path)) {
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
sourceFile === ROOT_LOCK ||
|
|
128
|
-
sourceFile === ROOT_PKG ||
|
|
129
|
-
(action === 'delete' && (sourceFile === OUTPUT_PATH || sourceFile === COMPILER_PATH))
|
|
130
|
-
) {
|
|
131
|
-
throw new Error('RESET', { cause: `${action}:${sourceFile}` });
|
|
132
|
-
}
|
|
158
|
+
const outEvents: CompilerWatchEvent[] = [];
|
|
133
159
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
160
|
+
for (const ev of events) {
|
|
161
|
+
const { action, file: sourceFile } = ev;
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
sourceFile === ROOT_LOCK ||
|
|
165
|
+
sourceFile === ROOT_PKG ||
|
|
166
|
+
(action === 'delete' && (sourceFile === OUTPUT_PATH || sourceFile === COMPILER_PATH))
|
|
167
|
+
) {
|
|
168
|
+
this.#reset(ev);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const fileType = ManifestModuleUtil.getFileType(sourceFile);
|
|
172
|
+
if (!CompilerUtil.validFile(fileType)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
138
175
|
|
|
139
|
-
|
|
176
|
+
let entry = this.#state.getBySource(sourceFile);
|
|
140
177
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
178
|
+
const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(sourceFile);
|
|
179
|
+
if (!mod) { // Unknown module
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const modRoot = mod.sourceFolder || this.#state.manifest.workspace.path;
|
|
184
|
+
const moduleFile = sourceFile.includes(modRoot) ? sourceFile.split(`${modRoot}/`)[1] : sourceFile;
|
|
185
|
+
|
|
186
|
+
if (action === 'create') {
|
|
187
|
+
entry = this.#state.registerInput(mod, moduleFile);
|
|
188
|
+
} else if (!entry) {
|
|
189
|
+
continue;
|
|
190
|
+
} else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.inputFile)) {
|
|
191
|
+
continue;
|
|
192
|
+
} else if (action === 'delete') {
|
|
193
|
+
this.#state.removeInput(entry.inputFile);
|
|
194
|
+
}
|
|
145
195
|
|
|
146
|
-
|
|
147
|
-
const moduleFile = sourceFile.includes(modRoot) ? sourceFile.split(`${modRoot}/`)[1] : sourceFile;
|
|
148
|
-
|
|
149
|
-
if (action === 'create') {
|
|
150
|
-
entry = this.#state.registerInput(mod, moduleFile);
|
|
151
|
-
} else if (!entry) {
|
|
152
|
-
continue;
|
|
153
|
-
} else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.inputFile)) {
|
|
154
|
-
continue;
|
|
155
|
-
} else if (action === 'delete') {
|
|
156
|
-
this.#state.removeInput(entry.inputFile);
|
|
196
|
+
outEvents.push({ action, file: entry.sourceFile, entry });
|
|
157
197
|
}
|
|
158
198
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
yield result;
|
|
199
|
+
await this.#rebuildManifestsIfNeeded(outEvents);
|
|
200
|
+
yield* outEvents;
|
|
162
201
|
}
|
|
163
202
|
}
|
|
164
203
|
}
|
package/support/server/server.ts
CHANGED
|
@@ -207,7 +207,7 @@ export class CompilerServer {
|
|
|
207
207
|
CommonUtil.nonBlockingTimeout(2000).then(reject); // Wait 2s max
|
|
208
208
|
this.#server.close(resolve);
|
|
209
209
|
this.#emitEvent({ type: 'state', payload: { state: 'closed' } });
|
|
210
|
-
|
|
210
|
+
CommonUtil.queueMacroTask().then(() => {
|
|
211
211
|
this.#server.closeAllConnections();
|
|
212
212
|
this.#shutdown.abort();
|
|
213
213
|
});
|
package/support/util.ts
CHANGED
|
@@ -58,6 +58,20 @@ export class CommonUtil {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Naive hashing
|
|
63
|
+
*/
|
|
64
|
+
static naiveHash(text: string): number {
|
|
65
|
+
let hash = 5381;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < text.length; i++) {
|
|
68
|
+
// eslint-disable-next-line no-bitwise
|
|
69
|
+
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Math.abs(hash);
|
|
73
|
+
}
|
|
74
|
+
|
|
61
75
|
/**
|
|
62
76
|
* Non-blocking timeout
|
|
63
77
|
*/
|