@jsenv/core 37.0.4 → 37.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- const clientFileChangeCallbackList = [];
45
- const clientFilesPruneCallbackList = [];
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
- clientFileChangeCallbackList.forEach((callback) => {
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
- clientFileChangeCallbackList.push(({ url }) => {
88
+ clientFileChangeEventEmitter.on(({ url }) => {
76
89
  const urlInfo = kitchen.graph.getUrlInfo(url);
77
90
  if (urlInfo) {
78
- urlInfo.considerModified();
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.createUrlInfoCallbackRef.current = (urlInfo) => {
140
+ kitchen.graph.urlInfoCreatedEventEmitter.on((urlInfoCreated) => {
130
141
  const { watch } = URL_META.applyAssociations({
131
- url: urlInfo.url,
142
+ url: urlInfoCreated.url,
132
143
  associations: watchAssociations,
133
144
  });
134
- urlInfo.isWatched = watch;
145
+ urlInfoCreated.isWatched = watch;
135
146
  // wehn an url depends on many others, we check all these (like package.json)
136
- urlInfo.isValid = () => {
137
- if (!urlInfo.url.startsWith("file:")) {
147
+ urlInfoCreated.isValid = () => {
148
+ if (!urlInfoCreated.url.startsWith("file:")) {
138
149
  return false;
139
150
  }
140
- if (urlInfo.content === undefined) {
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(urlInfo.url));
163
+ fileContentAsBuffer = readFileSync(new URL(urlInfoCreated.url));
153
164
  } catch (e) {
154
165
  if (e.code === "ENOENT") {
155
- urlInfo.considerModified();
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 !== urlInfo.originalContentEtag) {
163
- urlInfo.considerModified();
172
+ if (fileContentEtag !== urlInfoCreated.originalContentEtag) {
173
+ urlInfoCreated.onModified();
164
174
  // restore content to be able to compare it again later
165
- urlInfo.kitchen.urlInfoTransformer.setContent(
166
- urlInfo,
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 urlInfo.implicitUrlSet) {
176
- const implicitUrlInfo = kitchen.graph.getUrlInfo(implicitUrl);
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.pruneUrlInfoCallbackRef.current = (
185
- prunedUrlInfo,
186
- lastReferenceFromOther,
187
- ) => {
188
- clientFilesPruneCallbackList.forEach((callback) => {
189
- callback(prunedUrlInfo, lastReferenceFromOther);
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(clientServerEventsConfig),
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,35 +258,28 @@ export const createFileService = ({
244
258
  if (responseFromPlugin) {
245
259
  return responseFromPlugin;
246
260
  }
247
- let reference;
248
- const parentUrl = inferParentFromRequest(
249
- request,
250
- sourceDirectoryUrl,
251
- sourceMainFilePath,
252
- );
253
- if (parentUrl) {
254
- reference = kitchen.graph.inferReference(request.resource, parentUrl);
255
- }
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);
256
269
  if (!reference) {
257
- let parentUrlInfo;
258
- if (parentUrl) {
259
- parentUrlInfo = kitchen.graph.getUrlInfo(parentUrl);
260
- }
261
- if (!parentUrlInfo) {
262
- parentUrlInfo = kitchen.graph.rootUrlInfo;
263
- }
264
- reference = parentUrlInfo.dependencies.createResolveAndFinalize({
265
- trace: { message: parentUrl || sourceDirectoryUrl },
266
- type: "http_request",
267
- specifier: request.resource,
268
- });
270
+ reference =
271
+ kitchen.graph.rootUrlInfo.dependencies.createResolveAndFinalize({
272
+ trace: { message: parentUrl },
273
+ type: "http_request",
274
+ specifier: request.resource,
275
+ });
269
276
  }
270
277
  const urlInfo = reference.urlInfo;
271
278
  const ifNoneMatch = request.headers["if-none-match"];
272
279
  const urlInfoTargetedByCache = urlInfo.findParentIfInline() || urlInfo;
273
280
 
274
281
  try {
275
- if (ifNoneMatch) {
282
+ if (!urlInfo.error && ifNoneMatch) {
276
283
  const [clientOriginalContentEtag, clientContentEtag] =
277
284
  ifNoneMatch.split("_");
278
285
  if (
@@ -320,7 +327,7 @@ export const createFileService = ({
320
327
  }),
321
328
  ...urlInfo.headers,
322
329
  "content-type": urlInfo.contentType,
323
- "content-length": Buffer.byteLength(urlInfo.content),
330
+ "content-length": urlInfo.contentLength,
324
331
  },
325
332
  body: urlInfo.content,
326
333
  timing: urlInfo.timing,
@@ -359,7 +366,7 @@ export const createFileService = ({
359
366
  statusMessage: originalError.message,
360
367
  headers: {
361
368
  "content-type": urlInfo.contentType,
362
- "content-length": Buffer.byteLength(urlInfo.content),
369
+ "content-length": urlInfo.contentLength,
363
370
  "cache-control": "no-store",
364
371
  },
365
372
  body: urlInfo.content,
@@ -406,34 +413,6 @@ export const createFileService = ({
406
413
  statusText: e.reason,
407
414
  statusMessage: e.stack,
408
415
  };
409
- } finally {
410
- // remove http_request when there is other references keeping url info alive
411
- if (
412
- reference.type === "http_request" &&
413
- reference.urlInfo.referenceFromOthersSet.size > 1
414
- ) {
415
- reference.remove();
416
- }
417
416
  }
418
417
  };
419
418
  };
420
-
421
- const inferParentFromRequest = (
422
- request,
423
- sourceDirectoryUrl,
424
- sourceMainFilePath,
425
- ) => {
426
- const { referer } = request.headers;
427
- if (!referer) {
428
- return null;
429
- }
430
- const refererUrlObject = new URL(referer);
431
- const refererUrl =
432
- refererUrlObject.pathname === `/`
433
- ? new URL(sourceMainFilePath, request.origin).href
434
- : referer;
435
- return WEB_URL_CONVERTER.asFileUrl(refererUrl, {
436
- origin: request.origin,
437
- rootDirectoryUrl: sourceDirectoryUrl,
438
- });
439
- };
@@ -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
+ };
@@ -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 prevCallback = graph.pruneUrlInfoCallbackRef.current;
488
- graph.pruneUrlInfoCallbackRef.current(
489
- (prunedUrlInfo, lastReferenceFromOther) => {
490
- prevCallback();
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
- const firstReferenceFromOther =
593
- referencedUrlInfo.getFirstReferenceFromOther();
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
- if (reference.type !== "http_request") {
605
- referencedUrlInfo.deleteFromGraph(reference);
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,6 +4,7 @@ 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";
9
10
 
@@ -13,8 +14,8 @@ export const createUrlGraph = ({
13
14
  name = "anonymous",
14
15
  }) => {
15
16
  const urlGraph = {};
16
- const createUrlInfoCallbackRef = { current: () => {} };
17
- const pruneUrlInfoCallbackRef = { current: () => {} };
17
+ const urlInfoCreatedEventEmitter = createEventEmitter();
18
+ const urlInfoDereferencedEventEmitter = createEventEmitter();
18
19
 
19
20
  const urlInfoMap = new Map();
20
21
  const hasUrlInfo = (key) => {
@@ -35,27 +36,7 @@ export const createUrlGraph = ({
35
36
  }
36
37
  return null;
37
38
  };
38
- const deleteUrlInfo = (url, lastReferenceFromOther) => {
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
- };
39
+
59
40
  const addUrlInfo = (urlInfo) => {
60
41
  urlInfo.graph = urlGraph;
61
42
  urlInfo.kitchen = kitchen;
@@ -65,14 +46,15 @@ export const createUrlGraph = ({
65
46
  const referencedUrl = useGeneratedUrl
66
47
  ? reference.generatedUrl
67
48
  : reference.url;
68
- const existingUrlInfo = getUrlInfo(referencedUrl);
69
- if (existingUrlInfo) return existingUrlInfo;
70
- const ownerUrlInfo = reference.ownerUrlInfo;
71
- const ownerContext = ownerUrlInfo.context;
72
- const context = Object.create(ownerContext);
73
- const referencedUrlInfo = createUrlInfo(referencedUrl, context);
74
- addUrlInfo(referencedUrlInfo);
75
- createUrlInfoCallbackRef.current(referencedUrlInfo);
49
+ let referencedUrlInfo = getUrlInfo(referencedUrl);
50
+ if (!referencedUrlInfo) {
51
+ const ownerUrlInfo = reference.ownerUrlInfo;
52
+ const ownerContext = ownerUrlInfo.context;
53
+ const context = Object.create(ownerContext);
54
+ referencedUrlInfo = createUrlInfo(referencedUrl, context);
55
+ addUrlInfo(referencedUrlInfo);
56
+ urlInfoCreatedEventEmitter.emit(referencedUrlInfo);
57
+ }
76
58
  if (referencedUrlInfo.searchParams.size > 0 && !kitchen.context.shape) {
77
59
  // A resource is represented by a url.
78
60
  // Variations of a resource are represented by url search params
@@ -148,17 +130,16 @@ export const createUrlGraph = ({
148
130
  Object.assign(urlGraph, {
149
131
  name,
150
132
  rootUrlInfo,
151
- createUrlInfoCallbackRef,
152
- pruneUrlInfoCallbackRef,
153
133
 
154
134
  urlInfoMap,
155
135
  reuseOrCreateUrlInfo,
156
136
  hasUrlInfo,
157
137
  getUrlInfo,
158
- deleteUrlInfo,
159
138
  getEntryPoints,
160
139
 
161
140
  inferReference,
141
+ urlInfoCreatedEventEmitter,
142
+ urlInfoDereferencedEventEmitter,
162
143
 
163
144
  toObject: () => {
164
145
  const data = {};
@@ -196,6 +177,7 @@ const createUrlInfo = (url, context) => {
196
177
  context,
197
178
  error: null,
198
179
  modifiedTimestamp: 0,
180
+ dereferencedTimestamp: 0,
199
181
  originalContentEtag: null,
200
182
  contentEtag: null,
201
183
  isWatched: false,
@@ -220,6 +202,7 @@ const createUrlInfo = (url, context) => {
220
202
  originalContentAst: undefined,
221
203
  content: undefined,
222
204
  contentAst: undefined,
205
+ contentLength: undefined,
223
206
  contentFinalized: false,
224
207
 
225
208
  sourcemap: null,
@@ -246,37 +229,24 @@ const createUrlInfo = (url, context) => {
246
229
  urlInfo.searchParams = new URL(url).searchParams;
247
230
 
248
231
  urlInfo.dependencies = createDependencies(urlInfo);
249
- urlInfo.getFirstReferenceFromOther = ({ ignoreWeak } = {}) => {
250
- for (const referenceFromOther of urlInfo.referenceFromOthersSet) {
251
- if (referenceFromOther.url === urlInfo.url) {
252
- if (
253
- !referenceFromOther.isInline &&
254
- referenceFromOther.next &&
255
- referenceFromOther.next.isInline
256
- ) {
257
- // the url info was inlined, an other reference is required
258
- // to consider the non-inlined urlInfo as used
259
- continue;
260
- }
261
- if (ignoreWeak && referenceFromOther.isWeak) {
262
- // weak reference don't count as using the url
263
- continue;
264
- }
265
- return referenceFromOther;
266
- }
267
- }
268
- return null;
269
- };
270
232
  urlInfo.isUsed = () => {
271
233
  if (urlInfo.isRoot) {
272
234
  return true;
273
235
  }
274
- // if (urlInfo.type === "sourcemap") {
275
- // return true;
276
- // }
277
- // check if there is a strong reference to this urlInfo
278
- if (urlInfo.getFirstReferenceFromOther({ ignoreWeak: true })) {
279
- return true;
236
+ for (const referenceFromOther of urlInfo.referenceFromOthersSet) {
237
+ if (referenceFromOther.urlInfo !== urlInfo) {
238
+ continue;
239
+ }
240
+ if (referenceFromOther.isWeak) {
241
+ // weak reference don't count as using the url
242
+ continue;
243
+ }
244
+ if (referenceFromOther.gotInlined()) {
245
+ // the url info was inlined, an other reference is required
246
+ // to consider the non-inlined urlInfo as used
247
+ continue;
248
+ }
249
+ return referenceFromOther.ownerUrlInfo.isUsed();
280
250
  }
281
251
  // nothing uses this url anymore
282
252
  // - versioning update inline content
@@ -366,7 +336,7 @@ const createUrlInfo = (url, context) => {
366
336
  reference.next = referenceWithoutSearchParam;
367
337
  return referenceWithoutSearchParam.urlInfo;
368
338
  };
369
- urlInfo.considerModified = ({ modifiedTimestamp = Date.now() } = {}) => {
339
+ urlInfo.onModified = ({ modifiedTimestamp = Date.now() } = {}) => {
370
340
  const visitedSet = new Set();
371
341
  const iterate = (urlInfo) => {
372
342
  if (visitedSet.has(urlInfo)) {
@@ -387,9 +357,29 @@ const createUrlInfo = (url, context) => {
387
357
  };
388
358
  iterate(urlInfo);
389
359
  };
390
- urlInfo.deleteFromGraph = (reference) => {
391
- urlInfo.graph.deleteUrlInfo(urlInfo.url, reference);
360
+ urlInfo.onDereferenced = (lastReferenceFromOther) => {
361
+ urlInfo.dereferencedTimestamp = Date.now();
362
+ urlInfo.graph.urlInfoDereferencedEventEmitter.emit(
363
+ urlInfo,
364
+ lastReferenceFromOther,
365
+ );
392
366
  };
367
+
368
+ // not used for now
369
+ // urlInfo.deleteFromGraph = () => {
370
+ // urlInfo.kitchen.urlInfoTransformer.resetContent(urlInfo);
371
+ // urlInfo.graph.urlInfoMap.delete(url);
372
+ // urlInfo.referenceToOthersSet.forEach((referenceToOther) => {
373
+ // referenceToOther.remove();
374
+ // });
375
+ // if (urlInfo.searchParams.size > 0) {
376
+ // const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url);
377
+ // const urlInfoWithoutSearch = urlInfo.graph.getUrlInfo(urlWithoutSearch);
378
+ // if (urlInfoWithoutSearch) {
379
+ // urlInfoWithoutSearch.searchParamVariantSet.delete(urlInfo);
380
+ // }
381
+ // }
382
+ // };
393
383
  urlInfo.cook = (customContext) => {
394
384
  return urlInfo.context.cook(urlInfo, customContext);
395
385
  };