@jsenv/core 40.7.0 → 40.7.2
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 +95 -63
- package/dist/build/jsenv_core_packages.js +3 -3
- package/dist/client/directory_listing/js/directory_listing.js +41 -26
- package/dist/client/ribbon/ribbon.js +40 -37
- package/dist/start_dev_server/jsenv_core_packages.js +3 -3
- package/dist/start_dev_server/start_dev_server.js +92 -59
- 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 +8 -7
- package/src/helpers/web_url_converter.js +2 -2
- package/src/kitchen/errors.js +6 -1
- package/src/kitchen/out_directory_url.js +2 -2
- 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/plugin_controller.js +25 -7
- 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 +45 -30
- package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +5 -10
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +2 -1
- package/src/plugins/ribbon/client/ribbon.js +40 -37
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";
|
|
@@ -131,7 +131,7 @@ export const startDevServer = async ({
|
|
|
131
131
|
if (
|
|
132
132
|
process.env.CAPTURING_SIDE_EFFECTS ||
|
|
133
133
|
(!import.meta.build &&
|
|
134
|
-
|
|
134
|
+
urlIsOrIsInsideOf(sourceDirectoryUrl, jsenvCoreDirectoryUrl))
|
|
135
135
|
) {
|
|
136
136
|
outDirectoryUrl = new URL("../.jsenv/", sourceDirectoryUrl);
|
|
137
137
|
} else {
|
|
@@ -248,7 +248,7 @@ export const startDevServer = async ({
|
|
|
248
248
|
read: readPackageAtOrNull,
|
|
249
249
|
};
|
|
250
250
|
|
|
251
|
-
const devServerPluginStore = createPluginStore([
|
|
251
|
+
const devServerPluginStore = await createPluginStore([
|
|
252
252
|
jsenvPluginServerEvents({ clientAutoreload }),
|
|
253
253
|
...plugins,
|
|
254
254
|
...getCorePlugins({
|
|
@@ -274,7 +274,7 @@ export const startDevServer = async ({
|
|
|
274
274
|
ribbon,
|
|
275
275
|
}),
|
|
276
276
|
]);
|
|
277
|
-
const getOrCreateKitchen = (request) => {
|
|
277
|
+
const getOrCreateKitchen = async (request) => {
|
|
278
278
|
const { runtimeName, runtimeVersion } = parseUserAgentHeader(
|
|
279
279
|
request.headers["user-agent"] || "",
|
|
280
280
|
);
|
|
@@ -397,7 +397,7 @@ export const startDevServer = async ({
|
|
|
397
397
|
);
|
|
398
398
|
},
|
|
399
399
|
);
|
|
400
|
-
const devServerPluginController = createPluginController(
|
|
400
|
+
const devServerPluginController = await createPluginController(
|
|
401
401
|
devServerPluginStore,
|
|
402
402
|
kitchen,
|
|
403
403
|
);
|
|
@@ -413,8 +413,8 @@ export const startDevServer = async ({
|
|
|
413
413
|
|
|
414
414
|
finalServices.push({
|
|
415
415
|
name: "jsenv:dev_server_routes",
|
|
416
|
-
augmentRouteFetchSecondArg: (request) => {
|
|
417
|
-
const kitchen = getOrCreateKitchen(request);
|
|
416
|
+
augmentRouteFetchSecondArg: async (request) => {
|
|
417
|
+
const kitchen = await getOrCreateKitchen(request);
|
|
418
418
|
return { kitchen };
|
|
419
419
|
},
|
|
420
420
|
routes: [
|
|
@@ -617,6 +617,7 @@ export const startDevServer = async ({
|
|
|
617
617
|
},
|
|
618
618
|
],
|
|
619
619
|
});
|
|
620
|
+
finalServices.push(...devServerPluginStore.allDevServerServices);
|
|
620
621
|
}
|
|
621
622
|
// jsenv error handler service
|
|
622
623
|
{
|
|
@@ -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
|
@@ -73,6 +73,7 @@ export const createFetchUrlContentError = ({
|
|
|
73
73
|
const createFailedToFetchUrlContentError = ({
|
|
74
74
|
code = error.code || "FETCH_URL_CONTENT_ERROR",
|
|
75
75
|
reason,
|
|
76
|
+
parseErrorSourceType,
|
|
76
77
|
...details
|
|
77
78
|
}) => {
|
|
78
79
|
const reference = urlInfo.firstReference;
|
|
@@ -93,6 +94,7 @@ ${reason}`,
|
|
|
93
94
|
name: "FETCH_URL_CONTENT_ERROR",
|
|
94
95
|
code,
|
|
95
96
|
reason,
|
|
97
|
+
parseErrorSourceType,
|
|
96
98
|
url: urlInfo.url,
|
|
97
99
|
trace: code === "PARSE_ERROR" ? error.trace : reference.trace,
|
|
98
100
|
asResponse: error.asResponse,
|
|
@@ -127,6 +129,7 @@ ${reason}`,
|
|
|
127
129
|
return createFailedToFetchUrlContentError({
|
|
128
130
|
"code": "PARSE_ERROR",
|
|
129
131
|
"reason": error.reasonCode,
|
|
132
|
+
"parseErrorSourceType": error.parseErrorSourceType,
|
|
130
133
|
...(error.cause ? { "parse error message": error.cause.message } : {}),
|
|
131
134
|
"parse error trace": error.trace?.message,
|
|
132
135
|
});
|
|
@@ -176,7 +179,9 @@ ${error.message}`,
|
|
|
176
179
|
name: "TRANSFORM_URL_CONTENT_ERROR",
|
|
177
180
|
code: "PARSE_ERROR",
|
|
178
181
|
reason: error.message,
|
|
179
|
-
|
|
182
|
+
reasonCode: error.reasonCode,
|
|
183
|
+
parseErrorSourceType: error.parseErrorSourceType,
|
|
184
|
+
stack: transformError.stack,
|
|
180
185
|
trace,
|
|
181
186
|
asResponse: error.asResponse,
|
|
182
187
|
});
|
|
@@ -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
|
}
|
|
@@ -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({
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
2
|
import { jsenvPluginHtmlSyntaxErrorFallback } from "./html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js";
|
|
3
3
|
|
|
4
|
-
export const createPluginStore = (plugins) => {
|
|
4
|
+
export const createPluginStore = async (plugins) => {
|
|
5
5
|
const allDevServerRoutes = [];
|
|
6
|
+
const allDevServerServices = [];
|
|
6
7
|
const pluginArray = [];
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
const pluginPromises = [];
|
|
10
|
+
const addPlugin = async (plugin) => {
|
|
11
|
+
if (plugin && typeof plugin.then === "function") {
|
|
12
|
+
pluginPromises.push(plugin);
|
|
13
|
+
const value = await plugin;
|
|
14
|
+
addPlugin(value);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
8
17
|
if (Array.isArray(plugin)) {
|
|
9
18
|
for (const subplugin of plugin) {
|
|
10
19
|
addPlugin(subplugin);
|
|
@@ -23,21 +32,28 @@ export const createPluginStore = (plugins) => {
|
|
|
23
32
|
allDevServerRoutes.push(devServerRoute);
|
|
24
33
|
}
|
|
25
34
|
}
|
|
35
|
+
if (plugin.devServerServices) {
|
|
36
|
+
const devServerServices = plugin.devServerServices;
|
|
37
|
+
for (const devServerService of devServerServices) {
|
|
38
|
+
allDevServerServices.push(devServerService);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
26
41
|
pluginArray.push(plugin);
|
|
27
42
|
};
|
|
28
43
|
addPlugin(jsenvPluginHtmlSyntaxErrorFallback());
|
|
29
44
|
for (const plugin of plugins) {
|
|
30
45
|
addPlugin(plugin);
|
|
31
46
|
}
|
|
47
|
+
await Promise.all(pluginPromises);
|
|
32
48
|
|
|
33
49
|
return {
|
|
34
50
|
pluginArray,
|
|
35
|
-
|
|
36
51
|
allDevServerRoutes,
|
|
52
|
+
allDevServerServices,
|
|
37
53
|
};
|
|
38
54
|
};
|
|
39
55
|
|
|
40
|
-
export const createPluginController = (
|
|
56
|
+
export const createPluginController = async (
|
|
41
57
|
pluginStore,
|
|
42
58
|
kitchen,
|
|
43
59
|
{ initialPuginsMeta = {} } = {},
|
|
@@ -60,7 +76,7 @@ export const createPluginController = (
|
|
|
60
76
|
pluginCandidate.destroy?.();
|
|
61
77
|
continue;
|
|
62
78
|
}
|
|
63
|
-
const initPluginResult = initPlugin(pluginCandidate, kitchen);
|
|
79
|
+
const initPluginResult = await initPlugin(pluginCandidate, kitchen);
|
|
64
80
|
if (!initPluginResult) {
|
|
65
81
|
pluginCandidate.destroy?.();
|
|
66
82
|
continue;
|
|
@@ -112,6 +128,7 @@ export const createPluginController = (
|
|
|
112
128
|
key === "serverEvents" ||
|
|
113
129
|
key === "mustStayFirst" ||
|
|
114
130
|
key === "devServerRoutes" ||
|
|
131
|
+
key === "devServerServices" ||
|
|
115
132
|
key === "effect"
|
|
116
133
|
) {
|
|
117
134
|
continue;
|
|
@@ -285,6 +302,7 @@ export const createPluginController = (
|
|
|
285
302
|
const HOOK_NAMES = [
|
|
286
303
|
"init",
|
|
287
304
|
"devServerRoutes", // is called only during dev/tests
|
|
305
|
+
"devServerServices", // is called only during dev/tests
|
|
288
306
|
"resolveReference",
|
|
289
307
|
"redirectReference",
|
|
290
308
|
"transformReferenceSearchParams",
|
|
@@ -339,12 +357,12 @@ const testAppliesDuring = (plugin, kitchen) => {
|
|
|
339
357
|
`"appliesDuring" must be an object or a string, got ${appliesDuring}`,
|
|
340
358
|
);
|
|
341
359
|
};
|
|
342
|
-
const initPlugin = (plugin, kitchen) => {
|
|
360
|
+
const initPlugin = async (plugin, kitchen) => {
|
|
343
361
|
const { init } = plugin;
|
|
344
362
|
if (!init) {
|
|
345
363
|
return true;
|
|
346
364
|
}
|
|
347
|
-
const initReturnValue = init(kitchen.context, { plugin });
|
|
365
|
+
const initReturnValue = await init(kitchen.context, { plugin });
|
|
348
366
|
if (initReturnValue === false) {
|
|
349
367
|
return false;
|
|
350
368
|
}
|
|
@@ -6,7 +6,7 @@ const fileIconUrl = import.meta.resolve("./assets/file.png");
|
|
|
6
6
|
const homeIconUrl = import.meta.resolve("./assets/home.svg#root");
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
|
-
|
|
9
|
+
breadcrumb,
|
|
10
10
|
mainFilePath,
|
|
11
11
|
directoryContentItems,
|
|
12
12
|
enoentDetails,
|
|
@@ -49,38 +49,51 @@ const DirectoryListing = () => {
|
|
|
49
49
|
return (
|
|
50
50
|
<>
|
|
51
51
|
{enoentDetails ? <ErrorMessage /> : null}
|
|
52
|
-
<
|
|
52
|
+
<Breadcrumb items={breadcrumb} />
|
|
53
53
|
<DirectoryContent items={directoryItems} />
|
|
54
54
|
</>
|
|
55
55
|
);
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const ErrorMessage = () => {
|
|
59
|
-
const {
|
|
59
|
+
const { filePathExisting, filePathNotFound } = enoentDetails;
|
|
60
|
+
|
|
61
|
+
let errorText;
|
|
62
|
+
let errorSuggestion;
|
|
63
|
+
errorText = (
|
|
64
|
+
<>
|
|
65
|
+
<strong>File not found:</strong>
|
|
66
|
+
<code>
|
|
67
|
+
<span className="file_path_good">{filePathExisting}</span>
|
|
68
|
+
<span className="file_path_bad">{filePathNotFound}</span>
|
|
69
|
+
</code>{" "}
|
|
70
|
+
does not exist on the server.
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
errorSuggestion = (
|
|
74
|
+
<>
|
|
75
|
+
<span className="icon">🔍</span> Check available routes in{" "}
|
|
76
|
+
<a href="/.internal/route_inspector">route inspector</a>
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
60
79
|
|
|
61
80
|
return (
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<br />
|
|
72
|
-
<span className="error_text" style="font-size: 70%;">
|
|
73
|
-
See also available routes in the{" "}
|
|
74
|
-
<a href="/.internal/route_inspector">route inspector</a>.
|
|
75
|
-
</span>
|
|
76
|
-
</p>
|
|
81
|
+
<div className="error_message">
|
|
82
|
+
<p className="error_text">{errorText}</p>
|
|
83
|
+
<p
|
|
84
|
+
className="error_suggestion"
|
|
85
|
+
style="font-size: 0.8em; margin-top: 10px;"
|
|
86
|
+
>
|
|
87
|
+
{errorSuggestion}
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
77
90
|
);
|
|
78
91
|
};
|
|
79
92
|
|
|
80
|
-
const
|
|
93
|
+
const Breadcrumb = ({ items }) => {
|
|
81
94
|
return (
|
|
82
95
|
<h1 className="nav">
|
|
83
|
-
{
|
|
96
|
+
{items.map((navItem) => {
|
|
84
97
|
const {
|
|
85
98
|
url,
|
|
86
99
|
urlRelativeToServer,
|
|
@@ -91,7 +104,7 @@ const Nav = () => {
|
|
|
91
104
|
const isDirectory = new URL(url).pathname.endsWith("/");
|
|
92
105
|
return (
|
|
93
106
|
<>
|
|
94
|
-
<
|
|
107
|
+
<BreadcrumbItem
|
|
95
108
|
key={url}
|
|
96
109
|
url={urlRelativeToServer}
|
|
97
110
|
isCurrent={isCurrent}
|
|
@@ -99,7 +112,7 @@ const Nav = () => {
|
|
|
99
112
|
iconLinkUrl={isServerRootDirectory ? `/${mainFilePath}` : ""}
|
|
100
113
|
>
|
|
101
114
|
{name}
|
|
102
|
-
</
|
|
115
|
+
</BreadcrumbItem>
|
|
103
116
|
{isDirectory ? (
|
|
104
117
|
<span className="directory_separator">/</span>
|
|
105
118
|
) : null}
|
|
@@ -109,7 +122,13 @@ const Nav = () => {
|
|
|
109
122
|
</h1>
|
|
110
123
|
);
|
|
111
124
|
};
|
|
112
|
-
const
|
|
125
|
+
const BreadcrumbItem = ({
|
|
126
|
+
url,
|
|
127
|
+
iconImageUrl,
|
|
128
|
+
iconLinkUrl,
|
|
129
|
+
isCurrent,
|
|
130
|
+
children,
|
|
131
|
+
}) => {
|
|
113
132
|
return (
|
|
114
133
|
<span className="nav_item" data-current={isCurrent ? "" : undefined}>
|
|
115
134
|
{iconLinkUrl ? (
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
|
|
2
2
|
|
|
3
3
|
export const FILE_AND_SERVER_URLS_CONVERTER = {
|
|
4
4
|
asServerUrl: (fileUrl, serverRootDirectoryUrl) => {
|
|
5
|
-
if (fileUrl
|
|
6
|
-
return "/";
|
|
7
|
-
}
|
|
8
|
-
if (urlIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
|
|
5
|
+
if (urlIsOrIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
|
|
9
6
|
const urlRelativeToServer = urlToRelativeUrl(
|
|
10
7
|
fileUrl,
|
|
11
8
|
serverRootDirectoryUrl,
|