@travetto/compiler 4.0.7 → 4.0.9

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": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "description": "The compiler infrastructure for the Travetto framework",
5
5
  "keywords": [
6
6
  "compiler",
@@ -30,13 +30,13 @@
30
30
  "directory": "module/compiler"
31
31
  },
32
32
  "dependencies": {
33
- "@parcel/watcher": "^2.4.0",
34
- "@travetto/manifest": "^4.0.1",
35
- "@travetto/transformer": "^4.0.2",
36
- "@types/node": "^20.11.16"
33
+ "@parcel/watcher": "^2.4.1",
34
+ "@travetto/manifest": "^4.0.3",
35
+ "@travetto/transformer": "^4.0.4",
36
+ "@types/node": "^20.11.25"
37
37
  },
38
38
  "peerDependencies": {
39
- "@travetto/cli": "^4.0.5"
39
+ "@travetto/cli": "^4.0.7"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/cli": {
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 = '~~';
@@ -110,7 +110,7 @@ export class CompilerState implements ts.CompilerHost {
110
110
  if (force || !this.#program) {
111
111
  this.#program = ts.createProgram({ rootNames: this.getAllFiles(), host: this, options: this.#compilerOptions, oldProgram: this.#program });
112
112
  this.#transformerManager.init(this.#program.getTypeChecker());
113
- await timers.setImmediate();
113
+ await CommonUtil.queueMacroTask();
114
114
  }
115
115
  return this.#program;
116
116
  }
@@ -179,7 +179,7 @@ export class CompilerState implements ts.CompilerHost {
179
179
  if (!contents || (contents.length === 0 && prevHash)) {
180
180
  return false; // Ignore empty file
181
181
  }
182
- const currentHash = CompilerUtil.naiveHash(contents);
182
+ const currentHash = CommonUtil.naiveHash(contents);
183
183
  const changed = prevHash !== currentHash;
184
184
  if (changed) {
185
185
  this.#sourceHashes.set(inputFile, currentHash);
package/src/util.ts CHANGED
@@ -120,18 +120,4 @@ export class CompilerUtil {
120
120
  }
121
121
  return new Error(`Transpiling ${filename.replace(nativeCwd, '.')} failed: \n${errors.join('\n')}`);
122
122
  }
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
123
  }
package/src/watch.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ManifestContext, ManifestModuleUtil, ManifestUtil, PackageUtil, RuntimeIndex, path } from '@travetto/manifest';
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
@@ -40,8 +48,8 @@ export class CompilerWatcher {
40
48
  }
41
49
 
42
50
  /** Watch files */
43
- async * #watchFolder(rootPath: string): AsyncIterable<WatchEvent> {
44
- const q = new AsyncQueue<WatchEvent>(this.#signal);
51
+ async * #watchFolder(rootPath: string): AsyncIterable<WatchEvent[]> {
52
+ const q = new AsyncQueue<WatchEvent[]>(this.#signal);
45
53
  const lib = await import('@parcel/watcher');
46
54
  const ignore = this.#getIgnores();
47
55
 
@@ -51,9 +59,7 @@ export class CompilerWatcher {
51
59
  q.throw(err instanceof Error ? err : new Error((err as Error).message));
52
60
  return;
53
61
  }
54
- for (const ev of events) {
55
- q.add({ action: ev.type, file: path.toPosix(ev.path) });
56
- }
62
+ q.add(events.map(ev => ({ action: ev.type, file: path.toPosix(ev.path) })));
57
63
  }, { ignore });
58
64
 
