@jsenv/core 40.6.2 → 40.7.1
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/build/browserslist_index/browserslist_index.js +62 -48
- package/dist/build/build.js +412 -185
- package/dist/build/jsenv_core_packages.js +103 -105
- package/dist/client/directory_listing/js/directory_listing.js +41 -26
- package/dist/client/ribbon/ribbon.js +40 -37
- package/dist/jsenv_core.js +4 -0
- package/dist/start_build_server/jsenv_core_packages.js +29 -29
- package/dist/start_dev_server/jsenv_core_packages.js +103 -105
- package/dist/start_dev_server/start_dev_server.js +412 -182
- package/package.json +21 -12
- package/src/build/build.js +9 -9
- package/src/build/build_specifier_manager.js +3 -3
- package/src/build/build_urls_generator.js +2 -2
- package/src/dev/start_dev_server.js +11 -8
- package/src/helpers/web_url_converter.js +2 -2
- package/src/kitchen/errors.js +1 -1
- package/src/kitchen/kitchen.js +2 -0
- package/src/kitchen/out_directory_url.js +2 -2
- package/src/kitchen/url_graph/url_graph.js +1 -0
- package/src/kitchen/url_graph/url_info_injections.js +172 -0
- package/src/kitchen/url_graph/url_info_transformations.js +28 -7
- package/src/main.js +1 -1
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +2 -2
- package/src/plugins/chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js +1 -0
- package/src/plugins/global_scenarios/jsenv_plugin_global_scenarios.js +4 -9
- package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -0
- package/src/plugins/injections/jsenv_plugin_injections.js +51 -85
- package/src/plugins/plugin_controller.js +28 -7
- package/src/plugins/plugins.js +3 -1
- package/src/plugins/protocol_file/client/directory_listing.jsx +42 -23
- package/src/plugins/protocol_file/file_and_server_urls_converter.js +2 -5
- package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +65 -49
- package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +36 -3
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +3 -0
- package/src/plugins/ribbon/client/ribbon.js +40 -37
- package/src/plugins/injections/internal/inject_globals.js +0 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "40.
|
|
3
|
+
"version": "40.7.1",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -77,23 +77,28 @@
|
|
|
77
77
|
"playwright:install": "npx playwright install-deps && npx playwright install",
|
|
78
78
|
"https:setup": "npx @jsenv/https-local setup",
|
|
79
79
|
"prepublishOnly": "npm run build",
|
|
80
|
+
"database:install": "npx @jsenv/database install",
|
|
81
|
+
"database:start": "npx @jsenv/database start",
|
|
82
|
+
"database:stop": "npx @jsenv/database stop",
|
|
83
|
+
"database:setup": "npx @jsenv/database setup",
|
|
80
84
|
"oto:start": "npm run start -w oto"
|
|
81
85
|
},
|
|
82
86
|
"dependencies": {
|
|
83
87
|
"@financial-times/polyfill-useragent-normaliser": "1.10.2",
|
|
84
|
-
"@jsenv/ast": "6.7.
|
|
85
|
-
"@jsenv/js-module-fallback": "1.4.
|
|
86
|
-
"@jsenv/plugin-bundling": "2.9.
|
|
88
|
+
"@jsenv/ast": "6.7.6",
|
|
89
|
+
"@jsenv/js-module-fallback": "1.4.18",
|
|
90
|
+
"@jsenv/plugin-bundling": "2.9.8",
|
|
87
91
|
"@jsenv/plugin-minification": "1.7.0",
|
|
88
|
-
"@jsenv/plugin-supervisor": "1.7.
|
|
89
|
-
"@jsenv/plugin-transpilation": "1.5.
|
|
92
|
+
"@jsenv/plugin-supervisor": "1.7.3",
|
|
93
|
+
"@jsenv/plugin-transpilation": "1.5.50",
|
|
90
94
|
"@jsenv/server": "16.1.2",
|
|
91
|
-
"@jsenv/sourcemap": "1.3.
|
|
95
|
+
"@jsenv/sourcemap": "1.3.9",
|
|
96
|
+
"react-table": "7.8.0"
|
|
92
97
|
},
|
|
93
98
|
"devDependencies": {
|
|
94
|
-
"@babel/plugin-syntax-decorators": "7.
|
|
95
|
-
"@babel/plugin-syntax-import-attributes": "7.
|
|
96
|
-
"@babel/plugin-syntax-optional-chaining-assign": "7.
|
|
99
|
+
"@babel/plugin-syntax-decorators": "7.27.1",
|
|
100
|
+
"@babel/plugin-syntax-import-attributes": "7.27.1",
|
|
101
|
+
"@babel/plugin-syntax-optional-chaining-assign": "7.27.1",
|
|
97
102
|
"@jsenv/abort": "workspace:*",
|
|
98
103
|
"@jsenv/assert": "workspace:*",
|
|
99
104
|
"@jsenv/cli": "workspace:*",
|
|
@@ -111,6 +116,7 @@
|
|
|
111
116
|
"@jsenv/os-metrics": "workspace:*",
|
|
112
117
|
"@jsenv/performance-impact": "workspace:*",
|
|
113
118
|
"@jsenv/plugin-as-js-classic": "workspace:*",
|
|
119
|
+
"@jsenv/plugin-database-manager": "workspace:*",
|
|
114
120
|
"@jsenv/router": "workspace:*",
|
|
115
121
|
"@jsenv/runtime-compat": "workspace:*",
|
|
116
122
|
"@jsenv/snapshot": "workspace:*",
|
|
@@ -123,12 +129,15 @@
|
|
|
123
129
|
"@playwright/browser-firefox": "1.52.0",
|
|
124
130
|
"@playwright/browser-webkit": "1.52.0",
|
|
125
131
|
"babel-plugin-transform-async-to-promises": "0.8.18",
|
|
126
|
-
"eslint": "9.
|
|
127
|
-
"open": "10.1.
|
|
132
|
+
"eslint": "9.26.0",
|
|
133
|
+
"open": "10.1.2",
|
|
128
134
|
"playwright": "1.52.0",
|
|
129
135
|
"preact": "10.26.5",
|
|
136
|
+
"preact-iso": "2.9.1",
|
|
130
137
|
"prettier": "3.5.3",
|
|
138
|
+
"prettier-plugin-embed": "0.5.0",
|
|
131
139
|
"prettier-plugin-organize-imports": "4.1.0",
|
|
140
|
+
"prettier-plugin-sql": "0.19.0",
|
|
132
141
|
"strip-ansi": "7.1.0"
|
|
133
142
|
}
|
|
134
143
|
}
|
package/src/build/build.js
CHANGED
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
nodeDefaultRuntimeCompat,
|
|
53
53
|
} from "@jsenv/runtime-compat";
|
|
54
54
|
import {
|
|
55
|
-
|
|
55
|
+
urlIsOrIsInsideOf,
|
|
56
56
|
urlToBasename,
|
|
57
57
|
urlToExtension,
|
|
58
58
|
urlToFilename,
|
|
@@ -208,7 +208,7 @@ export const build = async ({
|
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
if (!
|
|
211
|
+
if (!urlIsOrIsInsideOf(sourceUrl, sourceDirectoryUrl)) {
|
|
212
212
|
throw new Error(
|
|
213
213
|
`The key "${key}" in "entryPoints" is invalid: it must be inside the source directory at ${sourceDirectoryUrl}.`,
|
|
214
214
|
);
|
|
@@ -259,7 +259,7 @@ export const build = async ({
|
|
|
259
259
|
`The buildRelativeUrl "${buildRelativeUrl}"${forEntryPointOrEmpty} is invalid: it must be a relative url.`,
|
|
260
260
|
);
|
|
261
261
|
}
|
|
262
|
-
if (!
|
|
262
|
+
if (!urlIsOrIsInsideOf(buildUrl, buildDirectoryUrl)) {
|
|
263
263
|
throw new Error(
|
|
264
264
|
`The buildRelativeUrl "${buildRelativeUrl}"${forEntryPointOrEmpty} is invalid: it must be inside the build directory at ${buildDirectoryUrl}.`,
|
|
265
265
|
);
|
|
@@ -552,7 +552,7 @@ export const build = async ({
|
|
|
552
552
|
if (
|
|
553
553
|
process.env.CAPTURING_SIDE_EFFECTS ||
|
|
554
554
|
(!import.meta.build &&
|
|
555
|
-
|
|
555
|
+
urlIsOrIsInsideOf(sourceDirectoryUrl, jsenvCoreDirectoryUrl))
|
|
556
556
|
) {
|
|
557
557
|
outDirectoryUrl = new URL("../.jsenv_b/", sourceDirectoryUrl).href;
|
|
558
558
|
} else if (packageDirectory.url) {
|
|
@@ -761,7 +761,7 @@ export const build = async ({
|
|
|
761
761
|
let hasSomeOutdatedSideEffectUrl = false;
|
|
762
762
|
for (const packageSideEffectUrl of packageSideEffectUrlSet) {
|
|
763
763
|
if (
|
|
764
|
-
|
|
764
|
+
urlIsOrIsInsideOf(packageSideEffectUrl, buildDirectoryUrl) &&
|
|
765
765
|
!buildSideEffectUrlSet.has(packageSideEffectUrl)
|
|
766
766
|
) {
|
|
767
767
|
hasSomeOutdatedSideEffectUrl = true;
|
|
@@ -1061,7 +1061,7 @@ const prepareEntryPointBuild = async (
|
|
|
1061
1061
|
});
|
|
1062
1062
|
|
|
1063
1063
|
let _getOtherEntryBuildInfo;
|
|
1064
|
-
const rawPluginStore = createPluginStore([
|
|
1064
|
+
const rawPluginStore = await createPluginStore([
|
|
1065
1065
|
...(mappings ? [jsenvPluginMappings(mappings)] : []),
|
|
1066
1066
|
{
|
|
1067
1067
|
name: "jsenv:other_entry_point_build_during_craft",
|
|
@@ -1106,7 +1106,7 @@ const prepareEntryPointBuild = async (
|
|
|
1106
1106
|
packageSideEffects,
|
|
1107
1107
|
}),
|
|
1108
1108
|
]);
|
|
1109
|
-
const rawPluginController = createPluginController(
|
|
1109
|
+
const rawPluginController = await createPluginController(
|
|
1110
1110
|
rawPluginStore,
|
|
1111
1111
|
rawKitchen,
|
|
1112
1112
|
);
|
|
@@ -1177,7 +1177,7 @@ const prepareEntryPointBuild = async (
|
|
|
1177
1177
|
rawKitchen.graph.getUrlInfo(entryReference.url).type === "html" &&
|
|
1178
1178
|
rawKitchen.context.isSupportedOnCurrentClients("importmap"),
|
|
1179
1179
|
});
|
|
1180
|
-
const finalPluginStore = createPluginStore([
|
|
1180
|
+
const finalPluginStore = await createPluginStore([
|
|
1181
1181
|
jsenvPluginReferenceAnalysis({
|
|
1182
1182
|
...referenceAnalysis,
|
|
1183
1183
|
fetchInlineUrls: false,
|
|
@@ -1210,7 +1210,7 @@ const prepareEntryPointBuild = async (
|
|
|
1210
1210
|
},
|
|
1211
1211
|
buildSpecifierManager.jsenvPluginMoveToBuildDirectory,
|
|
1212
1212
|
]);
|
|
1213
|
-
const finalPluginController = createPluginController(
|
|
1213
|
+
const finalPluginController = await createPluginController(
|
|
1214
1214
|
finalPluginStore,
|
|
1215
1215
|
finalKitchen,
|
|
1216
1216
|
{
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
ensurePathnameTrailingSlash,
|
|
17
17
|
injectQueryParamIntoSpecifierWithoutEncoding,
|
|
18
18
|
renderUrlOrRelativeUrlFilename,
|
|
19
|
-
|
|
19
|
+
urlIsOrIsInsideOf,
|
|
20
20
|
urlToRelativeUrl,
|
|
21
21
|
} from "@jsenv/urls";
|
|
22
22
|
import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
|
|
@@ -676,7 +676,7 @@ export const createBuildSpecifierManager = ({
|
|
|
676
676
|
// const urlInfoInsideThisDirectorySet = new Set();
|
|
677
677
|
const versionsInfluencingThisDirectorySet = new Set();
|
|
678
678
|
for (const [url, urlInfo] of finalKitchen.graph.urlInfoMap) {
|
|
679
|
-
if (!
|
|
679
|
+
if (!urlIsOrIsInsideOf(url, directoryUrl)) {
|
|
680
680
|
continue;
|
|
681
681
|
}
|
|
682
682
|
// ideally we should exclude eventual directories as the are redundant
|
|
@@ -1075,7 +1075,7 @@ export const createBuildSpecifierManager = ({
|
|
|
1075
1075
|
}
|
|
1076
1076
|
if (
|
|
1077
1077
|
urlInfo.type === "asset" &&
|
|
1078
|
-
|
|
1078
|
+
urlIsOrIsInsideOf(urlInfo.url, buildDirectoryUrl)
|
|
1079
1079
|
) {
|
|
1080
1080
|
return;
|
|
1081
1081
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// import { ANSI } from "@jsenv/humanize";
|
|
2
2
|
import {
|
|
3
3
|
injectQueryParams,
|
|
4
|
-
|
|
4
|
+
urlIsOrIsInsideOf,
|
|
5
5
|
urlToFilename,
|
|
6
6
|
urlToRelativeUrl,
|
|
7
7
|
} from "@jsenv/urls";
|
|
@@ -36,7 +36,7 @@ export const createBuildUrlsGenerator = ({
|
|
|
36
36
|
if (buildUrlFromMap) {
|
|
37
37
|
return buildUrlFromMap;
|
|
38
38
|
}
|
|
39
|
-
if (
|
|
39
|
+
if (urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
|
|
40
40
|
if (ownerUrlInfo.searchParams.has("dynamic_import_id")) {
|
|
41
41
|
const ownerDirectoryPath = determineDirectoryPath({
|
|
42
42
|
sourceDirectoryUrl,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "@jsenv/server";
|
|
16
16
|
import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js";
|
|
17
17
|
import { URL_META } from "@jsenv/url-meta";
|
|
18
|
-
import {
|
|
18
|
+
import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
|
|
19
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
20
20
|
import { defaultRuntimeCompat } from "../build/build_params.js";
|
|
21
21
|
import { createEventEmitter } from "../helpers/event_emitter.js";
|
|
@@ -96,6 +96,7 @@ export const startDevServer = async ({
|
|
|
96
96
|
ribbon = true,
|
|
97
97
|
// toolbar = false,
|
|
98
98
|
onKitchenCreated = () => {},
|
|
99
|
+
spa,
|
|
99
100
|
|
|
100
101
|
sourcemaps = "inline",
|
|
101
102
|
sourcemapsSourcesContent,
|
|
@@ -130,7 +131,7 @@ export const startDevServer = async ({
|
|
|
130
131
|
if (
|
|
131
132
|
process.env.CAPTURING_SIDE_EFFECTS ||
|
|
132
133
|
(!import.meta.build &&
|
|
133
|
-
|
|
134
|
+
urlIsOrIsInsideOf(sourceDirectoryUrl, jsenvCoreDirectoryUrl))
|
|
134
135
|
) {
|
|
135
136
|
outDirectoryUrl = new URL("../.jsenv/", sourceDirectoryUrl);
|
|
136
137
|
} else {
|
|
@@ -247,7 +248,7 @@ export const startDevServer = async ({
|
|
|
247
248
|
read: readPackageAtOrNull,
|
|
248
249
|
};
|
|
249
250
|
|
|
250
|
-
const devServerPluginStore = createPluginStore([
|
|
251
|
+
const devServerPluginStore = await createPluginStore([
|
|
251
252
|
jsenvPluginServerEvents({ clientAutoreload }),
|
|
252
253
|
...plugins,
|
|
253
254
|
...getCorePlugins({
|
|
@@ -265,6 +266,7 @@ export const startDevServer = async ({
|
|
|
265
266
|
supervisor,
|
|
266
267
|
injections,
|
|
267
268
|
transpilation,
|
|
269
|
+
spa,
|
|
268
270
|
|
|
269
271
|
clientAutoreload,
|
|
270
272
|
clientAutoreloadOnServerRestart,
|
|
@@ -272,7 +274,7 @@ export const startDevServer = async ({
|
|
|
272
274
|
ribbon,
|
|
273
275
|
}),
|
|
274
276
|
]);
|
|
275
|
-
const getOrCreateKitchen = (request) => {
|
|
277
|
+
const getOrCreateKitchen = async (request) => {
|
|
276
278
|
const { runtimeName, runtimeVersion } = parseUserAgentHeader(
|
|
277
279
|
request.headers["user-agent"] || "",
|
|
278
280
|
);
|
|
@@ -395,7 +397,7 @@ export const startDevServer = async ({
|
|
|
395
397
|
);
|
|
396
398
|
},
|
|
397
399
|
);
|
|
398
|
-
const devServerPluginController = createPluginController(
|
|
400
|
+
const devServerPluginController = await createPluginController(
|
|
399
401
|
devServerPluginStore,
|
|
400
402
|
kitchen,
|
|
401
403
|
);
|
|
@@ -411,8 +413,8 @@ export const startDevServer = async ({
|
|
|
411
413
|
|
|
412
414
|
finalServices.push({
|
|
413
415
|
name: "jsenv:dev_server_routes",
|
|
414
|
-
augmentRouteFetchSecondArg: (request) => {
|
|
415
|
-
const kitchen = getOrCreateKitchen(request);
|
|
416
|
+
augmentRouteFetchSecondArg: async (request) => {
|
|
417
|
+
const kitchen = await getOrCreateKitchen(request);
|
|
416
418
|
return { kitchen };
|
|
417
419
|
},
|
|
418
420
|
routes: [
|
|
@@ -615,6 +617,7 @@ export const startDevServer = async ({
|
|
|
615
617
|
},
|
|
616
618
|
],
|
|
617
619
|
});
|
|
620
|
+
finalServices.push(...devServerPluginStore.allDevServerServices);
|
|
618
621
|
}
|
|
619
622
|
// jsenv error handler service
|
|
620
623
|
{
|
|
@@ -643,7 +646,7 @@ export const startDevServer = async ({
|
|
|
643
646
|
body: response.body,
|
|
644
647
|
});
|
|
645
648
|
return {
|
|
646
|
-
status:
|
|
649
|
+
status: response.status,
|
|
647
650
|
headers: {
|
|
648
651
|
"content-type": "application/json",
|
|
649
652
|
"content-length": Buffer.byteLength(body),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ensureWindowsDriveLetter } from "@jsenv/filesystem";
|
|
2
|
-
import { moveUrl,
|
|
2
|
+
import { moveUrl, urlIsOrIsInsideOf } from "@jsenv/urls";
|
|
3
3
|
|
|
4
4
|
export const WEB_URL_CONVERTER = {
|
|
5
5
|
asWebUrl: (fileUrl, webServer) => {
|
|
6
|
-
if (
|
|
6
|
+
if (urlIsOrIsInsideOf(fileUrl, webServer.rootDirectoryUrl)) {
|
|
7
7
|
return moveUrl({
|
|
8
8
|
url: fileUrl,
|
|
9
9
|
from: webServer.rootDirectoryUrl,
|
package/src/kitchen/errors.js
CHANGED
package/src/kitchen/kitchen.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
determineSourcemapFileUrl,
|
|
18
18
|
} from "./out_directory_url.js";
|
|
19
19
|
import { createUrlGraph } from "./url_graph/url_graph.js";
|
|
20
|
+
import { isPlaceholderInjection } from "./url_graph/url_info_injections.js";
|
|
20
21
|
import { createUrlInfoTransformer } from "./url_graph/url_info_transformations.js";
|
|
21
22
|
import { urlSpecifierEncoding } from "./url_graph/url_specifier_encoding.js";
|
|
22
23
|
|
|
@@ -102,6 +103,7 @@ export const createKitchen = ({
|
|
|
102
103
|
inlineContentClientFileUrl,
|
|
103
104
|
isSupportedOnCurrentClients: memoizeIsSupported(clientRuntimeCompat),
|
|
104
105
|
isSupportedOnFutureClients: memoizeIsSupported(runtimeCompat),
|
|
106
|
+
isPlaceholderInjection,
|
|
105
107
|
getPluginMeta: null,
|
|
106
108
|
sourcemaps,
|
|
107
109
|
outDirectoryUrl,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureWindowsDriveLetter } from "@jsenv/filesystem";
|
|
2
2
|
import { generateSourcemapFileUrl } from "@jsenv/sourcemap";
|
|
3
|
-
import { moveUrl, setUrlFilename,
|
|
3
|
+
import { moveUrl, setUrlFilename, urlIsOrIsInsideOf } from "@jsenv/urls";
|
|
4
4
|
|
|
5
5
|
export const determineFileUrlForOutDirectory = (urlInfo) => {
|
|
6
6
|
let { url, filenameHint } = urlInfo;
|
|
@@ -11,7 +11,7 @@ export const determineFileUrlForOutDirectory = (urlInfo) => {
|
|
|
11
11
|
if (!url.startsWith("file:")) {
|
|
12
12
|
return url;
|
|
13
13
|
}
|
|
14
|
-
if (!
|
|
14
|
+
if (!urlIsOrIsInsideOf(url, rootDirectoryUrl)) {
|
|
15
15
|
const fsRootUrl = ensureWindowsDriveLetter("file:///", url);
|
|
16
16
|
url = `${rootDirectoryUrl}@fs/${url.slice(fsRootUrl.length)}`;
|
|
17
17
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { injectJsenvScript, parseHtml, stringifyHtmlAst } from "@jsenv/ast";
|
|
2
|
+
import { composeTwoSourcemaps, createMagicSource } from "@jsenv/sourcemap";
|
|
3
|
+
|
|
4
|
+
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
5
|
+
export const INJECTIONS = {
|
|
6
|
+
global: (value) => {
|
|
7
|
+
return { [injectionSymbol]: "global", value };
|
|
8
|
+
},
|
|
9
|
+
optional: (value) => {
|
|
10
|
+
return { [injectionSymbol]: "optional", value };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const isPlaceholderInjection = (value) => {
|
|
15
|
+
return (
|
|
16
|
+
!value || !value[injectionSymbol] || value[injectionSymbol] !== "global"
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const applyContentInjections = (content, contentInjections, urlInfo) => {
|
|
21
|
+
const keys = Object.keys(contentInjections);
|
|
22
|
+
const globals = {};
|
|
23
|
+
const placeholderReplacements = [];
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const contentInjection = contentInjections[key];
|
|
26
|
+
if (contentInjection && contentInjection[injectionSymbol]) {
|
|
27
|
+
const valueBehindSymbol = contentInjection[injectionSymbol];
|
|
28
|
+
if (valueBehindSymbol === "global") {
|
|
29
|
+
globals[key] = contentInjection.value;
|
|
30
|
+
} else if (valueBehindSymbol === "optional") {
|
|
31
|
+
placeholderReplacements.push({
|
|
32
|
+
key,
|
|
33
|
+
isOptional: true,
|
|
34
|
+
value: contentInjection.value,
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
throw new Error(`unknown injection type "${valueBehindSymbol}"`);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
placeholderReplacements.push({
|
|
41
|
+
key,
|
|
42
|
+
value: contentInjection,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const needGlobalsInjection = Object.keys(globals).length > 0;
|
|
48
|
+
const needPlaceholderReplacements = placeholderReplacements.length > 0;
|
|
49
|
+
|
|
50
|
+
if (needGlobalsInjection && needPlaceholderReplacements) {
|
|
51
|
+
const globalInjectionResult = injectGlobals(content, globals, urlInfo);
|
|
52
|
+
const replaceInjectionResult = injectPlaceholderReplacements(
|
|
53
|
+
globalInjectionResult.content,
|
|
54
|
+
placeholderReplacements,
|
|
55
|
+
urlInfo,
|
|
56
|
+
);
|
|
57
|
+
return {
|
|
58
|
+
content: replaceInjectionResult.content,
|
|
59
|
+
sourcemap: composeTwoSourcemaps(
|
|
60
|
+
globalInjectionResult.sourcemap,
|
|
61
|
+
replaceInjectionResult.sourcemap,
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (needGlobalsInjection) {
|
|
66
|
+
return injectGlobals(content, globals, urlInfo);
|
|
67
|
+
}
|
|
68
|
+
if (needPlaceholderReplacements) {
|
|
69
|
+
return injectPlaceholderReplacements(
|
|
70
|
+
content,
|
|
71
|
+
placeholderReplacements,
|
|
72
|
+
urlInfo,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const injectPlaceholderReplacements = (
|
|
79
|
+
content,
|
|
80
|
+
placeholderReplacements,
|
|
81
|
+
urlInfo,
|
|
82
|
+
) => {
|
|
83
|
+
const magicSource = createMagicSource(content);
|
|
84
|
+
for (const { key, isOptional, value } of placeholderReplacements) {
|
|
85
|
+
let index = content.indexOf(key);
|
|
86
|
+
if (index === -1) {
|
|
87
|
+
if (!isOptional) {
|
|
88
|
+
urlInfo.context.logger.warn(
|
|
89
|
+
`placeholder "${key}" not found in ${urlInfo.url}.
|
|
90
|
+
--- suggestion a ---
|
|
91
|
+
Add "${key}" in that file.
|
|
92
|
+
--- suggestion b ---
|
|
93
|
+
Fix eventual typo in "${key}"?
|
|
94
|
+
--- suggestion c ---
|
|
95
|
+
Mark injection as optional using INJECTIONS.optional():
|
|
96
|
+
import { INJECTIONS } from "@jsenv/core";
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"${key}": INJECTIONS.optional(${JSON.stringify(value)}),
|
|
100
|
+
};`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
while (index !== -1) {
|
|
107
|
+
const start = index;
|
|
108
|
+
const end = index + key.length;
|
|
109
|
+
magicSource.replace({
|
|
110
|
+
start,
|
|
111
|
+
end,
|
|
112
|
+
replacement:
|
|
113
|
+
urlInfo.type === "js_classic" ||
|
|
114
|
+
urlInfo.type === "js_module" ||
|
|
115
|
+
urlInfo.type === "html"
|
|
116
|
+
? JSON.stringify(value, null, " ")
|
|
117
|
+
: value,
|
|
118
|
+
});
|
|
119
|
+
index = content.indexOf(key, end);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return magicSource.toContentAndSourcemap();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const injectGlobals = (content, globals, urlInfo) => {
|
|
126
|
+
if (urlInfo.type === "html") {
|
|
127
|
+
return globalInjectorOnHtml(content, globals, urlInfo);
|
|
128
|
+
}
|
|
129
|
+
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
130
|
+
return globalsInjectorOnJs(content, globals, urlInfo);
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`cannot inject globals into "${urlInfo.type}"`);
|
|
133
|
+
};
|
|
134
|
+
const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
135
|
+
// ideally we would inject an importmap but browser support is too low
|
|
136
|
+
// (even worse for worker/service worker)
|
|
137
|
+
// so for now we inject code into entry points
|
|
138
|
+
const htmlAst = parseHtml({
|
|
139
|
+
html: content,
|
|
140
|
+
url: urlInfo.url,
|
|
141
|
+
storeOriginalPositions: false,
|
|
142
|
+
});
|
|
143
|
+
const clientCode = generateClientCodeForGlobals(globals, {
|
|
144
|
+
isWebWorker: false,
|
|
145
|
+
});
|
|
146
|
+
injectJsenvScript(htmlAst, {
|
|
147
|
+
content: clientCode,
|
|
148
|
+
pluginName: "jsenv:inject_globals",
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
content: stringifyHtmlAst(htmlAst),
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
const globalsInjectorOnJs = (content, globals, urlInfo) => {
|
|
155
|
+
const clientCode = generateClientCodeForGlobals(globals, {
|
|
156
|
+
isWebWorker:
|
|
157
|
+
urlInfo.subtype === "worker" ||
|
|
158
|
+
urlInfo.subtype === "service_worker" ||
|
|
159
|
+
urlInfo.subtype === "shared_worker",
|
|
160
|
+
});
|
|
161
|
+
const magicSource = createMagicSource(content);
|
|
162
|
+
magicSource.prepend(clientCode);
|
|
163
|
+
return magicSource.toContentAndSourcemap();
|
|
164
|
+
};
|
|
165
|
+
const generateClientCodeForGlobals = (globals, { isWebWorker = false }) => {
|
|
166
|
+
const globalName = isWebWorker ? "self" : "window";
|
|
167
|
+
return `Object.assign(${globalName}, ${JSON.stringify(
|
|
168
|
+
globals,
|
|
169
|
+
null,
|
|
170
|
+
" ",
|
|
171
|
+
)});`;
|
|
172
|
+
};
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
defineGettersOnPropertiesDerivedFromContent,
|
|
18
18
|
defineGettersOnPropertiesDerivedFromOriginalContent,
|
|
19
19
|
} from "./url_content.js";
|
|
20
|
+
import { applyContentInjections } from "./url_info_injections.js";
|
|
20
21
|
|
|
21
22
|
export const createUrlInfoTransformer = ({
|
|
22
23
|
logger,
|
|
@@ -195,6 +196,7 @@ export const createUrlInfoTransformer = ({
|
|
|
195
196
|
contentLength,
|
|
196
197
|
sourcemap,
|
|
197
198
|
sourcemapIsWrong,
|
|
199
|
+
contentInjections,
|
|
198
200
|
} = transformations;
|
|
199
201
|
if (type) {
|
|
200
202
|
urlInfo.type = type;
|
|
@@ -202,13 +204,23 @@ export const createUrlInfoTransformer = ({
|
|
|
202
204
|
if (contentType) {
|
|
203
205
|
urlInfo.contentType = contentType;
|
|
204
206
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
if (Object.hasOwn(transformations, "contentInjections")) {
|
|
208
|
+
if (contentInjections) {
|
|
209
|
+
Object.assign(urlInfo.contentInjections, contentInjections);
|
|
210
|
+
}
|
|
211
|
+
if (content === undefined) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
let contentModified;
|
|
216
|
+
if (Object.hasOwn(transformations, "content")) {
|
|
217
|
+
contentModified = setContentProperties(urlInfo, {
|
|
218
|
+
content,
|
|
219
|
+
contentAst,
|
|
220
|
+
contentEtag,
|
|
221
|
+
contentLength,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
212
224
|
if (
|
|
213
225
|
sourcemap &&
|
|
214
226
|
mayHaveSourcemap(urlInfo) &&
|
|
@@ -400,6 +412,15 @@ export const createUrlInfoTransformer = ({
|
|
|
400
412
|
if (transformations) {
|
|
401
413
|
applyTransformations(urlInfo, transformations);
|
|
402
414
|
}
|
|
415
|
+
const { contentInjections } = urlInfo;
|
|
416
|
+
if (contentInjections && Object.keys(contentInjections).length > 0) {
|
|
417
|
+
const injectionTransformations = applyContentInjections(
|
|
418
|
+
urlInfo.content,
|
|
419
|
+
contentInjections,
|
|
420
|
+
urlInfo,
|
|
421
|
+
);
|
|
422
|
+
applyTransformations(urlInfo, injectionTransformations);
|
|
423
|
+
}
|
|
403
424
|
applyContentEffects(urlInfo);
|
|
404
425
|
urlInfo.contentFinalized = true;
|
|
405
426
|
};
|
package/src/main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
|
|
2
2
|
|
|
3
3
|
export const jsenvPluginAutoreloadServer = ({
|
|
4
4
|
clientFileChangeEventEmitter,
|
|
@@ -10,7 +10,7 @@ export const jsenvPluginAutoreloadServer = ({
|
|
|
10
10
|
serverEvents: {
|
|
11
11
|
reload: (serverEventInfo) => {
|
|
12
12
|
const formatUrlForClient = (url) => {
|
|
13
|
-
if (
|
|
13
|
+
if (urlIsOrIsInsideOf(url, serverEventInfo.rootDirectoryUrl)) {
|
|
14
14
|
return urlToRelativeUrl(url, serverEventInfo.rootDirectoryUrl);
|
|
15
15
|
}
|
|
16
16
|
if (url.startsWith("file:")) {
|
|
@@ -28,6 +28,7 @@ export const jsenvPluginChromeDevtoolsJson = () => {
|
|
|
28
28
|
devServerRoutes: [
|
|
29
29
|
{
|
|
30
30
|
endpoint: "GET /.well-known/appspecific/com.chrome.devtools.json",
|
|
31
|
+
declarationSource: import.meta.url,
|
|
31
32
|
fetch: (request, { kitchen }) => {
|
|
32
33
|
const { rootDirectoryUrl } = kitchen.context;
|
|
33
34
|
return Response.json({
|
|
@@ -5,21 +5,16 @@
|
|
|
5
5
|
* That will be replaced with true/false
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
INJECTIONS,
|
|
10
|
-
replacePlaceholders,
|
|
11
|
-
} from "../injections/jsenv_plugin_injections.js";
|
|
8
|
+
import { INJECTIONS } from "../../kitchen/url_graph/url_info_injections.js";
|
|
12
9
|
|
|
13
10
|
export const jsenvPluginGlobalScenarios = () => {
|
|
14
11
|
const transformIfNeeded = (urlInfo) => {
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
{
|
|
12
|
+
return {
|
|
13
|
+
contentInjections: {
|
|
18
14
|
__DEV__: INJECTIONS.optional(urlInfo.context.dev),
|
|
19
15
|
__BUILD__: INJECTIONS.optional(urlInfo.context.build),
|
|
20
16
|
},
|
|
21
|
-
|
|
22
|
-
);
|
|
17
|
+
};
|
|
23
18
|
};
|
|
24
19
|
|
|
25
20
|
return {
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* - replaced by true: When scenario matches (import.meta.dev and it's the dev server)
|
|
7
7
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
8
8
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
9
|
+
*
|
|
10
|
+
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { applyBabelPlugins } from "@jsenv/ast";
|