@jsenv/core 41.5.24 → 41.5.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browserslist_index/browserslist_index.js +11 -0
- package/dist/build/build.js +172 -178
- package/dist/jsenv_core.js +1 -1
- package/dist/start_dev_server/start_dev_server.js +165 -162
- package/package.json +8 -8
- package/src/build/build.js +5 -2
- package/src/build/mappings_injection.js +24 -19
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +14 -2
- package/src/dev/start_dev_server.js +6 -1
- package/src/kitchen/url_graph/url_info_injections.js +10 -19
- package/src/plugins/plugins.js +0 -5
|
@@ -2873,6 +2873,14 @@ var require$$1 = [
|
|
|
2873
2873
|
security: false,
|
|
2874
2874
|
v8: "13.6.233.17"
|
|
2875
2875
|
},
|
|
2876
|
+
{
|
|
2877
|
+
name: "nodejs",
|
|
2878
|
+
version: "24.21.0",
|
|
2879
|
+
date: "2026-09-07",
|
|
2880
|
+
lts: "Krypton",
|
|
2881
|
+
security: false,
|
|
2882
|
+
v8: "13.6.233.17"
|
|
2883
|
+
},
|
|
2876
2884
|
{
|
|
2877
2885
|
name: "nodejs",
|
|
2878
2886
|
version: "25.0.0",
|
|
@@ -3410,8 +3418,11 @@ function requireVersions () {
|
|
|
3410
3418
|
"43.3": "150",
|
|
3411
3419
|
"43.4": "150",
|
|
3412
3420
|
"43.5": "150",
|
|
3421
|
+
"43.6": "150",
|
|
3413
3422
|
"44.0": "152",
|
|
3414
3423
|
"44.1": "152",
|
|
3424
|
+
"44.2": "152",
|
|
3425
|
+
"44.3": "152",
|
|
3415
3426
|
"45.0": "155"
|
|
3416
3427
|
};
|
|
3417
3428
|
return versions;
|
package/dist/build/build.js
CHANGED
|
@@ -2460,7 +2460,7 @@ ${urlInfo.url}`,
|
|
|
2460
2460
|
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
2461
2461
|
const INJECTIONS = {
|
|
2462
2462
|
/**
|
|
2463
|
-
* Inject `Object.assign(
|
|
2463
|
+
* Inject `Object.assign(globalThis, { [key]: value })` at the top of the file
|
|
2464
2464
|
* (into a script for html, into the module itself for js) instead of
|
|
2465
2465
|
* replacing a placeholder: the value is read at runtime as a global.
|
|
2466
2466
|
*/
|
|
@@ -2611,7 +2611,7 @@ const injectGlobals = (content, globals, urlInfo) => {
|
|
|
2611
2611
|
return globalInjectorOnHtml(content, globals, urlInfo);
|
|
2612
2612
|
}
|
|
2613
2613
|
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
2614
|
-
return globalsInjectorOnJs(content, globals
|
|
2614
|
+
return globalsInjectorOnJs(content, globals);
|
|
2615
2615
|
}
|
|
2616
2616
|
throw new Error(
|
|
2617
2617
|
createDetailedMessage(`cannot inject globals into "${urlInfo.type}"`, {
|
|
@@ -2631,9 +2631,7 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
2631
2631
|
url: urlInfo.url,
|
|
2632
2632
|
storeOriginalPositions: false,
|
|
2633
2633
|
});
|
|
2634
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
2635
|
-
isWebWorker: false,
|
|
2636
|
-
});
|
|
2634
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
2637
2635
|
injectJsenvScript(htmlAst, {
|
|
2638
2636
|
content: clientCode,
|
|
2639
2637
|
pluginName: "jsenv:inject_globals",
|
|
@@ -2642,24 +2640,17 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
2642
2640
|
content: stringifyHtmlAst(htmlAst),
|
|
2643
2641
|
};
|
|
2644
2642
|
};
|
|
2645
|
-
const globalsInjectorOnJs = (content, globals
|
|
2646
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
2647
|
-
isWebWorker:
|
|
2648
|
-
urlInfo.subtype === "worker" ||
|
|
2649
|
-
urlInfo.subtype === "service_worker" ||
|
|
2650
|
-
urlInfo.subtype === "shared_worker",
|
|
2651
|
-
});
|
|
2643
|
+
const globalsInjectorOnJs = (content, globals) => {
|
|
2644
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
2652
2645
|
const magicSource = createMagicSource(content);
|
|
2653
2646
|
magicSource.prepend(clientCode);
|
|
2654
2647
|
return magicSource.toContentAndSourcemap();
|
|
2655
2648
|
};
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
" ",
|
|
2662
|
-
)});`;
|
|
2649
|
+
// "globalThis" is the global object in a window, a worker and a service worker alike;
|
|
2650
|
+
// naming one of "window"/"self" would require knowing the file's subtype, which is not
|
|
2651
|
+
// known yet when the browser fetches a service worker on its own (update check).
|
|
2652
|
+
const generateClientCodeForGlobals = (globals) => {
|
|
2653
|
+
return `Object.assign(globalThis, ${JSON.stringify(globals, null, " ")});`;
|
|
2663
2654
|
};
|
|
2664
2655
|
|
|
2665
2656
|
const defineGettersOnPropertiesDerivedFromOriginalContent = (
|
|
@@ -4892,6 +4883,140 @@ const isBareSpecifier = (specifier) => {
|
|
|
4892
4883
|
}
|
|
4893
4884
|
};
|
|
4894
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
|
+
|
|
4895
5020
|
/*
|
|
4896
5021
|
* https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/css/src/CSSTransformer.js
|
|
4897
5022
|
*/
|
|
@@ -8184,140 +8309,6 @@ const asInheritedInjections = (injections) => {
|
|
|
8184
8309
|
return inheritedInjections;
|
|
8185
8310
|
};
|
|
8186
8311
|
|
|
8187
|
-
/*
|
|
8188
|
-
* Text patches applied to files as they are served and built, keyed by file:
|
|
8189
|
-
*
|
|
8190
|
-
* patches: {
|
|
8191
|
-
* "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
|
|
8192
|
-
* }
|
|
8193
|
-
*
|
|
8194
|
-
* A key is a url pattern relative to the root directory ("./main.js",
|
|
8195
|
-
* "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
|
|
8196
|
-
* walking up from the root directory into node_modules, the way node does,
|
|
8197
|
-
* so the key holds wherever the package manager hoists the package.
|
|
8198
|
-
*
|
|
8199
|
-
* Every `from` must occur exactly once in the file, otherwise the file fails
|
|
8200
|
-
* to cook and says which patch did not apply: a dependency update that moved
|
|
8201
|
-
* the patched code must be looked at, never silently unpatched.
|
|
8202
|
-
*/
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
const jsenvPluginPatches = (rawPatches) => {
|
|
8206
|
-
if (!rawPatches || Object.keys(rawPatches).length === 0) {
|
|
8207
|
-
return [];
|
|
8208
|
-
}
|
|
8209
|
-
let findPatches;
|
|
8210
|
-
const patchesPlugin = {
|
|
8211
|
-
name: "jsenv:patches",
|
|
8212
|
-
appliesDuring: "*",
|
|
8213
|
-
init: (context) => {
|
|
8214
|
-
const { rootDirectoryUrl } = context;
|
|
8215
|
-
const patchesByPattern = {};
|
|
8216
|
-
for (const key of Object.keys(rawPatches)) {
|
|
8217
|
-
const patches = rawPatches[key];
|
|
8218
|
-
assertPatches(patches, key);
|
|
8219
|
-
patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
|
|
8220
|
-
}
|
|
8221
|
-
const associations = URL_META.resolveAssociations(
|
|
8222
|
-
{ patches: patchesByPattern },
|
|
8223
|
-
rootDirectoryUrl,
|
|
8224
|
-
);
|
|
8225
|
-
findPatches = (url) => {
|
|
8226
|
-
const { patches } = URL_META.applyAssociations({
|
|
8227
|
-
url: asUrlWithoutSearch(url),
|
|
8228
|
-
associations,
|
|
8229
|
-
});
|
|
8230
|
-
return patches;
|
|
8231
|
-
};
|
|
8232
|
-
},
|
|
8233
|
-
transformUrlContent: (urlInfo) => {
|
|
8234
|
-
const patches = findPatches(urlInfo.url);
|
|
8235
|
-
if (!patches) {
|
|
8236
|
-
return null;
|
|
8237
|
-
}
|
|
8238
|
-
const { content } = urlInfo;
|
|
8239
|
-
const magicSource = createMagicSource(content);
|
|
8240
|
-
for (const { from, to } of patches) {
|
|
8241
|
-
const start = content.indexOf(from);
|
|
8242
|
-
const occurrenceCount =
|
|
8243
|
-
start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
|
|
8244
|
-
if (occurrenceCount !== 1) {
|
|
8245
|
-
const fileRelativeUrl = urlToRelativeUrl(
|
|
8246
|
-
urlInfo.url,
|
|
8247
|
-
urlInfo.context.rootDirectoryUrl,
|
|
8248
|
-
);
|
|
8249
|
-
throw new Error(
|
|
8250
|
-
`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.`,
|
|
8251
|
-
);
|
|
8252
|
-
}
|
|
8253
|
-
magicSource.replace({
|
|
8254
|
-
start,
|
|
8255
|
-
end: start + from.length,
|
|
8256
|
-
replacement: to,
|
|
8257
|
-
});
|
|
8258
|
-
}
|
|
8259
|
-
return magicSource.toContentAndSourcemap();
|
|
8260
|
-
},
|
|
8261
|
-
};
|
|
8262
|
-
return [patchesPlugin];
|
|
8263
|
-
};
|
|
8264
|
-
|
|
8265
|
-
const assertPatches = (patches, key) => {
|
|
8266
|
-
if (!Array.isArray(patches)) {
|
|
8267
|
-
throw new TypeError(
|
|
8268
|
-
`patches["${key}"] must be an array of { from, to }, got ${patches}`,
|
|
8269
|
-
);
|
|
8270
|
-
}
|
|
8271
|
-
for (const patch of patches) {
|
|
8272
|
-
if (
|
|
8273
|
-
!patch ||
|
|
8274
|
-
typeof patch.from !== "string" ||
|
|
8275
|
-
patch.from === "" ||
|
|
8276
|
-
typeof patch.to !== "string"
|
|
8277
|
-
) {
|
|
8278
|
-
throw new TypeError(
|
|
8279
|
-
`patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
|
|
8280
|
-
);
|
|
8281
|
-
}
|
|
8282
|
-
}
|
|
8283
|
-
};
|
|
8284
|
-
|
|
8285
|
-
// "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
|
|
8286
|
-
// else names a path inside a package, looked up in node_modules
|
|
8287
|
-
const resolvePatchKey = (key, rootDirectoryUrl) => {
|
|
8288
|
-
if (
|
|
8289
|
-
key.startsWith("./") ||
|
|
8290
|
-
key.startsWith("../") ||
|
|
8291
|
-
key.startsWith("/") ||
|
|
8292
|
-
key.startsWith("file:") ||
|
|
8293
|
-
key.startsWith("*")
|
|
8294
|
-
) {
|
|
8295
|
-
return key;
|
|
8296
|
-
}
|
|
8297
|
-
const segments = key.split("/");
|
|
8298
|
-
const packageName = key.startsWith("@")
|
|
8299
|
-
? `${segments[0]}/${segments[1]}`
|
|
8300
|
-
: segments[0];
|
|
8301
|
-
const pathInsidePackage = key.slice(packageName.length);
|
|
8302
|
-
let directoryUrl = new URL(rootDirectoryUrl);
|
|
8303
|
-
while (true) {
|
|
8304
|
-
const packageDirectoryUrl = new URL(
|
|
8305
|
-
`./node_modules/${packageName}/`,
|
|
8306
|
-
directoryUrl,
|
|
8307
|
-
);
|
|
8308
|
-
if (existsSync(packageDirectoryUrl)) {
|
|
8309
|
-
return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
|
|
8310
|
-
}
|
|
8311
|
-
const parentDirectoryUrl = new URL("../", directoryUrl);
|
|
8312
|
-
if (parentDirectoryUrl.href === directoryUrl.href) {
|
|
8313
|
-
throw new Error(
|
|
8314
|
-
`patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
|
|
8315
|
-
);
|
|
8316
|
-
}
|
|
8317
|
-
directoryUrl = parentDirectoryUrl;
|
|
8318
|
-
}
|
|
8319
|
-
};
|
|
8320
|
-
|
|
8321
8312
|
/*
|
|
8322
8313
|
* Some code uses globals specific to Node.js in code meant to run in browsers...
|
|
8323
8314
|
* This plugin will replace some node globals to things compatible with web:
|
|
@@ -10444,7 +10435,6 @@ const getCorePlugins = ({
|
|
|
10444
10435
|
directoryListing = true,
|
|
10445
10436
|
directoryReferenceEffect,
|
|
10446
10437
|
supervisor,
|
|
10447
|
-
patches,
|
|
10448
10438
|
injections,
|
|
10449
10439
|
transpilation = true,
|
|
10450
10440
|
inlining = true,
|
|
@@ -10486,9 +10476,6 @@ const getCorePlugins = ({
|
|
|
10486
10476
|
...(packageBundle
|
|
10487
10477
|
? [jsenvPluginWorkspaceBundle({ packageDirectory })]
|
|
10488
10478
|
: []),
|
|
10489
|
-
// before everything else: what the other plugins read must be the
|
|
10490
|
-
// patched file
|
|
10491
|
-
...jsenvPluginPatches(patches),
|
|
10492
10479
|
// before reference analysis: an url written by an injection must hold its
|
|
10493
10480
|
// final value when references are analyzed
|
|
10494
10481
|
jsenvPluginInjections(injections),
|
|
@@ -10858,28 +10845,33 @@ const logsDefault = {
|
|
|
10858
10845
|
// we nevery minify those because they are already very small
|
|
10859
10846
|
// and would hurt the readability of something that can be critical to debug
|
|
10860
10847
|
const injectGlobalMappings = async (urlInfo, mappings) => {
|
|
10861
|
-
if (
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
globalName: "window",
|
|
10867
|
-
minification: false,
|
|
10868
|
-
});
|
|
10869
|
-
await prependContent(urlInfo, { type: "js_classic", content });
|
|
10848
|
+
if (
|
|
10849
|
+
urlInfo.type !== "html" &&
|
|
10850
|
+
urlInfo.type !== "js_classic" &&
|
|
10851
|
+
urlInfo.type !== "js_module"
|
|
10852
|
+
) {
|
|
10870
10853
|
return;
|
|
10871
10854
|
}
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10855
|
+
// const minification = Boolean(
|
|
10856
|
+
// urlInfo.context.getPluginMeta("willMinifyJsClassic"),
|
|
10857
|
+
// );
|
|
10858
|
+
const content = generateClientCodeForMappings(mappings, {
|
|
10859
|
+
globalName: getGlobalName(urlInfo),
|
|
10860
|
+
minification: false,
|
|
10861
|
+
});
|
|
10862
|
+
await prependContent(urlInfo, { type: "js_classic", content });
|
|
10863
|
+
};
|
|
10864
|
+
|
|
10865
|
+
// "globalThis" names the global object in a window and in a worker alike;
|
|
10866
|
+
// the window/self split is only for runtimes predating it.
|
|
10867
|
+
const getGlobalName = (urlInfo) => {
|
|
10868
|
+
if (urlInfo.context.isSupportedOnCurrentClients("global_this")) {
|
|
10869
|
+
return "globalThis";
|
|
10882
10870
|
}
|
|
10871
|
+
if (isWebWorkerUrlInfo(urlInfo)) {
|
|
10872
|
+
return "self";
|
|
10873
|
+
}
|
|
10874
|
+
return "window";
|
|
10883
10875
|
};
|
|
10884
10876
|
|
|
10885
10877
|
const generateClientCodeForMappings = (
|
|
@@ -12801,7 +12793,7 @@ const jsenvPluginMappings = (mappings) => {
|
|
|
12801
12793
|
* `<script>window.backendUrl = __BACKEND_URL__;</script>` gets the JS literal,
|
|
12802
12794
|
* which is how a value is shared with every js file of the page.
|
|
12803
12795
|
* Use INJECTIONS.optional(value) for a placeholder that may be absent from the file
|
|
12804
|
-
* and INJECTIONS.global(value) to inject `Object.assign(
|
|
12796
|
+
* and INJECTIONS.global(value) to inject `Object.assign(globalThis, { ... })` instead of
|
|
12805
12797
|
* replacing a placeholder.
|
|
12806
12798
|
*
|
|
12807
12799
|
* @return {Promise<Object>} buildReturnValue
|
|
@@ -13841,6 +13833,9 @@ const prepareEntryPointBuild = async (
|
|
|
13841
13833
|
|
|
13842
13834
|
let _getOtherEntryBuildInfo;
|
|
13843
13835
|
const rawJsenvPluginStore = await createJsenvPluginStore([
|
|
13836
|
+
// First, ahead of the plugins given by the caller: what every other plugin
|
|
13837
|
+
// reads must be the patched file (see start_dev_server.js).
|
|
13838
|
+
...jsenvPluginPatches(patches),
|
|
13844
13839
|
...(mappings ? [jsenvPluginMappings(mappings)] : []),
|
|
13845
13840
|
{
|
|
13846
13841
|
name: "jsenv:other_entry_point_build_during_craft",
|
|
@@ -13875,7 +13870,6 @@ const prepareEntryPointBuild = async (
|
|
|
13875
13870
|
magicExtensions,
|
|
13876
13871
|
magicDirectoryIndex,
|
|
13877
13872
|
directoryReferenceEffect,
|
|
13878
|
-
patches,
|
|
13879
13873
|
injections,
|
|
13880
13874
|
transpilation: {
|
|
13881
13875
|
babelHelpersAsImport: !explicitJsModuleConversion,
|
package/dist/jsenv_core.js
CHANGED
|
@@ -4,7 +4,7 @@ import "@jsenv/sourcemap";
|
|
|
4
4
|
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
5
5
|
const INJECTIONS = {
|
|
6
6
|
/**
|
|
7
|
-
* Inject `Object.assign(
|
|
7
|
+
* Inject `Object.assign(globalThis, { [key]: value })` at the top of the file
|
|
8
8
|
* (into a script for html, into the module itself for js) instead of
|
|
9
9
|
* replacing a placeholder: the value is read at runtime as a global.
|
|
10
10
|
*/
|
|
@@ -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,
|
|
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
|
*/
|
|
@@ -5407,7 +5541,7 @@ const jsenvPluginDirectoryReferenceEffect = (
|
|
|
5407
5541
|
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
5408
5542
|
const INJECTIONS = {
|
|
5409
5543
|
/**
|
|
5410
|
-
* Inject `Object.assign(
|
|
5544
|
+
* Inject `Object.assign(globalThis, { [key]: value })` at the top of the file
|
|
5411
5545
|
* (into a script for html, into the module itself for js) instead of
|
|
5412
5546
|
* replacing a placeholder: the value is read at runtime as a global.
|
|
5413
5547
|
*/
|
|
@@ -5558,7 +5692,7 @@ const injectGlobals = (content, globals, urlInfo) => {
|
|
|
5558
5692
|
return globalInjectorOnHtml(content, globals, urlInfo);
|
|
5559
5693
|
}
|
|
5560
5694
|
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
5561
|
-
return globalsInjectorOnJs(content, globals
|
|
5695
|
+
return globalsInjectorOnJs(content, globals);
|
|
5562
5696
|
}
|
|
5563
5697
|
throw new Error(
|
|
5564
5698
|
createDetailedMessage(`cannot inject globals into "${urlInfo.type}"`, {
|
|
@@ -5578,9 +5712,7 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
5578
5712
|
url: urlInfo.url,
|
|
5579
5713
|
storeOriginalPositions: false,
|
|
5580
5714
|
});
|
|
5581
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
5582
|
-
isWebWorker: false,
|
|
5583
|
-
});
|
|
5715
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
5584
5716
|
injectJsenvScript(htmlAst, {
|
|
5585
5717
|
content: clientCode,
|
|
5586
5718
|
pluginName: "jsenv:inject_globals",
|
|
@@ -5589,24 +5721,17 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
5589
5721
|
content: stringifyHtmlAst(htmlAst),
|
|
5590
5722
|
};
|
|
5591
5723
|
};
|
|
5592
|
-
const globalsInjectorOnJs = (content, globals
|
|
5593
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
5594
|
-
isWebWorker:
|
|
5595
|
-
urlInfo.subtype === "worker" ||
|
|
5596
|
-
urlInfo.subtype === "service_worker" ||
|
|
5597
|
-
urlInfo.subtype === "shared_worker",
|
|
5598
|
-
});
|
|
5724
|
+
const globalsInjectorOnJs = (content, globals) => {
|
|
5725
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
5599
5726
|
const magicSource = createMagicSource(content);
|
|
5600
5727
|
magicSource.prepend(clientCode);
|
|
5601
5728
|
return magicSource.toContentAndSourcemap();
|
|
5602
5729
|
};
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
" ",
|
|
5609
|
-
)});`;
|
|
5730
|
+
// "globalThis" is the global object in a window, a worker and a service worker alike;
|
|
5731
|
+
// naming one of "window"/"self" would require knowing the file's subtype, which is not
|
|
5732
|
+
// known yet when the browser fetches a service worker on its own (update check).
|
|
5733
|
+
const generateClientCodeForGlobals = (globals) => {
|
|
5734
|
+
return `Object.assign(globalThis, ${JSON.stringify(globals, null, " ")});`;
|
|
5610
5735
|
};
|
|
5611
5736
|
|
|
5612
5737
|
const jsenvPluginInjections = (rawAssociations) => {
|
|
@@ -5771,140 +5896,6 @@ const asInheritedInjections = (injections) => {
|
|
|
5771
5896
|
return inheritedInjections;
|
|
5772
5897
|
};
|
|
5773
5898
|
|
|
5774
|
-
/*
|
|
5775
|
-
* Text patches applied to files as they are served and built, keyed by file:
|
|
5776
|
-
*
|
|
5777
|
-
* patches: {
|
|
5778
|
-
* "preact/dist/preact.mjs": [{ from: "a&&b", to: "a&&b&&c" }],
|
|
5779
|
-
* }
|
|
5780
|
-
*
|
|
5781
|
-
* A key is a url pattern relative to the root directory ("./main.js",
|
|
5782
|
-
* "**\/*.css"), or a path inside a package ("preact/dist/preact.mjs") found by
|
|
5783
|
-
* walking up from the root directory into node_modules, the way node does,
|
|
5784
|
-
* so the key holds wherever the package manager hoists the package.
|
|
5785
|
-
*
|
|
5786
|
-
* Every `from` must occur exactly once in the file, otherwise the file fails
|
|
5787
|
-
* to cook and says which patch did not apply: a dependency update that moved
|
|
5788
|
-
* the patched code must be looked at, never silently unpatched.
|
|
5789
|
-
*/
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
const jsenvPluginPatches = (rawPatches) => {
|
|
5793
|
-
if (!rawPatches || Object.keys(rawPatches).length === 0) {
|
|
5794
|
-
return [];
|
|
5795
|
-
}
|
|
5796
|
-
let findPatches;
|
|
5797
|
-
const patchesPlugin = {
|
|
5798
|
-
name: "jsenv:patches",
|
|
5799
|
-
appliesDuring: "*",
|
|
5800
|
-
init: (context) => {
|
|
5801
|
-
const { rootDirectoryUrl } = context;
|
|
5802
|
-
const patchesByPattern = {};
|
|
5803
|
-
for (const key of Object.keys(rawPatches)) {
|
|
5804
|
-
const patches = rawPatches[key];
|
|
5805
|
-
assertPatches(patches, key);
|
|
5806
|
-
patchesByPattern[resolvePatchKey(key, rootDirectoryUrl)] = patches;
|
|
5807
|
-
}
|
|
5808
|
-
const associations = URL_META.resolveAssociations(
|
|
5809
|
-
{ patches: patchesByPattern },
|
|
5810
|
-
rootDirectoryUrl,
|
|
5811
|
-
);
|
|
5812
|
-
findPatches = (url) => {
|
|
5813
|
-
const { patches } = URL_META.applyAssociations({
|
|
5814
|
-
url: asUrlWithoutSearch(url),
|
|
5815
|
-
associations,
|
|
5816
|
-
});
|
|
5817
|
-
return patches;
|
|
5818
|
-
};
|
|
5819
|
-
},
|
|
5820
|
-
transformUrlContent: (urlInfo) => {
|
|
5821
|
-
const patches = findPatches(urlInfo.url);
|
|
5822
|
-
if (!patches) {
|
|
5823
|
-
return null;
|
|
5824
|
-
}
|
|
5825
|
-
const { content } = urlInfo;
|
|
5826
|
-
const magicSource = createMagicSource(content);
|
|
5827
|
-
for (const { from, to } of patches) {
|
|
5828
|
-
const start = content.indexOf(from);
|
|
5829
|
-
const occurrenceCount =
|
|
5830
|
-
start === -1 ? 0 : content.indexOf(from, start + 1) === -1 ? 1 : 2;
|
|
5831
|
-
if (occurrenceCount !== 1) {
|
|
5832
|
-
const fileRelativeUrl = urlToRelativeUrl(
|
|
5833
|
-
urlInfo.url,
|
|
5834
|
-
urlInfo.context.rootDirectoryUrl,
|
|
5835
|
-
);
|
|
5836
|
-
throw new Error(
|
|
5837
|
-
`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.`,
|
|
5838
|
-
);
|
|
5839
|
-
}
|
|
5840
|
-
magicSource.replace({
|
|
5841
|
-
start,
|
|
5842
|
-
end: start + from.length,
|
|
5843
|
-
replacement: to,
|
|
5844
|
-
});
|
|
5845
|
-
}
|
|
5846
|
-
return magicSource.toContentAndSourcemap();
|
|
5847
|
-
},
|
|
5848
|
-
};
|
|
5849
|
-
return [patchesPlugin];
|
|
5850
|
-
};
|
|
5851
|
-
|
|
5852
|
-
const assertPatches = (patches, key) => {
|
|
5853
|
-
if (!Array.isArray(patches)) {
|
|
5854
|
-
throw new TypeError(
|
|
5855
|
-
`patches["${key}"] must be an array of { from, to }, got ${patches}`,
|
|
5856
|
-
);
|
|
5857
|
-
}
|
|
5858
|
-
for (const patch of patches) {
|
|
5859
|
-
if (
|
|
5860
|
-
!patch ||
|
|
5861
|
-
typeof patch.from !== "string" ||
|
|
5862
|
-
patch.from === "" ||
|
|
5863
|
-
typeof patch.to !== "string"
|
|
5864
|
-
) {
|
|
5865
|
-
throw new TypeError(
|
|
5866
|
-
`patches["${key}"] entries must be { from: string, to: string } with a non-empty "from"`,
|
|
5867
|
-
);
|
|
5868
|
-
}
|
|
5869
|
-
}
|
|
5870
|
-
};
|
|
5871
|
-
|
|
5872
|
-
// "./x", "../x", "/x", "file:///x" and "**/x" are url patterns; anything
|
|
5873
|
-
// else names a path inside a package, looked up in node_modules
|
|
5874
|
-
const resolvePatchKey = (key, rootDirectoryUrl) => {
|
|
5875
|
-
if (
|
|
5876
|
-
key.startsWith("./") ||
|
|
5877
|
-
key.startsWith("../") ||
|
|
5878
|
-
key.startsWith("/") ||
|
|
5879
|
-
key.startsWith("file:") ||
|
|
5880
|
-
key.startsWith("*")
|
|
5881
|
-
) {
|
|
5882
|
-
return key;
|
|
5883
|
-
}
|
|
5884
|
-
const segments = key.split("/");
|
|
5885
|
-
const packageName = key.startsWith("@")
|
|
5886
|
-
? `${segments[0]}/${segments[1]}`
|
|
5887
|
-
: segments[0];
|
|
5888
|
-
const pathInsidePackage = key.slice(packageName.length);
|
|
5889
|
-
let directoryUrl = new URL(rootDirectoryUrl);
|
|
5890
|
-
while (true) {
|
|
5891
|
-
const packageDirectoryUrl = new URL(
|
|
5892
|
-
`./node_modules/${packageName}/`,
|
|
5893
|
-
directoryUrl,
|
|
5894
|
-
);
|
|
5895
|
-
if (existsSync(packageDirectoryUrl)) {
|
|
5896
|
-
return String(new URL(`.${pathInsidePackage}`, packageDirectoryUrl));
|
|
5897
|
-
}
|
|
5898
|
-
const parentDirectoryUrl = new URL("../", directoryUrl);
|
|
5899
|
-
if (parentDirectoryUrl.href === directoryUrl.href) {
|
|
5900
|
-
throw new Error(
|
|
5901
|
-
`patches["${key}"]: package "${packageName}" not found in any node_modules above ${rootDirectoryUrl}`,
|
|
5902
|
-
);
|
|
5903
|
-
}
|
|
5904
|
-
directoryUrl = parentDirectoryUrl;
|
|
5905
|
-
}
|
|
5906
|
-
};
|
|
5907
|
-
|
|
5908
5899
|
const jsenvPluginInliningAsDataUrl = () => {
|
|
5909
5900
|
return {
|
|
5910
5901
|
name: "jsenv:inlining_as_data_url",
|
|
@@ -8344,7 +8335,6 @@ const getCorePlugins = ({
|
|
|
8344
8335
|
directoryListing = true,
|
|
8345
8336
|
directoryReferenceEffect,
|
|
8346
8337
|
supervisor,
|
|
8347
|
-
patches,
|
|
8348
8338
|
injections,
|
|
8349
8339
|
transpilation = true,
|
|
8350
8340
|
inlining = true,
|
|
@@ -8386,9 +8376,6 @@ const getCorePlugins = ({
|
|
|
8386
8376
|
...(packageBundle
|
|
8387
8377
|
? [jsenvPluginWorkspaceBundle({ packageDirectory })]
|
|
8388
8378
|
: []),
|
|
8389
|
-
// before everything else: what the other plugins read must be the
|
|
8390
|
-
// patched file
|
|
8391
|
-
...jsenvPluginPatches(patches),
|
|
8392
8379
|
// before reference analysis: an url written by an injection must hold its
|
|
8393
8380
|
// final value when references are analyzed
|
|
8394
8381
|
jsenvPluginInjections(injections),
|
|
@@ -12124,8 +12111,20 @@ const devServerPluginServeSourceFiles = ({
|
|
|
12124
12111
|
rootDirectoryUrl: sourceDirectoryUrl,
|
|
12125
12112
|
})
|
|
12126
12113
|
: sourceDirectoryUrl;
|
|
12114
|
+
// What the graph knows this resource as: the specifier a reference
|
|
12115
|
+
// decodes to never carries "?hot" (the client adds it to re-import,
|
|
12116
|
+
// see jsenv_plugin_hot_search_param), so the request is compared
|
|
12117
|
+
// without it — or nothing inline ever matches its own re-import,
|
|
12118
|
+
// and a file that a re-cook could create is created twice.
|
|
12119
|
+
const requestResourceWithoutHot = WEB_URL_CONVERTER.asWebUrl(
|
|
12120
|
+
requestedUrl,
|
|
12121
|
+
{
|
|
12122
|
+
origin: request.origin,
|
|
12123
|
+
rootDirectoryUrl: sourceDirectoryUrl,
|
|
12124
|
+
},
|
|
12125
|
+
).slice(request.origin.length);
|
|
12127
12126
|
let reference = kitchen.graph.inferReference(
|
|
12128
|
-
|
|
12127
|
+
requestResourceWithoutHot,
|
|
12129
12128
|
parentUrl,
|
|
12130
12129
|
);
|
|
12131
12130
|
if (!reference) {
|
|
@@ -12168,7 +12167,7 @@ const devServerPluginServeSourceFiles = ({
|
|
|
12168
12167
|
});
|
|
12169
12168
|
}
|
|
12170
12169
|
reference = kitchen.graph.inferReference(
|
|
12171
|
-
|
|
12170
|
+
requestResourceWithoutHot,
|
|
12172
12171
|
inlineParentUrl,
|
|
12173
12172
|
);
|
|
12174
12173
|
if (!reference) {
|
|
@@ -12748,6 +12747,11 @@ const startDevServer = async ({
|
|
|
12748
12747
|
serverStopCallbackSet.add(dependencyWatcher.stop);
|
|
12749
12748
|
|
|
12750
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),
|
|
12751
12755
|
jsenvPluginServerEvents({ clientAutoreload }),
|
|
12752
12756
|
// The client-monitoring dashboard is a dev-time convenience; a test-plan run
|
|
12753
12757
|
// doesn't use it and shouldn't pay for the reporter being injected into
|
|
@@ -12775,7 +12779,6 @@ const startDevServer = async ({
|
|
|
12775
12779
|
magicDirectoryIndex,
|
|
12776
12780
|
directoryListing,
|
|
12777
12781
|
supervisor,
|
|
12778
|
-
patches,
|
|
12779
12782
|
injections,
|
|
12780
12783
|
transpilation,
|
|
12781
12784
|
spa,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "41.5.
|
|
3
|
+
"version": "41.5.26",
|
|
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.
|
|
77
|
-
"@jsenv/js-module-fallback": "1.6.
|
|
78
|
-
"@jsenv/plugin-bundling": "2.10.
|
|
79
|
-
"@jsenv/plugin-minification": "1.7.
|
|
80
|
-
"@jsenv/plugin-supervisor": "1.8.
|
|
81
|
-
"@jsenv/plugin-transpilation": "1.7.
|
|
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.
|
|
83
|
+
"@jsenv/sourcemap": "1.4.10",
|
|
84
84
|
"react-table": "7.8.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
package/src/build/build.js
CHANGED
|
@@ -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";
|
|
@@ -177,7 +178,7 @@ import { jsenvPluginMappings } from "./jsenv_plugin_mappings.js";
|
|
|
177
178
|
* `<script>window.backendUrl = __BACKEND_URL__;</script>` gets the JS literal,
|
|
178
179
|
* which is how a value is shared with every js file of the page.
|
|
179
180
|
* Use INJECTIONS.optional(value) for a placeholder that may be absent from the file
|
|
180
|
-
* and INJECTIONS.global(value) to inject `Object.assign(
|
|
181
|
+
* and INJECTIONS.global(value) to inject `Object.assign(globalThis, { ... })` instead of
|
|
181
182
|
* replacing a placeholder.
|
|
182
183
|
*
|
|
183
184
|
* @return {Promise<Object>} buildReturnValue
|
|
@@ -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,
|
|
@@ -18,28 +18,33 @@ import { prependContent } from "../kitchen/prepend_content.js";
|
|
|
18
18
|
// we nevery minify those because they are already very small
|
|
19
19
|
// and would hurt the readability of something that can be critical to debug
|
|
20
20
|
export const injectGlobalMappings = async (urlInfo, mappings) => {
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
globalName: "window",
|
|
27
|
-
minification: false,
|
|
28
|
-
});
|
|
29
|
-
await prependContent(urlInfo, { type: "js_classic", content });
|
|
21
|
+
if (
|
|
22
|
+
urlInfo.type !== "html" &&
|
|
23
|
+
urlInfo.type !== "js_classic" &&
|
|
24
|
+
urlInfo.type !== "js_module"
|
|
25
|
+
) {
|
|
30
26
|
return;
|
|
31
27
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
// const minification = Boolean(
|
|
29
|
+
// urlInfo.context.getPluginMeta("willMinifyJsClassic"),
|
|
30
|
+
// );
|
|
31
|
+
const content = generateClientCodeForMappings(mappings, {
|
|
32
|
+
globalName: getGlobalName(urlInfo),
|
|
33
|
+
minification: false,
|
|
34
|
+
});
|
|
35
|
+
await prependContent(urlInfo, { type: "js_classic", content });
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// "globalThis" names the global object in a window and in a worker alike;
|
|
39
|
+
// the window/self split is only for runtimes predating it.
|
|
40
|
+
const getGlobalName = (urlInfo) => {
|
|
41
|
+
if (urlInfo.context.isSupportedOnCurrentClients("global_this")) {
|
|
42
|
+
return "globalThis";
|
|
43
|
+
}
|
|
44
|
+
if (isWebWorkerUrlInfo(urlInfo)) {
|
|
45
|
+
return "self";
|
|
42
46
|
}
|
|
47
|
+
return "window";
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
const generateClientCodeForMappings = (
|
|
@@ -274,8 +274,20 @@ export const devServerPluginServeSourceFiles = ({
|
|
|
274
274
|
rootDirectoryUrl: sourceDirectoryUrl,
|
|
275
275
|
})
|
|
276
276
|
: sourceDirectoryUrl;
|
|
277
|
+
// What the graph knows this resource as: the specifier a reference
|
|
278
|
+
// decodes to never carries "?hot" (the client adds it to re-import,
|
|
279
|
+
// see jsenv_plugin_hot_search_param), so the request is compared
|
|
280
|
+
// without it — or nothing inline ever matches its own re-import,
|
|
281
|
+
// and a file that a re-cook could create is created twice.
|
|
282
|
+
const requestResourceWithoutHot = WEB_URL_CONVERTER.asWebUrl(
|
|
283
|
+
requestedUrl,
|
|
284
|
+
{
|
|
285
|
+
origin: request.origin,
|
|
286
|
+
rootDirectoryUrl: sourceDirectoryUrl,
|
|
287
|
+
},
|
|
288
|
+
).slice(request.origin.length);
|
|
277
289
|
let reference = kitchen.graph.inferReference(
|
|
278
|
-
|
|
290
|
+
requestResourceWithoutHot,
|
|
279
291
|
parentUrl,
|
|
280
292
|
);
|
|
281
293
|
if (!reference) {
|
|
@@ -318,7 +330,7 @@ export const devServerPluginServeSourceFiles = ({
|
|
|
318
330
|
});
|
|
319
331
|
}
|
|
320
332
|
reference = kitchen.graph.inferReference(
|
|
321
|
-
|
|
333
|
+
requestResourceWithoutHot,
|
|
322
334
|
inlineParentUrl,
|
|
323
335
|
);
|
|
324
336
|
if (!reference) {
|
|
@@ -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,
|
|
@@ -5,7 +5,7 @@ import { composeTwoSourcemaps, createMagicSource } from "@jsenv/sourcemap";
|
|
|
5
5
|
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
6
6
|
export const INJECTIONS = {
|
|
7
7
|
/**
|
|
8
|
-
* Inject `Object.assign(
|
|
8
|
+
* Inject `Object.assign(globalThis, { [key]: value })` at the top of the file
|
|
9
9
|
* (into a script for html, into the module itself for js) instead of
|
|
10
10
|
* replacing a placeholder: the value is read at runtime as a global.
|
|
11
11
|
*/
|
|
@@ -156,7 +156,7 @@ export const injectGlobals = (content, globals, urlInfo) => {
|
|
|
156
156
|
return globalInjectorOnHtml(content, globals, urlInfo);
|
|
157
157
|
}
|
|
158
158
|
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
159
|
-
return globalsInjectorOnJs(content, globals
|
|
159
|
+
return globalsInjectorOnJs(content, globals);
|
|
160
160
|
}
|
|
161
161
|
throw new Error(
|
|
162
162
|
createDetailedMessage(`cannot inject globals into "${urlInfo.type}"`, {
|
|
@@ -176,9 +176,7 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
176
176
|
url: urlInfo.url,
|
|
177
177
|
storeOriginalPositions: false,
|
|
178
178
|
});
|
|
179
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
180
|
-
isWebWorker: false,
|
|
181
|
-
});
|
|
179
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
182
180
|
injectJsenvScript(htmlAst, {
|
|
183
181
|
content: clientCode,
|
|
184
182
|
pluginName: "jsenv:inject_globals",
|
|
@@ -187,22 +185,15 @@ const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
|
187
185
|
content: stringifyHtmlAst(htmlAst),
|
|
188
186
|
};
|
|
189
187
|
};
|
|
190
|
-
const globalsInjectorOnJs = (content, globals
|
|
191
|
-
const clientCode = generateClientCodeForGlobals(globals
|
|
192
|
-
isWebWorker:
|
|
193
|
-
urlInfo.subtype === "worker" ||
|
|
194
|
-
urlInfo.subtype === "service_worker" ||
|
|
195
|
-
urlInfo.subtype === "shared_worker",
|
|
196
|
-
});
|
|
188
|
+
const globalsInjectorOnJs = (content, globals) => {
|
|
189
|
+
const clientCode = generateClientCodeForGlobals(globals);
|
|
197
190
|
const magicSource = createMagicSource(content);
|
|
198
191
|
magicSource.prepend(clientCode);
|
|
199
192
|
return magicSource.toContentAndSourcemap();
|
|
200
193
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
" ",
|
|
207
|
-
)});`;
|
|
194
|
+
// "globalThis" is the global object in a window, a worker and a service worker alike;
|
|
195
|
+
// naming one of "window"/"self" would require knowing the file's subtype, which is not
|
|
196
|
+
// known yet when the browser fetches a service worker on its own (update check).
|
|
197
|
+
const generateClientCodeForGlobals = (globals) => {
|
|
198
|
+
return `Object.assign(globalThis, ${JSON.stringify(globals, null, " ")});`;
|
|
208
199
|
};
|
package/src/plugins/plugins.js
CHANGED
|
@@ -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),
|