@jsenv/core 40.6.2 → 40.7.0
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/build.js +335 -135
- package/dist/build/jsenv_core_packages.js +100 -102
- 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 +100 -102
- package/dist/start_dev_server/start_dev_server.js +338 -136
- package/package.json +4 -4
- package/src/dev/start_dev_server.js +3 -1
- package/src/kitchen/kitchen.js +2 -0
- 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/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 +3 -0
- package/src/plugins/plugins.js +3 -1
- package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +23 -22
- package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +41 -3
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +2 -0
- package/src/plugins/injections/internal/inject_globals.js +0 -52
package/dist/build/build.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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";
|
|
@@ -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) {
|
|
@@ -4365,6 +4557,9 @@ const returnValueAssertions = [
|
|
|
4365
4557
|
return undefined;
|
|
4366
4558
|
}
|
|
4367
4559
|
if (typeof content !== "string" && !Buffer.isBuffer(content) && !body) {
|
|
4560
|
+
if (Object.hasOwn(valueReturned, "contentInjections")) {
|
|
4561
|
+
return undefined;
|
|
4562
|
+
}
|
|
4368
4563
|
throw new Error(
|
|
4369
4564
|
`Unexpected "content" returned by "${hook.plugin.name}" ${hook.name} hook: it must be a string or a buffer; got ${content}`,
|
|
4370
4565
|
);
|
|
@@ -6172,103 +6367,6 @@ const FILE_AND_SERVER_URLS_CONVERTER = {
|
|
|
6172
6367
|
},
|
|
6173
6368
|
};
|
|
6174
6369
|
|
|
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
6370
|
/*
|
|
6273
6371
|
* NICE TO HAVE:
|
|
6274
6372
|
*
|
|
@@ -6378,22 +6476,22 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6378
6476
|
}
|
|
6379
6477
|
const request = urlInfo.context.request;
|
|
6380
6478
|
const { rootDirectoryUrl, mainFilePath } = urlInfo.context;
|
|
6381
|
-
|
|
6382
|
-
|
|
6479
|
+
const directoryListingInjections = generateDirectoryListingInjection(
|
|
6480
|
+
requestedUrl,
|
|
6383
6481
|
{
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
enoent,
|
|
6393
|
-
}),
|
|
6482
|
+
autoreload,
|
|
6483
|
+
request,
|
|
6484
|
+
urlMocks,
|
|
6485
|
+
directoryContentMagicName,
|
|
6486
|
+
rootDirectoryUrl,
|
|
6487
|
+
mainFilePath,
|
|
6488
|
+
packageDirectory,
|
|
6489
|
+
enoent,
|
|
6394
6490
|
},
|
|
6395
|
-
urlInfo,
|
|
6396
6491
|
);
|
|
6492
|
+
return {
|
|
6493
|
+
contentInjections: directoryListingInjections,
|
|
6494
|
+
};
|
|
6397
6495
|
},
|
|
6398
6496
|
},
|
|
6399
6497
|
devServerRoutes: [
|
|
@@ -6412,8 +6510,10 @@ const jsenvPluginDirectoryListing = ({
|
|
|
6412
6510
|
directoryRelativeUrl,
|
|
6413
6511
|
rootDirectoryUrl,
|
|
6414
6512
|
);
|
|
6415
|
-
const closestDirectoryUrl =
|
|
6416
|
-
|
|
6513
|
+
const closestDirectoryUrl = getFirstExistingDirectoryUrl(
|
|
6514
|
+
requestedUrl,
|
|
6515
|
+
rootDirectoryUrl,
|
|
6516
|
+
);
|
|
6417
6517
|
const sendMessage = (message) => {
|
|
6418
6518
|
websocket.send(JSON.stringify(message));
|
|
6419
6519
|
};
|
|
@@ -6627,15 +6727,15 @@ const generateDirectoryListingInjection = (
|
|
|
6627
6727
|
};
|
|
6628
6728
|
};
|
|
6629
6729
|
const getFirstExistingDirectoryUrl = (requestedUrl, serverRootDirectoryUrl) => {
|
|
6630
|
-
let
|
|
6631
|
-
while (!existsSync(
|
|
6632
|
-
|
|
6633
|
-
if (!urlIsInsideOf(
|
|
6634
|
-
|
|
6730
|
+
let directoryUrlCandidate = new URL("./", requestedUrl);
|
|
6731
|
+
while (!existsSync(directoryUrlCandidate)) {
|
|
6732
|
+
directoryUrlCandidate = new URL("../", directoryUrlCandidate);
|
|
6733
|
+
if (!urlIsInsideOf(directoryUrlCandidate, serverRootDirectoryUrl)) {
|
|
6734
|
+
directoryUrlCandidate = new URL(serverRootDirectoryUrl);
|
|
6635
6735
|
break;
|
|
6636
6736
|
}
|
|
6637
6737
|
}
|
|
6638
|
-
return
|
|
6738
|
+
return directoryUrlCandidate;
|
|
6639
6739
|
};
|
|
6640
6740
|
const getDirectoryContentItems = ({
|
|
6641
6741
|
serverRootDirectoryUrl,
|
|
@@ -6679,6 +6779,7 @@ const getDirectoryContentItems = ({
|
|
|
6679
6779
|
};
|
|
6680
6780
|
|
|
6681
6781
|
const jsenvPluginFsRedirection = ({
|
|
6782
|
+
spa = true,
|
|
6682
6783
|
directoryContentMagicName,
|
|
6683
6784
|
magicExtensions = ["inherit", ".js"],
|
|
6684
6785
|
magicDirectoryIndex = true,
|
|
@@ -6768,11 +6869,19 @@ const jsenvPluginFsRedirection = ({
|
|
|
6768
6869
|
// 3. The url pathname does not ends with "/"
|
|
6769
6870
|
// In that case we assume client explicitely asks to load a directory
|
|
6770
6871
|
if (
|
|
6872
|
+
spa &&
|
|
6771
6873
|
!urlToExtension(urlObject) &&
|
|
6772
6874
|
!urlToPathname(urlObject).endsWith("/")
|
|
6773
6875
|
) {
|
|
6774
|
-
const {
|
|
6876
|
+
const { requestedUrl, rootDirectoryUrl, mainFilePath } =
|
|
6775
6877
|
reference.ownerUrlInfo.context;
|
|
6878
|
+
const closestHtmlRootFile = getClosestHtmlRootFile(
|
|
6879
|
+
requestedUrl,
|
|
6880
|
+
rootDirectoryUrl,
|
|
6881
|
+
);
|
|
6882
|
+
if (closestHtmlRootFile) {
|
|
6883
|
+
return closestHtmlRootFile;
|
|
6884
|
+
}
|
|
6776
6885
|
return new URL(mainFilePath, rootDirectoryUrl);
|
|
6777
6886
|
}
|
|
6778
6887
|
return null;
|
|
@@ -6822,9 +6931,34 @@ const resolveSymlink = (fileUrl) => {
|
|
|
6822
6931
|
return realUrlObject.href;
|
|
6823
6932
|
};
|
|
6824
6933
|
|
|
6934
|
+
const getClosestHtmlRootFile = (requestedUrl, serverRootDirectoryUrl) => {
|
|
6935
|
+
let directoryUrl = new URL("./", requestedUrl);
|
|
6936
|
+
while (true) {
|
|
6937
|
+
const indexHtmlFileUrl = new URL(`index.html`, directoryUrl);
|
|
6938
|
+
if (existsSync(indexHtmlFileUrl)) {
|
|
6939
|
+
return indexHtmlFileUrl.href;
|
|
6940
|
+
}
|
|
6941
|
+
const htmlFileUrlCandidate = new URL(
|
|
6942
|
+
`${urlToFilename(directoryUrl)}.html`,
|
|
6943
|
+
directoryUrl,
|
|
6944
|
+
);
|
|
6945
|
+
if (existsSync(htmlFileUrlCandidate)) {
|
|
6946
|
+
return htmlFileUrlCandidate.href;
|
|
6947
|
+
}
|
|
6948
|
+
if (
|
|
6949
|
+
!urlIsInsideOf(directoryUrl, serverRootDirectoryUrl) ||
|
|
6950
|
+
directoryUrl.href === serverRootDirectoryUrl
|
|
6951
|
+
) {
|
|
6952
|
+
return null;
|
|
6953
|
+
}
|
|
6954
|
+
directoryUrl = new URL("../", directoryUrl);
|
|
6955
|
+
}
|
|
6956
|
+
};
|
|
6957
|
+
|
|
6825
6958
|
const directoryContentMagicName = "...";
|
|
6826
6959
|
|
|
6827
6960
|
const jsenvPluginProtocolFile = ({
|
|
6961
|
+
spa,
|
|
6828
6962
|
magicExtensions,
|
|
6829
6963
|
magicDirectoryIndex,
|
|
6830
6964
|
preserveSymlinks,
|
|
@@ -6836,6 +6970,7 @@ const jsenvPluginProtocolFile = ({
|
|
|
6836
6970
|
}) => {
|
|
6837
6971
|
return [
|
|
6838
6972
|
jsenvPluginFsRedirection({
|
|
6973
|
+
spa,
|
|
6839
6974
|
directoryContentMagicName,
|
|
6840
6975
|
magicExtensions,
|
|
6841
6976
|
magicDirectoryIndex,
|
|
@@ -7085,6 +7220,69 @@ const asValidFilename = (string) => {
|
|
|
7085
7220
|
return string;
|
|
7086
7221
|
};
|
|
7087
7222
|
|
|
7223
|
+
const jsenvPluginInjections = (rawAssociations) => {
|
|
7224
|
+
const getDefaultInjections = (urlInfo) => {
|
|
7225
|
+
if (urlInfo.context.dev && urlInfo.type === "html") {
|
|
7226
|
+
const relativeUrl = urlToRelativeUrl(
|
|
7227
|
+
urlInfo.url,
|
|
7228
|
+
urlInfo.context.rootDirectoryUrl,
|
|
7229
|
+
);
|
|
7230
|
+
return {
|
|
7231
|
+
HTML_ROOT_PATHNAME: INJECTIONS.global(`/${relativeUrl}`),
|
|
7232
|
+
};
|
|
7233
|
+
}
|
|
7234
|
+
return null;
|
|
7235
|
+
};
|
|
7236
|
+
let getInjections = null;
|
|
7237
|
+
|
|
7238
|
+
return {
|
|
7239
|
+
name: "jsenv:injections",
|
|
7240
|
+
appliesDuring: "*",
|
|
7241
|
+
init: (context) => {
|
|
7242
|
+
if (rawAssociations && Object.keys(rawAssociations).length > 0) {
|
|
7243
|
+
const resolvedAssociations = URL_META.resolveAssociations(
|
|
7244
|
+
{ injectionsGetter: rawAssociations },
|
|
7245
|
+
context.rootDirectoryUrl,
|
|
7246
|
+
);
|
|
7247
|
+
getInjections = (urlInfo) => {
|
|
7248
|
+
const { injectionsGetter } = URL_META.applyAssociations({
|
|
7249
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
7250
|
+
associations: resolvedAssociations,
|
|
7251
|
+
});
|
|
7252
|
+
if (!injectionsGetter) {
|
|
7253
|
+
return null;
|
|
7254
|
+
}
|
|
7255
|
+
if (typeof injectionsGetter !== "function") {
|
|
7256
|
+
throw new TypeError("injectionsGetter must be a function");
|
|
7257
|
+
}
|
|
7258
|
+
return injectionsGetter(urlInfo);
|
|
7259
|
+
};
|
|
7260
|
+
}
|
|
7261
|
+
},
|
|
7262
|
+
transformUrlContent: async (urlInfo) => {
|
|
7263
|
+
const defaultInjections = getDefaultInjections(urlInfo);
|
|
7264
|
+
if (!getInjections) {
|
|
7265
|
+
return {
|
|
7266
|
+
contentInjections: defaultInjections,
|
|
7267
|
+
};
|
|
7268
|
+
}
|
|
7269
|
+
const injectionsResult = getInjections(urlInfo);
|
|
7270
|
+
if (!injectionsResult) {
|
|
7271
|
+
return {
|
|
7272
|
+
contentInjections: defaultInjections,
|
|
7273
|
+
};
|
|
7274
|
+
}
|
|
7275
|
+
const injections = await injectionsResult;
|
|
7276
|
+
return {
|
|
7277
|
+
contentInjections: {
|
|
7278
|
+
...defaultInjections,
|
|
7279
|
+
...injections,
|
|
7280
|
+
},
|
|
7281
|
+
};
|
|
7282
|
+
},
|
|
7283
|
+
};
|
|
7284
|
+
};
|
|
7285
|
+
|
|
7088
7286
|
/*
|
|
7089
7287
|
* Some code uses globals specific to Node.js in code meant to run in browsers...
|
|
7090
7288
|
* This plugin will replace some node globals to things compatible with web:
|
|
@@ -7277,6 +7475,8 @@ const babelPluginMetadataExpressionPaths = (
|
|
|
7277
7475
|
* - replaced by true: When scenario matches (import.meta.dev and it's the dev server)
|
|
7278
7476
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
7279
7477
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
7478
|
+
*
|
|
7479
|
+
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
7280
7480
|
*/
|
|
7281
7481
|
|
|
7282
7482
|
|
|
@@ -7382,14 +7582,12 @@ const babelPluginMetadataImportMetaScenarios = () => {
|
|
|
7382
7582
|
|
|
7383
7583
|
const jsenvPluginGlobalScenarios = () => {
|
|
7384
7584
|
const transformIfNeeded = (urlInfo) => {
|
|
7385
|
-
return
|
|
7386
|
-
|
|
7387
|
-
{
|
|
7585
|
+
return {
|
|
7586
|
+
contentInjections: {
|
|
7388
7587
|
__DEV__: INJECTIONS.optional(urlInfo.context.dev),
|
|
7389
7588
|
__BUILD__: INJECTIONS.optional(urlInfo.context.build),
|
|
7390
7589
|
},
|
|
7391
|
-
|
|
7392
|
-
);
|
|
7590
|
+
};
|
|
7393
7591
|
};
|
|
7394
7592
|
|
|
7395
7593
|
return {
|
|
@@ -8632,6 +8830,7 @@ const getCorePlugins = ({
|
|
|
8632
8830
|
transpilation = true,
|
|
8633
8831
|
inlining = true,
|
|
8634
8832
|
http = false,
|
|
8833
|
+
spa,
|
|
8635
8834
|
|
|
8636
8835
|
clientAutoreload,
|
|
8637
8836
|
clientAutoreloadOnServerRestart,
|
|
@@ -8661,7 +8860,7 @@ const getCorePlugins = ({
|
|
|
8661
8860
|
|
|
8662
8861
|
return [
|
|
8663
8862
|
jsenvPluginReferenceAnalysis(referenceAnalysis),
|
|
8664
|
-
|
|
8863
|
+
jsenvPluginInjections(injections),
|
|
8665
8864
|
jsenvPluginTranspilation(transpilation),
|
|
8666
8865
|
// "jsenvPluginInlining" must be very soon because all other plugins will react differently once they see the file is inlined
|
|
8667
8866
|
...(inlining ? [jsenvPluginInlining()] : []),
|
|
@@ -8674,6 +8873,7 @@ const getCorePlugins = ({
|
|
|
8674
8873
|
*/
|
|
8675
8874
|
jsenvPluginProtocolHttp(http),
|
|
8676
8875
|
jsenvPluginProtocolFile({
|
|
8876
|
+
spa,
|
|
8677
8877
|
magicExtensions,
|
|
8678
8878
|
magicDirectoryIndex,
|
|
8679
8879
|
directoryListing,
|