@jsenv/core 41.5.25 → 41.5.27

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.
@@ -4883,6 +4883,140 @@ const isBareSpecifier = (specifier) => {
4883
4883
  }
4884
4884
  };
4885
4885
 
4886
+ /*
4887
+ * Text patches applied to files as they are served and built, keyed by file:
4888
+ *
4889
+ * patches: {
4890
+ * "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
4891
+ * }
4892
+ *
4893
+ * A key is a url pattern relative to the root directory ("./main.js",
4894
+ * "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
4895
+ * walking up from the root directory into node_modules, the way node does,
4896
+ * so the key holds wherever the package manager hoists the package.
4897
+ *
4898
+ * Every `from` must occur exactly once in the file, otherwise the file fails
4899
+ * to cook and says which patch did not apply: a dependency update that moved
4900
+ * the patched code must be looked at, never silently unpatched.
4901
+ */
4902
+
4903
+
4904
+ const jsenvPluginPatches = (rawPatches) => {
4905
+ if (!rawPatches || Object.keys(rawPatches).length === 0) {
4906
+ return [];
4907
+ }
4908
+ let findPatches;
4909
+ const patchesPlugin = {
4910
+ name: "jsenv:patches",
4911
+ appliesDuring: "*",
4912
+ init: (context) => {
4913
+ const { rootDirectoryUrl } = context;
4914
+ const patchesByPattern = {};
4915
+ for (const key of Object.keys(rawPatches)) {
4916
+ const patches = rawPatches[key];
4917
+ assertPatches(patches, key);
4918
+ patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
4919
+ }
4920
+ const associations = URL_META.resolveAssociations(
4921
+ { patches: patchesByPattern },
4922
+ rootDirectoryUrl,
4923
+ );
4924
+ findPatches = (url) => {
4925
+ const { patches } = URL_META.applyAssociations({
4926
+ url: asUrlWithoutSearch(url),
4927
+ associations,
4928
+ });
4929
+ return patches;
4930
+ };
4931
+ },
4932
+ transformUrlContent: (urlInfo) => {
4933
+ const patches = findPatches(urlInfo.url);
4934
+ if (!patches) {
4935
+ return null;
4936
+ }
4937
+ const { content } = urlInfo;
4938
+ const magicSource = createMagicSource(content);
4939
+ for (const { from, to } of patches) {
4940
+ const start = content.indexOf(from);
4941
+ const occurrenceCount =
4942
+ start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
4943
+ if (occurrenceCount !== 1) {
4944
+ const fileRelativeUrl = urlToRelativeUrl(
4945
+ urlInfo.url,
4946
+ urlInfo.context.rootDirectoryUrl,
4947
+ );
4948
+ throw new Error(
4949
+ `patch cannot apply on "${fileRelativeUrl}": ${JSON.stringify(from)} found ${occurrenceCount === 0 ? "nowhere" : "more than once"} in the file. The file may have changed since the patch was written.`,
4950
+ );
4951
+ }
4952
+ magicSource.replace({
4953
+ start,
4954
+ end: start + from.length,
4955
+ replacement: to,
4956
+ });
4957
+ }
4958
+ return magicSource.toContentAndSourcemap();
4959
+ },
4960
+ };
4961
+ return [patchesPlugin];
4962
+ };
4963
+
4964
+ const assertPatches = (patches, key) => {
4965
+ if (!Array.isArray(patches)) {
4966
+ throw new TypeError(
4967
+ `patches["${key}"] must be an array of { from, to }, got ${patches}`,
4968
+ );
4969
+ }
4970
+ for (const patch of patches) {
4971
+ if (
4972
+ !patch ||
4973
+ typeof patch.from !== "string" ||
4974
+ patch.from === "" ||
4975
+ typeof patch.to !== "string"
4976
+ ) {
4977
+ throw new TypeError(
4978
+ `patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
4979
+ );
4980
+ }
4981
+ }
4982
+ };
4983
+
4984
+ // "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
4985
+ // else names a path inside a package, looked up in node_modules
4986
+ const resolvePatchKey = (key, rootDirectoryUrl) => {
4987
+ if (
4988
+ key.startsWith("./") ||
4989
+ key.startsWith("../") ||
4990
+ key.startsWith("/") ||
4991
+ key.startsWith("file:") ||
4992
+ key.startsWith("*")
4993
+ ) {
4994
+ return key;
4995
+ }
4996
+ const segments = key.split("/");
4997
+ const packageName = key.startsWith("@")
4998
+ ? `${segments[0]}/${segments[1]}`
4999
+ : segments[0];
5000
+ const pathInsidePackage = key.slice(packageName.length);
5001
+ let directoryUrl = new URL(rootDirectoryUrl);
5002
+ while (true) {
5003
+ const packageDirectoryUrl = new URL(
5004
+ `./node_modules/${packageName}/`,
5005
+ directoryUrl,
5006
+ );
5007
+ if (existsSync(packageDirectoryUrl)) {
5008
+ return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
5009
+ }
5010
+ const parentDirectoryUrl = new URL("../", directoryUrl);
5011
+ if (parentDirectoryUrl.href === directoryUrl.href) {
5012
+ throw new Error(
5013
+ `patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
5014
+ );
5015
+ }
5016
+ directoryUrl = parentDirectoryUrl;
5017
+ }
5018
+ };
5019
+
4886
5020
  /*
4887
5021
  * https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/css/src/CSSTransformer.js
4888
5022
  */
