@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.
@@ -52,7 +52,7 @@ import {
52
52
  nodeDefaultRuntimeCompat,
53
53
  } from "@jsenv/runtime-compat";
54
54
  import {
55
- urlIsInsideOf,
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 (!urlIsInsideOf(sourceUrl, sourceDirectoryUrl)) {
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 (!urlIsInsideOf(buildUrl, buildDirectoryUrl)) {
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
- urlIsInsideOf(sourceDirectoryUrl, jsenvCoreDirectoryUrl))
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
- urlIsInsideOf(packageSideEffectUrl, buildDirectoryUrl) &&
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
- urlIsInsideOf,
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 (!urlIsInsideOf(url, directoryUrl)) {
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
- urlIsInsideOf(urlInfo.url, buildDirectoryUrl)
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
- urlIsInsideOf,
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 (urlIsInsideOf(url, buildDirectoryUrl)) {
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 { urlIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
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
- urlIsInsideOf(sourceDirectoryUrl, jsenvCoreDirectoryUrl))
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, urlIsInsideOf } from "@jsenv/urls";
2
+ import { moveUrl, urlIsOrIsInsideOf } from "@jsenv/urls";
3
3
 
4
4
  export const WEB_URL_CONVERTER = {
5
5
  asWebUrl: (fileUrl, webServer) => {
6
- if (urlIsInsideOf(fileUrl, webServer.rootDirectoryUrl)) {
6
+ if (urlIsOrIsInsideOf(fileUrl, webServer.rootDirectoryUrl)) {
7
7
  return moveUrl({
8
8
  url: fileUrl,
9
9
  from: webServer.rootDirectoryUrl,
@@ -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
- stack: error.stack,
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, urlIsInsideOf } from "@jsenv/urls";
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 (!urlIsInsideOf(url, rootDirectoryUrl)) {
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 { urlIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
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 (urlIsInsideOf(url, serverEventInfo.rootDirectoryUrl)) {
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
- const addPlugin = (plugin) => {
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
- navItems,
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
- <Nav />
52
+ <Breadcrumb items={breadcrumb} />
53
53
  <DirectoryContent items={directoryItems} />
54
54
  </>
55
55
  );
56
56
  };
57
57
 
58
58
  const ErrorMessage = () => {
59
- const { fileUrl, filePathExisting, filePathNotFound } = enoentDetails;
59
+ const { filePathExisting, filePathNotFound } = enoentDetails;
60
+
61
+ let errorText;
62
+ let errorSuggestion;
63
+ errorText = (
64
+ <>
65
+ <strong>File not found:</strong>&nbsp;
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
- <p className="error_message">
63
- <span className="error_text">
64
- No filesystem entry at{" "}
65
- <code title={fileUrl}>
66
- <span className="file_path_good">{filePathExisting}</span>
67
- <span className="file_path_bad">{filePathNotFound}</span>
68
- </code>
69
- .
70
- </span>
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 Nav = () => {
93
+ const Breadcrumb = ({ items }) => {
81
94
  return (
82
95
  <h1 className="nav">
83
- {navItems.map((navItem) => {
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
- <NavItem
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
- </NavItem>
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 NavItem = ({ url, iconImageUrl, iconLinkUrl, isCurrent, children }) => {
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 { urlIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
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 === serverRootDirectoryUrl) {
6
- return "/";
7
- }
8
- if (urlIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
5
+ if (urlIsOrIsInsideOf(fileUrl, serverRootDirectoryUrl)) {
9
6
  const urlRelativeToServer = urlToRelativeUrl(
10
7
  fileUrl,
11
8
  serverRootDirectoryUrl,