@ecopages/core 0.2.0-beta.37 → 0.2.0-beta.38
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 +11 -3
- package/src/eco/eco.browser.js +9 -2
- package/src/eco/eco.js +8 -2
- package/src/eco/eco.types.d.ts +29 -2
- package/src/route-renderer/README.md +2 -1
- package/src/route-renderer/orchestration/integration-renderer.d.ts +19 -3
- package/src/route-renderer/orchestration/integration-renderer.js +89 -8
- package/src/route-renderer/orchestration/page-browser-graph/grouped-graph-build-plan.d.ts +15 -0
- package/src/route-renderer/orchestration/page-browser-graph/grouped-graph-build-plan.js +9 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.merge.d.ts +5 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.merge.js +30 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +22 -6
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +145 -68
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.d.ts +9 -2
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +56 -39
- package/src/route-renderer/orchestration/page-browser-graph/route-instance-key.d.ts +29 -0
- package/src/route-renderer/orchestration/page-browser-graph/route-instance-key.js +54 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +3 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +3 -2
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.d.ts +1 -0
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +3 -5
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.d.ts +14 -4
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +31 -4
- package/src/route-renderer/page-loading/file-scoped-dependency-components.d.ts +42 -0
- package/src/route-renderer/page-loading/file-scoped-dependency-components.js +93 -0
- package/src/route-renderer/page-loading/page-module-loader.d.ts +1 -0
- package/src/route-renderer/page-loading/page-module-loader.js +1 -0
- package/src/route-renderer/page-loading/resolved-page-dependencies.d.ts +12 -0
- package/src/route-renderer/page-loading/resolved-page-dependencies.js +38 -0
- package/src/static-site-generator/production-page-browser-graph-prebuild.d.ts +10 -6
- package/src/static-site-generator/production-page-browser-graph-prebuild.js +29 -10
- package/src/static-site-generator/static-site-generator.js +4 -1
- package/src/types/public-types.d.ts +9 -1
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.d.ts +0 -9
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +0 -11
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { serializeGroupedGraphCacheKey } from './route-instance-key.js';
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { appLogger } from '../../../global/app-logger.js';
|
|
4
5
|
function isSkipCacheGroupedOutcome(outcome) {
|
|
5
6
|
return 'skipCache' in outcome;
|
|
6
7
|
}
|
|
7
8
|
function serializeGraphKey(key) {
|
|
8
|
-
return
|
|
9
|
+
return JSON.stringify([
|
|
10
|
+
'graph',
|
|
11
|
+
key.policy,
|
|
12
|
+
key.integrationName,
|
|
13
|
+
key.routeFile,
|
|
14
|
+
key.dependencyInstanceKey,
|
|
15
|
+
key.entryFingerprint,
|
|
16
|
+
]);
|
|
9
17
|
}
|
|
10
18
|
function normalizeDependencyPath(filePath) {
|
|
11
19
|
return path.resolve(filePath);
|
|
12
20
|
}
|
|
13
21
|
function parseSerializedGraphKey(serializedKey) {
|
|
14
|
-
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(serializedKey);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
15
27
|
return undefined;
|
|
16
28
|
}
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
if (!Array.isArray(parsed) ||
|
|
30
|
+
parsed.length !== 6 ||
|
|
31
|
+
parsed[0] !== 'graph' ||
|
|
32
|
+
typeof parsed[1] !== 'string' ||
|
|
33
|
+
typeof parsed[2] !== 'string' ||
|
|
34
|
+
typeof parsed[3] !== 'string' ||
|
|
35
|
+
typeof parsed[4] !== 'string' ||
|
|
36
|
+
typeof parsed[5] !== 'string') {
|
|
19
37
|
return undefined;
|
|
20
38
|
}
|
|
39
|
+
const [, policy, integrationName, routeFile, dependencyInstanceKey, entryFingerprint] = parsed;
|
|
21
40
|
if (policy !== 'development' && policy !== 'production') {
|
|
22
41
|
return undefined;
|
|
23
42
|
}
|
|
@@ -25,6 +44,7 @@ function parseSerializedGraphKey(serializedKey) {
|
|
|
25
44
|
policy,
|
|
26
45
|
integrationName,
|
|
27
46
|
routeFile,
|
|
47
|
+
dependencyInstanceKey,
|
|
28
48
|
entryFingerprint,
|
|
29
49
|
};
|
|
30
50
|
}
|
|
@@ -45,15 +65,23 @@ export class GraphDependencyIndex {
|
|
|
45
65
|
this.removeDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
|
|
46
66
|
}
|
|
47
67
|
}
|
|
48
|
-
bindGroupedRecord(
|
|
49
|
-
const serializedKey = `grouped::${integrationName}`;
|
|
68
|
+
bindGroupedRecord(cacheKey, record) {
|
|
50
69
|
for (const dependencyPath of record.dependencyPaths) {
|
|
51
|
-
this.addDependencyBinding(normalizeDependencyPath(dependencyPath),
|
|
70
|
+
this.addDependencyBinding(normalizeDependencyPath(dependencyPath), cacheKey);
|
|
52
71
|
}
|
|
53
72
|
}
|
|
54
|
-
unbindGroupedRecord(
|
|
55
|
-
const serializedKey = `grouped::${integrationName}`;
|
|
73
|
+
unbindGroupedRecord(cacheKey, record) {
|
|
56
74
|
for (const dependencyPath of record.dependencyPaths) {
|
|
75
|
+
this.removeDependencyBinding(normalizeDependencyPath(dependencyPath), cacheKey);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
bindPaths(serializedKey, dependencyPaths) {
|
|
79
|
+
for (const dependencyPath of dependencyPaths) {
|
|
80
|
+
this.addDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
unbindPaths(serializedKey, dependencyPaths) {
|
|
84
|
+
for (const dependencyPath of dependencyPaths) {
|
|
57
85
|
this.removeDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
|
|
58
86
|
}
|
|
59
87
|
}
|
|
@@ -85,34 +113,32 @@ export class GraphDependencyIndex {
|
|
|
85
113
|
export class SessionPageBrowserGraphCache {
|
|
86
114
|
records = new Map();
|
|
87
115
|
inFlight = new Map();
|
|
116
|
+
inFlightDependencyPaths = new Map();
|
|
117
|
+
inFlightGenerations = new Map();
|
|
88
118
|
groupedRecords = new Map();
|
|
89
119
|
groupedInFlight = new Map();
|
|
90
120
|
dependencyIndex = new GraphDependencyIndex();
|
|
91
121
|
graphGenerations = new Map();
|
|
92
122
|
groupedGenerations = new Map();
|
|
93
123
|
buildCount = 0;
|
|
94
|
-
getGraphByRoute(integrationName, routeFile, policy) {
|
|
95
|
-
const normalizedRoute = normalizeDependencyPath(routeFile);
|
|
96
|
-
for (const record of this.records.values()) {
|
|
97
|
-
if (record.key.integrationName !== integrationName) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (record.key.policy !== policy) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
return record.result;
|
|
107
|
-
}
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
124
|
/**
|
|
111
125
|
* Page Browser Graph compilations in this dev-server session.
|
|
112
126
|
*/
|
|
113
127
|
getBuildCount() {
|
|
114
128
|
return this.buildCount;
|
|
115
129
|
}
|
|
130
|
+
getGraphByRoute(integrationName, routeFile, policy, dependencyInstanceKey = '') {
|
|
131
|
+
const normalizedRoute = normalizeDependencyPath(routeFile);
|
|
132
|
+
for (const record of this.records.values()) {
|
|
133
|
+
if (record.key.integrationName === integrationName &&
|
|
134
|
+
record.key.policy === policy &&
|
|
135
|
+
normalizeDependencyPath(record.key.routeFile) === normalizedRoute &&
|
|
136
|
+
record.key.dependencyInstanceKey === dependencyInstanceKey) {
|
|
137
|
+
return record.result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
116
142
|
getAffectedGraphIdentities(filePath) {
|
|
117
143
|
const identities = [];
|
|
118
144
|
for (const serializedKey of this.dependencyIndex.getAffectedKeys(filePath)) {
|
|
@@ -123,7 +149,7 @@ export class SessionPageBrowserGraphCache {
|
|
|
123
149
|
}
|
|
124
150
|
return identities;
|
|
125
151
|
}
|
|
126
|
-
resolveGraph(key, build) {
|
|
152
|
+
resolveGraph(key, provisionalDependencyPaths, build) {
|
|
127
153
|
const serializedKey = serializeGraphKey(key);
|
|
128
154
|
const cached = this.records.get(serializedKey);
|
|
129
155
|
if (cached) {
|
|
@@ -134,35 +160,38 @@ export class SessionPageBrowserGraphCache {
|
|
|
134
160
|
return pending;
|
|
135
161
|
}
|
|
136
162
|
const buildGeneration = this.bumpGraphGeneration(serializedKey);
|
|
163
|
+
this.inFlightDependencyPaths.set(serializedKey, provisionalDependencyPaths);
|
|
164
|
+
this.inFlightGenerations.set(serializedKey, buildGeneration);
|
|
165
|
+
this.bindDependencyPaths(serializedKey, provisionalDependencyPaths);
|
|
137
166
|
const buildPromise = this.runGraphBuild(serializedKey, key, buildGeneration, build);
|
|
138
167
|
this.inFlight.set(serializedKey, buildPromise);
|
|
139
168
|
return buildPromise;
|
|
140
169
|
}
|
|
141
|
-
resolveGroupedGraph(
|
|
142
|
-
const
|
|
170
|
+
resolveGroupedGraph(scope, build) {
|
|
171
|
+
const cacheKey = serializeGroupedGraphCacheKey(scope);
|
|
172
|
+
const cached = this.groupedRecords.get(cacheKey);
|
|
143
173
|
if (cached) {
|
|
144
174
|
return Promise.resolve(cached.assetsByRoute);
|
|
145
175
|
}
|
|
146
|
-
const pending = this.groupedInFlight.get(
|
|
176
|
+
const pending = this.groupedInFlight.get(cacheKey);
|
|
147
177
|
if (pending) {
|
|
148
178
|
return pending;
|
|
149
179
|
}
|
|
150
|
-
const buildGeneration = this.bumpGroupedGeneration(
|
|
151
|
-
const buildPromise = this.runGroupedBuild(
|
|
152
|
-
this.groupedInFlight.set(
|
|
180
|
+
const buildGeneration = this.bumpGroupedGeneration(cacheKey);
|
|
181
|
+
const buildPromise = this.runGroupedBuild(cacheKey, buildGeneration, build);
|
|
182
|
+
this.groupedInFlight.set(cacheKey, buildPromise);
|
|
153
183
|
return buildPromise;
|
|
154
184
|
}
|
|
155
|
-
peekGroupedGraph(
|
|
156
|
-
return this.groupedRecords.get(
|
|
185
|
+
peekGroupedGraph(scope) {
|
|
186
|
+
return this.groupedRecords.get(serializeGroupedGraphCacheKey(scope))?.assetsByRoute;
|
|
157
187
|
}
|
|
158
188
|
invalidateByFilePath(filePath) {
|
|
159
189
|
const normalizedFilePath = normalizeDependencyPath(filePath);
|
|
160
190
|
const affectedKeys = this.dependencyIndex.getAffectedKeys(normalizedFilePath);
|
|
161
191
|
let invalidated = 0;
|
|
162
192
|
for (const serializedKey of affectedKeys) {
|
|
163
|
-
if (serializedKey.
|
|
164
|
-
|
|
165
|
-
invalidated += this.invalidateGroupedRecord(integrationName);
|
|
193
|
+
if (this.groupedRecords.has(serializedKey) || this.groupedInFlight.has(serializedKey)) {
|
|
194
|
+
invalidated += this.invalidateGroupedRecord(serializedKey);
|
|
166
195
|
continue;
|
|
167
196
|
}
|
|
168
197
|
invalidated += this.invalidateSerializedGraphKey(serializedKey);
|
|
@@ -178,9 +207,7 @@ export class SessionPageBrowserGraphCache {
|
|
|
178
207
|
if (affectedKeys.has(serializedKey)) {
|
|
179
208
|
continue;
|
|
180
209
|
}
|
|
181
|
-
this.
|
|
182
|
-
this.inFlight.delete(serializedKey);
|
|
183
|
-
invalidated += 1;
|
|
210
|
+
invalidated += this.invalidateSerializedGraphKey(serializedKey);
|
|
184
211
|
}
|
|
185
212
|
if (invalidated > 0) {
|
|
186
213
|
appLogger.debug(`[PageBrowserGraphSession] invalidated ${invalidated} graph record(s) for ${normalizedFilePath}`);
|
|
@@ -211,6 +238,8 @@ export class SessionPageBrowserGraphCache {
|
|
|
211
238
|
resetForTests() {
|
|
212
239
|
this.records.clear();
|
|
213
240
|
this.inFlight.clear();
|
|
241
|
+
this.inFlightDependencyPaths.clear();
|
|
242
|
+
this.inFlightGenerations.clear();
|
|
214
243
|
this.groupedRecords.clear();
|
|
215
244
|
this.groupedInFlight.clear();
|
|
216
245
|
this.graphGenerations.clear();
|
|
@@ -229,17 +258,23 @@ export class SessionPageBrowserGraphCache {
|
|
|
229
258
|
this.dependencyIndex.unbindRecord(record);
|
|
230
259
|
this.records.delete(serializedKey);
|
|
231
260
|
this.inFlight.delete(serializedKey);
|
|
261
|
+
const inFlightDependencyPaths = this.inFlightDependencyPaths.get(serializedKey);
|
|
262
|
+
if (inFlightDependencyPaths) {
|
|
263
|
+
this.unbindDependencyPaths(serializedKey, inFlightDependencyPaths);
|
|
264
|
+
this.inFlightDependencyPaths.delete(serializedKey);
|
|
265
|
+
}
|
|
266
|
+
this.inFlightGenerations.delete(serializedKey);
|
|
232
267
|
this.graphGenerations.delete(serializedKey);
|
|
233
268
|
}
|
|
234
269
|
if (policy === 'production') {
|
|
235
|
-
for (const
|
|
236
|
-
const groupedRecord = this.groupedRecords.get(
|
|
270
|
+
for (const cacheKey of [...this.groupedRecords.keys()]) {
|
|
271
|
+
const groupedRecord = this.groupedRecords.get(cacheKey);
|
|
237
272
|
if (groupedRecord) {
|
|
238
|
-
this.dependencyIndex.unbindGroupedRecord(
|
|
273
|
+
this.dependencyIndex.unbindGroupedRecord(cacheKey, groupedRecord);
|
|
239
274
|
}
|
|
240
|
-
this.groupedRecords.delete(
|
|
241
|
-
this.groupedInFlight.delete(
|
|
242
|
-
this.groupedGenerations.delete(
|
|
275
|
+
this.groupedRecords.delete(cacheKey);
|
|
276
|
+
this.groupedInFlight.delete(cacheKey);
|
|
277
|
+
this.groupedGenerations.delete(cacheKey);
|
|
243
278
|
}
|
|
244
279
|
}
|
|
245
280
|
}
|
|
@@ -252,6 +287,9 @@ export class SessionPageBrowserGraphCache {
|
|
|
252
287
|
if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
|
|
253
288
|
continue;
|
|
254
289
|
}
|
|
290
|
+
if (record.key.dependencyInstanceKey !== key.dependencyInstanceKey) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
255
293
|
if (record.key.entryFingerprint === key.entryFingerprint && record.key.policy === key.policy) {
|
|
256
294
|
continue;
|
|
257
295
|
}
|
|
@@ -263,9 +301,9 @@ export class SessionPageBrowserGraphCache {
|
|
|
263
301
|
this.graphGenerations.set(serializedKey, next);
|
|
264
302
|
return next;
|
|
265
303
|
}
|
|
266
|
-
bumpGroupedGeneration(
|
|
267
|
-
const next = (this.groupedGenerations.get(
|
|
268
|
-
this.groupedGenerations.set(
|
|
304
|
+
bumpGroupedGeneration(cacheKey) {
|
|
305
|
+
const next = (this.groupedGenerations.get(cacheKey) ?? 0) + 1;
|
|
306
|
+
this.groupedGenerations.set(cacheKey, next);
|
|
269
307
|
return next;
|
|
270
308
|
}
|
|
271
309
|
invalidateSerializedGraphKey(serializedKey) {
|
|
@@ -274,18 +312,24 @@ export class SessionPageBrowserGraphCache {
|
|
|
274
312
|
this.dependencyIndex.unbindRecord(record);
|
|
275
313
|
this.records.delete(serializedKey);
|
|
276
314
|
}
|
|
315
|
+
const inFlightDependencyPaths = this.inFlightDependencyPaths.get(serializedKey);
|
|
316
|
+
if (inFlightDependencyPaths) {
|
|
317
|
+
this.unbindDependencyPaths(serializedKey, inFlightDependencyPaths);
|
|
318
|
+
this.inFlightDependencyPaths.delete(serializedKey);
|
|
319
|
+
}
|
|
320
|
+
const hadInFlightBuild = this.inFlight.delete(serializedKey);
|
|
321
|
+
this.inFlightGenerations.delete(serializedKey);
|
|
277
322
|
this.bumpGraphGeneration(serializedKey);
|
|
278
|
-
|
|
279
|
-
return record ? 1 : 0;
|
|
323
|
+
return record || hadInFlightBuild ? 1 : 0;
|
|
280
324
|
}
|
|
281
|
-
invalidateGroupedRecord(
|
|
282
|
-
const groupedRecord = this.groupedRecords.get(
|
|
325
|
+
invalidateGroupedRecord(cacheKey) {
|
|
326
|
+
const groupedRecord = this.groupedRecords.get(cacheKey);
|
|
283
327
|
if (groupedRecord) {
|
|
284
|
-
this.dependencyIndex.unbindGroupedRecord(
|
|
285
|
-
this.groupedRecords.delete(
|
|
328
|
+
this.dependencyIndex.unbindGroupedRecord(cacheKey, groupedRecord);
|
|
329
|
+
this.groupedRecords.delete(cacheKey);
|
|
286
330
|
}
|
|
287
|
-
this.bumpGroupedGeneration(
|
|
288
|
-
this.groupedInFlight.delete(
|
|
331
|
+
this.bumpGroupedGeneration(cacheKey);
|
|
332
|
+
this.groupedInFlight.delete(cacheKey);
|
|
289
333
|
return groupedRecord ? 1 : 0;
|
|
290
334
|
}
|
|
291
335
|
async runGraphBuild(serializedKey, key, buildGeneration, build) {
|
|
@@ -299,6 +343,11 @@ export class SessionPageBrowserGraphCache {
|
|
|
299
343
|
const existing = this.records.get(serializedKey);
|
|
300
344
|
return existing?.result;
|
|
301
345
|
}
|
|
346
|
+
if (buildResult.cacheable === false) {
|
|
347
|
+
this.releaseInFlightDependencyPaths(serializedKey, buildGeneration);
|
|
348
|
+
return buildResult.result;
|
|
349
|
+
}
|
|
350
|
+
this.releaseInFlightDependencyPaths(serializedKey, buildGeneration);
|
|
302
351
|
this.pruneStaleRouteRecords(key);
|
|
303
352
|
const previous = this.records.get(serializedKey);
|
|
304
353
|
if (previous) {
|
|
@@ -319,10 +368,10 @@ export class SessionPageBrowserGraphCache {
|
|
|
319
368
|
return Promise.reject(error);
|
|
320
369
|
}
|
|
321
370
|
finally {
|
|
322
|
-
this.
|
|
371
|
+
this.clearInFlightBuild(serializedKey, buildGeneration);
|
|
323
372
|
}
|
|
324
373
|
}
|
|
325
|
-
async runGroupedBuild(
|
|
374
|
+
async runGroupedBuild(cacheKey, buildGeneration, build) {
|
|
326
375
|
try {
|
|
327
376
|
this.buildCount += 1;
|
|
328
377
|
const buildResult = await build();
|
|
@@ -332,35 +381,60 @@ export class SessionPageBrowserGraphCache {
|
|
|
332
381
|
if (isSkipCacheGroupedOutcome(buildResult)) {
|
|
333
382
|
return buildResult.assetsByRoute;
|
|
334
383
|
}
|
|
335
|
-
if (!this.canCommitGroupedBuild(
|
|
336
|
-
const existing = this.groupedRecords.get(
|
|
384
|
+
if (!this.canCommitGroupedBuild(cacheKey, buildGeneration)) {
|
|
385
|
+
const existing = this.groupedRecords.get(cacheKey);
|
|
337
386
|
return existing?.assetsByRoute ?? new Map();
|
|
338
387
|
}
|
|
339
|
-
const previous = this.groupedRecords.get(
|
|
388
|
+
const previous = this.groupedRecords.get(cacheKey);
|
|
340
389
|
if (previous) {
|
|
341
|
-
this.dependencyIndex.unbindGroupedRecord(
|
|
390
|
+
this.dependencyIndex.unbindGroupedRecord(cacheKey, previous);
|
|
342
391
|
}
|
|
343
392
|
const record = {
|
|
344
393
|
assetsByRoute: buildResult.assetsByRoute,
|
|
345
394
|
dependencyPaths: buildResult.dependencyPaths,
|
|
346
395
|
generation: buildGeneration,
|
|
347
396
|
};
|
|
348
|
-
this.groupedRecords.set(
|
|
349
|
-
this.dependencyIndex.bindGroupedRecord(
|
|
397
|
+
this.groupedRecords.set(cacheKey, record);
|
|
398
|
+
this.dependencyIndex.bindGroupedRecord(cacheKey, record);
|
|
350
399
|
return record.assetsByRoute;
|
|
351
400
|
}
|
|
352
401
|
catch (error) {
|
|
353
402
|
return Promise.reject(error);
|
|
354
403
|
}
|
|
355
404
|
finally {
|
|
356
|
-
this.groupedInFlight.delete(
|
|
405
|
+
this.groupedInFlight.delete(cacheKey);
|
|
357
406
|
}
|
|
358
407
|
}
|
|
359
408
|
canCommitGraphBuild(serializedKey, buildGeneration) {
|
|
360
409
|
return this.graphGenerations.get(serializedKey) === buildGeneration;
|
|
361
410
|
}
|
|
362
|
-
canCommitGroupedBuild(
|
|
363
|
-
return this.groupedGenerations.get(
|
|
411
|
+
canCommitGroupedBuild(cacheKey, buildGeneration) {
|
|
412
|
+
return this.groupedGenerations.get(cacheKey) === buildGeneration;
|
|
413
|
+
}
|
|
414
|
+
bindDependencyPaths(serializedKey, dependencyPaths) {
|
|
415
|
+
this.dependencyIndex.bindPaths(serializedKey, dependencyPaths);
|
|
416
|
+
}
|
|
417
|
+
unbindDependencyPaths(serializedKey, dependencyPaths) {
|
|
418
|
+
this.dependencyIndex.unbindPaths(serializedKey, dependencyPaths);
|
|
419
|
+
}
|
|
420
|
+
clearInFlightBuild(serializedKey, buildGeneration) {
|
|
421
|
+
if (this.inFlightGenerations.get(serializedKey) !== buildGeneration) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
this.releaseInFlightDependencyPaths(serializedKey, buildGeneration);
|
|
425
|
+
this.inFlight.delete(serializedKey);
|
|
426
|
+
this.inFlightGenerations.delete(serializedKey);
|
|
427
|
+
}
|
|
428
|
+
releaseInFlightDependencyPaths(serializedKey, buildGeneration) {
|
|
429
|
+
if (this.inFlightGenerations.get(serializedKey) !== buildGeneration) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const dependencyPaths = this.inFlightDependencyPaths.get(serializedKey);
|
|
433
|
+
if (!dependencyPaths) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
this.unbindDependencyPaths(serializedKey, dependencyPaths);
|
|
437
|
+
this.inFlightDependencyPaths.delete(serializedKey);
|
|
364
438
|
}
|
|
365
439
|
}
|
|
366
440
|
const sessionByAppConfig = new WeakMap();
|
|
@@ -424,6 +498,9 @@ export function collectPageBrowserGraphDependencyPaths(routeFile, contribution,
|
|
|
424
498
|
dependencyPaths.add(normalizeDependencyPath(bundledSourceFilepath));
|
|
425
499
|
}
|
|
426
500
|
}
|
|
501
|
+
for (const watchPath of contribution.watchPaths ?? []) {
|
|
502
|
+
dependencyPaths.add(normalizeDependencyPath(watchPath));
|
|
503
|
+
}
|
|
427
504
|
return dependencyPaths;
|
|
428
505
|
}
|
|
429
506
|
function describeProcessedAsset(asset) {
|
|
@@ -2,10 +2,18 @@ import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
|
2
2
|
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../../types/public-types.js';
|
|
3
3
|
import { type AssetProcessingService } from '../../../services/assets/asset-processing-service/index.js';
|
|
4
4
|
import { type GraphPolicy } from './page-browser-graph-session.js';
|
|
5
|
+
import type { GroupedGraphBuildPlan } from './grouped-graph-build-plan.js';
|
|
6
|
+
export type GroupedPageBrowserGraphContribution = {
|
|
7
|
+
routeFile: string;
|
|
8
|
+
dependencyInstanceKey: string;
|
|
9
|
+
contribution: PageBrowserGraphContribution | undefined;
|
|
10
|
+
};
|
|
5
11
|
export type PageBrowserGraphResolveInput = {
|
|
6
12
|
routeFile: string;
|
|
13
|
+
dependencyInstanceKey?: string;
|
|
7
14
|
integrationName: string;
|
|
8
|
-
collectContribution: (
|
|
15
|
+
collectContribution: () => Promise<PageBrowserGraphContribution | undefined>;
|
|
16
|
+
groupedBuildPlan?: GroupedGraphBuildPlan;
|
|
9
17
|
policy?: GraphPolicy;
|
|
10
18
|
};
|
|
11
19
|
/**
|
|
@@ -26,6 +34,5 @@ export declare class PageBrowserGraphService {
|
|
|
26
34
|
private materializePageBrowserGraph;
|
|
27
35
|
private resolveGroupedPageBrowserAssets;
|
|
28
36
|
private buildGroupedPageBrowserAssets;
|
|
29
|
-
private listIntegrationRouteFiles;
|
|
30
37
|
private partitionPageBrowserGraphAssets;
|
|
31
38
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { fileSystem } from '@ecopages/file-system';
|
|
3
2
|
import {} from '../../../services/assets/asset-processing-service/index.js';
|
|
4
3
|
import { appLogger } from '../../../global/app-logger.js';
|
|
5
4
|
import { startupTrace } from '../../../diagnostics/startup-trace.js';
|
|
6
5
|
import { collectPageBrowserGraphDependencyPaths, createPageBrowserGraphEntryFingerprint, getAppPageBrowserGraphSession, } from './page-browser-graph-session.js';
|
|
6
|
+
import { createRouteGraphLookupKey } from './route-instance-key.js';
|
|
7
7
|
function isGroupedContentScriptAsset(asset) {
|
|
8
8
|
return asset.kind === 'script' && asset.source === 'content' && Boolean(asset.groupedBundle?.id);
|
|
9
9
|
}
|
|
@@ -29,13 +29,14 @@ export class PageBrowserGraphService {
|
|
|
29
29
|
async getOrBuild(input) {
|
|
30
30
|
const policy = input.policy ?? this.resolveGraphPolicy();
|
|
31
31
|
const session = getAppPageBrowserGraphSession(this.appConfig);
|
|
32
|
+
const dependencyInstanceKey = input.dependencyInstanceKey ?? '';
|
|
32
33
|
if (!this.isHmrEnabled()) {
|
|
33
|
-
const cachedByRoute = session.getGraphByRoute(input.integrationName, input.routeFile, policy);
|
|
34
|
+
const cachedByRoute = session.getGraphByRoute(input.integrationName, input.routeFile, policy, dependencyInstanceKey);
|
|
34
35
|
if (cachedByRoute) {
|
|
35
36
|
return cachedByRoute;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
|
-
const contribution = await input.collectContribution(
|
|
39
|
+
const contribution = await input.collectContribution();
|
|
39
40
|
if (!contribution) {
|
|
40
41
|
return undefined;
|
|
41
42
|
}
|
|
@@ -43,9 +44,10 @@ export class PageBrowserGraphService {
|
|
|
43
44
|
return await session.resolveGraph({
|
|
44
45
|
integrationName: input.integrationName,
|
|
45
46
|
routeFile: input.routeFile,
|
|
47
|
+
dependencyInstanceKey,
|
|
46
48
|
entryFingerprint,
|
|
47
49
|
policy,
|
|
48
|
-
}, async () => this.buildPageBrowserGraphRecord(input, contribution));
|
|
50
|
+
}, collectPageBrowserGraphDependencyPaths(input.routeFile, contribution, contribution.assets ?? []), async () => this.buildPageBrowserGraphRecord(input, contribution));
|
|
49
51
|
}
|
|
50
52
|
resolveGraphPolicy() {
|
|
51
53
|
return this.isHmrEnabled() ? 'development' : 'production';
|
|
@@ -65,10 +67,10 @@ export class PageBrowserGraphService {
|
|
|
65
67
|
async materializePageBrowserGraph(input, contribution) {
|
|
66
68
|
const groupedDependencies = (contribution.dependencies ?? []).filter((dep) => isGroupedContentScriptAsset(dep));
|
|
67
69
|
const ungroupedDependencies = (contribution.dependencies ?? []).filter((dep) => !isGroupedContentScriptAsset(dep));
|
|
68
|
-
const
|
|
69
|
-
?
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const groupedResolution = groupedDependencies.length
|
|
71
|
+
? await this.resolveGroupedPageBrowserAssets(input, contribution)
|
|
72
|
+
: undefined;
|
|
73
|
+
const groupedAssets = groupedResolution?.assetsByRoute.get(createRouteGraphLookupKey(input.routeFile, input.dependencyInstanceKey ?? '')) ?? [];
|
|
72
74
|
const processedDependencies = ungroupedDependencies.length
|
|
73
75
|
? await this.assetProcessingService.processDependencies(ungroupedDependencies, `${input.integrationName}:${input.routeFile}`)
|
|
74
76
|
: [];
|
|
@@ -78,15 +80,29 @@ export class PageBrowserGraphService {
|
|
|
78
80
|
return {
|
|
79
81
|
result,
|
|
80
82
|
dependencyPaths,
|
|
83
|
+
cacheable: groupedResolution?.cacheable ?? true,
|
|
81
84
|
};
|
|
82
85
|
}
|
|
83
86
|
async resolveGroupedPageBrowserAssets(input, currentContribution) {
|
|
84
87
|
if (this.isHmrEnabled()) {
|
|
85
88
|
return await this.buildGroupedPageBrowserAssets(input, currentContribution);
|
|
86
89
|
}
|
|
90
|
+
if (!input.groupedBuildPlan) {
|
|
91
|
+
const built = await this.buildGroupedPageBrowserAssets(input, currentContribution);
|
|
92
|
+
return {
|
|
93
|
+
...built,
|
|
94
|
+
cacheable: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
87
97
|
const session = getAppPageBrowserGraphSession(this.appConfig);
|
|
88
|
-
const
|
|
98
|
+
const groupedScope = {
|
|
99
|
+
integrationName: input.integrationName,
|
|
100
|
+
planKey: input.groupedBuildPlan.planKey,
|
|
101
|
+
};
|
|
102
|
+
let groupedCacheable = true;
|
|
103
|
+
const assetsByRoute = await session.resolveGroupedGraph(groupedScope, async () => {
|
|
89
104
|
const built = await this.buildGroupedPageBrowserAssets(input, currentContribution);
|
|
105
|
+
groupedCacheable = !built.hasCollectionFailures;
|
|
90
106
|
if (built.hasCollectionFailures) {
|
|
91
107
|
return {
|
|
92
108
|
skipCache: true,
|
|
@@ -103,34 +119,43 @@ export class PageBrowserGraphService {
|
|
|
103
119
|
return {
|
|
104
120
|
assetsByRoute,
|
|
105
121
|
dependencyPaths: new Set(),
|
|
106
|
-
hasCollectionFailures:
|
|
122
|
+
hasCollectionFailures: !groupedCacheable,
|
|
123
|
+
cacheable: groupedCacheable,
|
|
107
124
|
};
|
|
108
125
|
}
|
|
109
126
|
async buildGroupedPageBrowserAssets(input, currentContribution) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
const dependencyInstanceKey = input.dependencyInstanceKey ?? '';
|
|
128
|
+
const currentRouteLookupKey = createRouteGraphLookupKey(input.routeFile, dependencyInstanceKey);
|
|
129
|
+
const groupedCollection = input.groupedBuildPlan
|
|
130
|
+
? {
|
|
131
|
+
contributions: input.groupedBuildPlan.instances.map((instance) => ({
|
|
132
|
+
routeFile: instance.routeFile,
|
|
133
|
+
dependencyInstanceKey: instance.dependencyInstanceKey,
|
|
134
|
+
contribution: instance.contribution,
|
|
135
|
+
})),
|
|
136
|
+
hasCollectionFailures: false,
|
|
137
|
+
}
|
|
138
|
+
: {
|
|
139
|
+
contributions: [],
|
|
140
|
+
hasCollectionFailures: false,
|
|
141
|
+
};
|
|
142
|
+
const groupedContributions = groupedCollection.contributions;
|
|
113
143
|
const currentRouteGroupedDependencies = (currentContribution.dependencies ?? []).filter((dep) => isGroupedContentScriptAsset(dep));
|
|
114
144
|
const groupedDependencies = [...currentRouteGroupedDependencies];
|
|
115
145
|
const groupedAssetKeysByRoute = new Map();
|
|
146
|
+
const routeDisplayPaths = new Map();
|
|
116
147
|
const dependencyPaths = new Set([path.resolve(input.routeFile)]);
|
|
117
|
-
let hasCollectionFailures =
|
|
148
|
+
let hasCollectionFailures = groupedCollection.hasCollectionFailures;
|
|
118
149
|
if (currentRouteGroupedDependencies.length > 0) {
|
|
119
|
-
groupedAssetKeysByRoute.set(
|
|
150
|
+
groupedAssetKeysByRoute.set(currentRouteLookupKey, new Set(currentRouteGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle))));
|
|
151
|
+
routeDisplayPaths.set(currentRouteLookupKey, input.routeFile);
|
|
120
152
|
}
|
|
121
|
-
for (const
|
|
122
|
-
if (routeFile === input.routeFile
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
let contribution;
|
|
126
|
-
try {
|
|
127
|
-
contribution = await input.collectContribution(routeFile);
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
hasCollectionFailures = true;
|
|
131
|
-
appLogger.warn(`Skipping grouped page-browser contribution for ${routeFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
153
|
+
for (const groupedContribution of groupedContributions) {
|
|
154
|
+
if (groupedContribution.routeFile === input.routeFile &&
|
|
155
|
+
groupedContribution.dependencyInstanceKey === dependencyInstanceKey) {
|
|
132
156
|
continue;
|
|
133
157
|
}
|
|
158
|
+
const { contribution, routeFile } = groupedContribution;
|
|
134
159
|
if (!contribution?.dependencies?.length) {
|
|
135
160
|
continue;
|
|
136
161
|
}
|
|
@@ -139,7 +164,8 @@ export class PageBrowserGraphService {
|
|
|
139
164
|
continue;
|
|
140
165
|
}
|
|
141
166
|
groupedDependencies.push(...routeGroupedDependencies);
|
|
142
|
-
groupedAssetKeysByRoute.set(routeFile, new Set(routeGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle))));
|
|
167
|
+
groupedAssetKeysByRoute.set(createRouteGraphLookupKey(routeFile, groupedContribution.dependencyInstanceKey), new Set(routeGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle))));
|
|
168
|
+
routeDisplayPaths.set(createRouteGraphLookupKey(routeFile, groupedContribution.dependencyInstanceKey), routeFile);
|
|
143
169
|
dependencyPaths.add(path.resolve(routeFile));
|
|
144
170
|
}
|
|
145
171
|
if (groupedDependencies.length === 0) {
|
|
@@ -147,6 +173,7 @@ export class PageBrowserGraphService {
|
|
|
147
173
|
assetsByRoute: new Map(),
|
|
148
174
|
dependencyPaths,
|
|
149
175
|
hasCollectionFailures,
|
|
176
|
+
cacheable: Boolean(input.groupedBuildPlan) && !hasCollectionFailures,
|
|
150
177
|
};
|
|
151
178
|
}
|
|
152
179
|
for (const dependency of groupedDependencies) {
|
|
@@ -164,7 +191,7 @@ export class PageBrowserGraphService {
|
|
|
164
191
|
return groupedAssetKeys.has(getGroupedBundleAssetKey(asset.groupedBundle));
|
|
165
192
|
});
|
|
166
193
|
if (groupedAssetKeys.size > 0 && matchedAssets.length === 0) {
|
|
167
|
-
appLogger.warn(`Grouped page-browser assets for ${routeFile} are missing groupedBundle metadata after processing. Hydration scripts may be omitted from HTML.`);
|
|
194
|
+
appLogger.warn(`Grouped page-browser assets for ${routeDisplayPaths.get(routeFile) ?? routeFile} are missing groupedBundle metadata after processing. Hydration scripts may be omitted from HTML.`);
|
|
168
195
|
}
|
|
169
196
|
groupedAssetsByRoute.set(routeFile, matchedAssets);
|
|
170
197
|
}
|
|
@@ -180,19 +207,9 @@ export class PageBrowserGraphService {
|
|
|
180
207
|
assetsByRoute: groupedAssetsByRoute,
|
|
181
208
|
dependencyPaths,
|
|
182
209
|
hasCollectionFailures,
|
|
210
|
+
cacheable: Boolean(input.groupedBuildPlan) && !hasCollectionFailures,
|
|
183
211
|
};
|
|
184
212
|
}
|
|
185
|
-
async listIntegrationRouteFiles(integrationName) {
|
|
186
|
-
const integration = this.appConfig.integrations.find((plugin) => plugin.name === integrationName);
|
|
187
|
-
if (!integration) {
|
|
188
|
-
return [];
|
|
189
|
-
}
|
|
190
|
-
const scannedFiles = await fileSystem.glob(integration.extensions.map((extension) => `**/*${extension}`), { cwd: this.appConfig.absolutePaths.pagesDir });
|
|
191
|
-
return scannedFiles
|
|
192
|
-
.filter((file) => !file.includes('.ecopages-node.'))
|
|
193
|
-
.map((file) => path.join(this.appConfig.absolutePaths.pagesDir, file))
|
|
194
|
-
.sort((left, right) => left.localeCompare(right));
|
|
195
|
-
}
|
|
196
213
|
partitionPageBrowserGraphAssets(assets) {
|
|
197
214
|
const entryAssets = [];
|
|
198
215
|
const chunkAssets = [];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PageParams, PageQuery } from '../../../types/public-types.js';
|
|
2
|
+
export type PageDependencyInstance = {
|
|
3
|
+
params?: PageParams;
|
|
4
|
+
query?: PageQuery;
|
|
5
|
+
};
|
|
6
|
+
export type GroupedGraphScope = {
|
|
7
|
+
integrationName: string;
|
|
8
|
+
planKey: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Builds a stable cache key for one concrete dependency resolver input.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The representation deliberately retains scalar versus array values. Delimiter-based
|
|
15
|
+
* encodings would make otherwise distinct route inputs collide.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createPageDependencyInstanceKey(input: PageDependencyInstance): string;
|
|
18
|
+
/**
|
|
19
|
+
* Combines a route file with an optional dependency-instance key for grouped graph maps.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRouteGraphLookupKey(routeFile: string, dependencyInstanceKey?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Serializes the grouped graph cache scope for one integration and dependency instance.
|
|
24
|
+
*/
|
|
25
|
+
export declare function serializeGroupedGraphCacheKey(scope: GroupedGraphScope): string;
|
|
26
|
+
/**
|
|
27
|
+
* Reconstructs dependency resolver inputs from a serialized dependency-instance key.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parsePageDependencyInstanceKey(dependencyInstanceKey: string): PageDependencyInstance;
|