@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/dist/build/build.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { parseHtml, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, stringifyHtmlAst, applyBabelPlugins, generateUrlForInlineContent, parseJsWithAcorn, visitHtmlNodes, analyzeScriptNode, getHtmlNodeText, getHtmlNodeAttribute, getHtmlNodePosition, getUrlForContentInsideHtml, setHtmlNodeAttributes, setHtmlNodeText, parseCssUrls, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, parseJsUrls, getUrlForContentInsideJs, analyzeLinkNode,
|
|
1
|
+
import { parseHtml, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, stringifyHtmlAst, applyBabelPlugins, generateUrlForInlineContent, injectJsenvScript, parseJsWithAcorn, visitHtmlNodes, analyzeScriptNode, getHtmlNodeText, getHtmlNodeAttribute, getHtmlNodePosition, getUrlForContentInsideHtml, setHtmlNodeAttributes, setHtmlNodeText, parseCssUrls, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, parseJsUrls, getUrlForContentInsideJs, analyzeLinkNode, findHtmlNode, removeHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
|
|
2
2
|
import { jsenvPluginBundling } from "@jsenv/plugin-bundling";
|
|
3
3
|
import { jsenvPluginMinification } from "@jsenv/plugin-minification";
|
|
4
4
|
import { jsenvPluginTranspilation, jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
|
|
5
5
|
import { memoryUsage } from "node:process";
|
|
6
6
|
import { readFileSync, existsSync, readdirSync, lstatSync, realpathSync } from "node:fs";
|
|
7
|
-
import { lookupPackageDirectory, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity,
|
|
7
|
+
import { lookupPackageDirectory, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity, urlIsOrIsInsideOf, ensureWindowsDriveLetter, setUrlFilename, moveUrl, getCallerPosition, urlToBasename, urlToExtension, asSpecifierWithoutSearch, asUrlWithoutSearch, injectQueryParamsIntoSpecifier, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, urlToFileSystemPath, writeFileSync, createLogger, URL_META, applyNodeEsmResolution, RUNTIME_COMPAT, normalizeUrl, ANSI, CONTENT_TYPE, urlToFilename, DATA_URL, errorToHTML, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, defaultLookupPackageScope, defaultReadPackageJson, readCustomConditionsFromProcessArgs, readEntryStatSync, ensurePathnameTrailingSlash, compareFileUrls, applyFileSystemMagicResolution, getExtensionsToTry, setUrlExtension, isSpecifierForNodeBuiltin, renderDetails, humanizeDuration, humanizeFileSize, renderTable, renderBigSection, distributePercentages, humanizeMemory, comparePathnames, UNICODE, escapeRegexpSpecialChars, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, injectQueryParams, assertAndNormalizeDirectoryUrl, Abort, raceProcessTeardownEvents, startMonitoringCpuUsage, startMonitoringMemoryUsage, createLookupPackageDirectory, readPackageAtOrNull, inferRuntimeCompatFromClosestPackage, browserDefaultRuntimeCompat, nodeDefaultRuntimeCompat, clearDirectorySync, createTaskLog, ensureEmptyDirectory, updateJsonFileSync, createDynamicLog } from "./jsenv_core_packages.js";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import { generateSourcemapFileUrl, createMagicSource, composeTwoSourcemaps, generateSourcemapDataUrl, SOURCEMAP } from "@jsenv/sourcemap";
|
|
10
10
|
import { performance } from "node:perf_hooks";
|
|
@@ -306,7 +306,7 @@ ${error.message}`,
|
|
|
306
306
|
name: "TRANSFORM_URL_CONTENT_ERROR",
|
|
307
307
|
code: "PARSE_ERROR",
|
|
308
308
|
reason: error.message,
|
|
309
|
-
stack:
|
|
309
|
+
stack: transformError.stack,
|
|
310
310
|
trace,
|
|
311
311
|
asResponse: error.asResponse,
|
|
312
312
|
});
|
|
@@ -531,7 +531,7 @@ const determineFileUrlForOutDirectory = (urlInfo) => {
|
|
|
531
531
|
if (!url.startsWith("file:")) {
|
|
532
532
|
return url;
|
|
533
533
|
}
|
|
534
|
-
if (!
|
|
534
|
+
if (!urlIsOrIsInsideOf(url, rootDirectoryUrl)) {
|
|
535
535
|
const fsRootUrl = ensureWindowsDriveLetter("file:///", url);
|
|
536
536
|
url = `${rootDirectoryUrl}@fs/${url.slice(fsRootUrl.length)}`;
|
|
537
537
|
}
|
|
@@ -1858,6 +1858,7 @@ const createUrlInfo = (url, context) => {
|
|
|
1858
1858
|
contentLength: undefined,
|
|
1859
1859
|
contentFinalized: false,
|
|
1860
1860
|
contentSideEffects: [],
|
|
1861
|
+
contentInjections: {},
|
|
1861
1862
|
|
|
1862
1863
|
sourcemap: null,
|
|
1863
1864
|
sourcemapIsWrong: false,
|
|
@@ -2122,6 +2123,176 @@ ${urlInfo.url}`,
|
|
|
2122
2123
|
return urlInfo;
|
|
2123
2124
|
};
|
|
2124
2125
|
|
|
2126
|
+
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
2127
|
+
const INJECTIONS = {
|
|
2128
|
+
global: (value) => {
|
|
2129
|
+
return { [injectionSymbol]: "global", value };
|
|
2130
|
+
},
|
|
2131
|
+
optional: (value) => {
|
|
2132
|
+
return { [injectionSymbol]: "optional", value };
|
|
2133
|
+
},
|
|
2134
|
+
};
|
|
2135
|
+
|
|
2136
|
+
const isPlaceholderInjection = (value) => {
|
|
2137
|
+
return (
|
|
2138
|
+
!value || !value[injectionSymbol] || value[injectionSymbol] !== "global"
|
|
2139
|
+
);
|
|
2140
|
+
};
|
|
2141
|
+
|
|
2142
|
+
const applyContentInjections = (content, contentInjections, urlInfo) => {
|
|
2143
|
+
const keys = Object.keys(contentInjections);
|
|
2144
|
+
const globals = {};
|
|
2145
|
+
const placeholderReplacements = [];
|
|
2146
|
+
for (const key of keys) {
|
|
2147
|
+
const contentInjection = contentInjections[key];
|
|
2148
|
+
if (contentInjection && contentInjection[injectionSymbol]) {
|
|
2149
|
+
const valueBehindSymbol = contentInjection[injectionSymbol];
|
|
2150
|
+
if (valueBehindSymbol === "global") {
|
|
2151
|
+
globals[key] = contentInjection.value;
|
|
2152
|
+
} else if (valueBehindSymbol === "optional") {
|
|
2153
|
+
placeholderReplacements.push({
|
|
2154
|
+
key,
|
|
2155
|
+
isOptional: true,
|
|
2156
|
+
value: contentInjection.value,
|
|
2157
|
+
});
|
|
2158
|
+
} else {
|
|
2159
|
+
throw new Error(`unknown injection type "${valueBehindSymbol}"`);
|
|
2160
|
+
}
|
|
2161
|
+
} else {
|
|
2162
|
+
placeholderReplacements.push({
|
|
2163
|
+
key,
|
|
2164
|
+
value: contentInjection,
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
const needGlobalsInjection = Object.keys(globals).length > 0;
|
|
2170
|
+
const needPlaceholderReplacements = placeholderReplacements.length > 0;
|
|
2171
|
+
|
|
2172
|
+
if (needGlobalsInjection && needPlaceholderReplacements) {
|
|
2173
|
+
const globalInjectionResult = injectGlobals(content, globals, urlInfo);
|
|
2174
|
+
const replaceInjectionResult = injectPlaceholderReplacements(
|
|
2175
|
+
globalInjectionResult.content,
|
|
2176
|
+
placeholderReplacements,
|
|
2177
|
+
urlInfo,
|
|
2178
|
+
);
|
|
2179
|
+
return {
|
|
2180
|
+
content: replaceInjectionResult.content,
|
|
2181
|
+
sourcemap: composeTwoSourcemaps(
|
|
2182
|
+
globalInjectionResult.sourcemap,
|
|
2183
|
+
replaceInjectionResult.sourcemap,
|
|
2184
|
+
),
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
if (needGlobalsInjection) {
|
|
2188
|
+
return injectGlobals(content, globals, urlInfo);
|
|
2189
|
+
}
|
|
2190
|
+
if (needPlaceholderReplacements) {
|
|
2191
|
+
return injectPlaceholderReplacements(
|
|
2192
|
+
content,
|
|
2193
|
+
placeholderReplacements,
|
|
2194
|
+
urlInfo,
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
return null;
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2200
|
+
const injectPlaceholderReplacements = (
|
|
2201
|
+
content,
|
|
2202
|
+
placeholderReplacements,
|
|
2203
|
+
urlInfo,
|
|
2204
|
+
) => {
|
|
2205
|
+
const magicSource = createMagicSource(content);
|
|
2206
|
+
for (const { key, isOptional, value } of placeholderReplacements) {
|
|
2207
|
+
let index = content.indexOf(key);
|
|
2208
|
+
if (index === -1) {
|
|
2209
|
+
if (!isOptional) {
|
|
2210
|
+
urlInfo.context.logger.warn(
|
|
2211
|
+
`placeholder "${key}" not found in ${urlInfo.url}.
|
|
2212
|
+
--- suggestion a ---
|
|
2213
|
+
Add "${key}" in that file.
|
|
2214
|
+
--- suggestion b ---
|
|
2215
|
+
Fix eventual typo in "${key}"?
|
|
2216
|
+
--- suggestion c ---
|
|
2217
|
+
Mark injection as optional using INJECTIONS.optional():
|
|
2218
|
+
import { INJECTIONS } from "@jsenv/core";
|
|
2219
|
+
|
|
2220
|
+
return {
|
|
2221
|
+
"${key}": INJECTIONS.optional(${JSON.stringify(value)}),
|
|
2222
|
+
};`,
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
while (index !== -1) {
|
|
2229
|
+
const start = index;
|
|
2230
|
+
const end = index + key.length;
|
|
2231
|
+
magicSource.replace({
|
|
2232
|
+
start,
|
|
2233
|
+
end,
|
|
2234
|
+
replacement:
|
|
2235
|
+
urlInfo.type === "js_classic" ||
|
|
2236
|
+
urlInfo.type === "js_module" ||
|
|
2237
|
+
urlInfo.type === "html"
|
|
2238
|
+
? JSON.stringify(value, null, " ")
|
|
2239
|
+
: value,
|
|
2240
|
+
});
|
|
2241
|
+
index = content.indexOf(key, end);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
return magicSource.toContentAndSourcemap();
|
|
2245
|
+
};
|
|
2246
|
+
|
|
2247
|
+
const injectGlobals = (content, globals, urlInfo) => {
|
|
2248
|
+
if (urlInfo.type === "html") {
|
|
2249
|
+
return globalInjectorOnHtml(content, globals, urlInfo);
|
|
2250
|
+
}
|
|
2251
|
+
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
2252
|
+
return globalsInjectorOnJs(content, globals, urlInfo);
|
|
2253
|
+
}
|
|
2254
|
+
throw new Error(`cannot inject globals into "${urlInfo.type}"`);
|
|
2255
|
+
};
|
|
2256
|
+
const globalInjectorOnHtml = (content, globals, urlInfo) => {
|
|
2257
|
+
// ideally we would inject an importmap but browser support is too low
|
|
2258
|
+
// (even worse for worker/service worker)
|
|
2259
|
+
// so for now we inject code into entry points
|
|
2260
|
+
const htmlAst = parseHtml({
|
|
2261
|
+
html: content,
|
|
2262
|
+
url: urlInfo.url,
|
|
2263
|
+
storeOriginalPositions: false,
|
|
2264
|
+
});
|
|
2265
|
+
const clientCode = generateClientCodeForGlobals(globals, {
|
|
2266
|
+
isWebWorker: false,
|
|
2267
|
+
});
|
|
2268
|
+
injectJsenvScript(htmlAst, {
|
|
2269
|
+
content: clientCode,
|
|
2270
|
+
pluginName: "jsenv:inject_globals",
|
|
2271
|
+
});
|
|
2272
|
+
return {
|
|
2273
|
+
content: stringifyHtmlAst(htmlAst),
|
|
2274
|
+
};
|
|
2275
|
+
};
|
|
2276
|
+
const globalsInjectorOnJs = (content, globals, urlInfo) => {
|
|
2277
|
+
const clientCode = generateClientCodeForGlobals(globals, {
|
|
2278
|
+
isWebWorker:
|
|
2279
|
+
urlInfo.subtype === "worker" ||
|
|
2280
|
+
urlInfo.subtype === "service_worker" ||
|
|
2281
|
+
urlInfo.subtype === "shared_worker",
|
|
2282
|
+
});
|
|
2283
|
+
const magicSource = createMagicSource(content);
|
|
2284
|
+
magicSource.prepend(clientCode);
|
|
2285
|
+
return magicSource.toContentAndSourcemap();
|
|
2286
|
+
};
|
|
2287
|
+
const generateClientCodeForGlobals = (globals, { isWebWorker = false }) => {
|
|
2288
|
+
const globalName = isWebWorker ? "self" : "window";
|
|
2289
|
+
return `Object.assign(${globalName}, ${JSON.stringify(
|
|
2290
|
+
globals,
|
|
2291
|
+
null,
|
|
2292
|
+
" ",
|
|
2293
|
+
)});`;
|
|
2294
|
+
};
|
|
2295
|
+
|
|
2125
2296
|
const defineGettersOnPropertiesDerivedFromOriginalContent = (
|
|
2126
2297
|
urlInfo,
|
|
2127
2298
|
) => {
|
|
@@ -2402,6 +2573,7 @@ const createUrlInfoTransformer = ({
|
|
|
2402
2573
|
contentLength,
|
|
2403
2574
|
sourcemap,
|
|
2404
2575
|
sourcemapIsWrong,
|
|
2576
|
+
contentInjections,
|
|
2405
2577
|
} = transformations;
|
|
2406
2578
|
if (type) {
|
|
2407
2579
|
urlInfo.type = type;
|
|
@@ -2409,13 +2581,23 @@ const createUrlInfoTransformer = ({
|
|
|
2409
2581
|
if (contentType) {
|
|
2410
2582
|
urlInfo.contentType = contentType;
|
|
2411
2583
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2584
|
+
if (Object.hasOwn(transformations, "contentInjections")) {
|
|
2585
|
+
if (contentInjections) {
|
|
2586
|
+
Object.assign(urlInfo.contentInjections, contentInjections);
|
|
2587
|
+
}
|
|
2588
|
+
if (content === undefined) {
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
let contentModified;
|
|
2593
|
+
if (Object.hasOwn(transformations, "content")) {
|
|
2594
|
+
contentModified = setContentProperties(urlInfo, {
|
|
2595
|
+
content,
|
|
2596
|
+
contentAst,
|
|
2597
|
+
contentEtag,
|
|
2598
|
+
contentLength,
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2419
2601
|
if (
|
|
2420
2602
|
sourcemap &&
|
|
2421
2603
|
mayHaveSourcemap(urlInfo) &&
|
|
@@ -2607,6 +2789,15 @@ const createUrlInfoTransformer = ({
|
|
|
2607
2789
|
if (transformations) {
|
|
2608
2790
|
applyTransformations(urlInfo, transformations);
|
|
2609
2791
|
}
|
|
2792
|
+
const { contentInjections } = urlInfo;
|
|
2793
|
+
if (contentInjections && Object.keys(contentInjections).length > 0) {
|
|
2794
|
+
const injectionTransformations = applyContentInjections(
|
|
2795
|
+
urlInfo.content,
|
|
2796
|
+
contentInjections,
|
|
2797
|
+
urlInfo,
|
|
2798
|
+
);
|
|
2799
|
+
applyTransformations(urlInfo, injectionTransformations);
|
|
2800
|
+
}
|
|
2610
2801
|
applyContentEffects(urlInfo);
|
|
2611
2802
|
urlInfo.contentFinalized = true;
|
|
2612
2803
|
};
|
|
@@ -2752,6 +2943,7 @@ const createKitchen = ({
|
|
|
2752
2943
|
inlineContentClientFileUrl,
|
|
2753
2944
|
isSupportedOnCurrentClients: memoizeIsSupported(clientRuntimeCompat),
|
|
2754
2945
|
isSupportedOnFutureClients: memoizeIsSupported(runtimeCompat),
|
|
2946
|
+
isPlaceholderInjection,
|
|
2755
2947
|
getPluginMeta: null,
|
|
2756
2948
|
sourcemaps,
|
|
2757
2949
|
outDirectoryUrl,
|
|
@@ -3927,10 +4119,10 @@ const generateHtmlForSyntaxError = (
|
|
|
3927
4119
|
errorLinkText: `${htmlRelativeUrl}:${line}:${column}`,
|
|
3928
4120
|
syntaxErrorHTML: errorToHTML(htmlErrorContentFrame),
|
|
3929
4121
|
};
|
|
3930
|
-
const html = replacePlaceholders
|
|
4122
|
+
const html = replacePlaceholders(htmlForSyntaxError, replacers);
|
|
3931
4123
|
return html;
|
|
3932
4124
|
};
|
|
3933
|
-
const replacePlaceholders
|
|
4125
|
+
const replacePlaceholders = (html, replacers) => {
|
|
3934
4126
|
return html.replace(/\$\{(\w+)\}/g, (match, name) => {
|
|
3935
4127
|
const replacer = replacers[name];
|
|
3936
4128
|
if (replacer === undefined) {
|
|
@@ -3943,10 +4135,19 @@ const replacePlaceholders$1 = (html, replacers) => {
|
|
|
3943
4135
|
});
|
|
3944
4136
|
};
|
|
3945
4137
|
|
|
3946
|
-
const createPluginStore = (plugins) => {
|
|
4138
|
+
const createPluginStore = async (plugins) => {
|
|
3947
4139
|
const allDevServerRoutes = [];
|
|
4140
|
+
const allDevServerServices = [];
|
|
3948
4141
|
const pluginArray = [];
|
|
3949
|
-
|
|
4142
|
+
|
|
4143
|
+
const pluginPromises = [];
|
|
4144
|
+
const addPlugin = async (plugin) => {
|
|
4145
|
+
if (plugin && typeof plugin.then === "function") {
|
|
4146
|
+
pluginPromises.push(plugin);
|
|
4147
|
+
const value = await plugin;
|
|
4148
|
+
addPlugin(value);
|
|
4149
|
+
return;
|
|
4150
|
+
}
|
|
3950
4151
|
if (Array.isArray(plugin)) {
|
|
3951
4152
|
for (const subplugin of plugin) {
|
|
3952
4153
|
addPlugin(subplugin);
|
|
@@ -3965,21 +4166,28 @@ const createPluginStore = (plugins) => {
|
|
|
3965
4166
|
allDevServerRoutes.push(devServerRoute);
|
|
3966
4167
|
}
|
|
3967
4168
|
}
|
|
4169
|
+
if (plugin.devServerServices) {
|
|
4170
|
+
const devServerServices = plugin.devServerServices;
|
|
4171
|
+
for (const devServerService of devServerServices) {
|
|
4172
|
+
allDevServerServices.push(devServerService);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
3968
4175
|
pluginArray.push(plugin);
|
|
3969
4176
|
};
|
|
3970
4177
|
addPlugin(jsenvPluginHtmlSyntaxErrorFallback());
|
|
3971
4178
|
for (const plugin of plugins) {
|
|
3972
4179
|
addPlugin(plugin);
|
|
3973
4180
|
}
|
|
4181
|
+
await Promise.all(pluginPromises);
|
|
3974
4182
|
|
|
3975
4183
|
return {
|
|
3976
4184
|
pluginArray,
|
|
3977
|
-
|
|
3978
4185
|
allDevServerRoutes,
|
|
4186
|
+
allDevServerServices,
|
|
3979
4187
|
};
|
|
3980
4188
|
};
|
|
3981
4189
|
|
|
3982
|
-
const createPluginController = (
|
|
4190
|
+
const createPluginController = async (
|
|
3983
4191
|
pluginStore,
|
|
3984
4192
|
kitchen,
|
|
3985
4193
|
{ initialPuginsMeta = {} } = {},
|
|
@@ -4002,7 +4210,7 @@ const createPluginController = (
|
|
|
4002
4210
|
pluginCandidate.destroy?.();
|
|
4003
4211
|
continue;
|
|
4004
4212
|
}
|
|
4005
|
-
const initPluginResult = initPlugin(pluginCandidate, kitchen);
|
|
4213
|
+
const initPluginResult = await initPlugin(pluginCandidate, kitchen);
|
|
4006
4214
|
if (!initPluginResult) {
|
|
4007
4215
|
pluginCandidate.destroy?.();
|
|
4008
4216
|
continue;
|
|
@@ -4054,6 +4262,7 @@ const createPluginController = (
|
|
|
4054
4262
|
key === "serverEvents" ||
|
|
4055
4263
|
key === "mustStayFirst" ||
|
|
4056
4264
|
key === "devServerRoutes" ||
|
|
4265
|
+
key === "devServerServices" ||
|
|
4057
4266
|
key === "effect"
|
|
4058
4267
|
) {
|
|
4059
4268
|
continue;
|
|
@@ -4227,6 +4436,7 @@ const createPluginController = (
|
|
|
4227
4436
|
const HOOK_NAMES = [
|
|
4228
4437
|
"init",
|
|
4229
4438
|
"devServerRoutes", // is called only during dev/tests
|
|
4439
|
+
"devServerServices", // is called only during dev/tests
|
|
4230
4440
|
"resolveReference",
|
|
4231
4441
|
"redirectReference",
|
|
4232
4442
|
"transformReferenceSearchParams",
|
|
@@ -4281,12 +4491,12 @@ const testAppliesDuring = (plugin, kitchen) => {
|
|
|
4281
4491
|
`"appliesDuring" must be an object or a string, got ${appliesDuring}`,
|
|
4282
4492
|
);
|
|
4283
4493
|
};
|
|
4284
|
-
const initPlugin = (plugin, kitchen) => {
|
|
4494
|
+
const initPlugin = async (plugin, kitchen) => {
|
|
4285
4495
|
const { init } = plugin;
|
|
4286
4496
|
if (!init) {
|
|
4287
4497
|
return true;
|
|
4288
4498
|
}
|
|
4289
|
-
const initReturnValue = init(kitchen.context, { plugin });
|
|
4499
|
+
const initReturnValue = await init(kitchen.context, { plugin });
|
|
4290
4500
|
if (initReturnValue === false) {
|
|
4291
4501
|
return false;
|
|
4292
4502
|
}
|
|
@@ -4365,6 +4575,9 @@ const returnValueAssertions = [
|
|
|
4365
4575
|
return undefined;
|
|
4366
4576
|
}
|
|
4367
4577
|
if (typeof content !== "string" && !Buffer.isBuffer(content) && !body) {
|
|
4578
|
+
if (Object.hasOwn(valueReturned, "contentInjections")) {
|
|
4579
|
+
return undefined;
|
|
4580
|
+
}
|
|
4368
4581
|
throw new Error(
|
|
4369
4582
|
`Unexpected "content" returned by "${hook.plugin.name}" ${hook.name} hook: it must be a string or a buffer; got ${content}`,
|
|
4370
4583
|
);
|
|
@@ -6143,10 +6356,7 @@ const jsenvPluginVersionSearchParam = () => {
|
|
|
6143
6356
|
|
|
6144
6357
|
const FILE_AND_SERVER_URLS_CONVERTER = {
|
|
6145
6358
|
asServerUrl: (fileUrl, serverRootDirectoryUrl) => {
|
|
6146
|
-
if (fileUrl
|
|
6147
|
-
return "/";
|
|
6148
|
-
}
|
|
6149
|
-
if (urlIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
|
|
6359
|
+
if (urlIsOrIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
|
|
6150
6360
|
const urlRelativeToServer = urlToRelativeUrl(
|
|
6151
6361
|
fileUrl,
|
|
6152
6362
|
serverRootDirectoryUrl,
|
|
@@ -6172,103 +6382,6 @@ const FILE_AND_SERVER_URLS_CONVERTER = {
|
|
|
6172
6382
|
},
|
|
6173
6383
|
};
|
|
6174
6384
|
|
|
6175
|
-
const jsenvPluginInjections = (rawAssociations) => {
|
|
6176
|
-
let resolvedAssociations;
|
|
6177
|
-
|
|
6178
|
-
return {
|
|
6179
|
-
name: "jsenv:injections",
|
|
6180
|
-
appliesDuring: "*",
|
|
6181
|
-
init: (context) => {
|
|
6182
|
-
resolvedAssociations = URL_META.resolveAssociations(
|
|
6183
|
-
{ injectionsGetter: rawAssociations },
|
|
6184
|
-
context.rootDirectoryUrl,
|
|
6185
|
-
);
|
|
6186
|
-
},
|
|
6187
|
-
transformUrlContent: async (urlInfo) => {
|
|
6188
|
-
const { injectionsGetter } = URL_META.applyAssociations({
|
|
6189
|
-
url: asUrlWithoutSearch(urlInfo.url),
|
|
6190
|
-
associations: resolvedAssociations,
|
|
6191
|
-
});
|
|
6192
|
-
if (!injectionsGetter) {
|
|
6193
|
-
return null;
|
|
6194
|
-
}
|
|
6195
|
-
if (typeof injectionsGetter !== "function") {
|
|
6196
|
-
throw new TypeError("injectionsGetter must be a function");
|
|
6197
|
-
}
|
|
6198
|
-
const injections = await injectionsGetter(urlInfo);
|
|
6199
|
-
if (!injections) {
|
|
6200
|
-
return null;
|
|
6201
|
-
}
|
|
6202
|
-
const keys = Object.keys(injections);
|
|
6203
|
-
if (keys.length === 0) {
|
|
6204
|
-
return null;
|
|
6205
|
-
}
|
|
6206
|
-
return replacePlaceholders(urlInfo.content, injections, urlInfo);
|
|
6207
|
-
},
|
|
6208
|
-
};
|
|
6209
|
-
};
|
|
6210
|
-
|
|
6211
|
-
const injectionSymbol = Symbol.for("jsenv_injection");
|
|
6212
|
-
const INJECTIONS = {
|
|
6213
|
-
optional: (value) => {
|
|
6214
|
-
return { [injectionSymbol]: "optional", value };
|
|
6215
|
-
},
|
|
6216
|
-
};
|
|
6217
|
-
|
|
6218
|
-
// we export this because it is imported by jsenv_plugin_placeholder.js and unit test
|
|
6219
|
-
const replacePlaceholders = (content, replacements, urlInfo) => {
|
|
6220
|
-
const magicSource = createMagicSource(content);
|
|
6221
|
-
for (const key of Object.keys(replacements)) {
|
|
6222
|
-
let index = content.indexOf(key);
|
|
6223
|
-
const replacement = replacements[key];
|
|
6224
|
-
let isOptional;
|
|
6225
|
-
let value;
|
|
6226
|
-
if (replacement && replacement[injectionSymbol]) {
|
|
6227
|
-
const valueBehindSymbol = replacement[injectionSymbol];
|
|
6228
|
-
isOptional = valueBehindSymbol === "optional";
|
|
6229
|
-
value = replacement.value;
|
|
6230
|
-
} else {
|
|
6231
|
-
value = replacement;
|
|
6232
|
-
}
|
|
6233
|
-
if (index === -1) {
|
|
6234
|
-
if (!isOptional) {
|
|
6235
|
-
urlInfo.context.logger.warn(
|
|
6236
|
-
`placeholder "${key}" not found in ${urlInfo.url}.
|
|
6237
|
-
--- suggestion a ---
|
|
6238
|
-
Add "${key}" in that file.
|
|
6239
|
-
--- suggestion b ---
|
|
6240
|
-
Fix eventual typo in "${key}"?
|
|
6241
|
-
--- suggestion c ---
|
|
6242
|
-
Mark injection as optional using INJECTIONS.optional():
|
|
6243
|
-
import { INJECTIONS } from "@jsenv/core";
|
|
6244
|
-
|
|
6245
|
-
return {
|
|
6246
|
-
"${key}": INJECTIONS.optional(${JSON.stringify(value)}),
|
|
6247
|
-
};`,
|
|
6248
|
-
);
|
|
6249
|
-
}
|
|
6250
|
-
continue;
|
|
6251
|
-
}
|
|
6252
|
-
|
|
6253
|
-
while (index !== -1) {
|
|
6254
|
-
const start = index;
|
|
6255
|
-
const end = index + key.length;
|
|
6256
|
-
magicSource.replace({
|
|
6257
|
-
start,
|
|
6258
|
-
end,
|
|
6259
|
-
replacement:
|
|
6260
|
-
urlInfo.type === "js_classic" ||
|
|
6261
|
-
urlInfo.type === "js_module" ||
|
|
6262
|
-
urlInfo.type === "html"
|
|
6263
|
-
? JSON.stringify(value, null, " ")
|
|
6264
|
-
: value,
|
|
6265
|
-
});
|
|
6266
|
-
index = content.indexOf(key, end);
|
|
6267
|
-
}
|
|
6268
|
-
}
|
|
6269
|
-
return magicSource.toContentAndSourcemap();
|
|
6270
|
-
};
|
|
6271
|
-
|
|
6272
6385
|
/*
|
|
6273
6386
|
* NICE TO HAVE:
|
|
6274
6387
|
*
|
|
@@ -6299,6 +6412,7 @@ const htmlFileUrlForDirectory = import.meta.resolve(
|
|
|
6299
6412
|
);
|
|
6300
6413
|
|
|
6301
6414
|
const jsenvPluginDirectoryListing = ({
|
|
6415
|
+
spa,
|
|
6302
6416
|
urlMocks = false,
|
|
6303
6417
|
autoreload = true,
|
|
6304
6418
|
directoryContentMagicName,
|
|
@@ -6340,7 +6454,7 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6340
6454
|
return null;
|
|
6341
6455
|
}
|
|
6342
6456
|
}
|
|
6343
|
-
return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(
|
|
6457
|
+
return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(requestedUrl)}&enoent`;
|
|
6344
6458
|
}
|
|
6345
6459
|
const isDirectory = fsStat?.isDirectory();
|
|
6346
6460
|
if (!isDirectory) {
|
|
@@ -6366,34 +6480,35 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6366
6480
|
if (urlWithoutSearch !== String(htmlFileUrlForDirectory)) {
|
|
6367
6481
|
return null;
|
|
6368
6482
|
}
|
|
6369
|
-
const
|
|
6370
|
-
if (!
|
|
6483
|
+
const urlNotFound = urlInfo.searchParams.get("url");
|
|
6484
|
+
if (!urlNotFound) {
|
|
6371
6485
|
return null;
|
|
6372
6486
|
}
|
|
6487
|
+
|
|
6373
6488
|
urlInfo.headers["cache-control"] = "no-cache";
|
|
6374
6489
|
const enoent = urlInfo.searchParams.has("enoent");
|
|
6375
6490
|
if (enoent) {
|
|
6376
6491
|
urlInfo.status = 404;
|
|
6377
|
-
urlInfo.headers["cache-control"] = "no-cache";
|
|
6378
6492
|
}
|
|
6379
6493
|
const request = urlInfo.context.request;
|
|
6380
6494
|
const { rootDirectoryUrl, mainFilePath } = urlInfo.context;
|
|
6381
|
-
|
|
6382
|
-
|
|
6495
|
+
const directoryListingInjections = generateDirectoryListingInjection(
|
|
6496
|
+
urlNotFound,
|
|
6383
6497
|
{
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
}),
|
|
6498
|
+
spa,
|
|
6499
|
+
autoreload,
|
|
6500
|
+
request,
|
|
6501
|
+
urlMocks,
|
|
6502
|
+
directoryContentMagicName,
|
|
6503
|
+
rootDirectoryUrl,
|
|
6504
|
+
mainFilePath,
|
|
6505
|
+
packageDirectory,
|
|
6506
|
+
enoent,
|
|
6394
6507
|
},
|
|
6395
|
-
urlInfo,
|
|
6396
6508
|
);
|
|
6509
|
+
return {
|
|
6510
|
+
contentInjections: directoryListingInjections,
|
|
6511
|
+
};
|
|
6397
6512
|
},
|
|
6398
6513
|
},
|
|
6399
6514
|
devServerRoutes: [
|
|
@@ -6412,8 +6527,10 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6412
6527
|
directoryRelativeUrl,
|
|
6413
6528
|
rootDirectoryUrl,
|
|
6414
6529
|
);
|
|
6415
|
-
const closestDirectoryUrl =
|
|
6416
|
-
|
|
6530
|
+
const closestDirectoryUrl = getFirstExistingDirectoryUrl(
|
|
6531
|
+
requestedUrl,
|
|
6532
|
+
rootDirectoryUrl,
|
|
6533
|
+
);
|
|
6417
6534
|
const sendMessage = (message) => {
|
|
6418
6535
|
websocket.send(JSON.stringify(message));
|
|
6419
6536
|
};
|
|
@@ -6471,8 +6588,9 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6471
6588
|
};
|
|
6472
6589
|
|
|
6473
6590
|
const generateDirectoryListingInjection = (
|
|
6474
|
-
|
|
6591
|
+
urlNotFound,
|
|
6475
6592
|
{
|
|
6593
|
+
spa,
|
|
6476
6594
|
rootDirectoryUrl,
|
|
6477
6595
|
mainFilePath,
|
|
6478
6596
|
packageDirectory,
|
|
@@ -6485,7 +6603,7 @@ const generateDirectoryListingInjection = (
|
|
|
6485
6603
|
) => {
|
|
6486
6604
|
let serverRootDirectoryUrl = rootDirectoryUrl;
|
|
6487
6605
|
const firstExistingDirectoryUrl = getFirstExistingDirectoryUrl(
|
|
6488
|
-
|
|
6606
|
+
urlNotFound,
|
|
6489
6607
|
serverRootDirectoryUrl,
|
|
6490
6608
|
);
|
|
6491
6609
|
const directoryContentItems = getDirectoryContentItems({
|
|
@@ -6536,8 +6654,8 @@ const generateDirectoryListingInjection = (
|
|
|
6536
6654
|
const { host } = new URL(request.url);
|
|
6537
6655
|
const websocketUrl = `${websocketScheme}://${host}/.internal/directory_content.websocket?directory=${encodeURIComponent(directoryUrlRelativeToServer)}`;
|
|
6538
6656
|
|
|
6539
|
-
const
|
|
6540
|
-
|
|
6657
|
+
const generateBreadcrumb = () => {
|
|
6658
|
+
const breadcrumb = [];
|
|
6541
6659
|
const lastItemUrl = firstExistingDirectoryUrl;
|
|
6542
6660
|
const lastItemRelativeUrl = urlToRelativeUrl(lastItemUrl, rootDirectoryUrl);
|
|
6543
6661
|
const rootDirectoryUrlName = urlToFilename(rootDirectoryUrl);
|
|
@@ -6547,7 +6665,6 @@ const generateDirectoryListingInjection = (
|
|
|
6547
6665
|
} else {
|
|
6548
6666
|
parts = [rootDirectoryUrlName];
|
|
6549
6667
|
}
|
|
6550
|
-
|
|
6551
6668
|
let i = 0;
|
|
6552
6669
|
while (i < parts.length) {
|
|
6553
6670
|
const part = parts[i];
|
|
@@ -6568,7 +6685,7 @@ const generateDirectoryListingInjection = (
|
|
|
6568
6685
|
navItemUrl,
|
|
6569
6686
|
serverRootDirectoryUrl,
|
|
6570
6687
|
);
|
|
6571
|
-
let urlRelativeToDocument = urlToRelativeUrl(navItemUrl,
|
|
6688
|
+
let urlRelativeToDocument = urlToRelativeUrl(navItemUrl, urlNotFound);
|
|
6572
6689
|
const isServerRootDirectory = navItemUrl === serverRootDirectoryUrl;
|
|
6573
6690
|
if (isServerRootDirectory) {
|
|
6574
6691
|
urlRelativeToServer = `/${directoryContentMagicName}`;
|
|
@@ -6576,7 +6693,7 @@ const generateDirectoryListingInjection = (
|
|
|
6576
6693
|
}
|
|
6577
6694
|
const name = part;
|
|
6578
6695
|
const isCurrent = navItemUrl === String(firstExistingDirectoryUrl);
|
|
6579
|
-
|
|
6696
|
+
breadcrumb.push({
|
|
6580
6697
|
url: navItemUrl,
|
|
6581
6698
|
urlRelativeToServer,
|
|
6582
6699
|
urlRelativeToDocument,
|
|
@@ -6586,34 +6703,47 @@ const generateDirectoryListingInjection = (
|
|
|
6586
6703
|
});
|
|
6587
6704
|
i++;
|
|
6588
6705
|
}
|
|
6589
|
-
|
|
6706
|
+
return breadcrumb;
|
|
6707
|
+
};
|
|
6708
|
+
const breadcrumb = generateBreadcrumb();
|
|
6590
6709
|
|
|
6591
6710
|
let enoentDetails = null;
|
|
6592
6711
|
if (enoent) {
|
|
6712
|
+
const buildEnoentPathInfo = (urlBase, closestExistingUrl) => {
|
|
6713
|
+
let filePathExisting;
|
|
6714
|
+
let filePathNotFound;
|
|
6715
|
+
const existingIndex = String(closestExistingUrl).length;
|
|
6716
|
+
filePathExisting = urlToRelativeUrl(
|
|
6717
|
+
closestExistingUrl,
|
|
6718
|
+
serverRootDirectoryUrl,
|
|
6719
|
+
);
|
|
6720
|
+
filePathNotFound = urlBase.slice(existingIndex);
|
|
6721
|
+
return [filePathExisting, filePathNotFound];
|
|
6722
|
+
};
|
|
6593
6723
|
const fileRelativeUrl = urlToRelativeUrl(
|
|
6594
|
-
|
|
6724
|
+
urlNotFound,
|
|
6595
6725
|
serverRootDirectoryUrl,
|
|
6596
6726
|
);
|
|
6597
|
-
let filePathExisting;
|
|
6598
|
-
let filePathNotFound;
|
|
6599
|
-
const existingIndex = String(firstExistingDirectoryUrl).length;
|
|
6600
|
-
filePathExisting = urlToRelativeUrl(
|
|
6601
|
-
firstExistingDirectoryUrl,
|
|
6602
|
-
serverRootDirectoryUrl,
|
|
6603
|
-
);
|
|
6604
|
-
filePathNotFound = requestedUrl.slice(existingIndex);
|
|
6605
6727
|
enoentDetails = {
|
|
6606
|
-
fileUrl:
|
|
6728
|
+
fileUrl: urlNotFound,
|
|
6607
6729
|
fileRelativeUrl,
|
|
6730
|
+
};
|
|
6731
|
+
|
|
6732
|
+
const [filePathExisting, filePathNotFound] = buildEnoentPathInfo(
|
|
6733
|
+
urlNotFound,
|
|
6734
|
+
firstExistingDirectoryUrl,
|
|
6735
|
+
);
|
|
6736
|
+
Object.assign(enoentDetails, {
|
|
6608
6737
|
filePathExisting: `/${filePathExisting}`,
|
|
6609
6738
|
filePathNotFound,
|
|
6610
|
-
};
|
|
6739
|
+
});
|
|
6611
6740
|
}
|
|
6612
6741
|
|
|
6613
6742
|
return {
|
|
6614
6743
|
__DIRECTORY_LISTING__: {
|
|
6744
|
+
spa,
|
|
6615
6745
|
enoentDetails,
|
|
6616
|
-
|
|
6746
|
+
breadcrumb,
|
|
6617
6747
|
urlMocks,
|
|
6618
6748
|
directoryContentMagicName,
|
|
6619
6749
|
directoryUrl: firstExistingDirectoryUrl,
|
|
@@ -6626,16 +6756,16 @@ const generateDirectoryListingInjection = (
|
|
|
6626
6756
|
},
|
|
6627
6757
|
};
|
|
6628
6758
|
};
|
|
6629
|
-
const getFirstExistingDirectoryUrl = (
|
|
6630
|
-
let
|
|
6631
|
-
while (!existsSync(
|
|
6632
|
-
|
|
6633
|
-
if (!
|
|
6634
|
-
|
|
6759
|
+
const getFirstExistingDirectoryUrl = (urlBase, serverRootDirectoryUrl) => {
|
|
6760
|
+
let directoryUrlCandidate = new URL("./", urlBase);
|
|
6761
|
+
while (!existsSync(directoryUrlCandidate)) {
|
|
6762
|
+
directoryUrlCandidate = new URL("../", directoryUrlCandidate);
|
|
6763
|
+
if (!urlIsOrIsInsideOf(directoryUrlCandidate, serverRootDirectoryUrl)) {
|
|
6764
|
+
directoryUrlCandidate = new URL(serverRootDirectoryUrl);
|
|
6635
6765
|
break;
|
|
6636
6766
|
}
|
|
6637
6767
|
}
|
|
6638
|
-
return
|
|
6768
|
+
return directoryUrlCandidate;
|
|
6639
6769
|
};
|
|
6640
6770
|
const getDirectoryContentItems = ({
|
|
6641
6771
|
serverRootDirectoryUrl,
|
|
@@ -6679,6 +6809,7 @@ const getDirectoryContentItems = ({
|
|
|
6679
6809
|
};
|
|
6680
6810
|
|
|
6681
6811
|
const jsenvPluginFsRedirection = ({
|
|
6812
|
+
spa,
|
|
6682
6813
|
directoryContentMagicName,
|
|
6683
6814
|
magicExtensions = ["inherit", ".js"],
|
|
6684
6815
|
magicDirectoryIndex = true,
|
|
@@ -6768,11 +6899,19 @@ const jsenvPluginFsRedirection = ({
|
|
|
6768
6899
|
// 3. The url pathname does not ends with "/"
|
|
6769
6900
|
// In that case we assume client explicitely asks to load a directory
|
|
6770
6901
|
if (
|
|
6902
|
+
spa &&
|
|
6771
6903
|
!urlToExtension(urlObject) &&
|
|
6772
6904
|
!urlToPathname(urlObject).endsWith("/")
|
|
6773
6905
|
) {
|
|
6774
|
-
const {
|
|
6906
|
+
const { requestedUrl, rootDirectoryUrl, mainFilePath } =
|
|
6775
6907
|
reference.ownerUrlInfo.context;
|
|
6908
|
+
const closestHtmlRootFile = getClosestHtmlRootFile(
|
|
6909
|
+
requestedUrl,
|
|
6910
|
+
rootDirectoryUrl,
|
|
6911
|
+
);
|
|
6912
|
+
if (closestHtmlRootFile) {
|
|
6913
|
+
return closestHtmlRootFile;
|
|
6914
|
+
}
|
|
6776
6915
|
return new URL(mainFilePath, rootDirectoryUrl);
|
|
6777
6916
|
}
|
|
6778
6917
|
return null;
|
|
@@ -6822,9 +6961,29 @@ const resolveSymlink = (fileUrl) => {
|
|
|
6822
6961
|
return realUrlObject.href;
|
|
6823
6962
|
};
|
|
6824
6963
|
|
|
6964
|
+
const getClosestHtmlRootFile = (requestedUrl, serverRootDirectoryUrl) => {
|
|
6965
|
+
let directoryUrl = new URL("./", requestedUrl);
|
|
6966
|
+
while (true) {
|
|
6967
|
+
const indexHtmlFileUrl = new URL(`index.html`, directoryUrl);
|
|
6968
|
+
if (existsSync(indexHtmlFileUrl)) {
|
|
6969
|
+
return indexHtmlFileUrl.href;
|
|
6970
|
+
}
|
|
6971
|
+
const filename = urlToFilename(directoryUrl);
|
|
6972
|
+
const htmlFileUrlCandidate = new URL(`${filename}.html`, directoryUrl);
|
|
6973
|
+
if (existsSync(htmlFileUrlCandidate)) {
|
|
6974
|
+
return htmlFileUrlCandidate.href;
|
|
6975
|
+
}
|
|
6976
|
+
if (!urlIsOrIsInsideOf(directoryUrl, serverRootDirectoryUrl)) {
|
|
6977
|
+
return null;
|
|
6978
|
+
}
|
|
6979
|
+
directoryUrl = new URL("../", directoryUrl);
|
|
6980
|
+
}
|
|
6981
|
+
};
|
|
6982
|
+
|
|
6825
6983
|
const directoryContentMagicName = "...";
|
|
6826
6984
|
|
|
6827
6985
|
const jsenvPluginProtocolFile = ({
|
|
6986
|
+
spa = true,
|
|
6828
6987
|
magicExtensions,
|
|
6829
6988
|
magicDirectoryIndex,
|
|
6830
6989
|
preserveSymlinks,
|
|
@@ -6836,6 +6995,7 @@ const jsenvPluginProtocolFile = ({
|
|
|
6836
6995
|
}) => {
|
|
6837
6996
|
return [
|
|
6838
6997
|
jsenvPluginFsRedirection({
|
|
6998
|
+
spa,
|
|
6839
6999
|
directoryContentMagicName,
|
|
6840
7000
|
magicExtensions,
|
|
6841
7001
|
magicDirectoryIndex,
|
|
@@ -6891,6 +7051,7 @@ const jsenvPluginProtocolFile = ({
|
|
|
6891
7051
|
...(directoryListing
|
|
6892
7052
|
? [
|
|
6893
7053
|
jsenvPluginDirectoryListing({
|
|
7054
|
+
spa,
|
|
6894
7055
|
...directoryListing,
|
|
6895
7056
|
directoryContentMagicName,
|
|
6896
7057
|
rootDirectoryUrl,
|
|
@@ -7085,6 +7246,69 @@ const asValidFilename = (string) => {
|
|
|
7085
7246
|
return string;
|
|
7086
7247
|
};
|
|
7087
7248
|
|
|
7249
|
+
const jsenvPluginInjections = (rawAssociations) => {
|
|
7250
|
+
const getDefaultInjections = (urlInfo) => {
|
|
7251
|
+
if (urlInfo.context.dev && urlInfo.type === "html") {
|
|
7252
|
+
const relativeUrl = urlToRelativeUrl(
|
|
7253
|
+
urlInfo.url,
|
|
7254
|
+
urlInfo.context.rootDirectoryUrl,
|
|
7255
|
+
);
|
|
7256
|
+
return {
|
|
7257
|
+
HTML_ROOT_PATHNAME: INJECTIONS.global(`/${relativeUrl}`),
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
return null;
|
|
7261
|
+
};
|
|
7262
|
+
let getInjections = null;
|
|
7263
|
+
|
|
7264
|
+
return {
|
|
7265
|
+
name: "jsenv:injections",
|
|
7266
|
+
appliesDuring: "*",
|
|
7267
|
+
init: (context) => {
|
|
7268
|
+
if (rawAssociations && Object.keys(rawAssociations).length > 0) {
|
|
7269
|
+
const resolvedAssociations = URL_META.resolveAssociations(
|
|
7270
|
+
{ injectionsGetter: rawAssociations },
|
|
7271
|
+
context.rootDirectoryUrl,
|
|
7272
|
+
);
|
|
7273
|
+
getInjections = (urlInfo) => {
|
|
7274
|
+
const { injectionsGetter } = URL_META.applyAssociations({
|
|
7275
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
7276
|
+
associations: resolvedAssociations,
|
|
7277
|
+
});
|
|
7278
|
+
if (!injectionsGetter) {
|
|
7279
|
+
return null;
|
|
7280
|
+
}
|
|
7281
|
+
if (typeof injectionsGetter !== "function") {
|
|
7282
|
+
throw new TypeError("injectionsGetter must be a function");
|
|
7283
|
+
}
|
|
7284
|
+
return injectionsGetter(urlInfo);
|
|
7285
|
+
};
|
|
7286
|
+
}
|
|
7287
|
+
},
|
|
7288
|
+
transformUrlContent: async (urlInfo) => {
|
|
7289
|
+
const defaultInjections = getDefaultInjections(urlInfo);
|
|
7290
|
+
if (!getInjections) {
|
|
7291
|
+
return {
|
|
7292
|
+
contentInjections: defaultInjections,
|
|
7293
|
+
};
|
|
7294
|
+
}
|
|
7295
|
+
const injectionsResult = getInjections(urlInfo);
|
|
7296
|
+
if (!injectionsResult) {
|
|
7297
|
+
return {
|
|
7298
|
+
contentInjections: defaultInjections,
|
|
7299
|
+
};
|
|
7300
|
+
}
|
|
7301
|
+
const injections = await injectionsResult;
|
|
7302
|
+
return {
|
|
7303
|
+
contentInjections: {
|
|
7304
|
+
...defaultInjections,
|
|
7305
|
+
...injections,
|
|
7306
|
+
},
|
|
7307
|
+
};
|
|
7308
|
+
},
|
|
7309
|
+
};
|
|
7310
|
+
};
|
|
7311
|
+
|
|
7088
7312
|
/*
|
|
7089
7313
|
* Some code uses globals specific to Node.js in code meant to run in browsers...
|
|
7090
7314
|
* This plugin will replace some node globals to things compatible with web:
|
|
@@ -7277,6 +7501,8 @@ const babelPluginMetadataExpressionPaths = (
|
|
|
7277
7501
|
* - replaced by true: When scenario matches (import.meta.dev and it's the dev server)
|
|
7278
7502
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
7279
7503
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
7504
|
+
*
|
|
7505
|
+
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
7280
7506
|
*/
|
|
7281
7507
|
|
|
7282
7508
|
|
|
@@ -7382,14 +7608,12 @@ const babelPluginMetadataImportMetaScenarios = () => {
|
|
|
7382
7608
|
|
|
7383
7609
|
const jsenvPluginGlobalScenarios = () => {
|
|
7384
7610
|
const transformIfNeeded = (urlInfo) => {
|
|
7385
|
-
return
|
|
7386
|
-
|
|
7387
|
-
{
|
|
7611
|
+
return {
|
|
7612
|
+
contentInjections: {
|
|
7388
7613
|
__DEV__: INJECTIONS.optional(urlInfo.context.dev),
|
|
7389
7614
|
__BUILD__: INJECTIONS.optional(urlInfo.context.build),
|
|
7390
7615
|
},
|
|
7391
|
-
|
|
7392
|
-
);
|
|
7616
|
+
};
|
|
7393
7617
|
};
|
|
7394
7618
|
|
|
7395
7619
|
return {
|
|
@@ -7822,7 +8046,7 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
7822
8046
|
serverEvents: {
|
|
7823
8047
|
reload: (serverEventInfo) => {
|
|
7824
8048
|
const formatUrlForClient = (url) => {
|
|
7825
|
-
if (
|
|
8049
|
+
if (urlIsOrIsInsideOf(url, serverEventInfo.rootDirectoryUrl)) {
|
|
7826
8050
|
return urlToRelativeUrl(url, serverEventInfo.rootDirectoryUrl);
|
|
7827
8051
|
}
|
|
7828
8052
|
if (url.startsWith("file:")) {
|
|
@@ -8397,6 +8621,7 @@ const jsenvPluginChromeDevtoolsJson = () => {
|
|
|
8397
8621
|
devServerRoutes: [
|
|
8398
8622
|
{
|
|
8399
8623
|
endpoint: "GET /.well-known/appspecific/com.chrome.devtools.json",
|
|
8624
|
+
declarationSource: import.meta.url,
|
|
8400
8625
|
fetch: (request, { kitchen }) => {
|
|
8401
8626
|
const { rootDirectoryUrl } = kitchen.context;
|
|
8402
8627
|
return Response.json({
|
|
@@ -8632,6 +8857,7 @@ const getCorePlugins = ({
|
|
|
8632
8857
|
transpilation = true,
|
|
8633
8858
|
inlining = true,
|
|
8634
8859
|
http = false,
|
|
8860
|
+
spa,
|
|
8635
8861
|
|
|
8636
8862
|
clientAutoreload,
|
|
8637
8863
|
clientAutoreloadOnServerRestart,
|
|
@@ -8661,7 +8887,7 @@ const getCorePlugins = ({
|
|
|
8661
8887
|
|
|
8662
8888
|
return [
|
|
8663
8889
|
jsenvPluginReferenceAnalysis(referenceAnalysis),
|
|
8664
|
-
|
|
8890
|
+
jsenvPluginInjections(injections),
|
|
8665
8891
|
jsenvPluginTranspilation(transpilation),
|
|
8666
8892
|
// "jsenvPluginInlining" must be very soon because all other plugins will react differently once they see the file is inlined
|
|
8667
8893
|
...(inlining ? [jsenvPluginInlining()] : []),
|
|
@@ -8674,6 +8900,7 @@ const getCorePlugins = ({
|
|
|
8674
8900
|
*/
|
|
8675
8901
|
jsenvPluginProtocolHttp(http),
|
|
8676
8902
|
jsenvPluginProtocolFile({
|
|
8903
|
+
spa,
|
|
8677
8904
|
magicExtensions,
|
|
8678
8905
|
magicDirectoryIndex,
|
|
8679
8906
|
directoryListing,
|
|
@@ -9783,7 +10010,7 @@ const createBuildSpecifierManager = ({
|
|
|
9783
10010
|
// const urlInfoInsideThisDirectorySet = new Set();
|
|
9784
10011
|
const versionsInfluencingThisDirectorySet = new Set();
|
|
9785
10012
|
for (const [url, urlInfo] of finalKitchen.graph.urlInfoMap) {
|
|
9786
|
-
if (!
|
|
10013
|
+
if (!urlIsOrIsInsideOf(url, directoryUrl)) {
|
|
9787
10014
|
continue;
|
|
9788
10015
|
}
|
|
9789
10016
|
// ideally we should exclude eventual directories as the are redundant
|
|
@@ -10182,7 +10409,7 @@ const createBuildSpecifierManager = ({
|
|
|
10182
10409
|
}
|
|
10183
10410
|
if (
|
|
10184
10411
|
urlInfo.type === "asset" &&
|
|
10185
|
-
|
|
10412
|
+
urlIsOrIsInsideOf(urlInfo.url, buildDirectoryUrl)
|
|
10186
10413
|
) {
|
|
10187
10414
|
return;
|
|
10188
10415
|
}
|
|
@@ -10497,7 +10724,7 @@ const createBuildUrlsGenerator = ({
|
|
|
10497
10724
|
if (buildUrlFromMap) {
|
|
10498
10725
|
return buildUrlFromMap;
|
|
10499
10726
|
}
|
|
10500
|
-
if (
|
|
10727
|
+
if (urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
|
|
10501
10728
|
if (ownerUrlInfo.searchParams.has("dynamic_import_id")) {
|
|
10502
10729
|
const ownerDirectoryPath = determineDirectoryPath({
|
|
10503
10730
|
sourceDirectoryUrl,
|
|
@@ -10912,7 +11139,7 @@ const build = async ({
|
|
|
10912
11139
|
);
|
|
10913
11140
|
}
|
|
10914
11141
|
}
|
|
10915
|
-
if (!
|
|
11142
|
+
if (!urlIsOrIsInsideOf(sourceUrl, sourceDirectoryUrl)) {
|
|
10916
11143
|
throw new Error(
|
|
10917
11144
|
`The key "${key}" in "entryPoints" is invalid: it must be inside the source directory at ${sourceDirectoryUrl}.`,
|
|
10918
11145
|
);
|
|
@@ -10963,7 +11190,7 @@ const build = async ({
|
|
|
10963
11190
|
`The buildRelativeUrl "${buildRelativeUrl}"${forEntryPointOrEmpty} is invalid: it must be a relative url.`,
|
|
10964
11191
|
);
|
|
10965
11192
|
}
|
|
10966
|
-
if (!
|
|
11193
|
+
if (!urlIsOrIsInsideOf(buildUrl, buildDirectoryUrl)) {
|
|
10967
11194
|
throw new Error(
|
|
10968
11195
|
`The buildRelativeUrl "${buildRelativeUrl}"${forEntryPointOrEmpty} is invalid: it must be inside the build directory at ${buildDirectoryUrl}.`,
|
|
10969
11196
|
);
|
|
@@ -11463,7 +11690,7 @@ const build = async ({
|
|
|
11463
11690
|
let hasSomeOutdatedSideEffectUrl = false;
|
|
11464
11691
|
for (const packageSideEffectUrl of packageSideEffectUrlSet) {
|
|
11465
11692
|
if (
|
|
11466
|
-
|
|
11693
|
+
urlIsOrIsInsideOf(packageSideEffectUrl, buildDirectoryUrl) &&
|
|
11467
11694
|
!buildSideEffectUrlSet.has(packageSideEffectUrl)
|
|
11468
11695
|
) {
|
|
11469
11696
|
hasSomeOutdatedSideEffectUrl = true;
|
|
@@ -11763,7 +11990,7 @@ const prepareEntryPointBuild = async (
|
|
|
11763
11990
|
});
|
|
11764
11991
|
|
|
11765
11992
|
let _getOtherEntryBuildInfo;
|
|
11766
|
-
const rawPluginStore = createPluginStore([
|
|
11993
|
+
const rawPluginStore = await createPluginStore([
|
|
11767
11994
|
...(mappings ? [jsenvPluginMappings(mappings)] : []),
|
|
11768
11995
|
{
|
|
11769
11996
|
name: "jsenv:other_entry_point_build_during_craft",
|
|
@@ -11808,7 +12035,7 @@ const prepareEntryPointBuild = async (
|
|
|
11808
12035
|
packageSideEffects,
|
|
11809
12036
|
}),
|
|
11810
12037
|
]);
|
|
11811
|
-
const rawPluginController = createPluginController(
|
|
12038
|
+
const rawPluginController = await createPluginController(
|
|
11812
12039
|
rawPluginStore,
|
|
11813
12040
|
rawKitchen,
|
|
11814
12041
|
);
|
|
@@ -11879,7 +12106,7 @@ const prepareEntryPointBuild = async (
|
|
|
11879
12106
|
rawKitchen.graph.getUrlInfo(entryReference.url).type === "html" &&
|
|
11880
12107
|
rawKitchen.context.isSupportedOnCurrentClients("importmap"),
|
|
11881
12108
|
});
|
|
11882
|
-
const finalPluginStore = createPluginStore([
|
|
12109
|
+
const finalPluginStore = await createPluginStore([
|
|
11883
12110
|
jsenvPluginReferenceAnalysis({
|
|
11884
12111
|
...referenceAnalysis,
|
|
11885
12112
|
fetchInlineUrls: false,
|
|
@@ -11912,7 +12139,7 @@ const prepareEntryPointBuild = async (
|
|
|
11912
12139
|
},
|
|
11913
12140
|
buildSpecifierManager.jsenvPluginMoveToBuildDirectory,
|
|
11914
12141
|
]);
|
|
11915
|
-
const finalPluginController = createPluginController(
|
|
12142
|
+
const finalPluginController = await createPluginController(
|
|
11916
12143
|
finalPluginStore,
|
|
11917
12144
|
finalKitchen,
|
|
11918
12145
|
{
|