59
65
  if (this.#signal.aborted) { // If already aborted, can happen async
@@ -66,30 +72,49 @@ export class CompilerWatcher {
66
72
  yield* q;
67
73
  }
68
74
 
69
- async #rebuildManifestsIfNeeded(event: CompilerWatchEvent, moduleFile: string): Promise<void> {
70
- if (!event.entry.outputFile || event.action === 'update') {
75
+ async #rebuildManifestsIfNeeded(events: CompilerWatchEvent[]): Promise<void> {
76
+ events = events.filter(x => x.entry.outputFile && x.action !== 'update');
77
+
78
+ if (!events.length) {
71
79
  return;
72
80
  }
73
81
 
74
- const toUpdate: ManifestContext[] = RuntimeIndex.getDependentModules(event.entry.module.name, 'parents')
75
- .map(el => ManifestUtil.getModuleContext(this.#state.manifest, el.sourceFolder));
76
-
77
- toUpdate.push(this.#state.manifest);
78
-
79
- const mod = event.entry.module;
80
- const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
81
- const fileType = ManifestModuleUtil.getFileType(moduleFile);
82
+ const mods = [...new Set(events.map(v => v.entry.module.name))];
83
+
84
+ const moduleToFiles = new Map(mods.map(m => [m, {
85
+ context: ManifestUtil.getModuleContext(this.#state.manifest, RuntimeIndex.getManifestModule(m)!.sourceFolder),
86
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
87
+ files: [] as FileShape[]
88
+ }] as const));
89
+
90
+ const parents = new Map<string, string[]>(
91
+ mods.map(m => [m, RuntimeIndex.getDependentModules(m, 'parents').map(x => x.name)])
92
+ );
93
+
94
+ const allFiles = events.map(ev => {
95
+ const modRoot = ev.entry.module.sourceFolder || this.#state.manifest.workspace.path;
96
+ const moduleFile = ev.file.includes(modRoot) ? ev.file.split(`${modRoot}/`)[1] : ev.file;
97
+ const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
98
+ const fileType = ManifestModuleUtil.getFileType(moduleFile);
99
+ return { mod: ev.entry.module.name, action: ev.action, moduleFile, folderKey, fileType };
100
+ });
101
+
102
+ for (const file of allFiles) {
103
+ for (const parent of parents.get(file.mod)!) {
104
+ moduleToFiles.get(parent)!.files.push(file);
105
+ }
106
+ }
82
107
 
83
- for (const ctx of toUpdate) {
84
- const newManifest = await ManifestUtil.buildManifest(ctx);
85
- if (mod.name in newManifest.modules) {
86
- const modFiles = newManifest.modules[mod.name].files[folderKey] ??= [];
108
+ for (const { context, files } of moduleToFiles.values()) {
109
+ const newManifest = await ManifestUtil.buildManifest(context);
110
+ for (const { action, mod, fileType, moduleFile, folderKey } of files) {
111
+ const modFiles = newManifest.modules[mod].files[folderKey] ??= [];
87
112
  const idx = modFiles.findIndex(x => x[0] === moduleFile);
88
113
 
89
- if (event.action === 'create' && idx < 0) {
114
+ if (action === 'create' && idx < 0) {
90
115
  modFiles.push([moduleFile, fileType, Date.now()]);
91
116
  } else if (idx >= 0) {
92
- if (event.action === 'delete') {
117
+ if (action === 'delete') {
93
118
  modFiles.splice(idx, 1);
94
119
  } else {
95
120
  modFiles[idx] = [moduleFile, fileType, Date.now()];
@@ -120,45 +145,51 @@ export class CompilerWatcher {
120
145
  const OUTPUT_PATH = path.resolve(manifest.workspace.path, manifest.build.outputFolder);
121
146
  const COMPILER_PATH = path.resolve(manifest.workspace.path, manifest.build.compilerFolder);
122
147
 
123
- for await (const ev of this.#watchFolder(this.#state.manifest.workspace.path)) {
124
- const { action, file: sourceFile } = ev;
148
+ for await (const events of this.#watchFolder(this.#state.manifest.workspace.path)) {
125
149
 
126
- if (
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
- }
150
+ const outEvents: CompilerWatchEvent[] = [];
133
151
 
134
- const fileType = ManifestModuleUtil.getFileType(sourceFile);
135
- if (!CompilerUtil.validFile(fileType)) {
136
- continue;
137
- }
152
+ for (const ev of events) {
153
+ const { action, file: sourceFile } = ev;
154
+
155
+ if (
156
+ sourceFile === ROOT_LOCK ||
157
+ sourceFile === ROOT_PKG ||
158
+ (action === 'delete' && (sourceFile === OUTPUT_PATH || sourceFile === COMPILER_PATH))
159
+ ) {
160
+ throw new Error('RESET', { cause: `${action}:${sourceFile}` });
161
+ }
138
162
 
139
- let entry = this.#state.getBySource(sourceFile);
163
+ const fileType = ManifestModuleUtil.getFileType(sourceFile);
164
+ if (!CompilerUtil.validFile(fileType)) {
165
+ continue;
166
+ }
140
167
 
141
- const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(sourceFile);
142
- if (!mod) { // Unknown module
143
- continue;
144
- }
168
+ let entry = this.#state.getBySource(sourceFile);
169
+
170
+ const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(sourceFile);
171
+ if (!mod) { // Unknown module
172
+ continue;
173
+ }
174
+
175
+ const modRoot = mod.sourceFolder || this.#state.manifest.workspace.path;
176
+ const moduleFile = sourceFile.includes(modRoot) ? sourceFile.split(`${modRoot}/`)[1] : sourceFile;
177
+
178
+ if (action === 'create') {
179
+ entry = this.#state.registerInput(mod, moduleFile);
180
+ } else if (!entry) {
181
+ continue;
182
+ } else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.inputFile)) {
183
+ continue;
184
+ } else if (action === 'delete') {
185
+ this.#state.removeInput(entry.inputFile);
186
+ }
145
187
 
146
- const modRoot = mod.sourceFolder || this.#state.manifest.workspace.path;
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);
188
+ outEvents.push({ action, file: entry.sourceFile, entry });
157
189
  }
158
190
 
159
- const result: CompilerWatchEvent = { action, file: entry.sourceFile, entry };
160
- await this.#rebuildManifestsIfNeeded(result, moduleFile);
161
- yield result;
191
+ await this.#rebuildManifestsIfNeeded(outEvents);
192
+ yield* outEvents;
162
193
  }
163
194
  }
164
195
  }
@@ -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
- setImmediate(() => {
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
  */
package/tsconfig.trv.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "module": "CommonJS",
4
4
  "target": "esnext",
5
- "moduleResolution": "node",
5
+ "moduleResolution": "Bundler",
6
6
  "lib": [
7
7
  "es2022"
8
8
  ],