@jsenv/core 37.0.5 → 37.1.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/dist/js/autoreload.js +23 -20
- package/dist/js/import_meta_hot.js +0 -1
- package/dist/jsenv_core.js +691 -591
- package/package.json +2 -2
- package/src/build/build.js +141 -154
- package/src/build/build_versions_manager.js +53 -56
- package/src/dev/file_service.js +68 -77
- package/src/dev/start_dev_server.js +2 -4
- package/src/helpers/event_emitter.js +18 -0
- package/src/kitchen/kitchen.js +4 -6
- package/src/kitchen/url_graph/references.js +29 -8
- package/src/kitchen/url_graph/url_content.js +9 -0
- package/src/kitchen/url_graph/url_graph.js +50 -57
- package/src/kitchen/url_graph/url_graph_report.js +62 -59
- package/src/kitchen/url_graph/url_graph_visitor.js +30 -0
- package/src/kitchen/url_graph/url_info_transformations.js +26 -26
- package/src/plugins/autoreload/client/autoreload.js +7 -7
- package/src/plugins/autoreload/client/reload.js +16 -13
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -4
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +138 -96
- package/src/plugins/autoreload/jsenv_plugin_hot_search_param.js +43 -25
- package/src/plugins/import_meta_hot/client/import_meta_hot.js +0 -1
- package/src/plugins/plugins.js +3 -14
- package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +4 -0
package/src/dev/file_service.js
CHANGED
|
@@ -6,6 +6,7 @@ import { RUNTIME_COMPAT } from "@jsenv/runtime-compat";
|
|
|
6
6
|
|
|
7
7
|
import { WEB_URL_CONVERTER } from "../helpers/web_url_converter.js";
|
|
8
8
|
import { watchSourceFiles } from "../helpers/watch_source_files.js";
|
|
9
|
+
import { createEventEmitter } from "../helpers/event_emitter.js";
|
|
9
10
|
import { createKitchen } from "../kitchen/kitchen.js";
|
|
10
11
|
import { getCorePlugins } from "../plugins/plugins.js";
|
|
11
12
|
import { jsenvPluginServerEventsClientInjection } from "../plugins/server_events/jsenv_plugin_server_events_client_injection.js";
|
|
@@ -17,6 +18,7 @@ export const createFileService = ({
|
|
|
17
18
|
serverStopCallbacks,
|
|
18
19
|
serverEventsDispatcher,
|
|
19
20
|
kitchenCache,
|
|
21
|
+
onKitchenCreated = () => {},
|
|
20
22
|
|
|
21
23
|
sourceDirectoryUrl,
|
|
22
24
|
sourceMainFilePath,
|
|
@@ -32,8 +34,6 @@ export const createFileService = ({
|
|
|
32
34
|
supervisor,
|
|
33
35
|
transpilation,
|
|
34
36
|
clientAutoreload,
|
|
35
|
-
cooldownBetweenFileEvents,
|
|
36
|
-
clientServerEventsConfig,
|
|
37
37
|
cacheControl,
|
|
38
38
|
ribbon,
|
|
39
39
|
sourcemaps,
|
|
@@ -41,19 +41,32 @@ export const createFileService = ({
|
|
|
41
41
|
sourcemapsSourcesContent,
|
|
42
42
|
outDirectoryUrl,
|
|
43
43
|
}) => {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if (clientAutoreload === true) {
|
|
45
|
+
clientAutoreload = {};
|
|
46
|
+
}
|
|
47
|
+
if (clientAutoreload === false) {
|
|
48
|
+
clientAutoreload = { enabled: false };
|
|
49
|
+
}
|
|
50
|
+
const clientFileChangeEventEmitter = createEventEmitter();
|
|
51
|
+
const clientFileDereferencedEventEmitter = createEventEmitter();
|
|
52
|
+
|
|
53
|
+
clientAutoreload = {
|
|
54
|
+
enabled: true,
|
|
55
|
+
clientServerEventsConfig: {},
|
|
56
|
+
clientFileChangeEventEmitter,
|
|
57
|
+
clientFileDereferencedEventEmitter,
|
|
58
|
+
...clientAutoreload,
|
|
59
|
+
};
|
|
60
|
+
|
|
46
61
|
const stopWatchingSourceFiles = watchSourceFiles(
|
|
47
62
|
sourceDirectoryUrl,
|
|
48
63
|
(fileInfo) => {
|
|
49
|
-
|
|
50
|
-
callback(fileInfo);
|
|
51
|
-
});
|
|
64
|
+
clientFileChangeEventEmitter.emit(fileInfo);
|
|
52
65
|
},
|
|
53
66
|
{
|
|
54
67
|
sourceFilesConfig,
|
|
55
68
|
keepProcessAlive: false,
|
|
56
|
-
cooldownBetweenFileEvents,
|
|
69
|
+
cooldownBetweenFileEvents: clientAutoreload.cooldownBetweenFileEvents,
|
|
57
70
|
},
|
|
58
71
|
);
|
|
59
72
|
serverStopCallbacks.push(stopWatchingSourceFiles);
|
|
@@ -72,10 +85,10 @@ export const createFileService = ({
|
|
|
72
85
|
sourceDirectoryUrl,
|
|
73
86
|
);
|
|
74
87
|
let kitchen;
|
|
75
|
-
|
|
88
|
+
clientFileChangeEventEmitter.on(({ url }) => {
|
|
76
89
|
const urlInfo = kitchen.graph.getUrlInfo(url);
|
|
77
90
|
if (urlInfo) {
|
|
78
|
-
urlInfo.
|
|
91
|
+
urlInfo.onModified();
|
|
79
92
|
}
|
|
80
93
|
});
|
|
81
94
|
const clientRuntimeCompat = { [runtimeName]: runtimeVersion };
|
|
@@ -111,8 +124,6 @@ export const createFileService = ({
|
|
|
111
124
|
transpilation,
|
|
112
125
|
|
|
113
126
|
clientAutoreload,
|
|
114
|
-
clientFileChangeCallbackList,
|
|
115
|
-
clientFilesPruneCallbackList,
|
|
116
127
|
cacheControl,
|
|
117
128
|
ribbon,
|
|
118
129
|
}),
|
|
@@ -126,18 +137,18 @@ export const createFileService = ({
|
|
|
126
137
|
? new URL(`${runtimeName}@${runtimeVersion}/`, outDirectoryUrl)
|
|
127
138
|
: undefined,
|
|
128
139
|
});
|
|
129
|
-
kitchen.graph.
|
|
140
|
+
kitchen.graph.urlInfoCreatedEventEmitter.on((urlInfoCreated) => {
|
|
130
141
|
const { watch } = URL_META.applyAssociations({
|
|
131
|
-
url:
|
|
142
|
+
url: urlInfoCreated.url,
|
|
132
143
|
associations: watchAssociations,
|
|
133
144
|
});
|
|
134
|
-
|
|
145
|
+
urlInfoCreated.isWatched = watch;
|
|
135
146
|
// wehn an url depends on many others, we check all these (like package.json)
|
|
136
|
-
|
|
137
|
-
if (!
|
|
147
|
+
urlInfoCreated.isValid = () => {
|
|
148
|
+
if (!urlInfoCreated.url.startsWith("file:")) {
|
|
138
149
|
return false;
|
|
139
150
|
}
|
|
140
|
-
if (
|
|
151
|
+
if (urlInfoCreated.content === undefined) {
|
|
141
152
|
// urlInfo content is undefined when:
|
|
142
153
|
// - url info content never fetched
|
|
143
154
|
// - it is considered as modified because undelying file is watched and got saved
|
|
@@ -149,21 +160,20 @@ export const createFileService = ({
|
|
|
149
160
|
// file is not watched, check the filesystem
|
|
150
161
|
let fileContentAsBuffer;
|
|
151
162
|
try {
|
|
152
|
-
fileContentAsBuffer = readFileSync(new URL(
|
|
163
|
+
fileContentAsBuffer = readFileSync(new URL(urlInfoCreated.url));
|
|
153
164
|
} catch (e) {
|
|
154
165
|
if (e.code === "ENOENT") {
|
|
155
|
-
|
|
156
|
-
urlInfo.deleteFromGraph();
|
|
166
|
+
urlInfoCreated.onModified();
|
|
157
167
|
return false;
|
|
158
168
|
}
|
|
159
169
|
return false;
|
|
160
170
|
}
|
|
161
171
|
const fileContentEtag = bufferToEtag(fileContentAsBuffer);
|
|
162
|
-
if (fileContentEtag !==
|
|
163
|
-
|
|
172
|
+
if (fileContentEtag !== urlInfoCreated.originalContentEtag) {
|
|
173
|
+
urlInfoCreated.onModified();
|
|
164
174
|
// restore content to be able to compare it again later
|
|
165
|
-
|
|
166
|
-
|
|
175
|
+
urlInfoCreated.kitchen.urlInfoTransformer.setContent(
|
|
176
|
+
urlInfoCreated,
|
|
167
177
|
String(fileContentAsBuffer),
|
|
168
178
|
{
|
|
169
179
|
contentEtag: fileContentEtag,
|
|
@@ -172,23 +182,24 @@ export const createFileService = ({
|
|
|
172
182
|
return false;
|
|
173
183
|
}
|
|
174
184
|
}
|
|
175
|
-
for (const implicitUrl of
|
|
176
|
-
const implicitUrlInfo =
|
|
185
|
+
for (const implicitUrl of urlInfoCreated.implicitUrlSet) {
|
|
186
|
+
const implicitUrlInfo = urlInfoCreated.graph.getUrlInfo(implicitUrl);
|
|
177
187
|
if (implicitUrlInfo && !implicitUrlInfo.isValid()) {
|
|
178
188
|
return false;
|
|
179
189
|
}
|
|
180
190
|
}
|
|
181
191
|
return true;
|
|
182
192
|
};
|
|
183
|
-
};
|
|
184
|
-
kitchen.graph.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
193
|
+
});
|
|
194
|
+
kitchen.graph.urlInfoDereferencedEventEmitter.on(
|
|
195
|
+
(urlInfoDereferenced, lastReferenceFromOther) => {
|
|
196
|
+
clientFileDereferencedEventEmitter.emit(
|
|
197
|
+
urlInfoDereferenced,
|
|
198
|
+
lastReferenceFromOther,
|
|
199
|
+
);
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
192
203
|
serverStopCallbacks.push(() => {
|
|
193
204
|
kitchen.pluginController.callHooks("destroy", kitchen.context);
|
|
194
205
|
});
|
|
@@ -221,12 +232,15 @@ export const createFileService = ({
|
|
|
221
232
|
});
|
|
222
233
|
// "pushPlugin" so that event source client connection can be put as early as possible in html
|
|
223
234
|
kitchen.pluginController.pushPlugin(
|
|
224
|
-
jsenvPluginServerEventsClientInjection(
|
|
235
|
+
jsenvPluginServerEventsClientInjection(
|
|
236
|
+
clientAutoreload.clientServerEventsConfig,
|
|
237
|
+
),
|
|
225
238
|
);
|
|
226
239
|
}
|
|
227
240
|
}
|
|
228
241
|
|
|
229
242
|
kitchenCache.set(runtimeId, kitchen);
|
|
243
|
+
onKitchenCreated(kitchen);
|
|
230
244
|
return kitchen;
|
|
231
245
|
};
|
|
232
246
|
|
|
@@ -244,31 +258,28 @@ export const createFileService = ({
|
|
|
244
258
|
if (responseFromPlugin) {
|
|
245
259
|
return responseFromPlugin;
|
|
246
260
|
}
|
|
247
|
-
|
|
248
|
-
const parentUrl =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
261
|
+
const { referer } = request.headers;
|
|
262
|
+
const parentUrl = referer
|
|
263
|
+
? WEB_URL_CONVERTER.asFileUrl(referer, {
|
|
264
|
+
origin: request.origin,
|
|
265
|
+
rootDirectoryUrl: sourceDirectoryUrl,
|
|
266
|
+
})
|
|
267
|
+
: sourceDirectoryUrl;
|
|
268
|
+
let reference = kitchen.graph.inferReference(request.resource, parentUrl);
|
|
252
269
|
if (!reference) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
reference = parentUrlInfo.dependencies.createResolveAndFinalize({
|
|
261
|
-
trace: { message: parentUrl || sourceDirectoryUrl },
|
|
262
|
-
type: "http_request",
|
|
263
|
-
specifier: request.resource,
|
|
264
|
-
});
|
|
270
|
+
reference =
|
|
271
|
+
kitchen.graph.rootUrlInfo.dependencies.createResolveAndFinalize({
|
|
272
|
+
trace: { message: parentUrl },
|
|
273
|
+
type: "http_request",
|
|
274
|
+
specifier: request.resource,
|
|
275
|
+
});
|
|
265
276
|
}
|
|
266
277
|
const urlInfo = reference.urlInfo;
|
|
267
278
|
const ifNoneMatch = request.headers["if-none-match"];
|
|
268
279
|
const urlInfoTargetedByCache = urlInfo.findParentIfInline() || urlInfo;
|
|
269
280
|
|
|
270
281
|
try {
|
|
271
|
-
if (ifNoneMatch) {
|
|
282
|
+
if (!urlInfo.error && ifNoneMatch) {
|
|
272
283
|
const [clientOriginalContentEtag, clientContentEtag] =
|
|
273
284
|
ifNoneMatch.split("_");
|
|
274
285
|
if (
|
|
@@ -316,7 +327,7 @@ export const createFileService = ({
|
|
|
316
327
|
}),
|
|
317
328
|
...urlInfo.headers,
|
|
318
329
|
"content-type": urlInfo.contentType,
|
|
319
|
-
"content-length":
|
|
330
|
+
"content-length": urlInfo.contentLength,
|
|
320
331
|
},
|
|
321
332
|
body: urlInfo.content,
|
|
322
333
|
timing: urlInfo.timing,
|
|
@@ -355,7 +366,7 @@ export const createFileService = ({
|
|
|
355
366
|
statusMessage: originalError.message,
|
|
356
367
|
headers: {
|
|
357
368
|
"content-type": urlInfo.contentType,
|
|
358
|
-
"content-length":
|
|
369
|
+
"content-length": urlInfo.contentLength,
|
|
359
370
|
"cache-control": "no-store",
|
|
360
371
|
},
|
|
361
372
|
body: urlInfo.content,
|
|
@@ -402,26 +413,6 @@ export const createFileService = ({
|
|
|
402
413
|
statusText: e.reason,
|
|
403
414
|
statusMessage: e.stack,
|
|
404
415
|
};
|
|
405
|
-
} finally {
|
|
406
|
-
// remove http_request when there is other references keeping url info alive
|
|
407
|
-
if (
|
|
408
|
-
reference.type === "http_request" &&
|
|
409
|
-
reference.urlInfo.referenceFromOthersSet.size > 1
|
|
410
|
-
) {
|
|
411
|
-
reference.remove();
|
|
412
|
-
}
|
|
413
416
|
}
|
|
414
417
|
};
|
|
415
418
|
};
|
|
416
|
-
|
|
417
|
-
const inferParentFromRequest = (request, sourceDirectoryUrl) => {
|
|
418
|
-
const { referer } = request.headers;
|
|
419
|
-
if (!referer) {
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
const refererUrl = referer;
|
|
423
|
-
return WEB_URL_CONVERTER.asFileUrl(refererUrl, {
|
|
424
|
-
origin: request.origin,
|
|
425
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
426
|
-
});
|
|
427
|
-
};
|
|
@@ -44,8 +44,6 @@ export const startDevServer = async ({
|
|
|
44
44
|
|
|
45
45
|
sourceFilesConfig,
|
|
46
46
|
clientAutoreload = true,
|
|
47
|
-
cooldownBetweenFileEvents,
|
|
48
|
-
clientServerEventsConfig = {},
|
|
49
47
|
|
|
50
48
|
// runtimeCompat is the runtimeCompat for the build
|
|
51
49
|
// when specified, dev server use it to warn in case
|
|
@@ -61,6 +59,7 @@ export const startDevServer = async ({
|
|
|
61
59
|
cacheControl = true,
|
|
62
60
|
ribbon = true,
|
|
63
61
|
// toolbar = false,
|
|
62
|
+
onKitchenCreated = () => {},
|
|
64
63
|
|
|
65
64
|
sourcemaps = "inline",
|
|
66
65
|
sourcemapsSourcesProtocol,
|
|
@@ -185,6 +184,7 @@ export const startDevServer = async ({
|
|
|
185
184
|
serverStopCallbacks,
|
|
186
185
|
serverEventsDispatcher,
|
|
187
186
|
kitchenCache,
|
|
187
|
+
onKitchenCreated,
|
|
188
188
|
|
|
189
189
|
sourceDirectoryUrl,
|
|
190
190
|
sourceMainFilePath,
|
|
@@ -200,8 +200,6 @@ export const startDevServer = async ({
|
|
|
200
200
|
supervisor,
|
|
201
201
|
transpilation,
|
|
202
202
|
clientAutoreload,
|
|
203
|
-
cooldownBetweenFileEvents,
|
|
204
|
-
clientServerEventsConfig,
|
|
205
203
|
cacheControl,
|
|
206
204
|
ribbon,
|
|
207
205
|
sourcemaps,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const createEventEmitter = () => {
|
|
2
|
+
const callbackSet = new Set();
|
|
3
|
+
const on = (callback) => {
|
|
4
|
+
callbackSet.add(callback);
|
|
5
|
+
return () => {
|
|
6
|
+
callbackSet.delete(callback);
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
const off = (callback) => {
|
|
10
|
+
callbackSet.delete(callback);
|
|
11
|
+
};
|
|
12
|
+
const emit = (...args) => {
|
|
13
|
+
callbackSet.forEach((callback) => {
|
|
14
|
+
callback(...args);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
return { on, off, emit };
|
|
18
|
+
};
|
package/src/kitchen/kitchen.js
CHANGED
|
@@ -484,12 +484,10 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
484
484
|
// "cooked" hook
|
|
485
485
|
pluginController.callHooks("cooked", urlInfo, (cookedReturnValue) => {
|
|
486
486
|
if (typeof cookedReturnValue === "function") {
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if (prunedUrlInfo === urlInfo.url) {
|
|
492
|
-
graph.pruneUrlInfoCallbackRef.current = prevCallback;
|
|
487
|
+
const removeCallback = urlInfo.graph.urlInfoDereferencedEventEmitter.on(
|
|
488
|
+
(urlInfoDereferenced, lastReferenceFromOther) => {
|
|
489
|
+
if (urlInfoDereferenced === urlInfo) {
|
|
490
|
+
removeCallback();
|
|
493
491
|
cookedReturnValue(lastReferenceFromOther.urlInfo);
|
|
494
492
|
}
|
|
495
493
|
},
|
|
@@ -463,6 +463,10 @@ const createReference = ({
|
|
|
463
463
|
return implicitReference;
|
|
464
464
|
};
|
|
465
465
|
|
|
466
|
+
reference.gotInlined = () => {
|
|
467
|
+
return !reference.isInline && reference.next && reference.next.isInline;
|
|
468
|
+
};
|
|
469
|
+
|
|
466
470
|
reference.remove = () => removeDependency(reference);
|
|
467
471
|
|
|
468
472
|
// Object.preventExtensions(reference) // useful to ensure all properties are declared here
|
|
@@ -557,7 +561,6 @@ const canAddOrRemoveReference = (reference) => {
|
|
|
557
561
|
const applyDependencyRemovalEffects = (reference) => {
|
|
558
562
|
const { ownerUrlInfo } = reference;
|
|
559
563
|
const { referenceToOthersSet } = ownerUrlInfo;
|
|
560
|
-
|
|
561
564
|
if (reference.isImplicit && !reference.isInline) {
|
|
562
565
|
let hasAnOtherImplicitRef = false;
|
|
563
566
|
for (const referenceToOther of referenceToOthersSet) {
|
|
@@ -589,8 +592,29 @@ const applyDependencyRemovalEffects = (reference) => {
|
|
|
589
592
|
const referencedUrlInfo = reference.urlInfo;
|
|
590
593
|
referencedUrlInfo.referenceFromOthersSet.delete(reference);
|
|
591
594
|
|
|
592
|
-
|
|
593
|
-
|
|
595
|
+
let firstReferenceFromOther;
|
|
596
|
+
for (const referenceFromOther of referencedUrlInfo.referenceFromOthersSet) {
|
|
597
|
+
if (referenceFromOther.urlInfo !== referencedUrlInfo) {
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
// Here we want to know if the file is referenced by an other file.
|
|
601
|
+
// So we want to ignore reference that are created by other means:
|
|
602
|
+
// - "http_request"
|
|
603
|
+
// This type of reference is created when client request a file
|
|
604
|
+
// that we don't know yet
|
|
605
|
+
// 1. reference(s) to this file are not yet discovered
|
|
606
|
+
// 2. there is no reference to this file
|
|
607
|
+
if (referenceFromOther.type === "http_request") {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (referenceFromOther.gotInlined()) {
|
|
611
|
+
// the url info was inlined, an other reference is required
|
|
612
|
+
// to consider the non-inlined urlInfo as used
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
firstReferenceFromOther = referenceFromOther;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
594
618
|
if (firstReferenceFromOther) {
|
|
595
619
|
// either applying new ref should override old ref
|
|
596
620
|
// or we should first remove effects before adding new ones
|
|
@@ -601,11 +625,8 @@ const applyDependencyRemovalEffects = (reference) => {
|
|
|
601
625
|
}
|
|
602
626
|
return false;
|
|
603
627
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return true;
|
|
607
|
-
}
|
|
608
|
-
return false;
|
|
628
|
+
referencedUrlInfo.onDereferenced(reference);
|
|
629
|
+
return true;
|
|
609
630
|
};
|
|
610
631
|
|
|
611
632
|
const traceFromUrlSite = (urlSite) => {
|
|
@@ -25,6 +25,15 @@ export const defineGettersOnPropertiesDerivedFromOriginalContent = (
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export const defineGettersOnPropertiesDerivedFromContent = (urlInfo) => {
|
|
28
|
+
const contentLengthDescriptor = Object.getOwnPropertyDescriptor(
|
|
29
|
+
urlInfo,
|
|
30
|
+
"contentLength",
|
|
31
|
+
);
|
|
32
|
+
if (contentLengthDescriptor.value === undefined) {
|
|
33
|
+
defineVolatileGetter(urlInfo, "contentLength", () => {
|
|
34
|
+
return Buffer.byteLength(urlInfo.content);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
28
37
|
const contentAstDescriptor = Object.getOwnPropertyDescriptor(
|
|
29
38
|
urlInfo,
|
|
30
39
|
"contentAst",
|
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
injectQueryParamsIntoSpecifier,
|
|
5
5
|
} from "@jsenv/urls";
|
|
6
6
|
|
|
7
|
+
import { createEventEmitter } from "../../helpers/event_emitter.js";
|
|
7
8
|
import { urlSpecifierEncoding } from "./url_specifier_encoding.js";
|
|
8
9
|
import { createDependencies } from "./references.js";
|
|
10
|
+
import { GRAPH_VISITOR } from "./url_graph_visitor.js";
|
|
9
11
|
|
|
10
12
|
export const createUrlGraph = ({
|
|
11
13
|
rootDirectoryUrl,
|
|
@@ -13,8 +15,8 @@ export const createUrlGraph = ({
|
|
|
13
15
|
name = "anonymous",
|
|
14
16
|
}) => {
|
|
15
17
|
const urlGraph = {};
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
+
const urlInfoCreatedEventEmitter = createEventEmitter();
|
|
19
|
+
const urlInfoDereferencedEventEmitter = createEventEmitter();
|
|
18
20
|
|
|
19
21
|
const urlInfoMap = new Map();
|
|
20
22
|
const hasUrlInfo = (key) => {
|
|
@@ -35,27 +37,7 @@ export const createUrlGraph = ({
|
|
|
35
37
|
}
|
|
36
38
|
return null;
|
|
37
39
|
};
|
|
38
|
-
|
|
39
|
-
const urlInfo = urlInfoMap.get(url);
|
|
40
|
-
if (urlInfo) {
|
|
41
|
-
urlInfo.kitchen.urlInfoTransformer.resetContent(urlInfo);
|
|
42
|
-
urlInfoMap.delete(url);
|
|
43
|
-
urlInfo.modifiedTimestamp = Date.now();
|
|
44
|
-
if (lastReferenceFromOther && !urlInfo.isInline) {
|
|
45
|
-
pruneUrlInfoCallbackRef.current(urlInfo, lastReferenceFromOther);
|
|
46
|
-
}
|
|
47
|
-
urlInfo.referenceToOthersSet.forEach((referenceToOther) => {
|
|
48
|
-
referenceToOther.remove();
|
|
49
|
-
});
|
|
50
|
-
if (urlInfo.searchParams.size > 0) {
|
|
51
|
-
const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url);
|
|
52
|
-
const urlInfoWithoutSearch = getUrlInfo(urlWithoutSearch);
|
|
53
|
-
if (urlInfoWithoutSearch) {
|
|
54
|
-
urlInfoWithoutSearch.searchParamVariantSet.delete(urlInfo);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
};
|
|
40
|
+
|
|
59
41
|
const addUrlInfo = (urlInfo) => {
|
|
60
42
|
urlInfo.graph = urlGraph;
|
|
61
43
|
urlInfo.kitchen = kitchen;
|
|
@@ -72,7 +54,7 @@ export const createUrlGraph = ({
|
|
|
72
54
|
const context = Object.create(ownerContext);
|
|
73
55
|
referencedUrlInfo = createUrlInfo(referencedUrl, context);
|
|
74
56
|
addUrlInfo(referencedUrlInfo);
|
|
75
|
-
|
|
57
|
+
urlInfoCreatedEventEmitter.emit(referencedUrlInfo);
|
|
76
58
|
}
|
|
77
59
|
if (referencedUrlInfo.searchParams.size > 0 && !kitchen.context.shape) {
|
|
78
60
|
// A resource is represented by a url.
|
|
@@ -149,17 +131,16 @@ export const createUrlGraph = ({
|
|
|
149
131
|
Object.assign(urlGraph, {
|
|
150
132
|
name,
|
|
151
133
|
rootUrlInfo,
|
|
152
|
-
createUrlInfoCallbackRef,
|
|
153
|
-
pruneUrlInfoCallbackRef,
|
|
154
134
|
|
|
155
135
|
urlInfoMap,
|
|
156
136
|
reuseOrCreateUrlInfo,
|
|
157
137
|
hasUrlInfo,
|
|
158
138
|
getUrlInfo,
|
|
159
|
-
deleteUrlInfo,
|
|
160
139
|
getEntryPoints,
|
|
161
140
|
|
|
162
141
|
inferReference,
|
|
142
|
+
urlInfoCreatedEventEmitter,
|
|
143
|
+
urlInfoDereferencedEventEmitter,
|
|
163
144
|
|
|
164
145
|
toObject: () => {
|
|
165
146
|
const data = {};
|
|
@@ -197,6 +178,7 @@ const createUrlInfo = (url, context) => {
|
|
|
197
178
|
context,
|
|
198
179
|
error: null,
|
|
199
180
|
modifiedTimestamp: 0,
|
|
181
|
+
dereferencedTimestamp: 0,
|
|
200
182
|
originalContentEtag: null,
|
|
201
183
|
contentEtag: null,
|
|
202
184
|
isWatched: false,
|
|
@@ -221,6 +203,7 @@ const createUrlInfo = (url, context) => {
|
|
|
221
203
|
originalContentAst: undefined,
|
|
222
204
|
content: undefined,
|
|
223
205
|
contentAst: undefined,
|
|
206
|
+
contentLength: undefined,
|
|
224
207
|
contentFinalized: false,
|
|
225
208
|
|
|
226
209
|
sourcemap: null,
|
|
@@ -247,37 +230,24 @@ const createUrlInfo = (url, context) => {
|
|
|
247
230
|
urlInfo.searchParams = new URL(url).searchParams;
|
|
248
231
|
|
|
249
232
|
urlInfo.dependencies = createDependencies(urlInfo);
|
|
250
|
-
urlInfo.getFirstReferenceFromOther = ({ ignoreWeak } = {}) => {
|
|
251
|
-
for (const referenceFromOther of urlInfo.referenceFromOthersSet) {
|
|
252
|
-
if (referenceFromOther.url === urlInfo.url) {
|
|
253
|
-
if (
|
|
254
|
-
!referenceFromOther.isInline &&
|
|
255
|
-
referenceFromOther.next &&
|
|
256
|
-
referenceFromOther.next.isInline
|
|
257
|
-
) {
|
|
258
|
-
// the url info was inlined, an other reference is required
|
|
259
|
-
// to consider the non-inlined urlInfo as used
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (ignoreWeak && referenceFromOther.isWeak) {
|
|
263
|
-
// weak reference don't count as using the url
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
return referenceFromOther;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return null;
|
|
270
|
-
};
|
|
271
233
|
urlInfo.isUsed = () => {
|
|
272
234
|
if (urlInfo.isRoot) {
|
|
273
235
|
return true;
|
|
274
236
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
237
|
+
for (const referenceFromOther of urlInfo.referenceFromOthersSet) {
|
|
238
|
+
if (referenceFromOther.urlInfo !== urlInfo) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (referenceFromOther.isWeak) {
|
|
242
|
+
// weak reference don't count as using the url
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (referenceFromOther.gotInlined()) {
|
|
246
|
+
// the url info was inlined, an other reference is required
|
|
247
|
+
// to consider the non-inlined urlInfo as used
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
return referenceFromOther.ownerUrlInfo.isUsed();
|
|
281
251
|
}
|
|
282
252
|
// nothing uses this url anymore
|
|
283
253
|
// - versioning update inline content
|
|
@@ -297,6 +267,9 @@ const createUrlInfo = (url, context) => {
|
|
|
297
267
|
}
|
|
298
268
|
return null;
|
|
299
269
|
};
|
|
270
|
+
urlInfo.findDependent = (callback) => {
|
|
271
|
+
return GRAPH_VISITOR.findDependent(urlInfo, callback);
|
|
272
|
+
};
|
|
300
273
|
urlInfo.isSearchParamVariantOf = (otherUrlInfo) => {
|
|
301
274
|
if (urlInfo.searchParams.size === 0) {
|
|
302
275
|
return false;
|
|
@@ -367,7 +340,7 @@ const createUrlInfo = (url, context) => {
|
|
|
367
340
|
reference.next = referenceWithoutSearchParam;
|
|
368
341
|
return referenceWithoutSearchParam.urlInfo;
|
|
369
342
|
};
|
|
370
|
-
urlInfo.
|
|
343
|
+
urlInfo.onModified = ({ modifiedTimestamp = Date.now() } = {}) => {
|
|
371
344
|
const visitedSet = new Set();
|
|
372
345
|
const iterate = (urlInfo) => {
|
|
373
346
|
if (visitedSet.has(urlInfo)) {
|
|
@@ -388,9 +361,29 @@ const createUrlInfo = (url, context) => {
|
|
|
388
361
|
};
|
|
389
362
|
iterate(urlInfo);
|
|
390
363
|
};
|
|
391
|
-
urlInfo.
|
|
392
|
-
urlInfo.
|
|
364
|
+
urlInfo.onDereferenced = (lastReferenceFromOther) => {
|
|
365
|
+
urlInfo.dereferencedTimestamp = Date.now();
|
|
366
|
+
urlInfo.graph.urlInfoDereferencedEventEmitter.emit(
|
|
367
|
+
urlInfo,
|
|
368
|
+
lastReferenceFromOther,
|
|
369
|
+
);
|
|
393
370
|
};
|
|
371
|
+
|
|
372
|
+
// not used for now
|
|
373
|
+
// urlInfo.deleteFromGraph = () => {
|
|
374
|
+
// urlInfo.kitchen.urlInfoTransformer.resetContent(urlInfo);
|
|
375
|
+
// urlInfo.graph.urlInfoMap.delete(url);
|
|
376
|
+
// urlInfo.referenceToOthersSet.forEach((referenceToOther) => {
|
|
377
|
+
// referenceToOther.remove();
|
|
378
|
+
// });
|
|
379
|
+
// if (urlInfo.searchParams.size > 0) {
|
|
380
|
+
// const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url);
|
|
381
|
+
// const urlInfoWithoutSearch = urlInfo.graph.getUrlInfo(urlWithoutSearch);
|
|
382
|
+
// if (urlInfoWithoutSearch) {
|
|
383
|
+
// urlInfoWithoutSearch.searchParamVariantSet.delete(urlInfo);
|
|
384
|
+
// }
|
|
385
|
+
// }
|
|
386
|
+
// };
|
|
394
387
|
urlInfo.cook = (customContext) => {
|
|
395
388
|
return urlInfo.context.cook(urlInfo, customContext);
|
|
396
389
|
};
|