@jsenv/core 39.3.2 → 39.3.4

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.
@@ -0,0 +1,54 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>404 ENOENT</title>
5
+ <meta charset="utf-8" />
6
+ <link rel="icon" href="data:," />
7
+ <link
8
+ rel="stylesheet"
9
+ href="node_esm:@jsenv/core/src/plugins/protocol_file/client/assets/directory.css?inline"
10
+ />
11
+ <style>
12
+ .error_message {
13
+ background-color: #fce4e4;
14
+ border: 1px solid #fcc2c3;
15
+ padding: 1.5em 1.2em;
16
+ margin: 1em;
17
+ display: block;
18
+ }
19
+ .error_text {
20
+ color: #cc0033;
21
+ font-weight: bold;
22
+ line-height: 2em;
23
+ text-shadow: 1px 1px rgba(250, 250, 250, 0.3);
24
+ }
25
+
26
+ .hint_message {
27
+ background-color: lightblue;
28
+ border: 1px solid #fcc2c3;
29
+ padding: 1.5em 1.2em;
30
+ margin: 1em;
31
+ display: block;
32
+ }
33
+ .hint_text {
34
+ color: black;
35
+ font-weight: bold;
36
+ line-height: 2em;
37
+ text-shadow: 1px 1px rgba(250, 250, 250, 0.3);
38
+ }
39
+ </style>
40
+ </head>
41
+
42
+ <body data-theme="dark">
43
+ <p class="error_message">
44
+ <span class="error_text">
45
+ No entry on the filesystem for <code>/${fileRelativeUrl}</code> (at
46
+ ${fileUrl})
47
+ <br />
48
+ Content of the parent directory is listed below:
49
+ </span>
50
+ </p>
51
+ <h1 class="directory_nav">${parentDirectoryNav}</h1>
52
+ ${parentDirectoryContent}
53
+ </body>
54
+ </html>
@@ -6,21 +6,21 @@ import { pickContentType } from "@jsenv/server";
6
6
  import {
7
7
  ensurePathnameTrailingSlash,
8
8
  urlIsInsideOf,
9
+ urlToFilename,
9
10
  urlToRelativeUrl,
10
11
  } from "@jsenv/urls";
11
12
  import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
12
13
  import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
13
14
  import { jsenvPluginFsRedirection } from "./jsenv_plugin_fs_redirection.js";
14
15
 
