@jsenv/core 41.4.6 → 41.4.7

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.
@@ -6551,7 +6551,13 @@ const jsenvPluginWebResolution = () => {
6551
6551
  if (ownerUrlInfo.originalUrl?.startsWith("http")) {
6552
6552
  return new URL(resource, ownerUrlInfo.originalUrl);
6553
6553
  }
6554
- const url = new URL(resource.slice(1), ownerUrlInfo.entryUrlInfo.url);
6554
+ // "/x" is the web meaning of a root-relative url: the root of what is
6555
+ // served, which is the source directory — not the directory the entry
6556
+ // point happens to sit in.
6557
+ const url = new URL(
6558
+ resource.slice(1),
6559
+ ownerUrlInfo.context.rootDirectoryUrl,
6560
+ );
6555
6561
  return url;
6556
6562
  }
6557
6563
  // baseUrl happens second argument to new URL() is different from
@@ -10364,6 +10370,7 @@ const createBuildSpecifierManager = ({
10364
10370
  buildDirectoryUrl,
10365
10371
  assetsDirectory,
10366
10372
  buildUrlsGenerator,
10373
+ entryKey,
10367
10374
  base,
10368
10375
  length = 8,
10369
10376
 
@@ -10402,6 +10409,7 @@ const createBuildSpecifierManager = ({
10402
10409
  urlInfo,
10403
10410
  ownerUrlInfo: reference.ownerUrlInfo,
10404
10411
  assetsDirectory,
10412
+ entryKey,
10405
10413
  });
10406
10414
  }
10407
10415
 
@@ -11459,9 +11467,14 @@ const createBuildSpecifierManager = ({
11459
11467
  let contentKey;
11460
11468
  // if to guard for html where versioned build specifier is not generated
11461
11469
  if (buildSpecifierVersioned) {
11462
- const buildUrlVersioned = asBuildUrlVersioned({
11463
- buildSpecifierVersioned,
11464
- buildDirectoryUrl,
11470
+ // The version goes on the build url, not on the specifier: a
11471
+ // specifier is written for one importer to read (base: "./" makes
11472
+ // it relative to that importer, base: "https://cdn…" makes it
11473
+ // absolute elsewhere) and says nothing about where the file lands.
11474
+ const buildUrlVersioned = injectVersionIntoBuildSpecifier({
11475
+ buildSpecifier: buildUrl,
11476
+ version: versionMap.get(urlInfo),
11477
+ versioningMethod,
11465
11478
  });
11466
11479
  const buildRelativeUrlVersioned = urlToRelativeUrl(
11467
11480
  buildUrlVersioned,
@@ -11705,23 +11718,6 @@ const injectVersionIntoBuildSpecifier = ({
11705
11718
  );
11706
11719
  };
11707
11720
 
11708
- const asBuildUrlVersioned = ({
11709
- buildSpecifierVersioned,
11710
- buildDirectoryUrl,
11711
- }) => {
11712
- if (buildSpecifierVersioned[0] === "/") {
11713
- return new URL(buildSpecifierVersioned.slice(1), buildDirectoryUrl).href;
11714
- }
11715
- const buildUrl = new URL(buildSpecifierVersioned, buildDirectoryUrl).href;
11716
- if (buildUrl.startsWith(buildDirectoryUrl)) {
11717
- return buildUrl;
11718
- }
11719
- // it's likely "base" parameter was set to an url origin like "https://cdn.example.com"
11720
- // let's move url to build directory
11721
- const { pathname, search, hash } = new URL(buildSpecifierVersioned);
11722
- return `${buildDirectoryUrl}${pathname}${search}${hash}`;
11723
- };
11724
-
11725
11721
  const targetsAFileThatDoesNotExist = (url) => {
11726
11722
  if (!url.startsWith("file:")) {
11727
11723
  return false;
@@ -11759,12 +11755,41 @@ const createBuildUrlsGenerator = ({
11759
11755
  };
11760
11756
 
11761
11757
  const nameSetPerDirectoryMap = new Map();
11762
- const generate = (url, { urlInfo, ownerUrlInfo, assetsDirectory }) => {
11763
- const buildUrlFromMap = buildUrlMap.get(url);
11758
+ const reserveName = (directoryPath, urlName) => {
11759
+ let nameSet = nameSetPerDirectoryMap.get(directoryPath);
11760
+ if (!nameSet) {
11761
+ nameSet = new Set();
11762
+ nameSetPerDirectoryMap.set(directoryPath, nameSet);
11763
+ }
11764
+ let [basename, extension] = splitFileExtension(urlName);
11765
+ extension = extensionMappings[extension] || extension;
11766
+ let nameCandidate = `${basename}${extension}`; // reconstruct name in case extension was normalized
11767
+ let integer = 1;
11768
+ while (nameSet.has(nameCandidate)) {
11769
+ integer++;
11770
+ nameCandidate = `${basename}${integer}${extension}`;
11771
+ }
11772
+ nameSet.add(nameCandidate);
11773
+ return nameCandidate;
11774
+ };
11775
+ const generate = (
11776
+ url,
11777
+ { urlInfo, ownerUrlInfo, assetsDirectory, entryKey },
11778
+ ) => {
11779
+ // A url already inside the build directory names a chunk a bundler just
11780
+ // produced, and each entry point is bundled on its own: two of them
11781
+ // routinely produce a chunk of the same name holding different code (the
11782
+ // shared dependencies of THAT entry point, exported under names decided by
11783
+ // THAT bundle). They are told apart by the entry point they come from —
11784
+ // sharing the file would leave one entry importing exports the file on disk
11785
+ // does not have.
11786
+ const insideBuildDirectory = urlIsOrIsInsideOf(url, buildDirectoryUrl);
11787
+ const key = insideBuildDirectory ? `${entryKey} ${url}` : url;
11788
+ const buildUrlFromMap = buildUrlMap.get(key);
11764
11789
  if (buildUrlFromMap) {
11765
11790
  return buildUrlFromMap;
11766
11791
  }
11767
- if (urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
11792
+ if (insideBuildDirectory) {
11768
11793
  if (ownerUrlInfo.searchParams.has("dynamic_import_id")) {
11769
11794
  const ownerDirectoryPath = determineDirectoryPath({
11770
11795
  sourceDirectoryUrl,
@@ -11776,11 +11801,18 @@ const createBuildUrlsGenerator = ({
11776
11801
  buildUrl = injectQueryParams(buildUrl, {
11777
11802
  dynamic_import_id: undefined,
11778
11803
  });
11779
- associateBuildUrl(url, buildUrl);
11804
+ associateBuildUrl(key, buildUrl);
11780
11805
  return buildUrl;
11781
11806
  }
11782
- associateBuildUrl(url, url);
11783
- return url;
11807
+ const urlObject = new URL(url);
11808
+ const chunkDirectoryPath = urlToRelativeUrl(
11809
+ new URL("./", urlObject),
11810
+ buildDirectoryUrl,
11811
+ );
11812
+ const chunkName = reserveName(chunkDirectoryPath, urlToFilename(url));
11813
+ const buildUrl = `${buildDirectoryUrl}${chunkDirectoryPath}${chunkName}${urlObject.search}${urlObject.hash}`;
11814
+ associateBuildUrl(key, buildUrl);
11815
+ return buildUrl;
11784
11816
  }
11785
11817
  if (urlInfo.type === "entry_build") {
11786
11818
  const buildUrl = new URL(urlInfo.filenameHint, buildDirectoryUrl).href;
@@ -11812,25 +11844,10 @@ const createBuildUrlsGenerator = ({
11812
11844
  urlInfo,
11813
11845
  ownerUrlInfo,
11814
11846
  });
11815
- let nameSet = nameSetPerDirectoryMap.get(directoryPath);
11816
- if (!nameSet) {
11817
- nameSet = new Set();
11818
- nameSetPerDirectoryMap.set(directoryPath, nameSet);
11819
- }
11820
11847
  const urlObject = new URL(url);
11821
11848
  injectQueryParams(urlObject, { dynamic_import_id: undefined });
11822
11849
  let { search, hash } = urlObject;
11823
- let urlName = getUrlName(url, urlInfo);
11824
- let [basename, extension] = splitFileExtension(urlName);
11825
- extension = extensionMappings[extension] || extension;
11826
- let nameCandidate = `${basename}${extension}`; // reconstruct name in case extension was normalized
11827
- let integer = 1;
11828
- while (nameSet.has(nameCandidate)) {
11829
- integer++;
11830
- nameCandidate = `${basename}${integer}${extension}`;
11831
- }
11832
- const name = nameCandidate;
11833
- nameSet.add(name);
11850
+ const name = reserveName(directoryPath, getUrlName(url, urlInfo));
11834
11851
  const buildUrl = `${buildDirectoryUrl}${directoryPath}${name}${search}${hash}`;
11835
11852
  associateBuildUrl(url, buildUrl);
11836
11853
  return buildUrl;
@@ -13217,6 +13234,7 @@ const prepareEntryPointBuild = async (
13217
13234
  base,
13218
13235
  assetsDirectory,
13219
13236
  buildUrlsGenerator,
13237
+ entryKey: sourceRelativeUrl,
13220
13238
 
13221
13239
  versioning,
13222
13240
  versioningMethod,
@@ -3228,7 +3228,13 @@ const jsenvPluginWebResolution = () => {
3228
3228
  if (ownerUrlInfo.originalUrl?.startsWith("http")) {
3229
3229
  return new URL(resource, ownerUrlInfo.originalUrl);
3230
3230
  }
3231
- const url = new URL(resource.slice(1), ownerUrlInfo.entryUrlInfo.url);
3231
+ // "/x" is the web meaning of a root-relative url: the root of what is
3232
+ // served, which is the source directory — not the directory the entry
3233
+ // point happens to sit in.
3234
+ const url = new URL(
3235
+ resource.slice(1),
3236
+ ownerUrlInfo.context.rootDirectoryUrl,
3237
+ );
3232
3238
  return url;
3233
3239
  }
3234
3240
  // baseUrl happens second argument to new URL() is different from
@@ -11154,7 +11160,16 @@ const devServerPluginServeSourceFiles = ({
11154
11160
  }
11155
11161
  }
11156
11162
  });
11157
- const clientRuntimeCompat = { [runtimeName]: runtimeVersion };
11163
+ // A client we cannot identify (curl, fetch, a healthcheck, a proxy dropping
11164
+ // the user agent) is assumed to be one of the runtimes the project targets.
11165
+ // Treating it as a runtime supporting nothing would serve it the js module
11166
+ // fallback, breaking tooling perfectly capable of ES modules; an actual
11167
+ // ancient browser hiding its identity fails loudly instead, which is the
11168
+ // cheaper of the two mistakes.
11169
+ const clientRuntimeCompat =
11170
+ runtimeName === "unknown"
11171
+ ? runtimeCompat
11172
+ : { [runtimeName]: runtimeVersion };
11158
11173
 
11159
11174
  kitchen = createKitchen({
11160
11175
  name: runtimeId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "41.4.6",
3
+ "version": "41.4.7",
4
4
  "type": "module",
5
5
  "description": "Tool to develop, test and build js projects",
6
6
  "repository": {
@@ -1249,6 +1249,7 @@ const prepareEntryPointBuild = async (
1249
1249
  base,
1250
1250
  assetsDirectory,
1251
1251
  buildUrlsGenerator,
1252
+ entryKey: sourceRelativeUrl,
1252
1253
 
1253
1254
  versioning,
1254
1255
  versioningMethod,
@@ -39,6 +39,7 @@ export const createBuildSpecifierManager = ({
39
39
  buildDirectoryUrl,
40
40
  assetsDirectory,
41
41
  buildUrlsGenerator,
42
+ entryKey,
42
43
  base,
43
44
  length = 8,
44
45
 
@@ -77,6 +78,7 @@ export const createBuildSpecifierManager = ({
77
78
  urlInfo,
78
79
  ownerUrlInfo: reference.ownerUrlInfo,
79
80
  assetsDirectory,
81
+ entryKey,
80
82
  });
81
83
  }
82
84
 
@@ -1134,9 +1136,14 @@ export const createBuildSpecifierManager = ({
1134
1136
  let contentKey;
1135
1137
  // if to guard for html where versioned build specifier is not generated
1136
1138
  if (buildSpecifierVersioned) {
1137
- const buildUrlVersioned = asBuildUrlVersioned({
1138
- buildSpecifierVersioned,
1139
- buildDirectoryUrl,
1139
+ // The version goes on the build url, not on the specifier: a
1140
+ // specifier is written for one importer to read (base: "./" makes
1141
+ // it relative to that importer, base: "https://cdn…" makes it
1142
+ // absolute elsewhere) and says nothing about where the file lands.
1143
+ const buildUrlVersioned = injectVersionIntoBuildSpecifier({
1144
+ buildSpecifier: buildUrl,
1145
+ version: versionMap.get(urlInfo),
1146
+ versioningMethod,
1140
1147
  });
1141
1148
  const buildRelativeUrlVersioned = urlToRelativeUrl(
1142
1149
  buildUrlVersioned,
@@ -1380,23 +1387,6 @@ const injectVersionIntoBuildSpecifier = ({
1380
1387
  );
1381
1388
  };
1382
1389
 
1383
- const asBuildUrlVersioned = ({
1384
- buildSpecifierVersioned,
1385
- buildDirectoryUrl,
1386
- }) => {
1387
- if (buildSpecifierVersioned[0] === "/") {
1388
- return new URL(buildSpecifierVersioned.slice(1), buildDirectoryUrl).href;
1389
- }
1390
- const buildUrl = new URL(buildSpecifierVersioned, buildDirectoryUrl).href;
1391
- if (buildUrl.startsWith(buildDirectoryUrl)) {
1392
- return buildUrl;
1393
- }
1394
- // it's likely "base" parameter was set to an url origin like "https://cdn.example.com"
1395
- // let's move url to build directory
1396
- const { pathname, search, hash } = new URL(buildSpecifierVersioned);
1397
- return `${buildDirectoryUrl}${pathname}${search}${hash}`;
1398
- };
1399
-
1400
1390
  const targetsAFileThatDoesNotExist = (url) => {
1401
1391
  if (!url.startsWith("file:")) {
1402
1392
  return false;
@@ -31,12 +31,41 @@ export const createBuildUrlsGenerator = ({
31
31
  };
32
32
 
33
33
  const nameSetPerDirectoryMap = new Map();
34
- const generate = (url, { urlInfo, ownerUrlInfo, assetsDirectory }) => {
35
- const buildUrlFromMap = buildUrlMap.get(url);
34
+ const reserveName = (directoryPath, urlName) => {
35
+ let nameSet = nameSetPerDirectoryMap.get(directoryPath);
36
+ if (!nameSet) {
37
+ nameSet = new Set();
38
+ nameSetPerDirectoryMap.set(directoryPath, nameSet);
39
+ }
40
+ let [basename, extension] = splitFileExtension(urlName);
41
+ extension = extensionMappings[extension] || extension;
42
+ let nameCandidate = `${basename}${extension}`; // reconstruct name in case extension was normalized
43
+ let integer = 1;
44
+ while (nameSet.has(nameCandidate)) {
45
+ integer++;
46
+ nameCandidate = `${basename}${integer}${extension}`;
47
+ }
48
+ nameSet.add(nameCandidate);
49
+ return nameCandidate;
50
+ };
51
+ const generate = (
52
+ url,
53
+ { urlInfo, ownerUrlInfo, assetsDirectory, entryKey },
54
+ ) => {
55
+ // A url already inside the build directory names a chunk a bundler just
56
+ // produced, and each entry point is bundled on its own: two of them
57
+ // routinely produce a chunk of the same name holding different code (the
58
+ // shared dependencies of THAT entry point, exported under names decided by
59
+ // THAT bundle). They are told apart by the entry point they come from —
60
+ // sharing the file would leave one entry importing exports the file on disk
61
+ // does not have.
62
+ const insideBuildDirectory = urlIsOrIsInsideOf(url, buildDirectoryUrl);
63
+ const key = insideBuildDirectory ? `${entryKey} ${url}` : url;
64
+ const buildUrlFromMap = buildUrlMap.get(key);
36
65
  if (buildUrlFromMap) {
37
66
  return buildUrlFromMap;
38
67
  }
39
- if (urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
68
+ if (insideBuildDirectory) {
40
69
  if (ownerUrlInfo.searchParams.has("dynamic_import_id")) {
41
70
  const ownerDirectoryPath = determineDirectoryPath({
42
71
  sourceDirectoryUrl,
@@ -48,11 +77,18 @@ export const createBuildUrlsGenerator = ({
48
77
  buildUrl = injectQueryParams(buildUrl, {
49
78
  dynamic_import_id: undefined,
50
79
  });
51
- associateBuildUrl(url, buildUrl);
80
+ associateBuildUrl(key, buildUrl);
52
81
  return buildUrl;
53
82
  }
54
- associateBuildUrl(url, url);
55
- return url;
83
+ const urlObject = new URL(url);
84
+ const chunkDirectoryPath = urlToRelativeUrl(
85
+ new URL("./", urlObject),
86
+ buildDirectoryUrl,
87
+ );
88
+ const chunkName = reserveName(chunkDirectoryPath, urlToFilename(url));
89
+ const buildUrl = `${buildDirectoryUrl}${chunkDirectoryPath}${chunkName}${urlObject.search}${urlObject.hash}`;
90
+ associateBuildUrl(key, buildUrl);
91
+ return buildUrl;
56
92
  }
57
93
  if (urlInfo.type === "entry_build") {
58
94
  const buildUrl = new URL(urlInfo.filenameHint, buildDirectoryUrl).href;
@@ -84,25 +120,10 @@ export const createBuildUrlsGenerator = ({
84
120
  urlInfo,
85
121
  ownerUrlInfo,
86
122
  });
87
- let nameSet = nameSetPerDirectoryMap.get(directoryPath);
88
- if (!nameSet) {
89
- nameSet = new Set();
90
- nameSetPerDirectoryMap.set(directoryPath, nameSet);
91
- }
92
123
  const urlObject = new URL(url);
93
124
  injectQueryParams(urlObject, { dynamic_import_id: undefined });
94
125
  let { search, hash } = urlObject;
95
- let urlName = getUrlName(url, urlInfo);
96
- let [basename, extension] = splitFileExtension(urlName);
97
- extension = extensionMappings[extension] || extension;
98
- let nameCandidate = `${basename}${extension}`; // reconstruct name in case extension was normalized
99
- let integer = 1;
100
- while (nameSet.has(nameCandidate)) {
101
- integer++;
102
- nameCandidate = `${basename}${integer}${extension}`;
103
- }
104
- const name = nameCandidate;
105
- nameSet.add(name);
126
+ const name = reserveName(directoryPath, getUrlName(url, urlInfo));
106
127
  const buildUrl = `${buildDirectoryUrl}${directoryPath}${name}${search}${hash}`;
107
128
  associateBuildUrl(url, buildUrl);
108
129
  return buildUrl;
@@ -71,7 +71,16 @@ export const devServerPluginServeSourceFiles = ({
71
71
  }
72
72
  }
73
73
  });
74
- const clientRuntimeCompat = { [runtimeName]: runtimeVersion };
74
+ // A client we cannot identify (curl, fetch, a healthcheck, a proxy dropping
75
+ // the user agent) is assumed to be one of the runtimes the project targets.
76
+ // Treating it as a runtime supporting nothing would serve it the js module
77
+ // fallback, breaking tooling perfectly capable of ES modules; an actual
78
+ // ancient browser hiding its identity fails loudly instead, which is the
79
+ // cheaper of the two mistakes.
80
+ const clientRuntimeCompat =
81
+ runtimeName === "unknown"
82
+ ? runtimeCompat
83
+ : { [runtimeName]: runtimeVersion };
75
84
 
76
85
  kitchen = createKitchen({
77
86
  name: runtimeId,
@@ -9,7 +9,13 @@ export const jsenvPluginWebResolution = () => {
9
9
  if (ownerUrlInfo.originalUrl?.startsWith("http")) {
10
10
  return new URL(resource, ownerUrlInfo.originalUrl);
11
11
  }
12
- const url = new URL(resource.slice(1), ownerUrlInfo.entryUrlInfo.url);
12
+ // "/x" is the web meaning of a root-relative url: the root of what is
13
+ // served, which is the source directory — not the directory the entry
14
+ // point happens to sit in.
15
+ const url = new URL(
16
+ resource.slice(1),
17
+ ownerUrlInfo.context.rootDirectoryUrl,
18
+ );
13
19
  return url;
14
20
  }
15
21
  // baseUrl happens second argument to new URL() is different from