@travetto/compiler 3.4.4 → 4.0.0-rc.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 +29 -27
- package/bin/common.js +51 -46
- package/bin/trvc.js +21 -14
- package/package.json +6 -7
- package/src/compiler.ts +96 -43
- package/src/event.ts +3 -3
- package/src/log.ts +1 -1
- package/src/state.ts +43 -25
- package/src/types.ts +1 -1
- package/src/util.ts +6 -6
- package/src/watch.ts +107 -117
- package/support/entry.trvc.ts +67 -37
- package/support/log.ts +55 -24
- package/support/queue.ts +11 -6
- package/support/server/client.ts +122 -81
- package/support/server/process-handle.ts +57 -0
- package/support/server/runner.ts +37 -64
- package/support/server/server.ts +86 -58
- package/support/setup.ts +38 -36
- package/support/types.ts +3 -4
- package/support/util.ts +20 -25
- package/src/internal/watch-core.ts +0 -104
package/src/state.ts
CHANGED
|
@@ -3,9 +3,10 @@ import ts from 'typescript';
|
|
|
3
3
|
import { path, ManifestModuleUtil, ManifestModule, ManifestRoot, ManifestIndex } from '@travetto/manifest';
|
|
4
4
|
import { TransformerManager } from '@travetto/transformer';
|
|
5
5
|
|
|
6
|
+
import { CommonUtil } from '../support/util';
|
|
7
|
+
|
|
6
8
|
import { CompilerUtil } from './util';
|
|
7
9
|
import { CompileStateEntry } from './types';
|
|
8
|
-
import { CommonUtil } from '../support/util';
|
|
9
10
|
|
|
10
11
|
function folderMapper(root: string, prefix: string): { dir: string, translate: (val: string) => string } {
|
|
11
12
|
let matched: string = '~~';
|
|
@@ -26,7 +27,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
26
27
|
private constructor() { }
|
|
27
28
|
|
|
28
29
|
#rootDir: string;
|
|
29
|
-
#
|
|
30
|
+
#inputPathToSourcePath: (file: string) => string;
|
|
30
31
|
#outputPath: string;
|
|
31
32
|
#inputFiles = new Set<string>();
|
|
32
33
|
#inputDirectoryToSource = new Map<string, string>();
|
|
@@ -36,6 +37,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
36
37
|
|
|
37
38
|
#sourceContents = new Map<string, string | undefined>();
|
|
38
39
|
#sourceFileObjects = new Map<string, ts.SourceFile>();
|
|
40
|
+
#sourceHashes = new Map<string, number>();
|
|
39
41
|
|
|
40
42
|
#manifestIndex: ManifestIndex;
|
|
41
43
|
#manifest: ManifestRoot;
|
|
@@ -43,14 +45,18 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
43
45
|
#transformerManager: TransformerManager;
|
|
44
46
|
#compilerOptions: ts.CompilerOptions;
|
|
45
47
|
|
|
48
|
+
#readFile(inputFile: string): string | undefined {
|
|
49
|
+
return ts.sys.readFile(this.#inputToEntry.get(inputFile)?.sourceFile ?? this.#inputPathToSourcePath(inputFile));
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
async init(idx: ManifestIndex): Promise<this> {
|
|
47
53
|
this.#manifestIndex = idx;
|
|
48
54
|
this.#manifest = idx.manifest;
|
|
49
|
-
const mapper = folderMapper(this.#manifest.
|
|
55
|
+
const mapper = folderMapper(this.#manifest.workspace.path, '##');
|
|
50
56
|
this.#rootDir = mapper.dir;
|
|
51
|
-
this.#
|
|
57
|
+
this.#inputPathToSourcePath = mapper.translate;
|
|
52
58
|
|
|
53
|
-
this.#outputPath = path.resolve(this.#manifest.
|
|
59
|
+
this.#outputPath = path.resolve(this.#manifest.workspace.path, this.#manifest.build.outputFolder);
|
|
54
60
|
this.#modules = Object.values(this.#manifest.modules);
|
|
55
61
|
|
|
56
62
|
// Register all inputs
|
|
@@ -91,11 +97,11 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
resolveOutputFile(file: string): string {
|
|
94
|
-
return path.resolve(this.#manifest.
|
|
100
|
+
return path.resolve(this.#manifest.workspace.path, this.#manifest.build.outputFolder, file);
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
getArbitraryInputFile(): string {
|
|
98
|
-
return this.getBySource(this.#manifestIndex.getModule('@travetto/manifest')!.files.src[0].sourceFile)!.
|
|
104
|
+
return this.getBySource(this.#manifestIndex.getModule('@travetto/manifest')!.files.src[0].sourceFile)!.inputFile;
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
createProgram(oldProgram?: ts.Program): ts.Program {
|
|
@@ -107,9 +113,9 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
107
113
|
writeInputFile(program: ts.Program, inputFile: string): ts.EmitResult | undefined | void {
|
|
108
114
|
switch (ManifestModuleUtil.getFileType(inputFile)) {
|
|
109
115
|
case 'package-json':
|
|
110
|
-
return this.writeFile(this.#inputToEntry.get(inputFile)!.
|
|
116
|
+
return this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, this.readFile(inputFile)!, false);
|
|
111
117
|
case 'js':
|
|
112
|
-
return this.writeFile(this.#inputToEntry.get(inputFile)!.
|
|
118
|
+
return this.writeFile(this.#inputToEntry.get(inputFile)!.outputFile!, ts.transpile(this.readFile(inputFile)!, this.#compilerOptions), false);
|
|
113
119
|
case 'ts':
|
|
114
120
|
return program.emit(
|
|
115
121
|
program.getSourceFile(inputFile)!,
|
|
@@ -125,7 +131,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
125
131
|
|
|
126
132
|
registerInput(module: ManifestModule, moduleFile: string): CompileStateEntry {
|
|
127
133
|
const relativeInput = `${module.outputFolder}/${moduleFile}`;
|
|
128
|
-
const sourceFile = path.resolve(this.#manifest.
|
|
134
|
+
const sourceFile = path.resolve(this.#manifest.workspace.path, module.sourceFolder, moduleFile);
|
|
129
135
|
const sourceFolder = path.dirname(sourceFile);
|
|
130
136
|
const inputFile = path.resolve(this.#rootDir, relativeInput); // Ensure input is isolated
|
|
131
137
|
const inputFolder = path.dirname(inputFile);
|
|
@@ -134,7 +140,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
134
140
|
undefined :
|
|
135
141
|
path.resolve(this.#outputPath, ManifestModuleUtil.sourceToOutputExt(relativeInput));
|
|
136
142
|
|
|
137
|
-
const entry = {
|
|
143
|
+
const entry = { sourceFile, inputFile, outputFile, module };
|
|
138
144
|
|
|
139
145
|
this.#inputToEntry.set(inputFile, entry);
|
|
140
146
|
this.#sourceToEntry.set(sourceFile, entry);
|
|
@@ -145,23 +151,37 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
this.#inputFiles.add(inputFile);
|
|
148
|
-
|
|
154
|
+
this.#sourceHashes.set(sourceFile, -1); // Unknown
|
|
149
155
|
return entry;
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
checkIfSourceChanged(inputFile: string): boolean {
|
|
159
|
+
const contents = this.#readFile(inputFile);
|
|
160
|
+
const prevHash = this.#sourceHashes.get(inputFile);
|
|
161
|
+
if (!contents || (contents.length === 0 && prevHash)) {
|
|
162
|
+
return false; // Ignore empty file
|
|
156
163
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
const currentHash = CompilerUtil.naiveHash(contents);
|
|
165
|
+
const changed = prevHash !== currentHash;
|
|
166
|
+
if (changed) {
|
|
167
|
+
this.#sourceHashes.set(inputFile, currentHash);
|
|
168
|
+
this.#sourceContents.set(inputFile, contents);
|
|
169
|
+
this.#sourceFileObjects.delete(inputFile);
|
|
170
|
+
}
|
|
171
|
+
return changed;
|
|
160
172
|
}
|
|
161
173
|
|
|
162
|
-
|
|
174
|
+
removeInput(inputFile: string): void {
|
|
175
|
+
const { outputFile, sourceFile } = this.#inputToEntry.get(inputFile)!;
|
|
176
|
+
if (outputFile) {
|
|
177
|
+
this.#outputToEntry.delete(outputFile);
|
|
178
|
+
}
|
|
163
179
|
this.#sourceFileObjects.delete(inputFile);
|
|
164
180
|
this.#sourceContents.delete(inputFile);
|
|
181
|
+
this.#sourceHashes.delete(inputFile);
|
|
182
|
+
this.#sourceToEntry.delete(sourceFile);
|
|
183
|
+
this.#inputToEntry.delete(inputFile);
|
|
184
|
+
this.#inputFiles.delete(inputFile);
|
|
165
185
|
}
|
|
166
186
|
|
|
167
187
|
getAllFiles(): string[] {
|
|
@@ -177,11 +197,11 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
177
197
|
getDefaultLibLocation(): string { return path.dirname(ts.getDefaultLibFilePath(this.#compilerOptions)); }
|
|
178
198
|
|
|
179
199
|
fileExists(inputFile: string): boolean {
|
|
180
|
-
return this.#inputToEntry.has(inputFile) || ts.sys.fileExists(this.#
|
|
200
|
+
return this.#inputToEntry.has(inputFile) || ts.sys.fileExists(this.#inputPathToSourcePath(inputFile));
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
directoryExists(inputDir: string): boolean {
|
|
184
|
-
return this.#inputDirectoryToSource.has(inputDir) || ts.sys.directoryExists(this.#
|
|
204
|
+
return this.#inputDirectoryToSource.has(inputDir) || ts.sys.directoryExists(this.#inputPathToSourcePath(inputDir));
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
writeFile(
|
|
@@ -203,9 +223,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
readFile(inputFile: string): string | undefined {
|
|
206
|
-
const res = this.#sourceContents.get(inputFile) ??
|
|
207
|
-
this.#inputToEntry.get(inputFile)?.source ?? this.#inputPathToSource(inputFile)
|
|
208
|
-
);
|
|
226
|
+
const res = this.#sourceContents.get(inputFile) ?? this.#readFile(inputFile);
|
|
209
227
|
this.#sourceContents.set(inputFile, res);
|
|
210
228
|
return res;
|
|
211
229
|
}
|
package/src/types.ts
CHANGED
|
@@ -5,4 +5,4 @@ import type { ManifestModule } from '@travetto/manifest';
|
|
|
5
5
|
export type CompileEmitError = Error | readonly ts.Diagnostic[];
|
|
6
6
|
export type CompileEmitter = (file: string, newProgram?: boolean) => Promise<CompileEmitError | undefined>;
|
|
7
7
|
export type CompileEmitEvent = { file: string, i: number, total: number, err?: CompileEmitError };
|
|
8
|
-
export type CompileStateEntry = {
|
|
8
|
+
export type CompileStateEntry = { sourceFile: string, inputFile: string, outputFile?: string, module: ManifestModule };
|
package/src/util.ts
CHANGED
|
@@ -2,7 +2,7 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { ManifestContext, ManifestModuleFileType, ManifestModuleUtil, ManifestRoot, Package, path } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
type OutputToSource = (outputFile: string) => ({
|
|
5
|
+
type OutputToSource = (outputFile: string) => ({ sourceFile: string } | undefined);
|
|
6
6
|
|
|
7
7
|
const nativeCwd = process.cwd();
|
|
8
8
|
|
|
@@ -31,12 +31,12 @@ export class CompilerUtil {
|
|
|
31
31
|
*/
|
|
32
32
|
static rewriteSourceMap(ctx: ManifestContext, text: string, outputToSource: OutputToSource): string {
|
|
33
33
|
const data: { sourceRoot?: string, sources: string[] } = JSON.parse(text);
|
|
34
|
-
const output = ManifestModuleUtil.sourceToOutputExt(path.resolve(ctx.
|
|
35
|
-
const {
|
|
34
|
+
const output = ManifestModuleUtil.sourceToOutputExt(path.resolve(ctx.workspace.path, ctx.build.outputFolder, data.sources[0]));
|
|
35
|
+
const { sourceFile } = outputToSource(output) ?? {};
|
|
36
36
|
|
|
37
|
-
if (
|
|
37
|
+
if (sourceFile) {
|
|
38
38
|
delete data.sourceRoot;
|
|
39
|
-
data.sources = [
|
|
39
|
+
data.sources = [sourceFile];
|
|
40
40
|
text = JSON.stringify(data);
|
|
41
41
|
}
|
|
42
42
|
return text;
|
|
@@ -81,7 +81,7 @@ export class CompilerUtil {
|
|
|
81
81
|
if (pkg.main) {
|
|
82
82
|
pkg.main = ManifestModuleUtil.sourceToOutputExt(pkg.main);
|
|
83
83
|
}
|
|
84
|
-
pkg.type = manifest.
|
|
84
|
+
pkg.type = manifest.workspace.type;
|
|
85
85
|
for (const key of ['devDependencies', 'dependencies', 'peerDependencies'] as const) {
|
|
86
86
|
if (key in pkg) {
|
|
87
87
|
for (const dep of Object.keys(pkg[key] ?? {})) {
|
package/src/watch.ts
CHANGED
|
@@ -1,95 +1,96 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { setMaxListeners } from 'events';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
ManifestContext, ManifestModuleUtil, ManifestUtil, ManifestModuleFolderType, ManifestModuleFileType,
|
|
6
|
-
path, ManifestModule,
|
|
7
|
-
} from '@travetto/manifest';
|
|
8
|
-
import { getManifestContext } from '@travetto/manifest/bin/context';
|
|
1
|
+
import { ManifestContext, ManifestModuleUtil, ManifestUtil, RuntimeIndex, path } from '@travetto/manifest';
|
|
9
2
|
|
|
10
3
|
import type { CompileStateEntry } from './types';
|
|
11
4
|
import { CompilerState } from './state';
|
|
12
5
|
import { CompilerUtil } from './util';
|
|
13
6
|
|
|
14
|
-
import {
|
|
7
|
+
import { AsyncQueue } from '../support/queue';
|
|
15
8
|
|
|
16
|
-
type
|
|
9
|
+
type WatchAction = 'create' | 'update' | 'delete';
|
|
10
|
+
type WatchEvent = { action: WatchAction, file: string };
|
|
11
|
+
type CompilerWatchEvent = WatchEvent & { entry: CompileStateEntry };
|
|
17
12
|
|
|
18
13
|
/**
|
|
19
14
|
* Watch support, based on compiler state and manifest details
|
|
20
15
|
*/
|
|
21
16
|
export class CompilerWatcher {
|
|
17
|
+
#state: CompilerState;
|
|
18
|
+
#signal: AbortSignal;
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* @returns
|
|
27
|
-
*/
|
|
28
|
-
static watch(state: CompilerState): AsyncIterable<WatchEvent<{ entry: CompileStateEntry }>> {
|
|
29
|
-
return new CompilerWatcher(state).watchChanges();
|
|
20
|
+
constructor(state: CompilerState, signal: AbortSignal) {
|
|
21
|
+
this.#state = state;
|
|
22
|
+
this.#signal = signal;
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
/** Watch files */
|
|
26
|
+
async * #watchFolder(rootPath: string): AsyncIterable<WatchEvent> {
|
|
27
|
+
const q = new AsyncQueue<WatchEvent>(this.#signal);
|
|
28
|
+
const lib = await import('@parcel/watcher');
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const cleanup = await lib.subscribe(rootPath, (err, events) => {
|
|
31
|
+
if (err) {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
33
|
+
q.throw(err instanceof Error ? err : new Error((err as Error).message));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const ev of events) {
|
|
37
|
+
q.add({ action: ev.type, file: path.toPosix(ev.path) });
|
|
38
|
+
}
|
|
39
|
+
}, {
|
|
40
|
+
// TODO: Read .gitignore?
|
|
41
|
+
ignore: [
|
|
42
|
+
'node_modules', '**/node_modules', '.git', '**/.git',
|
|
43
|
+
`${this.#state.manifest.build.outputFolder}/node_modules/**`,
|
|
44
|
+
`${this.#state.manifest.build.compilerFolder}/node_modules/**`,
|
|
45
|
+
`${this.#state.manifest.build.toolFolder}/**`
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (this.#signal.aborted) { // If already aborted, can happen async
|
|
50
|
+
cleanup.unsubscribe();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.#signal.addEventListener('abort', () => cleanup.unsubscribe());
|
|
55
|
+
|
|
56
|
+
yield* q;
|
|
39
57
|
}
|
|
40
58
|
|
|
41
|
-
async #rebuildManifestsIfNeeded(): Promise<void> {
|
|
42
|
-
if (!
|
|
59
|
+
async #rebuildManifestsIfNeeded(event: CompilerWatchEvent, moduleFile: string): Promise<void> {
|
|
60
|
+
if (!event.entry.outputFile || event.action === 'update') {
|
|
43
61
|
return;
|
|
44
62
|
}
|
|
45
|
-
const mods = [...new Set(this.#dirtyFiles.map(x => x.modFolder))];
|
|
46
|
-
const contexts = await Promise.all(mods.map(async folder => {
|
|
47
|
-
if (!this.#manifestContexts.has(folder)) {
|
|
48
|
-
const ctx = await getManifestContext(folder);
|
|
49
|
-
this.#manifestContexts.set(folder, ctx);
|
|
50
|
-
}
|
|
51
|
-
return this.#manifestContexts.get(folder)!;
|
|
52
|
-
}));
|
|
53
63
|
|
|
54
|
-
const
|
|
55
|
-
|
|
64
|
+
const toUpdate: ManifestContext[] = RuntimeIndex.getDependentModules(event.entry.module.name, 'parents')
|
|
65
|
+
.map(el => ManifestUtil.getModuleContext(this.#state.manifest, el.sourceFolder));
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
toUpdate.push(this.#state.manifest);
|
|
68
|
+
|
|
69
|
+
const mod = event.entry.module;
|
|
70
|
+
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
71
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
72
|
+
|
|
73
|
+
for (const ctx of toUpdate) {
|
|
58
74
|
const newManifest = await ManifestUtil.buildManifest(ctx);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
modFiles[idx] = [file.moduleFile, file.type, Date.now()];
|
|
71
|
-
}
|
|
75
|
+
if (mod.name in newManifest.modules) {
|
|
76
|
+
const modFiles = newManifest.modules[mod.name].files[folderKey] ??= [];
|
|
77
|
+
const idx = modFiles.findIndex(x => x[0] === moduleFile);
|
|
78
|
+
|
|
79
|
+
if (event.action === 'create' && idx < 0) {
|
|
80
|
+
modFiles.push([moduleFile, fileType, Date.now()]);
|
|
81
|
+
} else if (idx >= 0) {
|
|
82
|
+
if (event.action === 'delete') {
|
|
83
|
+
modFiles.splice(idx, 1);
|
|
84
|
+
} else {
|
|
85
|
+
modFiles[idx] = [moduleFile, fileType, Date.now()];
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
|
-
await ManifestUtil.writeManifest(
|
|
89
|
+
await ManifestUtil.writeManifest(newManifest);
|
|
76
90
|
}
|
|
77
|
-
// Reindex
|
|
78
|
-
this.#state.manifestIndex.init(this.#state.manifestIndex.manifestFile);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#getModuleMap(): Record<string, ManifestModule> {
|
|
82
|
-
return Object.fromEntries(
|
|
83
|
-
Object.values(this.#state.manifest.modules).map(x => [path.resolve(this.#state.manifest.workspacePath, x.sourceFolder), x])
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
this.#
|
|
89
|
-
mod: mod.name, modFolder: folder, remove, moduleFile,
|
|
90
|
-
folderKey: ManifestModuleUtil.getFolderKey(moduleFile),
|
|
91
|
-
type: ManifestModuleUtil.getFileType(moduleFile),
|
|
92
|
-
});
|
|
92
|
+
// Reindex at workspace root
|
|
93
|
+
this.#state.manifestIndex.init(ManifestUtil.getManifestLocation(this.#state.manifest));
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
/**
|
|
@@ -98,67 +99,56 @@ export class CompilerWatcher {
|
|
|
98
99
|
* @param handler
|
|
99
100
|
* @returns
|
|
100
101
|
*/
|
|
101
|
-
async * watchChanges(): AsyncIterable<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const modules = [...this.#state.manifestIndex.getModuleList('all')].map(x => this.#state.manifestIndex.getModule(x)!);
|
|
107
|
-
|
|
108
|
-
const stream = fileWatchEvents(this.#state.manifest, modules, ctrl.signal);
|
|
109
|
-
for await (const ev of stream) {
|
|
102
|
+
async * watchChanges(): AsyncIterable<CompilerWatchEvent> {
|
|
103
|
+
if (this.#signal.aborted) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
110
106
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
const manifest = this.#state.manifest;
|
|
108
|
+
const ROOT_LOCK = path.resolve(manifest.workspace.path, 'package-lock.json');
|
|
109
|
+
const ROOT_PKG = path.resolve(manifest.workspace.path, 'package.json');
|
|
110
|
+
const OUTPUT_PATH = path.resolve(manifest.workspace.path, manifest.build.outputFolder);
|
|
111
|
+
const COMPILER_PATH = path.resolve(manifest.workspace.path, manifest.build.compilerFolder);
|
|
112
|
+
|
|
113
|
+
for await (const ev of this.#watchFolder(this.#state.manifest.workspace.path)) {
|
|
114
|
+
const { action, file: sourceFile } = ev;
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
sourceFile === ROOT_LOCK ||
|
|
118
|
+
sourceFile === ROOT_PKG ||
|
|
119
|
+
(action === 'delete' && (sourceFile === OUTPUT_PATH || sourceFile === COMPILER_PATH))
|
|
120
|
+
) {
|
|
121
|
+
throw new Error('RESET');
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
sourceFile.replace(`${this.#state.manifest.workspacePath}/`, '');
|
|
124
|
+
const fileType = ManifestModuleUtil.getFileType(sourceFile);
|
|
125
|
+
if (!CompilerUtil.validFile(fileType)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
122
128
|
|
|
123
129
|
let entry = this.#state.getBySource(sourceFile);
|
|
124
130
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.#addDirtyFile(mod, folder, moduleFile);
|
|
129
|
-
if (CompilerUtil.validFile(fileType)) {
|
|
130
|
-
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
131
|
-
entry = this.#state.registerInput(mod, moduleFile);
|
|
132
|
-
this.#sourceHashes.set(sourceFile, hash);
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
case 'update': {
|
|
137
|
-
if (entry) {
|
|
138
|
-
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
139
|
-
if (this.#sourceHashes.get(sourceFile) !== hash) {
|
|
140
|
-
this.#state.resetInputSource(entry.input);
|
|
141
|
-
this.#sourceHashes.set(sourceFile, hash);
|
|
142
|
-
} else {
|
|
143
|
-
entry = undefined;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case 'delete': {
|
|
149
|
-
if (entry) {
|
|
150
|
-
this.#state.removeInput(entry.input);
|
|
151
|
-
if (entry.output) {
|
|
152
|
-
this.#addDirtyFile(mod, folder, moduleFile, true);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
131
|
+
const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(sourceFile);
|
|
132
|
+
if (!mod) { // Unknown module
|
|
133
|
+
continue;
|
|
156
134
|
}
|
|
157
135
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
136
|
+
const modRoot = mod.sourceFolder || this.#state.manifest.workspace.path;
|
|
137
|
+
const moduleFile = sourceFile.includes(modRoot) ? sourceFile.split(`${modRoot}/`)[1] : sourceFile;
|
|
138
|
+
|
|
139
|
+
if (action === 'create') {
|
|
140
|
+
entry = this.#state.registerInput(mod, moduleFile);
|
|
141
|
+
} else if (!entry) {
|
|
142
|
+
continue;
|
|
143
|
+
} else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.inputFile)) {
|
|
144
|
+
continue;
|
|
145
|
+
} else if (action === 'delete') {
|
|
146
|
+
this.#state.removeInput(entry.inputFile);
|
|
161
147
|
}
|
|
148
|
+
|
|
149
|
+
const result: CompilerWatchEvent = { action, file: entry.sourceFile, entry };
|
|
150
|
+
await this.#rebuildManifestsIfNeeded(result, moduleFile);
|
|
151
|
+
yield result;
|
|
162
152
|
}
|
|
163
153
|
}
|
|
164
154
|
}
|
package/support/entry.trvc.ts
CHANGED
|
@@ -1,73 +1,103 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import type { ManifestContext } from '@travetto/manifest';
|
|
5
5
|
|
|
6
|
+
import type { CompilerLogLevel, CompilerMode, CompilerServerInfo } from './types';
|
|
6
7
|
import { LogUtil } from './log';
|
|
8
|
+
import { CommonUtil } from './util';
|
|
7
9
|
import { CompilerSetup } from './setup';
|
|
8
10
|
import { CompilerServer } from './server/server';
|
|
9
11
|
import { CompilerRunner } from './server/runner';
|
|
10
|
-
import
|
|
11
|
-
import { CompilerClientUtil } from './server/client';
|
|
12
|
-
import { CommonUtil } from './util';
|
|
12
|
+
import { CompilerClient } from './server/client';
|
|
13
13
|
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
15
|
-
export const main = (
|
|
15
|
+
export const main = (ctx: ManifestContext) => {
|
|
16
|
+
const log = LogUtil.logger('client.main');
|
|
17
|
+
const client = new CompilerClient(ctx, log);
|
|
18
|
+
const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
|
|
19
|
+
|
|
20
|
+
/** Main entry point for compilation */
|
|
21
|
+
const compile = async (op: CompilerMode, logLevel: CompilerLogLevel, setupOnly = false): Promise<void> => {
|
|
22
|
+
LogUtil.initLogs(ctx, logLevel ?? 'info');
|
|
23
|
+
|
|
24
|
+
const server = await new CompilerServer(ctx, op).listen();
|
|
25
|
+
|
|
26
|
+
// Wait for build to be ready
|
|
27
|
+
if (server) {
|
|
28
|
+
log('debug', 'Start Server');
|
|
29
|
+
await server.processEvents(async function* (signal) {
|
|
30
|
+
const changed = await CompilerSetup.setup(ctx);
|
|
31
|
+
if (!setupOnly) {
|
|
32
|
+
yield* CompilerRunner.runProcess(ctx, changed, op, signal);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
log('debug', 'End Server');
|
|
36
|
+
} else {
|
|
37
|
+
log('info', 'Server already running, waiting for initial compile to complete');
|
|
38
|
+
const ctrl = new AbortController();
|
|
39
|
+
LogUtil.consumeProgressEvents(() => client.fetchEvents('progress', { until: ev => !!ev.complete, signal: ctrl.signal }));
|
|
40
|
+
await client.waitForState(['compile-end', 'watch-start'], 'Successfully built');
|
|
41
|
+
ctrl.abort();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
16
45
|
const ops = {
|
|
17
46
|
/** Stop the server */
|
|
18
47
|
async stop(): Promise<void> {
|
|
19
|
-
if (await
|
|
20
|
-
console.log(`Stopped server ${ctx.
|
|
48
|
+
if (await client.stop()) {
|
|
49
|
+
console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
|
|
21
50
|
} else {
|
|
22
|
-
console.log(`Server not running ${ctx.
|
|
51
|
+
console.log(`Server not running ${ctx.workspace.path}: ${client}`);
|
|
23
52
|
}
|
|
24
53
|
},
|
|
25
54
|
|
|
55
|
+
/** Restart the server */
|
|
56
|
+
async restart(): Promise<void> { await client.stop().then(() => ops.watch()); },
|
|
57
|
+
|
|
26
58
|
/** Get server info */
|
|
27
|
-
info(): Promise<CompilerServerInfo>
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
29
|
-
return fetch(ctx.compilerUrl).then(v => v.json(), () => ({ state: 'Server not running' })) as Promise<CompilerServerInfo>;
|
|
30
|
-
},
|
|
59
|
+
info: (): Promise<CompilerServerInfo | undefined> => client.info(),
|
|
31
60
|
|
|
32
61
|
/** Clean the server */
|
|
33
62
|
async clean(): Promise<void> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
if (await client.clean()) {
|
|
64
|
+
return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
|
|
65
|
+
} else {
|
|
66
|
+
await Promise.all(buildFolders.map(f => fs.rm(path.resolve(ctx.workspace.path, f), { force: true, recursive: true })));
|
|
67
|
+
return console.log(`Cleaned ${ctx.workspace.path}:`, buildFolders);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/** Stream events */
|
|
72
|
+
events: async (type: string, handler: (ev: unknown) => unknown): Promise<void> => {
|
|
73
|
+
LogUtil.initLogs(ctx, 'error');
|
|
74
|
+
if (type === 'change' || type === 'log' || type === 'progress' || type === 'state') {
|
|
75
|
+
for await (const ev of client.fetchEvents(type)) { await handler(ev); }
|
|
37
76
|
} else {
|
|
38
|
-
|
|
39
|
-
return console.log(`Cleaned ${ctx.workspacePath}:`, folders);
|
|
77
|
+
throw new Error(`Unknown event type: ${type}`);
|
|
40
78
|
}
|
|
41
79
|
},
|
|
42
80
|
|
|
43
|
-
/**
|
|
44
|
-
async
|
|
45
|
-
LogUtil.initLogs(ctx, op === 'run' ? 'error' : 'info');
|
|
81
|
+
/** Build the project */
|
|
82
|
+
async build(): Promise<void> { await compile('build', 'info'); },
|
|
46
83
|
|
|
47
|
-
|
|
84
|
+
/** Build and watch the project */
|
|
85
|
+
async watch(): Promise<void> { await compile('watch', 'info'); },
|
|
48
86
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
yield* CompilerRunner.runProcess(root, manifest, changed, op, signal);
|
|
55
|
-
} else {
|
|
56
|
-
yield* [];
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
} else {
|
|
60
|
-
await CompilerClientUtil.waitForBuild(root);
|
|
87
|
+
/** Build and return a loader */
|
|
88
|
+
async getLoader(): Promise<(mod: string) => Promise<unknown>> {
|
|
89
|
+
// Short circuit if we can
|
|
90
|
+
if (!(await client.isWatching())) {
|
|
91
|
+
await compile('build', 'error');
|
|
61
92
|
}
|
|
62
93
|
return CommonUtil.moduleLoader(ctx);
|
|
63
94
|
},
|
|
64
95
|
|
|
65
96
|
/** Manifest entry point */
|
|
66
97
|
async manifest(output?: string, prod?: boolean): Promise<void> {
|
|
67
|
-
await
|
|
98
|
+
await compile('build', 'error', true);
|
|
68
99
|
await CompilerSetup.exportManifest(ctx, output, prod); return;
|
|
69
100
|
}
|
|
70
101
|
};
|
|
71
102
|
return ops;
|
|
72
|
-
};
|
|
73
|
-
|
|
103
|
+
};
|