15
- const html404AndParentDirIsEmptyFileUrl = new URL(
16
- "./html_404_and_parent_dir_is_empty.html",
16
+ const html404AndParentDirFileUrl = new URL(
17
+ "./client/html_404_and_parent_dir.html",
17
18
  import.meta.url,
18
19
  );
19
- const html404AndParentDirFileUrl = new URL(
20
- "./html_404_and_parent_dir.html",
20
+ const htmlFileUrlForDirectory = new URL(
21
+ "./client/directory.html",
21
22
  import.meta.url,
22
23
  );
23
- const htmlFileUrlForDirectory = new URL("./directory.html", import.meta.url);
24
24
 
25
25
  export const jsenvPluginProtocolFile = ({
26
26
  magicExtensions,
@@ -97,12 +97,14 @@ export const jsenvPluginProtocolFile = ({
97
97
  ? pickContentType(urlInfo.context.request, ["text/html"])
98
98
  : false;
99
99
  if (acceptsHtml) {
100
+ firstReference.expectedType = "html";
100
101
  const html = generateHtmlForDirectory(
101
102
  urlObject.href,
102
103
  directoryContentArray,
103
104
  urlInfo.context.rootDirectoryUrl,
104
105
  );
105
106
  return {
107
+ type: "html",
106
108
  contentType: "text/html",
107
109
  content: html,
108
110
  };
@@ -143,6 +145,9 @@ export const jsenvPluginProtocolFile = ({
143
145
  return {
144
146
  contentType: "text/html",
145
147
  content: html,
148
+ headers: {
149
+ "cache-control": "no-cache",
150
+ },
146
151
  };
147
152
  }
148
153
  }
@@ -166,10 +171,13 @@ const generateHtmlForDirectory = (
166
171
  rootDirectoryUrl,
167
172
  ) => {
168
173
  directoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl);
174
+
169
175
  const htmlForDirectory = String(readFileSync(htmlFileUrlForDirectory));
176
+ const directoryRelativeUrl = urlToRelativeUrl(directoryUrl, rootDirectoryUrl);
170
177
  const replacers = {
171
- directoryRelativeUrl: urlToRelativeUrl(directoryUrl, rootDirectoryUrl),
172
178
  directoryUrl,
179
+ directoryNav: () =>
180
+ generateDirectoryNav(directoryRelativeUrl, rootDirectoryUrl),
173
181
  directoryContent: () =>
174
182
  generateDirectoryContent(
175
183
  directoryContentArray,
@@ -186,30 +194,21 @@ const generateHtmlForENOENTOnHtmlFile = (
186
194
  parentDirectoryUrl,
187
195
  rootDirectoryUrl,
188
196
  ) => {
189
- if (parentDirectoryContentArray.length === 0) {
190
- const htmlFor404AndParentDirIsEmpty = String(
191
- readFileSync(html404AndParentDirIsEmptyFileUrl),
192
- );
193
- return replacePlaceholders(htmlFor404AndParentDirIsEmpty, {
194
- fileRelativeUrl: urlToRelativeUrl(url, rootDirectoryUrl),
195
- parentDirectoryRelativeUrl: urlToRelativeUrl(
196
- parentDirectoryUrl,
197
- rootDirectoryUrl,
198
- ),
199
- });
200
- }
201
197
  const htmlFor404AndParentDir = String(
202
198
  readFileSync(html404AndParentDirFileUrl),
203
199
  );
204
-
200
+ const fileRelativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
201
+ const parentDirectoryRelativeUrl = urlToRelativeUrl(
202
+ parentDirectoryUrl,
203
+ rootDirectoryUrl,
204
+ );
205
205
  const replacers = {
206
206
  fileUrl: url,
207
- fileRelativeUrl: urlToRelativeUrl(url, rootDirectoryUrl),
207
+ fileRelativeUrl,
208
208
  parentDirectoryUrl,
209
- parentDirectoryRelativeUrl: urlToRelativeUrl(
210
- parentDirectoryUrl,
211
- rootDirectoryUrl,
212
- ),
209
+ parentDirectoryRelativeUrl,
210
+ parentDirectoryNav: () =>
211
+ generateDirectoryNav(parentDirectoryRelativeUrl, rootDirectoryUrl),
213
212
  parentDirectoryContent: () =>
214
213
  generateDirectoryContent(
215
214
  parentDirectoryContentArray,
@@ -220,11 +219,51 @@ const generateHtmlForENOENTOnHtmlFile = (
220
219
  const html = replacePlaceholders(htmlFor404AndParentDir, replacers);
221
220
  return html;
222
221
  };
222
+ const generateDirectoryNav = (relativeUrl, rootDirectoryUrl) => {
223
+ const rootDirectoryUrlName = urlToFilename(rootDirectoryUrl);
224
+ const relativeUrlWithRoot = relativeUrl
225
+ ? `${rootDirectoryUrlName}/${relativeUrl}`
226
+ : `${rootDirectoryUrlName}/`;
227
+ const isDir = relativeUrlWithRoot.endsWith("/");
228
+ const parts = isDir
229
+ ? relativeUrlWithRoot.slice(0, -1).split("/")
230
+ : relativeUrlWithRoot.split("/");
231
+ let dirPartsHtml = "";
232
+ let i = 0;
233
+ while (i < parts.length) {
234
+ const part = parts[i];
235
+ const href = i === 0 ? "/" : `/${parts.slice(1, i + 1).join("/")}/`;
236
+ const text = part;
237
+ const isLastPart = i === parts.length - 1;
238
+ if (isLastPart) {
239
+ dirPartsHtml += `
240
+ <span class="directory_nav_item" data-current>
241
+ ${text}
242
+ </span>`;
243
+ break;
244
+ }
245
+ dirPartsHtml += `
246
+ <a class="directory_nav_item" href="${href}">
247
+ ${text}
248
+ </a>`;
249
+ dirPartsHtml += `
250
+ <span class="directory_separator">/</span>`;
251
+ i++;
252
+ }
253
+ if (isDir) {
254
+ dirPartsHtml += `
255
+ <span class="directory_separator">/</span>`;
256
+ }
257
+ return dirPartsHtml;
258
+ };
223
259
  const generateDirectoryContent = (
224
260
  directoryContentArray,
225
261
  directoryUrl,
226
262
  rootDirectoryUrl,
227
263
  ) => {
264
+ if (directoryContentArray.length === 0) {
265
+ return `<p>Directory is empty</p>`;
266
+ }
228
267
  const sortedNames = [];
229
268
  for (const filename of directoryContentArray) {
230
269
  const fileUrlObject = new URL(filename, directoryUrl);
@@ -235,15 +274,20 @@ const generateDirectoryContent = (
235
274
  }
236
275
  }
237
276
  sortedNames.sort(comparePathnames);
238
- return sortedNames.map((filename) => {
277
+ let html = `<ul class="directory_content">`;
278
+ for (const filename of sortedNames) {
239
279
  const fileUrlObject = new URL(filename, directoryUrl);
240
280
  const fileUrl = String(fileUrlObject);
241
- let fileUrlRelative = urlToRelativeUrl(fileUrl, rootDirectoryUrl);
242
- return `<li>
243
- <a href="/${fileUrlRelative}">/${fileUrlRelative}</a>
244
- </li>`;
245
- }).join(`
246
- `);
281
+ const fileUrlRelativeToParent = urlToRelativeUrl(fileUrl, directoryUrl);
282
+ const fileUrlRelativeToRoot = urlToRelativeUrl(fileUrl, rootDirectoryUrl);
283
+ const type = fileUrlRelativeToParent.endsWith("/") ? "dir" : "file";
284
+ html += `
285
+ <li class="directory_child" data-type="${type}">
286
+ <a href="/${fileUrlRelativeToRoot}">${fileUrlRelativeToParent}</a>
287
+ </li>`;
288
+ }
289
+ html += `\n </ul>`;
290
+ return html;
247
291
  };
248
292
  const replacePlaceholders = (html, replacers) => {
249
293
  return html.replace(/\$\{(\w+)\}/g, (match, name) => {
@@ -57,6 +57,11 @@ export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
57
57
  }
58
58
  },
59
59
  resolveReference: (reference) => {
60
+ if (reference.specifier.startsWith("node_esm:")) {
61
+ reference.specifier = reference.specifier.slice("node_esm:".length);
62
+ const result = nodeEsmResolverDefault(reference);
63
+ return result;
64
+ }
60
65
  const urlType = urlTypeFromReference(reference);
61
66
  const resolver = resolvers[urlType];
62
67
  return resolver ? resolver(reference) : null;
@@ -83,6 +88,5 @@ const urlTypeFromReference = (reference) => {
83
88
  if (reference.injected) {
84
89
  return reference.expectedType;
85
90
  }
86
-
87
91
  return reference.ownerUrlInfo.type;
88
92
  };
@@ -1,16 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>404 ENOENT</title>
5
- <meta charset="utf-8">
6
- <link rel="icon" href="data:,">
7
- </head>
8
-
9
- <body>
10
- <p>No entry on the filesystem for <strong>/${fileRelativeUrl}</strong></p>
11
- <p>
12
- <a jsenv-ignore="" href="/${parentDirectoryRelativeUrl}">/${parentDirectoryRelativeUrl}</a>
13
- directory is empty.
14
- </p>
15
- </body>
16
- </html>
@@ -1,20 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>Directory explorer</title>
5
- <meta charset="utf-8" />
6
- <link rel="icon" href="data:," />
7
- </head>
8
-
9
- <body>
10
- <h1>
11
- <a jsenv-ignore href="/${directoryRelativeUrl}"
12
- >/${directoryRelativeUrl}</a
13
- >
14
- directory content:
15
- </h1>
16
- <ul>
17
- ${directoryContent}
18
- </ul>
19
- </body>
20
- </html>
@@ -1,21 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>404 ENOENT</title>
5
- <meta charset="utf-8" />
6
- <link rel="icon" href="data:," />
7
- </head>
8
-
9
- <body>
10
- <p>No entry on the filesystem for <strong>/${fileRelativeUrl}</strong></p>
11
- <p>
12
- <a jsenv-ignore href="/${parentDirectoryRelativeUrl}"
13
- >/${parentDirectoryRelativeUrl}</a
14
- >
15
- directory content:
16
- </p>
17
- <ul>
18
- ${parentDirectoryContent}
19
- </ul>
20
- </body>
21
- </html>
@@ -1,18 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>404 ENOENT</title>
5
- <meta charset="utf-8" />
6
- <link rel="icon" href="data:," />
7
- </head>
8
-
9
- <body>
10
- <p>No entry on the filesystem for <strong>/${fileRelativeUrl}</strong></p>
11
- <p>
12
- <a jsenv-ignore href="/${parentDirectoryRelativeUrl}"
13
- >/${parentDirectoryRelativeUrl}</a
14
- >
15
- directory is empty.
16
- </p>
17
- </body>
18
- </html>