@jsenv/core 40.6.1 → 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.
@@ -3,11 +3,17 @@ import {
3
3
  applyFileSystemMagicResolution,
4
4
  getExtensionsToTry,
5
5
  } from "@jsenv/node-esm-resolution";
6
- import { urlToExtension, urlToPathname } from "@jsenv/urls";
7
- import { realpathSync } from "node:fs";
6
+ import {
7
+ urlIsInsideOf,
8
+ urlToExtension,
9
+ urlToFilename,
10
+ urlToPathname,
11
+ } from "@jsenv/urls";
12
+ import { existsSync, realpathSync } from "node:fs";
8
13
  import { pathToFileURL } from "node:url";
9
14
 
10
15
  export const jsenvPluginFsRedirection = ({
16
+ spa = true,
11
17
  directoryContentMagicName,
12
18
  magicExtensions = ["inherit", ".js"],
13
19
  magicDirectoryIndex = true,
@@ -97,11 +103,19 @@ export const jsenvPluginFsRedirection = ({
97
103
  // 3. The url pathname does not ends with "/"
98
104
  // In that case we assume client explicitely asks to load a directory
99
105
  if (
106
+ spa &&
100
107
  !urlToExtension(urlObject) &&
101
108
  !urlToPathname(urlObject).endsWith("/")
102
109
  ) {
103
- const { mainFilePath, rootDirectoryUrl } =
110
+ const { requestedUrl, rootDirectoryUrl, mainFilePath } =
104
111
  reference.ownerUrlInfo.context;
112
+ const closestHtmlRootFile = getClosestHtmlRootFile(
113
+ requestedUrl,
114
+ rootDirectoryUrl,
115
+ );
116
+ if (closestHtmlRootFile) {
117
+ return closestHtmlRootFile;
118
+ }
105
119
  return new URL(mainFilePath, rootDirectoryUrl);
106
120
  }
107
121
  return null;
@@ -150,3 +164,27 @@ const resolveSymlink = (fileUrl) => {
150
164
  }
151
165
  return realUrlObject.href;
152
166
  };
167
+
168
+ const getClosestHtmlRootFile = (requestedUrl, serverRootDirectoryUrl) => {
169
+ let directoryUrl = new URL("./", requestedUrl);
170
+ while (true) {
171
+ const indexHtmlFileUrl = new URL(`index.html`, directoryUrl);
172
+ if (existsSync(indexHtmlFileUrl)) {
173
+ return indexHtmlFileUrl.href;
174
+ }
175
+ const htmlFileUrlCandidate = new URL(
176
+ `${urlToFilename(directoryUrl)}.html`,
177
+ directoryUrl,
178
+ );
179
+ if (existsSync(htmlFileUrlCandidate)) {
180
+ return htmlFileUrlCandidate.href;
181
+ }
182
+ if (
183
+ !urlIsInsideOf(directoryUrl, serverRootDirectoryUrl) ||
184
+ directoryUrl.href === serverRootDirectoryUrl
185
+ ) {
186
+ return null;
187
+ }
188
+ directoryUrl = new URL("../", directoryUrl);
189
+ }
190
+ };
@@ -9,6 +9,7 @@ import { jsenvPluginFsRedirection } from "./jsenv_plugin_fs_redirection.js";
9
9
  const directoryContentMagicName = "...";
10
10
 
11
11
  export const jsenvPluginProtocolFile = ({
12
+ spa,
12
13
  magicExtensions,
13
14
  magicDirectoryIndex,
14
15
  preserveSymlinks,
@@ -20,6 +21,7 @@ export const jsenvPluginProtocolFile = ({
20
21
  }) => {
21
22
  return [
22
23
  jsenvPluginFsRedirection({
24
+ spa,
23
25
  directoryContentMagicName,
24
26
  magicExtensions,
25
27
  magicDirectoryIndex,
@@ -1,52 +0,0 @@
1
- import { injectJsenvScript, parseHtml, stringifyHtmlAst } from "@jsenv/ast";
2
- import { createMagicSource } from "@jsenv/sourcemap";
3
-
4
- export const injectGlobals = (content, globals, urlInfo) => {
5
- if (urlInfo.type === "html") {
6
- return globalInjectorOnHtml(content, globals, urlInfo);
7
- }
8
- if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
9
- return globalsInjectorOnJs(content, globals, urlInfo);
10
- }
11
- throw new Error(`cannot inject globals into "${urlInfo.type}"`);
12
- };
13
-
14
- const globalInjectorOnHtml = (content, globals, urlInfo) => {
15
- // ideally we would inject an importmap but browser support is too low
16
- // (even worse for worker/service worker)
17
- // so for now we inject code into entry points
18
- const htmlAst = parseHtml({
19
- html: content,
20
- url: urlInfo.url,
21
- storeOriginalPositions: false,
22
- });
23
- const clientCode = generateClientCodeForGlobals(globals, {
24
- isWebWorker: false,
25
- });
26
- injectJsenvScript(htmlAst, {
27
- content: clientCode,
28
- pluginName: "jsenv:inject_globals",
29
- });
30
- return stringifyHtmlAst(htmlAst);
31
- };
32
-
33
- const globalsInjectorOnJs = (content, globals, urlInfo) => {
34
- const clientCode = generateClientCodeForGlobals(globals, {
35
- isWebWorker:
36
- urlInfo.subtype === "worker" ||
37
- urlInfo.subtype === "service_worker" ||
38
- urlInfo.subtype === "shared_worker",
39
- });
40
- const magicSource = createMagicSource(content);
41
- magicSource.prepend(clientCode);
42
- return magicSource.toContentAndSourcemap();
43
- };
44
-
45
- const generateClientCodeForGlobals = (globals, { isWebWorker = false }) => {
46
- const globalName = isWebWorker ? "self" : "window";
47
- return `Object.assign(${globalName}, ${JSON.stringify(
48
- globals,
49
- null,
50
- " ",
51
- )});`;
52
- };