@jsenv/core 40.12.7 → 40.12.8

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": "@jsenv/core",
3
- "version": "40.12.7",
3
+ "version": "40.12.8",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -77,7 +77,7 @@
77
77
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
78
78
  "@jsenv/ast": "6.7.20",
79
79
  "@jsenv/js-module-fallback": "1.4.28",
80
- "@jsenv/plugin-bundling": "2.10.7",
80
+ "@jsenv/plugin-bundling": "2.10.8",
81
81
  "@jsenv/plugin-minification": "1.7.3",
82
82
  "@jsenv/plugin-supervisor": "1.7.14",
83
83
  "@jsenv/plugin-transpilation": "1.5.69",
@@ -24,7 +24,6 @@ import {
24
24
  compareFileUrls,
25
25
  createLookupPackageDirectory,
26
26
  ensureEmptyDirectory,
27
- lookupPackageDirectory,
28
27
  readPackageAtOrNull,
29
28
  updateJsonFileSync,
30
29
  writeFileSync,
@@ -62,6 +61,7 @@ import { memoryUsage as processMemoryUsage } from "node:process";
62
61
  import { watchSourceFiles } from "../helpers/watch_source_files.js";
63
62
  import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
64
63
  import { createKitchen } from "../kitchen/kitchen.js";
64
+ import { createPackageDirectory } from "../kitchen/package_directory.js";
65
65
  import { GRAPH_VISITOR } from "../kitchen/url_graph/url_graph_visitor.js";
66
66
  import { jsenvPluginDirectoryReferenceEffect } from "../plugins/directory_reference_effect/jsenv_plugin_directory_reference_effect.js";
67
67
  import { jsenvPluginInlining } from "../plugins/inlining/jsenv_plugin_inlining.js";
@@ -562,20 +562,19 @@ export const build = async ({
562
562
  return compareFileUrls(a.sourceUrl, b.sourceUrl);
563
563
  });
564
564
 
565
- const lookupPackageDirectoryUrl = createLookupPackageDirectory();
565
+ const lookupPackageDirectory = createLookupPackageDirectory();
566
+ const packageDirectory = createPackageDirectory({
567
+ sourceDirectoryUrl,
568
+ lookupPackageDirectory,
569
+ });
566
570
  const packageDirectoryCache = new Map();
567
- const readPackageDirectory = (url) => {
571
+ packageDirectory.read = (url) => {
568
572
  const fromCache = packageDirectoryCache.get(url);
569
573
  if (fromCache !== undefined) {
570
574
  return fromCache;
571
575
  }
572
576
  return readPackageAtOrNull(url);
573
577
  };
574
- const packageDirectory = {
575
- url: lookupPackageDirectory(sourceDirectoryUrl),
576
- find: lookupPackageDirectoryUrl,
577
- read: readPackageDirectory,
578
- };
579
578
 
580
579
  if (outDirectoryUrl === undefined) {
581
580
  if (
@@ -772,7 +771,7 @@ export const build = async ({
772
771
  sideEffects: sideEffectRelativeUrlArray,
773
772
  });
774
773
  };
775
- const sideEffects = readPackageDirectory(
774
+ const sideEffects = packageDirectory.read(
776
775
  packageDirectory.url,
777
776
  )?.sideEffects;
778
777
  if (sideEffects === false) {
@@ -2,7 +2,6 @@ import {
2
2
  assertAndNormalizeDirectoryUrl,
3
3
  bufferToEtag,
4
4
  lookupPackageDirectory,
5
- readPackageAtOrNull,
6
5
  } from "@jsenv/filesystem";
7
6
  import { createLogger, createTaskLog, formatError } from "@jsenv/humanize";
8
7
  import {
@@ -23,6 +22,7 @@ import { watchSourceFiles } from "../helpers/watch_source_files.js";
23
22
  import { WEB_URL_CONVERTER } from "../helpers/web_url_converter.js";
24
23
  import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
25
24
  import { createKitchen } from "../kitchen/kitchen.js";
25
+ import { createPackageDirectory } from "../kitchen/package_directory.js";
26
26
  import {
27
27
  createPluginController,
28
28
  createPluginStore,
@@ -97,9 +97,11 @@ export const startDevServer = async ({
97
97
  transpilation,
98
98
  cacheControl = true,
99
99
  ribbon = true,
100
+ dropToOpen = true,
100
101
  // toolbar = false,
101
102
  onKitchenCreated = () => {},
102
103
  spa,
104
+ packageBundle,
103
105
 
104
106
  sourcemaps = "inline",
105
107
  sourcemapsSourcesContent,
@@ -245,11 +247,9 @@ export const startDevServer = async ({
245
247
  );
246
248
  serverStopCallbackSet.add(stopWatchingSourceFiles);
247
249
 
248
- const packageDirectory = {
249
- url: lookupPackageDirectory(sourceDirectoryUrl),
250
- find: lookupPackageDirectory,
251
- read: readPackageAtOrNull,
252
- };
250
+ const packageDirectory = createPackageDirectory({
251
+ sourceDirectoryUrl,
252
+ });
253
253
 
254
254
  const devServerPluginStore = await createPluginStore([
255
255
  jsenvPluginServerEvents({ clientAutoreload }),
@@ -272,11 +272,13 @@ export const startDevServer = async ({
272
272
  injections,
273
273
  transpilation,
274
274
  spa,
275
+ packageBundle,
275
276
 
276
277
  clientAutoreload,
277
278
  clientAutoreloadOnServerRestart,
278
279
  cacheControl,
279
280
  ribbon,
281
+ dropToOpen,
280
282
  }),
281
283
  ]);
282
284
  const getOrCreateKitchen = async (request) => {
@@ -331,66 +333,74 @@ export const startDevServer = async ({
331
333
  urlInfoCreated.isWatched = watch;
332
334
  // when an url depends on many others, we check all these (like package.json)
333
335
  urlInfoCreated.isValid = () => {
334
- if (!urlInfoCreated.url.startsWith("file:")) {
335
- return false;
336
- }
337
- if (urlInfoCreated.content === undefined) {
338
- // urlInfo content is undefined when:
339
- // - url info content never fetched
340
- // - it is considered as modified because undelying file is watched and got saved
341
- // - it is considered as modified because underlying file content
342
- // was compared using etag and it has changed
343
- return false;
344
- }
345
- if (!watch) {
346
- // file is not watched, check the filesystem
347
- let fileContentAsBuffer;
348
- try {
349
- fileContentAsBuffer = readFileSync(new URL(urlInfoCreated.url));
350
- } catch (e) {
351
- if (e.code === "ENOENT") {
352
- urlInfoCreated.onModified();
353
- return false;
354
- }
355
- return false;
336
+ const seenSet = new Set();
337
+ const checkValidity = (urlInfo) => {
338
+ if (seenSet.has(urlInfo)) {
339
+ return true;
356
340
  }
357
- const fileContentEtag = bufferToEtag(fileContentAsBuffer);
358
- if (fileContentEtag !== urlInfoCreated.originalContentEtag) {
359
- urlInfoCreated.onModified();
360
- // restore content to be able to compare it again later
361
- urlInfoCreated.kitchen.urlInfoTransformer.setContent(
362
- urlInfoCreated,
363
- String(fileContentAsBuffer),
364
- {
365
- contentEtag: fileContentEtag,
366
- },
367
- );
341
+ seenSet.add(urlInfo);
342
+ if (!urlInfo.url.startsWith("file:")) {
368
343
  return false;
369
344
  }
370
- }
371
- for (const implicitUrl of urlInfoCreated.implicitUrlSet) {
372
- const implicitUrlInfo =
373
- urlInfoCreated.graph.getUrlInfo(implicitUrl);
374
- if (!implicitUrlInfo) {
375
- continue;
345
+ if (urlInfo.content === undefined) {
346
+ // urlInfo content is undefined when:
347
+ // - url info content never fetched
348
+ // - it is considered as modified because undelying file is watched and got saved
349
+ // - it is considered as modified because underlying file content
350
+ // was compared using etag and it has changed
351
+ return false;
376
352
  }
377
- if (implicitUrlInfo.content === undefined) {
378
- // happens when we explicitely load an url with a search param
379
- // - it creates an implicit url info to the url without params
380
- // - we never explicitely request the url without search param so it has no content
381
- // in that case the underlying urlInfo cannot be invalidate by the implicit
382
- // we use modifiedTimestamp to detect if the url was loaded once
383
- // or is just here to be used later
384
- if (implicitUrlInfo.modifiedTimestamp) {
353
+ if (!urlInfo.isWatched) {
354
+ // file is not watched, check the filesystem
355
+ let fileContentAsBuffer;
356
+ try {
357
+ fileContentAsBuffer = readFileSync(new URL(urlInfo.url));
358
+ } catch (e) {
359
+ if (e.code === "ENOENT") {
360
+ urlInfo.onModified();
361
+ return false;
362
+ }
363
+ return false;
364
+ }
365
+ const fileContentEtag = bufferToEtag(fileContentAsBuffer);
366
+ if (fileContentEtag !== urlInfo.originalContentEtag) {
367
+ urlInfo.onModified();
368
+ // restore content to be able to compare it again later
369
+ urlInfo.kitchen.urlInfoTransformer.setContent(
370
+ urlInfo,
371
+ String(fileContentAsBuffer),
372
+ {
373
+ contentEtag: fileContentEtag,
374
+ },
375
+ );
385
376
  return false;
386
377
  }
387
- continue;
388
378
  }
389
- if (!implicitUrlInfo.isValid()) {
390
- return false;
379
+ for (const implicitUrl of urlInfo.implicitUrlSet) {
380
+ const implicitUrlInfo = urlInfo.graph.getUrlInfo(implicitUrl);
381
+ if (!implicitUrlInfo) {
382
+ continue;
383
+ }
384
+ if (implicitUrlInfo.content === undefined) {
385
+ // happens when we explicitely load an url with a search param
386
+ // - it creates an implicit url info to the url without params
387
+ // - we never explicitely request the url without search param so it has no content
388
+ // in that case the underlying urlInfo cannot be invalidate by the implicit
389
+ // we use modifiedTimestamp to detect if the url was loaded once
390
+ // or is just here to be used later
391
+ if (implicitUrlInfo.modifiedTimestamp) {
392
+ return false;
393
+ }
394
+ continue;
395
+ }
396
+ if (!checkValidity(implicitUrlInfo)) {
397
+ return false;
398
+ }
391
399
  }
392
- }
393
- return true;
400
+ return true;
401
+ };
402
+ const valid = checkValidity(urlInfoCreated);
403
+ return valid;
394
404
  };
395
405
  });
396
406
  kitchen.graph.urlInfoDereferencedEventEmitter.on(
@@ -338,6 +338,14 @@ const detailsFromPluginController = (pluginController) => {
338
338
 
339
339
  const detailsFromValueThrown = (valueThrownByPlugin) => {
340
340
  if (valueThrownByPlugin && valueThrownByPlugin instanceof Error) {
341
+ // if (
342
+ // valueThrownByPlugin.message.includes("Maximum call stack size exceeded")
343
+ // ) {
344
+ // return {
345
+ // "error message": valueThrownByPlugin.message,
346
+ // "error stack": valueThrownByPlugin.stack,
347
+ // };
348
+ // }
341
349
  if (
342
350
  valueThrownByPlugin.code === "PARSE_ERROR" ||
343
351
  valueThrownByPlugin.code === "MODULE_NOT_FOUND" ||
@@ -0,0 +1,22 @@
1
+ import {
2
+ lookupPackageDirectory as lookupPackageDirectoryDefault,
3
+ readPackageAtOrNull,
4
+ } from "@jsenv/filesystem";
5
+
6
+ export const createPackageDirectory = ({
7
+ sourceDirectoryUrl,
8
+ lookupPackageDirectory = lookupPackageDirectoryDefault,
9
+ }) => {
10
+ const packageDirectory = {
11
+ url: lookupPackageDirectory(sourceDirectoryUrl),
12
+ find: (url) => {
13
+ const urlString = typeof url === "string" ? url : url?.href;
14
+ if (!urlString.startsWith("file:")) {
15
+ return null;
16
+ }
17
+ return lookupPackageDirectory(url);
18
+ },
19
+ read: readPackageAtOrNull,
20
+ };
21
+ return packageDirectory;
22
+ };
@@ -324,26 +324,13 @@ const createUrlInfo = (url, context) => {
324
324
  }
325
325
  return false;
326
326
  };
327
- urlInfo.getWithoutSearchParam = (searchParam, { expectedType } = {}) => {
328
- // The search param can be
329
- // 1. injected by a plugin during "redirectReference"
330
- // - import assertions
331
- // - js module fallback to systemjs
332
- // 2. already inside source files
333
- // - turn js module into js classic for convenience ?as_js_classic
334
- // - turn js classic to js module for to make it importable
335
- if (!urlInfo.searchParams.has(searchParam)) {
336
- return null;
337
- }
327
+ const getNextUrlInfo = (newProps) => {
338
328
  const reference = urlInfo.firstReference;
339
- const newSpecifier = injectQueryParamsIntoSpecifier(reference.specifier, {
340
- [searchParam]: undefined,
341
- });
342
- const referenceWithoutSearchParam = reference.addImplicit({
329
+ const nextReference = reference.addImplicit({
343
330
  type: reference.type,
344
331
  subtype: reference.subtype,
345
332
  expectedContentType: reference.expectedContentType,
346
- expectedType: expectedType || reference.expectedType,
333
+ expectedType: reference.expectedType,
347
334
  expectedSubtype: reference.expectedSubtype,
348
335
  integrity: reference.integrity,
349
336
  crossorigin: reference.crossorigin,
@@ -367,7 +354,6 @@ const createUrlInfo = (url, context) => {
367
354
  astInfo: reference.astInfo,
368
355
  mutation: reference.mutation,
369
356
  data: { ...reference.data },
370
- specifier: newSpecifier,
371
357
  isWeak: true,
372
358
  isInline: reference.isInline,
373
359
  original: reference.original || reference,
@@ -377,9 +363,37 @@ const createUrlInfo = (url, context) => {
377
363
  // generatedUrl: null,
378
364
  // generatedSpecifier: null,
379
365
  // filename: null,
366
+ ...newProps,
367
+ });
368
+ reference.next = nextReference;
369
+ return nextReference.urlInfo;
370
+ };
371
+
372
+ urlInfo.redirect = (props) => {
373
+ return getNextUrlInfo(props);
374
+ };
375
+ urlInfo.getWithoutSearchParam = (searchParam, props) => {
376
+ // The search param can be
377
+ // 1. injected by a plugin during "redirectReference"
378
+ // - import assertions
379
+ // - js module fallback to systemjs
380
+ // 2. already inside source files
381
+ // - turn js module into js classic for convenience ?as_js_classic
382
+ // - turn js classic to js module for to make it importable
383
+ if (!urlInfo.searchParams.has(searchParam)) {
384
+ return null;
385
+ }
386
+ const reference = urlInfo.firstReference;
387
+ const specifierWithoutSearchParam = injectQueryParamsIntoSpecifier(
388
+ reference.specifier,
389
+ {
390
+ [searchParam]: undefined,
391
+ },
392
+ );
393
+ return urlInfo.redirect({
394
+ specifier: specifierWithoutSearchParam,
395
+ ...props,
380
396
  });
381
- reference.next = referenceWithoutSearchParam;
382
- return referenceWithoutSearchParam.urlInfo;
383
397
  };
384
398
  urlInfo.onRemoved = () => {
385
399
  urlInfo.kitchen.urlInfoTransformer.resetContent(urlInfo);
@@ -28,6 +28,7 @@ import { jsenvPluginCleanHTML } from "./clean_html/jsenv_plugin_clean_html.js";
28
28
  import { jsenvPluginChromeDevtoolsJson } from "./chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js";
29
29
  import { jsenvPluginAutoreloadOnServerRestart } from "./autoreload_on_server_restart/jsenv_plugin_autoreload_on_server_restart.js";
30
30
  import { jsenvPluginPackageSideEffects } from "./package_side_effects/jsenv_plugin_package_side_effects.js";
31
+ import { jsenvPluginWorkspaceBundle } from "./workspace_bundle/jsenv_plugin_workspace_bundle.js";
31
32
 
32
33
  export const getCorePlugins = ({
33
34
  rootDirectoryUrl,
@@ -50,12 +51,14 @@ export const getCorePlugins = ({
50
51
  inlining = true,
51
52
  http = false,
52
53
  spa,
54
+ packageBundle,
53
55
 
54
56
  clientAutoreload,
55
57
  clientAutoreloadOnServerRestart,
56
58
  cacheControl,
57
59
  scenarioPlaceholders = true,
58
60
  ribbon = true,
61
+ dropToOpen = true,
59
62
  packageSideEffects = false,
60
63
  } = {}) => {
61
64
  if (cacheControl === true) {
@@ -78,6 +81,9 @@ export const getCorePlugins = ({
78
81
  }
79
82
 
80
83
  return [
84
+ ...(packageBundle
85
+ ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
86
+ : []),
81
87
  jsenvPluginReferenceAnalysis(referenceAnalysis),
82
88
  jsenvPluginInjections(injections),
83
89
  jsenvPluginTranspilation(transpilation),
@@ -116,11 +122,12 @@ export const getCorePlugins = ({
116
122
  },
117
123
  ...(nodeEsmResolution
118
124
  ? [
119
- jsenvPluginNodeEsmResolution(
120
- nodeEsmResolution,
125
+ jsenvPluginNodeEsmResolution({
126
+ packageDirectory,
127
+ resolutionConfig: nodeEsmResolution,
121
128
  packageConditions,
122
129
  packageConditionsConfig,
123
- ),
130
+ }),
124
131
  ]
125
132
  : []),
126
133
  jsenvPluginWebResolution(),
@@ -147,7 +154,7 @@ export const getCorePlugins = ({
147
154
  : []),
148
155
  ...(cacheControl ? [jsenvPluginCacheControl(cacheControl)] : []),
149
156
  ...(ribbon ? [jsenvPluginRibbon({ rootDirectoryUrl, ...ribbon })] : []),
150
- jsenvPluginDropToOpen(),
157
+ ...(dropToOpen ? [jsenvPluginDropToOpen()] : []),
151
158
  jsenvPluginCleanHTML(),
152
159
  jsenvPluginChromeDevtoolsJson(),
153
160
  ...(packageSideEffects
@@ -1,10 +1,11 @@
1
1
  import { createNodeEsmResolver } from "./node_esm_resolver.js";
2
2
 
3
- export const jsenvPluginNodeEsmResolution = (
3
+ export const jsenvPluginNodeEsmResolution = ({
4
+ packageDirectory,
4
5
  resolutionConfig = {},
5
6
  packageConditions,
6
7
  packageConditionsConfig = {},
7
- ) => {
8
+ }) => {
8
9
  let nodeEsmResolverDefault;
9
10
  const resolverMap = new Map();
10
11
  let anyTypeResolver;
@@ -22,6 +23,7 @@ export const jsenvPluginNodeEsmResolution = (
22
23
  );
23
24
  }
24
25
  return createNodeEsmResolver({
26
+ packageDirectory,
25
27
  runtimeCompat: kitchenContext.runtimeCompat,
26
28
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
27
29
  packageConditions,
@@ -38,9 +40,10 @@ export const jsenvPluginNodeEsmResolution = (
38
40
  appliesDuring: "*",
39
41
  init: (kitchenContext) => {
40
42
  nodeEsmResolverDefault = createNodeEsmResolver({
43
+ packageDirectory,
41
44
  runtimeCompat: kitchenContext.runtimeCompat,
42
45
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
43
- preservesSymlink: true,
46
+ // preservesSymlink: true,
44
47
  packageConditions,
45
48
  packageConditionsConfig: {
46
49
  ...kitchenContext.packageConditionsConfig,
@@ -9,8 +9,6 @@
9
9
 
10
10
  import {
11
11
  applyNodeEsmResolution,
12
- defaultLookupPackageScope,
13
- defaultReadPackageJson,
14
12
  readCustomConditionsFromProcessArgs,
15
13
  } from "@jsenv/node-esm-resolution";
16
14
  import { URL_META } from "@jsenv/url-meta";
@@ -18,18 +16,27 @@ import { urlToBasename, urlToExtension } from "@jsenv/urls";
18
16
  import { readFileSync } from "node:fs";
19
17
 
20
18
  export const createNodeEsmResolver = ({
19
+ packageDirectory,
21
20
  runtimeCompat,
22
21
  rootDirectoryUrl,
23
22
  packageConditions = {},
24
23
  packageConditionsConfig,
25
24
  preservesSymlink,
26
25
  }) => {
26
+ const applyNodeEsmResolutionMemo = (params) =>
27
+ applyNodeEsmResolution({
28
+ lookupPackageScope: packageDirectory.find,
29
+ readPackageJson: packageDirectory.read,
30
+ preservesSymlink,
31
+ ...params,
32
+ });
27
33
  const buildPackageConditions = createBuildPackageConditions(
28
34
  packageConditions,
29
35
  {
30
36
  packageConditionsConfig,
31
37
  rootDirectoryUrl,
32
38
  runtimeCompat,
39
+ preservesSymlink,
33
40
  },
34
41
  );
35
42
 
@@ -61,7 +68,7 @@ export const createNodeEsmResolver = ({
61
68
  reference.type === "sourcemap_comment";
62
69
 
63
70
  const resolveNodeEsmFallbackOnWeb = createResolverWithFallbackOnError(
64
- applyNodeEsmResolution,
71
+ applyNodeEsmResolutionMemo,
65
72
  ({ specifier, parentUrl }) => {
66
73
  const url = new URL(specifier, parentUrl).href;
67
74
  return { url };
@@ -70,7 +77,7 @@ export const createNodeEsmResolver = ({
70
77
  const DELEGATE_TO_WEB_RESOLUTION_PLUGIN = {};
71
78
  const resolveNodeEsmFallbackNullToDelegateToWebPlugin =
72
79
  createResolverWithFallbackOnError(
73
- applyNodeEsmResolution,
80
+ applyNodeEsmResolutionMemo,
74
81
  () => DELEGATE_TO_WEB_RESOLUTION_PLUGIN,
75
82
  );
76
83
 
@@ -78,11 +85,11 @@ export const createNodeEsmResolver = ({
78
85
  webResolutionFallback,
79
86
  resolver: webResolutionFallback
80
87
  ? resolveNodeEsmFallbackOnWeb
81
- : applyNodeEsmResolution,
88
+ : applyNodeEsmResolutionMemo,
82
89
  });
83
90
  const resolver = webResolutionFallback
84
91
  ? resolveNodeEsmFallbackNullToDelegateToWebPlugin
85
- : applyNodeEsmResolution;
92
+ : applyNodeEsmResolutionMemo;
86
93
 
87
94
  const result = resolver({
88
95
  conditions,
@@ -113,52 +120,79 @@ export const createNodeEsmResolver = ({
113
120
  if (ownerUrlInfo.context.build) {
114
121
  return url;
115
122
  }
116
- const dependsOnPackageJson =
117
- type !== "relative_specifier" &&
118
- type !== "absolute_specifier" &&
119
- type !== "node_builtin_specifier";
120
- if (dependsOnPackageJson) {
121
- // this reference depends on package.json and node_modules
122
- // to be resolved. Each file using this specifier
123
- // must be invalidated when corresponding package.json changes
124
- addRelationshipWithPackageJson({
125
- reference,
126
- packageJsonUrl: `${packageDirectoryUrl}package.json`,
127
- field: type.startsWith("field:")
128
- ? `#${type.slice("field:".length)}`
129
- : "",
130
- });
131
- }
132
- // without this check a file inside a project without package.json
133
- // could be considered as a node module if there is a ancestor package.json
134
- // but we want to version only node modules
135
- if (url.includes("/node_modules/")) {
136
- const packageDirectoryUrl = defaultLookupPackageScope(url);
137
- if (
138
- packageDirectoryUrl &&
139
- packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
140
- ) {
141
- const packageVersion =
142
- defaultReadPackageJson(packageDirectoryUrl).version;
143
- // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
144
- if (packageVersion) {
123
+
124
+ package_relationships: {
125
+ if (!url.startsWith("file:")) {
126
+ // data:, javascript:void(0), etc...
127
+ break package_relationships;
128
+ }
129
+
130
+ // packageDirectoryUrl can be already known thanks to node resolution
131
+ // otherwise we look for it
132
+ const closestPackageDirectoryUrl =
133
+ packageDirectoryUrl || packageDirectory.find(url);
134
+ if (!closestPackageDirectoryUrl) {
135
+ // happens for projects without package.json or some files outside of package scope
136
+ // (generated files like sourcemaps or cache files for example)
137
+ break package_relationships;
138
+ }
139
+
140
+ resolution_relationship: {
141
+ const dependsOnPackageJson = Boolean(packageDirectoryUrl);
142
+ if (dependsOnPackageJson) {
143
+ // this reference depends on package.json and node_modules
144
+ // to be resolved. Each file using this specifier
145
+ // must be invalidated when corresponding package.json changes
145
146
  addRelationshipWithPackageJson({
146
147
  reference,
147
148
  packageJsonUrl: `${packageDirectoryUrl}package.json`,
148
- field: "version",
149
- hasVersioningEffect: true,
149
+ field: type.startsWith("field:")
150
+ ? `#${type.slice("field:".length)}`
151
+ : "",
150
152
  });
151
153
  }
152
- reference.version = packageVersion;
154
+ }
155
+ version_relationship: {
156
+ const packageVersion = packageDirectory.read(
157
+ closestPackageDirectoryUrl,
158
+ ).version;
159
+ if (!packageVersion) {
160
+ // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
161
+ break version_relationship;
162
+ }
163
+ // We want the versioning effect
164
+ // which would put the file in browser cache for 1 year based on that version
165
+ // only for files we don't control and touch ourselves (node modules)
166
+ // which would never change until their version change
167
+ // (minus the case you update them yourselves in your node modules without updating the package version)
168
+ // (in that case you would have to clear browser cache to use the modified version of the node module files)
169
+ const hasVersioningEffect =
170
+ closestPackageDirectoryUrl !== packageDirectory.url &&
171
+ url.includes("/node_modules/");
172
+ addRelationshipWithPackageJson({
173
+ reference,
174
+ packageJsonUrl: `${closestPackageDirectoryUrl}package.json`,
175
+ field: "version",
176
+ hasVersioningEffect,
177
+ });
178
+ if (hasVersioningEffect) {
179
+ reference.version = packageVersion;
180
+ }
153
181
  }
154
182
  }
183
+
155
184
  return url;
156
185
  };
157
186
  };
158
187
 
159
188
  const createBuildPackageConditions = (
160
189
  packageConditions,
161
- { packageConditionsConfig, rootDirectoryUrl, runtimeCompat },
190
+ {
191
+ packageConditionsConfig,
192
+ rootDirectoryUrl,
193
+ runtimeCompat,
194
+ preservesSymlink,
195
+ },
162
196
  ) => {
163
197
  let resolveConditionsFromSpecifier = () => null;
164
198
  let resolveConditionsFromContext = () => [];
@@ -185,6 +219,7 @@ const createBuildPackageConditions = (
185
219
  const { packageDirectoryUrl } = applyNodeEsmResolution({
186
220
  specifier: key.slice(0, -1), // avoid package path not exported
187
221
  parentUrl: rootDirectoryUrl,
222
+ preservesSymlink,
188
223
  });
189
224
  const url = packageDirectoryUrl;
190
225
  associationsRaw[url] = associatedValue;
@@ -193,6 +228,7 @@ const createBuildPackageConditions = (
193
228
  const { url } = applyNodeEsmResolution({
194
229
  specifier: key,
195
230
  parentUrl: rootDirectoryUrl,
231
+ preservesSymlink,
196
232
  });
197
233
  associationsRaw[url] = associatedValue;
198
234
  } catch {