@@ -8175,140 +8309,6 @@ const asInheritedInjections = (injections) => {
8175
8309
  return inheritedInjections;
8176
8310
  };
8177
8311
 
8178
- /*
8179
- * Text patches applied to files as they are served and built, keyed by file:
8180
- *
8181
- * patches: {
8182
- * "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
8183
- * }
8184
- *
8185
- * A key is a url pattern relative to the root directory ("./main.js",
8186
- * "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
8187
- * walking up from the root directory into node_modules, the way node does,
8188
- * so the key holds wherever the package manager hoists the package.
8189
- *
8190
- * Every `from` must occur exactly once in the file, otherwise the file fails
8191
- * to cook and says which patch did not apply: a dependency update that moved
8192
- * the patched code must be looked at, never silently unpatched.
8193
- */
8194
-
8195
-
8196
- const jsenvPluginPatches = (rawPatches) => {
8197
- if (!rawPatches || Object.keys(rawPatches).length === 0) {
8198
- return [];
8199
- }
8200
- let findPatches;
8201
- const patchesPlugin = {
8202
- name: "jsenv:patches",
8203
- appliesDuring: "*",
8204
- init: (context) => {
8205
- const { rootDirectoryUrl } = context;
8206
- const patchesByPattern = {};
8207
- for (const key of Object.keys(rawPatches)) {
8208
- const patches = rawPatches[key];
8209
- assertPatches(patches, key);
8210
- patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
8211
- }
8212
- const associations = URL_META.resolveAssociations(
8213
- { patches: patchesByPattern },
8214
- rootDirectoryUrl,
8215
- );
8216
- findPatches = (url) => {
8217
- const { patches } = URL_META.applyAssociations({
8218
- url: asUrlWithoutSearch(url),
8219
- associations,
8220
- });
8221
- return patches;
8222
- };
8223
- },
8224
- transformUrlContent: (urlInfo) => {
8225
- const patches = findPatches(urlInfo.url);
8226
- if (!patches) {
8227
- return null;
8228
- }
8229
- const { content } = urlInfo;
8230
- const magicSource = createMagicSource(content);
8231
- for (const { from, to } of patches) {
8232
- const start = content.indexOf(from);
8233
- const occurrenceCount =
8234
- start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
8235
- if (occurrenceCount !== 1) {
8236
- const fileRelativeUrl = urlToRelativeUrl(
8237
- urlInfo.url,
8238
- urlInfo.context.rootDirectoryUrl,
8239
- );
8240
- throw new Error(
8241
- `patch cannot apply on "${fileRelativeUrl}": ${JSON.stringify(from)} found ${occurrenceCount === 0 ? "nowhere" : "more than once"} in the file. The file may have changed since the patch was written.`,
8242
- );
8243
- }
8244
- magicSource.replace({
8245
- start,
8246
- end: start + from.length,
8247
- replacement: to,
8248
- });
8249
- }
8250
- return magicSource.toContentAndSourcemap();
8251
- },
8252
- };
8253
- return [patchesPlugin];
8254
- };
8255
-
8256
- const assertPatches = (patches, key) => {
8257
- if (!Array.isArray(patches)) {
8258
- throw new TypeError(
8259
- `patches["${key}"] must be an array of { from, to }, got ${patches}`,
8260
- );
8261
- }
8262
- for (const patch of patches) {
8263
- if (
8264
- !patch ||
8265
- typeof patch.from !== "string" ||
8266
- patch.from === "" ||
8267
- typeof patch.to !== "string"
8268
- ) {
8269
- throw new TypeError(
8270
- `patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
8271
- );
8272
- }
8273
- }
8274
- };
8275
-
8276
- // "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
8277
- // else names a path inside a package, looked up in node_modules
8278
- const resolvePatchKey = (key, rootDirectoryUrl) => {
8279
- if (
8280
- key.startsWith("./") ||
8281
- key.startsWith("../") ||
8282
- key.startsWith("/") ||
8283
- key.startsWith("file:") ||
8284
- key.startsWith("*")
8285
- ) {
8286
- return key;
8287
- }
8288
- const segments = key.split("/");
8289
- const packageName = key.startsWith("@")
8290
- ? `${segments[0]}/${segments[1]}`
8291
- : segments[0];
8292
- const pathInsidePackage = key.slice(packageName.length);
8293
- let directoryUrl = new URL(rootDirectoryUrl);
8294
- while (true) {
8295
- const packageDirectoryUrl = new URL(
8296
- `./node_modules/${packageName}/`,
8297
- directoryUrl,
8298
- );
8299
- if (existsSync(packageDirectoryUrl)) {
8300
- return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
8301
- }
8302
- const parentDirectoryUrl = new URL("../", directoryUrl);
8303
- if (parentDirectoryUrl.href === directoryUrl.href) {
8304
- throw new Error(
8305
- `patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
8306
- );
8307
- }
8308
- directoryUrl = parentDirectoryUrl;
8309
- }
8310
- };
8311
-
8312
8312
  /*
8313
8313
  * Some code uses globals specific to Node.js in code meant to run in browsers...
8314
8314
  * This plugin will replace some node globals to things compatible with web:
@@ -10435,7 +10435,6 @@ const getCorePlugins = ({
10435
10435
  directoryListing = true,
10436
10436
  directoryReferenceEffect,
10437
10437
  supervisor,
10438
- patches,
10439
10438
  injections,
10440
10439
  transpilation = true,
10441
10440
  inlining = true,
@@ -10477,9 +10476,6 @@ const getCorePlugins = ({
10477
10476
  ...(packageBundle
10478
10477
  ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
10479
10478
  : []),
10480
- // before everything else: what the other plugins read must be the
10481
- // patched file
10482
- ...jsenvPluginPatches(patches),
10483
10479
  // before reference analysis: an url written by an injection must hold its
10484
10480
  // final value when references are analyzed
10485
10481
  jsenvPluginInjections(injections),
@@ -11559,6 +11555,11 @@ const createBuildSpecifierManager = ({
11559
11555
  // when versioning is dynamic no need to take into account
11560
11556
  continue;
11561
11557
  }
11558
+ if (referenceVersioningInfo.type === "not_versioned") {
11559
+ // the specifier is emitted bare (entry point, webmanifest, ...):
11560
+ // the referencing file's bytes cannot change with that file's content
11561
+ continue;
11562
+ }
11562
11563
  placeholderInfluencingVersionSet.add(containedPlaceholder);
11563
11564
  const referencedUrlInfo = reference.urlInfo;
11564
11565
  visitContainedPlaceholders(referencedUrlInfo);
@@ -13837,6 +13838,9 @@ const prepareEntryPointBuild = async (
13837
13838
 
13838
13839
  let _getOtherEntryBuildInfo;
13839
13840
  const rawJsenvPluginStore = await createJsenvPluginStore([
13841
+ // First, ahead of the plugins given by the caller: what every other plugin
13842
+ // reads must be the patched file (see start_dev_server.js).
13843
+ ...jsenvPluginPatches(patches),
13840
13844
  ...(mappings ? [jsenvPluginMappings(mappings)] : []),
13841
13845
  {
13842
13846
  name: "jsenv:other_entry_point_build_during_craft",
@@ -13871,7 +13875,6 @@ const prepareEntryPointBuild = async (
13871
13875
  magicExtensions,
13872
13876
  magicDirectoryIndex,
13873
13877
  directoryReferenceEffect,
13874
- patches,
13875
13878
  injections,
13876
13879
  transpilation: {
13877
13880
  babelHelpersAsImport: !explicitJsModuleConversion,
@@ -1,12 +1,12 @@
1
1
  import { WebSocketResponse, pickContentType, ServerEvents, serverPluginErrorHandler, fetchDirectory, composeTwoResponses, serverPluginCORS, jsenvAccessControlAllowedHeaders, startServer } from "@jsenv/server";
2
2
  import { existsSync, statSync, readFileSync, realpathSync, readdirSync, lstatSync } from "node:fs";
3
3
  import { pathToFileURL } from "node:url";
4
- import { urlToRelativeUrl, registerFileLifecycle, lookupPackageDirectory, readPackageAtOrNull, generateContentFrame, errorToHTML, DATA_URL, CONTENT_TYPE, normalizeImportMap, composeTwoImportMaps, resolveImport, createDetailedMessage, UNICODE, JS_QUOTES, urlToExtension, urlToBasename, applyNodeEsmResolution, URL_META, readCustomConditionsFromProcessArgs, urlIsOrIsInsideOf, collectFiles, registerDirectoryLifecycle, readEntryStatSync, applyFileSystemMagicResolution, getExtensionsToTry, urlToFilename, asUrlWithoutSearch, ensurePathnameTrailingSlash, compareFileUrls, setUrlExtension, stringifyUrlSite, injectQueryParamsIntoSpecifier, isSpecifierForNodeBuiltin, injectQueryParams, urlToFileSystemPath, writeFileSync, moveUrl, ensureWindowsDriveLetter, validateResponseIntegrity, setUrlFilename, getCallerPosition, asSpecifierWithoutSearch, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, createLogger, normalizeUrl, ANSI, RUNTIME_COMPAT, formatError, assertAndNormalizeDirectoryUrl, browserDefaultRuntimeCompat, inferRuntimeCompatFromClosestPackage, createTaskLog } from "./jsenv_core_packages.js";
4
+ import { urlToRelativeUrl, registerFileLifecycle, lookupPackageDirectory, readPackageAtOrNull, generateContentFrame, errorToHTML, URL_META, asUrlWithoutSearch, DATA_URL, CONTENT_TYPE, normalizeImportMap, composeTwoImportMaps, resolveImport, createDetailedMessage, UNICODE, JS_QUOTES, urlToExtension, urlToBasename, applyNodeEsmResolution, readCustomConditionsFromProcessArgs, urlIsOrIsInsideOf, collectFiles, registerDirectoryLifecycle, readEntryStatSync, applyFileSystemMagicResolution, getExtensionsToTry, urlToFilename, ensurePathnameTrailingSlash, compareFileUrls, setUrlExtension, stringifyUrlSite, injectQueryParamsIntoSpecifier, isSpecifierForNodeBuiltin, injectQueryParams, urlToFileSystemPath, writeFileSync, moveUrl, ensureWindowsDriveLetter, validateResponseIntegrity, setUrlFilename, getCallerPosition, asSpecifierWithoutSearch, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, createLogger, normalizeUrl, ANSI, RUNTIME_COMPAT, formatError, assertAndNormalizeDirectoryUrl, browserDefaultRuntimeCompat, inferRuntimeCompatFromClosestPackage, createTaskLog } from "./jsenv_core_packages.js";
5
5
  import { createPluginsController } from "@jsenv/server/src/plugins_controller.js";
6
6
  import { parseHtml, injectJsenvScript, stringifyHtmlAst, parseCssUrls, getHtmlNodeAttribute, getHtmlNodePosition, getHtmlNodeAttributePosition, setHtmlNodeAttributes, parseSrcSet, getUrlForContentInsideHtml, removeHtmlNodeText, setHtmlNodeText, getHtmlNodeText, analyzeScriptNode, visitHtmlNodes, parseJsUrls, hasCssOpaqueDirective, getUrlForContentInsideJs, renderCssTemplateLiteral, applyBabelPlugins, visitJsAst, getImportMetaPropertyName, visitJsAstUntil, analyzeLinkNode, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, generateUrlForInlineContent, parseJsWithAcorn } from "@jsenv/ast";
7
+ import { createMagicSource, composeTwoSourcemaps, generateSourcemapFileUrl, generateSourcemapDataUrl, SOURCEMAP, applyContentEditsOnSourcemap, composeSourcemaps } from "@jsenv/sourcemap";
7
8
  import { jsenvPluginSupervisor } from "@jsenv/plugin-supervisor";
8
9
  import { jsenvPluginTranspilation } from "@jsenv/plugin-transpilation";
9
- import { createMagicSource, composeTwoSourcemaps, generateSourcemapFileUrl, generateSourcemapDataUrl, SOURCEMAP, applyContentEditsOnSourcemap, composeSourcemaps } from "@jsenv/sourcemap";
10
10
  import { bundleJsModules } from "@jsenv/plugin-bundling";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/plugins/filesystem/filesystem_error_to_response.js";
@@ -1574,6 +1574,140 @@ const jsenvPluginPageSwitcher = () => {
1574
1574
  };
1575
1575
  };
1576
1576
 
1577
+ /*
1578
+ * Text patches applied to files as they are served and built, keyed by file:
1579
+ *
1580
+ * patches: {
1581
+ * "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
1582
+ * }
1583
+ *
1584
+ * A key is a url pattern relative to the root directory ("./main.js",
1585
+ * "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
1586
+ * walking up from the root directory into node_modules, the way node does,
1587
+ * so the key holds wherever the package manager hoists the package.
1588
+ *
1589
+ * Every `from` must occur exactly once in the file, otherwise the file fails
1590
+ * to cook and says which patch did not apply: a dependency update that moved
1591
+ * the patched code must be looked at, never silently unpatched.
1592
+ */
1593
+
1594
+
1595
+ const jsenvPluginPatches = (rawPatches) => {
1596
+ if (!rawPatches || Object.keys(rawPatches).length === 0) {
1597
+ return [];
1598
+ }
1599
+ let findPatches;
1600
+ const patchesPlugin = {
1601
+ name: "jsenv:patches",
1602
+ appliesDuring: "*",
1603
+ init: (context) => {
1604
+ const { rootDirectoryUrl } = context;
1605
+ const patchesByPattern = {};
1606
+ for (const key of Object.keys(rawPatches)) {
1607
+ const patches = rawPatches[key];
1608
+ assertPatches(patches, key);
1609
+ patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
1610
+ }
1611
+ const associations = URL_META.resolveAssociations(
1612
+ { patches: patchesByPattern },
1613
+ rootDirectoryUrl,
1614
+ );
1615
+ findPatches = (url) => {
1616
+ const { patches } = URL_META.applyAssociations({
1617
+ url: asUrlWithoutSearch(url),
1618
+ associations,
1619
+ });
1620
+ return patches;
1621
+ };
1622
+ },
1623
+ transformUrlContent: (urlInfo) => {
1624
+ const patches = findPatches(urlInfo.url);
1625
+ if (!patches) {
1626
+ return null;
1627
+ }
1628
+ const { content } = urlInfo;
1629
+ const magicSource = createMagicSource(content);
1630
+ for (const { from, to } of patches) {
1631
+ const start = content.indexOf(from);
1632
+ const occurrenceCount =
1633
+ start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
1634
+ if (occurrenceCount !== 1) {
1635
+ const fileRelativeUrl = urlToRelativeUrl(
1636
+ urlInfo.url,
1637
+ urlInfo.context.rootDirectoryUrl,
1638
+ );
1639
+ throw new Error(
1640
+ `patch cannot apply on "${fileRelativeUrl}": ${JSON.stringify(from)} found ${occurrenceCount === 0 ? "nowhere" : "more than once"} in the file. The file may have changed since the patch was written.`,
1641
+ );
1642
+ }
1643
+ magicSource.replace({
1644
+ start,
1645
+ end: start + from.length,
1646
+ replacement: to,
1647
+ });
1648
+ }
1649
+ return magicSource.toContentAndSourcemap();
1650
+ },
1651
+ };
1652
+ return [patchesPlugin];
1653
+ };
1654
+
1655
+ const assertPatches = (patches, key) => {
1656
+ if (!Array.isArray(patches)) {
1657
+ throw new TypeError(
1658
+ `patches["${key}"] must be an array of { from, to }, got ${patches}`,
1659
+ );
1660
+ }
1661
+ for (const patch of patches) {
1662
+ if (
1663
+ !patch ||
1664
+ typeof patch.from !== "string" ||
1665
+ patch.from === "" ||
1666
+ typeof patch.to !== "string"
1667
+ ) {
1668
+ throw new TypeError(
1669
+ `patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
1670
+ );
1671
+ }
1672
+ }
1673
+ };
1674
+
1675
+ // "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
1676
+ // else names a path inside a package, looked up in node_modules
1677
+ const resolvePatchKey = (key, rootDirectoryUrl) => {
1678
+ if (
1679
+ key.startsWith("./") ||
1680
+ key.startsWith("../") ||
1681
+ key.startsWith("/") ||
1682
+ key.startsWith("file:") ||
1683
+ key.startsWith("*")
1684
+ ) {
1685
+ return key;
1686
+ }
1687
+ const segments = key.split("/");
1688
+ const packageName = key.startsWith("@")
1689
+ ? `${segments[0]}/${segments[1]}`
1690
+ : segments[0];
1691
+ const pathInsidePackage = key.slice(packageName.length);
1692
+ let directoryUrl = new URL(rootDirectoryUrl);
1693
+ while (true) {
1694
+ const packageDirectoryUrl = new URL(
1695
+ `./node_modules/${packageName}/`,
1696
+ directoryUrl,
1697
+ );
1698
+ if (existsSync(packageDirectoryUrl)) {
1699
+ return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
1700
+ }
1701
+ const parentDirectoryUrl = new URL("../", directoryUrl);
1702
+ if (parentDirectoryUrl.href === directoryUrl.href) {
1703
+ throw new Error(
1704
+ `patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
1705
+ );
1706
+ }
1707
+ directoryUrl = parentDirectoryUrl;
1708
+ }
1709
+ };
1710
+
1577
1711
  /*
1578
1712
  * https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/css/src/CSSTransformer.js
1579
1713
  */
