@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.
@@ -31,7 +31,7 @@ import { pickContentType, WebSocketResponse } from "@jsenv/server";
31
31
  import {
32
32
  asUrlWithoutSearch,
33
33
  ensurePathnameTrailingSlash,
34
- urlIsInsideOf,
34
+ urlIsOrIsInsideOf,
35
35
  urlToFilename,
36
36
  urlToRelativeUrl,
37
37
  } from "@jsenv/urls";
@@ -44,6 +44,7 @@ const htmlFileUrlForDirectory = import.meta.resolve(
44
44
  );
45
45
 
46
46
  export const jsenvPluginDirectoryListing = ({
47
+ spa,
47
48
  urlMocks = false,
48
49
  autoreload = true,
49
50
  directoryContentMagicName,
@@ -85,7 +86,7 @@ export const jsenvPluginDirectoryListing = ({
85
86
  return null;
86
87
  }
87
88
  }
88
- return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(url)}&enoent`;
89
+ return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(requestedUrl)}&enoent`;
89
90
  }
90
91
  const isDirectory = fsStat?.isDirectory();
91
92
  if (!isDirectory) {
@@ -111,21 +112,22 @@ export const jsenvPluginDirectoryListing = ({
111
112
  if (urlWithoutSearch !== String(htmlFileUrlForDirectory)) {
112
113
  return null;
113
114
  }
114
- const requestedUrl = urlInfo.searchParams.get("url");
115
- if (!requestedUrl) {
115
+ const urlNotFound = urlInfo.searchParams.get("url");
116
+ if (!urlNotFound) {
116
117
  return null;
117
118
  }
119
+
118
120
  urlInfo.headers["cache-control"] = "no-cache";
119
121
  const enoent = urlInfo.searchParams.has("enoent");
120
122
  if (enoent) {
121
123
  urlInfo.status = 404;
122
- urlInfo.headers["cache-control"] = "no-cache";
123
124
  }
124
125
  const request = urlInfo.context.request;
125
126
  const { rootDirectoryUrl, mainFilePath } = urlInfo.context;
126
127
  const directoryListingInjections = generateDirectoryListingInjection(
127
- requestedUrl,
128
+ urlNotFound,
128
129
  {
130
+ spa,
129
131
  autoreload,
130
132
  request,
131
133
  urlMocks,
@@ -219,8 +221,9 @@ export const jsenvPluginDirectoryListing = ({
219
221
  };
220
222
 
221
223
  const generateDirectoryListingInjection = (
222
- requestedUrl,
224
+ urlNotFound,
223
225
  {
226
+ spa,
224
227
  rootDirectoryUrl,
225
228
  mainFilePath,
226
229
  packageDirectory,
@@ -233,13 +236,13 @@ const generateDirectoryListingInjection = (
233
236
  ) => {
234
237
  let serverRootDirectoryUrl = rootDirectoryUrl;
235
238
  const firstExistingDirectoryUrl = getFirstExistingDirectoryUrl(
236
- requestedUrl,
239
+ urlNotFound,
237
240
  serverRootDirectoryUrl,
238
241
  );
239
242
  const directoryContentItems = getDirectoryContentItems({
240
243
  serverRootDirectoryUrl,
241
244
  mainFilePath,
242
- requestedUrl,
245
+ requestedUrl: urlNotFound,
243
246
  firstExistingDirectoryUrl,
244
247
  });
245
248
  package_workspaces: {
@@ -285,8 +288,8 @@ const generateDirectoryListingInjection = (
285
288
  const { host } = new URL(request.url);
286
289
  const websocketUrl = `${websocketScheme}://${host}/.internal/directory_content.websocket?directory=${encodeURIComponent(directoryUrlRelativeToServer)}`;
287
290
 
288
- const navItems = [];
289
- nav_items: {
291
+ const generateBreadcrumb = () => {
292
+ const breadcrumb = [];
290
293
  const lastItemUrl = firstExistingDirectoryUrl;
291
294
  const lastItemRelativeUrl = urlToRelativeUrl(lastItemUrl, rootDirectoryUrl);
292
295
  const rootDirectoryUrlName = urlToFilename(rootDirectoryUrl);
@@ -296,7 +299,6 @@ const generateDirectoryListingInjection = (
296
299
  } else {
297
300
  parts = [rootDirectoryUrlName];
298
301
  }
299
-
300
302
  let i = 0;
301
303
  while (i < parts.length) {
302
304
  const part = parts[i];
@@ -317,7 +319,7 @@ const generateDirectoryListingInjection = (
317
319
  navItemUrl,
318
320
  serverRootDirectoryUrl,
319
321
  );
320
- let urlRelativeToDocument = urlToRelativeUrl(navItemUrl, requestedUrl);
322
+ let urlRelativeToDocument = urlToRelativeUrl(navItemUrl, urlNotFound);
321
323
  const isServerRootDirectory = navItemUrl === serverRootDirectoryUrl;
322
324
  if (isServerRootDirectory) {
323
325
  urlRelativeToServer = `/${directoryContentMagicName}`;
@@ -325,7 +327,7 @@ const generateDirectoryListingInjection = (
325
327
  }
326
328
  const name = part;
327
329
  const isCurrent = navItemUrl === String(firstExistingDirectoryUrl);
328
- navItems.push({
330
+ breadcrumb.push({
329
331
  url: navItemUrl,
330
332
  urlRelativeToServer,
331
333
  urlRelativeToDocument,
@@ -335,34 +337,47 @@ const generateDirectoryListingInjection = (
335
337
  });
336
338
  i++;
337
339
  }
338
- }
340
+ return breadcrumb;
341
+ };
342
+ const breadcrumb = generateBreadcrumb(urlNotFound);
339
343
 
340
344
  let enoentDetails = null;
341
345
  if (enoent) {
346
+ const buildEnoentPathInfo = (urlBase, closestExistingUrl) => {
347
+ let filePathExisting;
348
+ let filePathNotFound;
349
+ const existingIndex = String(closestExistingUrl).length;
350
+ filePathExisting = urlToRelativeUrl(
351
+ closestExistingUrl,
352
+ serverRootDirectoryUrl,
353
+ );
354
+ filePathNotFound = urlBase.slice(existingIndex);
355
+ return [filePathExisting, filePathNotFound];
356
+ };
342
357
  const fileRelativeUrl = urlToRelativeUrl(
343
- requestedUrl,
344
- serverRootDirectoryUrl,
345
- );
346
- let filePathExisting;
347
- let filePathNotFound;
348
- const existingIndex = String(firstExistingDirectoryUrl).length;
349
- filePathExisting = urlToRelativeUrl(
350
- firstExistingDirectoryUrl,
358
+ urlNotFound,
351
359
  serverRootDirectoryUrl,
352
360
  );
353
- filePathNotFound = requestedUrl.slice(existingIndex);
354
361
  enoentDetails = {
355
- fileUrl: requestedUrl,
362
+ fileUrl: urlNotFound,
356
363
  fileRelativeUrl,
364
+ };
365
+
366
+ const [filePathExisting, filePathNotFound] = buildEnoentPathInfo(
367
+ urlNotFound,
368
+ firstExistingDirectoryUrl,
369
+ );
370
+ Object.assign(enoentDetails, {
357
371
  filePathExisting: `/${filePathExisting}`,
358
372
  filePathNotFound,
359
- };
373
+ });
360
374
  }
361
375
 
362
376
  return {
363
377
  __DIRECTORY_LISTING__: {
378
+ spa,
364
379
  enoentDetails,
365
- navItems,
380
+ breadcrumb,
366
381
  urlMocks,
367
382
  directoryContentMagicName,
368
383
  directoryUrl: firstExistingDirectoryUrl,
@@ -375,11 +390,11 @@ const generateDirectoryListingInjection = (
375
390
  },
376
391
  };
377
392
  };
378
- const getFirstExistingDirectoryUrl = (requestedUrl, serverRootDirectoryUrl) => {
379
- let directoryUrlCandidate = new URL("./", requestedUrl);
393
+ const getFirstExistingDirectoryUrl = (urlBase, serverRootDirectoryUrl) => {
394
+ let directoryUrlCandidate = new URL("./", urlBase);
380
395
  while (!existsSync(directoryUrlCandidate)) {
381
396
  directoryUrlCandidate = new URL("../", directoryUrlCandidate);
382
- if (!urlIsInsideOf(directoryUrlCandidate, serverRootDirectoryUrl)) {
397
+ if (!urlIsOrIsInsideOf(directoryUrlCandidate, serverRootDirectoryUrl)) {
383
398
  directoryUrlCandidate = new URL(serverRootDirectoryUrl);
384
399
  break;
385
400
  }
@@ -4,7 +4,7 @@ import {
4
4
  getExtensionsToTry,
5
5
  } from "@jsenv/node-esm-resolution";
6
6
  import {
7
- urlIsInsideOf,
7
+ urlIsOrIsInsideOf,
8
8
  urlToExtension,
9
9
  urlToFilename,
10
10
  urlToPathname,
@@ -13,7 +13,7 @@ import { existsSync, realpathSync } from "node:fs";
13
13
  import { pathToFileURL } from "node:url";
14
14
 
15
15
  export const jsenvPluginFsRedirection = ({
16
- spa = true,
16
+ spa,
17
17
  directoryContentMagicName,
18
18
  magicExtensions = ["inherit", ".js"],
19
19
  magicDirectoryIndex = true,
@@ -172,17 +172,12 @@ const getClosestHtmlRootFile = (requestedUrl, serverRootDirectoryUrl) => {
172
172
  if (existsSync(indexHtmlFileUrl)) {
173
173
  return indexHtmlFileUrl.href;
174
174
  }
175
- const htmlFileUrlCandidate = new URL(
176
- `${urlToFilename(directoryUrl)}.html`,
177
- directoryUrl,
178
- );
175
+ const filename = urlToFilename(directoryUrl);
176
+ const htmlFileUrlCandidate = new URL(`${filename}.html`, directoryUrl);
179
177
  if (existsSync(htmlFileUrlCandidate)) {
180
178
  return htmlFileUrlCandidate.href;
181
179
  }
182
- if (
183
- !urlIsInsideOf(directoryUrl, serverRootDirectoryUrl) ||
184
- directoryUrl.href === serverRootDirectoryUrl
185
- ) {
180
+ if (!urlIsOrIsInsideOf(directoryUrl, serverRootDirectoryUrl)) {
186
181
  return null;
187
182
  }
188
183
  directoryUrl = new URL("../", directoryUrl);
@@ -9,7 +9,7 @@ import { jsenvPluginFsRedirection } from "./jsenv_plugin_fs_redirection.js";
9
9
  const directoryContentMagicName = "...";
10
10
 
11
11
  export const jsenvPluginProtocolFile = ({
12
- spa,
12
+ spa = true,
13
13
  magicExtensions,
14
14
  magicDirectoryIndex,
15
15
  preserveSymlinks,
@@ -77,6 +77,7 @@ export const jsenvPluginProtocolFile = ({
77
77
  ...(directoryListing
78
78
  ? [
79
79
  jsenvPluginDirectoryListing({
80
+ spa,
80
81
  ...directoryListing,
81
82
  directoryContentMagicName,
82
83
  rootDirectoryUrl,
@@ -1,43 +1,46 @@
1
1
  export const injectRibbon = ({ text }) => {
2
2
  const css = /* css */ `
3
- #jsenv_ribbon_container {
4
- position: fixed;
5
- z-index: 1001;
6
- top: 0;
7
- right: 0;
8
- width: 100px;
9
- height: 100px;
10
- overflow: hidden;
11
- opacity: 0.5;
12
- pointer-events: none;
13
- }
14
- #jsenv_ribbon {
15
- position: absolute;
16
- top: -10px;
17
- right: -10px;
18
- width: 100%;
19
- height: 100%;
20
- }
21
- #jsenv_ribbon_text {
22
- position: absolute;
23
- left: 0px;
24
- top: 20px;
25
- transform: rotate(45deg);
26
- display: block;
27
- width: 125px;
28
- line-height: 36px;
29
- background-color: orange;
30
- color: rgb(55, 7, 7);
31
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
32
- font-weight: 700;
33
- font-size: 16px;
34
- font-family: "Lato", sans-serif;
35
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
36
- text-align: center;
37
- user-select: none;
38
- }`;
3
+ #jsenv_ribbon_container {
4
+ position: fixed;
5
+ z-index: 1001;
6
+ top: 0;
7
+ right: 0;
8
+ width: 100px;
9
+ height: 100px;
10
+ overflow: hidden;
11
+ opacity: 0.5;
12
+ pointer-events: none;
13
+ }
14
+ #jsenv_ribbon {
15
+ position: absolute;
16
+ top: -10px;
17
+ right: -10px;
18
+ width: 100%;
19
+ height: 100%;
20
+ }
21
+ #jsenv_ribbon_text {
22
+ position: absolute;
23
+ left: 0px;
24
+ top: 20px;
25
+ transform: rotate(45deg);
26
+ display: block;
27
+ width: 125px;
28
+ line-height: 36px;
29
+ background-color: orange;
30
+ color: rgb(55, 7, 7);
31
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
32
+ font-weight: 700;
33
+ font-size: 16px;
34
+ font-family: "Lato", sans-serif;
35
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
36
+ text-align: center;
37
+ user-select: none;
38
+ }
39
+ `;
39
40
  const html = /* html */ `<div id="jsenv_ribbon_container">
40
- <style>${css}</style>
41
+ <style>
42
+ ${css}
43
+ </style>
41
44
  <div id="jsenv_ribbon">
42
45
  <div id="jsenv_ribbon_text">${text}</div>
43
46
  </div>