@@ -5762,140 +5896,6 @@ const asInheritedInjections = (injections) => {
5762
5896
  return inheritedInjections;
5763
5897
  };
5764
5898
 
5765
- /*
5766
- * Text patches applied to files as they are served and built, keyed by file:
5767
- *
5768
- * patches: {
5769
- * "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
5770
- * }
5771
- *
5772
- * A key is a url pattern relative to the root directory ("./main.js",
5773
- * "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
5774
- * walking up from the root directory into node_modules, the way node does,
5775
- * so the key holds wherever the package manager hoists the package.
5776
- *
5777
- * Every `from` must occur exactly once in the file, otherwise the file fails
5778
- * to cook and says which patch did not apply: a dependency update that moved
5779
- * the patched code must be looked at, never silently unpatched.
5780
- */
5781
-
5782
-
5783
- const jsenvPluginPatches = (rawPatches) => {
5784
- if (!rawPatches || Object.keys(rawPatches).length === 0) {
5785
- return [];
5786
- }
5787
- let findPatches;
5788
- const patchesPlugin = {
5789
- name: "jsenv:patches",
5790
- appliesDuring: "*",
5791
- init: (context) => {
5792
- const { rootDirectoryUrl } = context;
5793
- const patchesByPattern = {};
5794
- for (const key of Object.keys(rawPatches)) {
5795
- const patches = rawPatches[key];
5796
- assertPatches(patches, key);
5797
- patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
5798
- }
5799
- const associations = URL_META.resolveAssociations(
5800
- { patches: patchesByPattern },
5801
- rootDirectoryUrl,
5802
- );
5803
- findPatches = (url) => {
5804
- const { patches } = URL_META.applyAssociations({
5805
- url: asUrlWithoutSearch(url),
5806
- associations,
5807
- });
5808
- return patches;
5809
- };
5810
- },
5811
- transformUrlContent: (urlInfo) => {
5812
- const patches = findPatches(urlInfo.url);
5813
- if (!patches) {
5814
- return null;
5815
- }
5816
- const { content } = urlInfo;
5817
- const magicSource = createMagicSource(content);
5818
- for (const { from, to } of patches) {
5819
- const start = content.indexOf(from);
5820
- const occurrenceCount =
5821
- start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
5822
- if (occurrenceCount !== 1) {
5823
- const fileRelativeUrl = urlToRelativeUrl(
5824
- urlInfo.url,
5825
- urlInfo.context.rootDirectoryUrl,
5826
- );
5827
- throw new Error(
5828
- `patch cannot apply on "${fileRelativeUrl}": ${JSON.stringify(from)} found ${occurrenceCount === 0 ? "nowhere" : "more than once"} in the file. The file may have changed since the patch was written.`,
5829
- );
5830
- }
5831
- magicSource.replace({
5832
- start,
5833
- end: start + from.length,
5834
- replacement: to,
5835
- });
5836
- }
5837
- return magicSource.toContentAndSourcemap();
5838
- },
5839
- };
5840
- return [patchesPlugin];
5841
- };
5842
-
5843
- const assertPatches = (patches, key) => {
5844
- if (!Array.isArray(patches)) {
5845
- throw new TypeError(
5846
- `patches["${key}"] must be an array of { from, to }, got ${patches}`,
5847
- );
5848
- }
5849
- for (const patch of patches) {
5850
- if (
5851
- !patch ||
5852
- typeof patch.from !== "string" ||
5853
- patch.from === "" ||
5854
- typeof patch.to !== "string"
5855
- ) {
5856
- throw new TypeError(
5857
- `patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
5858
- );
5859
- }
5860
- }
5861
- };
5862
-
5863
- // "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
5864
- // else names a path inside a package, looked up in node_modules
5865
- const resolvePatchKey = (key, rootDirectoryUrl) => {
5866
- if (
5867
- key.startsWith("./") ||
5868
- key.startsWith("../") ||
5869
- key.startsWith("/") ||
5870
- key.startsWith("file:") ||
5871
- key.startsWith("*")
5872
- ) {
5873
- return key;
5874
- }
5875
- const segments = key.split("/");
5876
- const packageName = key.startsWith("@")
5877
- ? `${segments[0]}/${segments[1]}`
5878
- : segments[0];
5879
- const pathInsidePackage = key.slice(packageName.length);
5880
- let directoryUrl = new URL(rootDirectoryUrl);
5881
- while (true) {
5882
- const packageDirectoryUrl = new URL(
5883
- `./node_modules/${packageName}/`,
5884
- directoryUrl,
5885
- );
5886
- if (existsSync(packageDirectoryUrl)) {
5887
- return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
5888
- }
5889
- const parentDirectoryUrl = new URL("../", directoryUrl);
5890
- if (parentDirectoryUrl.href === directoryUrl.href) {
5891
- throw new Error(
5892
- `patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
5893
- );
5894
- }
5895
- directoryUrl = parentDirectoryUrl;
5896
- }
5897
- };
5898
-
5899
5899
  const jsenvPluginInliningAsDataUrl = () => {
5900
5900
  return {
5901
5901
  name: "jsenv:inlining_as_data_url",
@@ -8335,7 +8335,6 @@ const getCorePlugins = ({
8335
8335
  directoryListing = true,
8336
8336
  directoryReferenceEffect,
8337
8337
  supervisor,
8338
- patches,
8339
8338
  injections,
8340
8339
  transpilation = true,
8341
8340
  inlining = true,
@@ -8377,9 +8376,6 @@ const getCorePlugins = ({
8377
8376
  ...(packageBundle
8378
8377
  ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
8379
8378
  : []),
8380
- // before everything else: what the other plugins read must be the
8381
- // patched file
8382
- ...jsenvPluginPatches(patches),
8383
8379
  // before reference analysis: an url written by an injection must hold its
8384
8380
  // final value when references are analyzed
8385
8381
  jsenvPluginInjections(injections),
@@ -12751,6 +12747,11 @@ const startDevServer = async ({
12751
12747
  serverStopCallbackSet.add(dependencyWatcher.stop);
12752
12748
 
12753
12749
  const devServerJsenvPluginStore = await createJsenvPluginStore([
12750
+ // First, ahead of the plugins given by the caller: what every other plugin
12751
+ // reads must be the patched file, and a caller's plugin may rewrite a file
12752
+ // (plugin-preact reprints one it instruments) before a patch written
12753
+ // against its text on disk gets to see it.
12754
+ ...jsenvPluginPatches(patches),
12754
12755
  jsenvPluginServerEvents({ clientAutoreload }),
12755
12756
  // The client-monitoring dashboard is a dev-time convenience; a test-plan run
12756
12757
  // doesn't use it and shouldn't pay for the reporter being injected into
@@ -12778,7 +12779,6 @@ const startDevServer = async ({
12778
12779
  magicDirectoryIndex,
12779
12780
  directoryListing,
12780
12781
  supervisor,
12781
- patches,
12782
12782
  injections,
12783
12783
  transpilation,
12784
12784
  spa,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "41.5.25",
3
+ "version": "41.5.27",
4
4
  "type": "module",
5
5
  "description": "Tool to develop, test and build js projects",
6
6
  "repository": {
@@ -73,14 +73,14 @@
73
73
  "test:snapshot_clear": "npx @jsenv/filesystem clear **/tests/**/side_effects/"
74
74
  },
75
75
  "dependencies": {
76
- "@jsenv/ast": "6.10.6",
77
- "@jsenv/js-module-fallback": "1.6.10",
78
- "@jsenv/plugin-bundling": "2.10.28",
79
- "@jsenv/plugin-minification": "1.7.23",
80
- "@jsenv/plugin-supervisor": "1.8.25",
81
- "@jsenv/plugin-transpilation": "1.7.10",
76
+ "@jsenv/ast": "6.10.7",
77
+ "@jsenv/js-module-fallback": "1.6.11",
78
+ "@jsenv/plugin-bundling": "2.10.29",
79
+ "@jsenv/plugin-minification": "1.7.24",
80
+ "@jsenv/plugin-supervisor": "1.8.26",
81
+ "@jsenv/plugin-transpilation": "1.7.11",
82
82
  "@jsenv/server": "17.6.6",
83
- "@jsenv/sourcemap": "1.4.9",
83
+ "@jsenv/sourcemap": "1.4.10",
84
84
  "react-table": "7.8.0"
85
85
  },
86
86
  "devDependencies": {
@@ -67,6 +67,7 @@ import {
67
67
  createJsenvPluginStore,
68
68
  } from "../plugins/jsenv_plugins_controller.js";
69
69
  import { isBareSpecifier } from "../helpers/bare_specifier.js";
70
+ import { jsenvPluginPatches } from "../plugins/patches/jsenv_plugin_patches.js";
70
71
  import { getCorePlugins } from "../plugins/plugins.js";
71
72
  import { jsenvPluginReferenceAnalysis } from "../plugins/reference_analysis/jsenv_plugin_reference_analysis.js";
72
73
  import { renderBuildDoneLog } from "./build_content_report.js";
@@ -1219,6 +1220,9 @@ const prepareEntryPointBuild = async (
1219
1220
 
1220
1221
  let _getOtherEntryBuildInfo;
1221
1222
  const rawJsenvPluginStore = await createJsenvPluginStore([
1223
+ // First, ahead of the plugins given by the caller: what every other plugin
1224
+ // reads must be the patched file (see start_dev_server.js).
1225
+ ...jsenvPluginPatches(patches),
1222
1226
  ...(mappings ? [jsenvPluginMappings(mappings)] : []),
1223
1227
  {
1224
1228
  name: "jsenv:other_entry_point_build_during_craft",
@@ -1253,7 +1257,6 @@ const prepareEntryPointBuild = async (
1253
1257
  magicExtensions,
1254
1258
  magicDirectoryIndex,
1255
1259
  directoryReferenceEffect,
1256
- patches,
1257
1260
  injections,
1258
1261
  transpilation: {
1259
1262
  babelHelpersAsImport: !explicitJsModuleConversion,
@@ -626,6 +626,11 @@ export const createBuildSpecifierManager = ({
626
626
  // when versioning is dynamic no need to take into account
627
627
  continue;
628
628
  }
629
+ if (referenceVersioningInfo.type === "not_versioned") {
630
+ // the specifier is emitted bare (entry point, webmanifest, ...):
631
+ // the referencing file's bytes cannot change with that file's content
632
+ continue;
633
+ }
629
634
  placeholderInfluencingVersionSet.add(containedPlaceholder);
630
635
  const referencedUrlInfo = reference.urlInfo;
631
636
  visitContainedPlaceholders(referencedUrlInfo);
@@ -27,6 +27,7 @@ import { createPackageDirectory } from "../kitchen/package_directory.js";
27
27
  import { createJsenvPluginStore } from "../plugins/jsenv_plugins_controller.js";
28
28
  import { jsenvPluginClientMonitoring } from "../plugins/client_monitoring/jsenv_plugin_client_monitoring.js";
29
29
  import { jsenvPluginPageSwitcher } from "../plugins/page_switcher/jsenv_plugin_page_switcher.js";
30
+ import { jsenvPluginPatches } from "../plugins/patches/jsenv_plugin_patches.js";
30
31
  import { getCorePlugins } from "../plugins/plugins.js";
31
32
  import { jsenvPluginServerEvents } from "../plugins/server_events/jsenv_plugin_server_events.js";
32
33
  import { devServerPluginChromeDevToolsJson } from "./dev_server_plugins/dev_server_plugin_chrome_devtools_json.js";
@@ -284,6 +285,11 @@ export const startDevServer = async ({
284
285
  serverStopCallbackSet.add(dependencyWatcher.stop);
285
286
 
286
287
  const devServerJsenvPluginStore = await createJsenvPluginStore([
288
+ // First, ahead of the plugins given by the caller: what every other plugin
289
+ // reads must be the patched file, and a caller's plugin may rewrite a file
290
+ // (plugin-preact reprints one it instruments) before a patch written
291
+ // against its text on disk gets to see it.
292
+ ...jsenvPluginPatches(patches),
287
293
  jsenvPluginServerEvents({ clientAutoreload }),
288
294
  // The client-monitoring dashboard is a dev-time convenience; a test-plan run
289
295
  // doesn't use it and shouldn't pay for the reporter being injected into
@@ -311,7 +317,6 @@ export const startDevServer = async ({
311
317
  magicDirectoryIndex,
312
318
  directoryListing,
313
319
  supervisor,
314
- patches,
315
320
  injections,
316
321
  transpilation,
317
322
  spa,
@@ -11,7 +11,6 @@ import { jsenvPluginProtocolFile } from "./protocol_file/jsenv_plugin_protocol_f
11
11
  import { jsenvPluginProtocolHttp } from "./protocol_http/jsenv_plugin_protocol_http.js";
12
12
  import { jsenvPluginDirectoryReferenceEffect } from "./directory_reference_effect/jsenv_plugin_directory_reference_effect.js";
13
13
  import { jsenvPluginInjections } from "./injections/jsenv_plugin_injections.js";
14
- import { jsenvPluginPatches } from "./patches/jsenv_plugin_patches.js";
15
14
  import { jsenvPluginInlining } from "./inlining/jsenv_plugin_inlining.js";
16
15
  import { jsenvPluginCommonJsGlobals } from "./commonjs_globals/jsenv_plugin_commonjs_globals.js";
17
16
  import { jsenvPluginImportMetaScenarios } from "./import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js";
@@ -52,7 +51,6 @@ export const getCorePlugins = ({
52
51
  directoryListing = true,
53
52
  directoryReferenceEffect,
54
53
  supervisor,
55
- patches,
56
54
  injections,
57
55
  transpilation = true,
58
56
  inlining = true,
@@ -94,9 +92,6 @@ export const getCorePlugins = ({
94
92
  ...(packageBundle
95
93
  ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
96
94
  : []),
97
- // before everything else: what the other plugins read must be the
98
- // patched file
99
- ...jsenvPluginPatches(patches),
100
95
  // before reference analysis: an url written by an injection must hold its
101
96
  // final value when references are analyzed
102
97
  jsenvPluginInjections(injections),