@jsenv/core 39.14.3 → 40.0.1

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.
Files changed (34) hide show
  1. package/dist/js/directory_listing.js +16 -9
  2. package/dist/js/server_events_client.js +2 -2
  3. package/dist/jsenv_core.js +7220 -11089
  4. package/package.json +22 -19
  5. package/src/build/build.js +122 -93
  6. package/src/build/build_specifier_manager.js +103 -94
  7. package/src/build/build_urls_generator.js +1 -1
  8. package/src/build/{version_mappings_injection.js → mappings_injection.js} +62 -21
  9. package/src/build/start_build_server.js +46 -36
  10. package/src/dev/start_dev_server.js +246 -248
  11. package/src/helpers/watch_source_files.js +50 -36
  12. package/src/kitchen/fetched_content_compliance.js +4 -2
  13. package/src/kitchen/kitchen.js +31 -24
  14. package/src/kitchen/url_graph/references.js +10 -2
  15. package/src/kitchen/url_graph/url_graph.js +3 -0
  16. package/src/kitchen/url_graph/url_graph_visitor.js +3 -0
  17. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +29 -16
  18. package/src/plugins/html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js +1 -1
  19. package/src/plugins/plugin_controller.js +194 -200
  20. package/src/plugins/plugins.js +5 -0
  21. package/src/plugins/protocol_file/client/directory_listing.jsx +5 -0
  22. package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +92 -67
  23. package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +17 -7
  24. package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +6 -0
  25. package/src/plugins/protocol_http/jsenv_plugin_protocol_http.js +33 -3
  26. package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +15 -22
  27. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +53 -2
  28. package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +37 -30
  29. package/src/plugins/resolution_node_esm/node_esm_resolver.js +4 -8
  30. package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +8 -6
  31. package/src/plugins/server_events/client/server_events_client.js +2 -2
  32. package/src/plugins/server_events/jsenv_plugin_server_events.js +18 -16
  33. package/dist/js/ws.js +0 -6863
  34. package/src/helpers/lookup_package_directory.js +0 -9
@@ -24,10 +24,11 @@
24
24
 
25
25
  import {
26
26
  comparePathnames,
27
+ lookupPackageDirectory,
27
28
  readEntryStatSync,
28
29
  registerDirectoryLifecycle,
29
30
  } from "@jsenv/filesystem";
30
- import { pickContentType } from "@jsenv/server";
31
+ import { pickContentType, WebSocketResponse } from "@jsenv/server";
31
32
  import {
32
33
  asUrlWithoutSearch,
33
34
  ensurePathnameTrailingSlash,
@@ -36,7 +37,7 @@ import {
36
37
  urlToRelativeUrl,
37
38
  } from "@jsenv/urls";
38
39
  import { existsSync, lstatSync, readdirSync } from "node:fs";
39
- import { lookupPackageDirectory } from "../../helpers/lookup_package_directory.js";
40
+ import { getDirectoryWatchPatterns } from "../../helpers/watch_source_files.js";
40
41
  import { replacePlaceholders } from "../injections/jsenv_plugin_injections.js";
41
42
  import { FILE_AND_SERVER_URLS_CONVERTER } from "./file_and_server_urls_converter.js";
42
43
 
@@ -49,6 +50,9 @@ export const jsenvPluginDirectoryListing = ({
49
50
  urlMocks = false,
50
51
  autoreload = true,
51
52
  directoryContentMagicName,
53
+ rootDirectoryUrl,
54
+ mainFilePath,
55
+ sourceFilesConfig,
52
56
  }) => {
53
57
  return {
54
58
  name: "jsenv:directory_listing",
@@ -66,16 +70,24 @@ export const jsenvPluginDirectoryListing = ({
66
70
  fsStat = readEntryStatSync(url, { nullIfNotFound: true });
67
71
  reference.fsStat = fsStat;
68
72
  }
69
- const { request, requestedUrl } = reference.ownerUrlInfo.context;
73
+ const { request, requestedUrl, mainFilePath, rootDirectoryUrl } =
74
+ reference.ownerUrlInfo.context;
70
75
  if (!fsStat) {
71
- if (
72
- requestedUrl === url &&
73
- request &&
74
- request.headers["sec-fetch-dest"] === "document"
75
- ) {
76
- return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(url)}&enoent`;
76
+ if (!request || request.headers["sec-fetch-dest"] !== "document") {
77
+ return null;
77
78
  }
78
- return null;
79
+ if (url !== requestedUrl) {
80
+ const mainFileUrl = new URL(mainFilePath, rootDirectoryUrl);
81
+ mainFileUrl.search = "";
82
+ mainFileUrl.hash = "";
83
+ const referenceUrl = new URL(url);
84
+ referenceUrl.search = "";
85
+ referenceUrl.hash = "";
86
+ if (mainFileUrl.href !== referenceUrl.href) {
87
+ return null;
88
+ }
89
+ }
90
+ return `${htmlFileUrlForDirectory}?url=${encodeURIComponent(url)}&enoent`;
79
91
  }
80
92
  const isDirectory = fsStat?.isDirectory();
81
93
  if (!isDirectory) {
@@ -130,65 +142,78 @@ export const jsenvPluginDirectoryListing = ({
130
142
  );
131
143
  },
132
144
  },
133
- serveWebsocket: ({ websocket, request, context }) => {
134
- if (!autoreload) {
135
- return false;
136
- }
137
- const secProtocol = request.headers["sec-websocket-protocol"];
138
- if (secProtocol !== "watch-directory") {
139
- return false;
140
- }
141
- const { rootDirectoryUrl, mainFilePath } = context;
142
- const requestedUrl = FILE_AND_SERVER_URLS_CONVERTER.asFileUrl(
143
- request.pathname,
144
- rootDirectoryUrl,
145
- );
146
- const closestDirectoryUrl = getFirstExistingDirectoryUrl(requestedUrl);
147
- const sendMessage = (message) => {
148
- websocket.send(JSON.stringify(message));
149
- };
150
- const generateItems = () => {
151
- const firstExistingDirectoryUrl = getFirstExistingDirectoryUrl(
152
- requestedUrl,
153
- rootDirectoryUrl,
154
- );
155
- const items = getDirectoryContentItems({
156
- serverRootDirectoryUrl: rootDirectoryUrl,
157
- mainFilePath,
158
- requestedUrl,
159
- firstExistingDirectoryUrl,
160
- });
161
- return items;
162
- };
145
+ devServerRoutes: [
146
+ {
147
+ endpoint:
148
+ "GET /.internal/directory_content.websocket?directory=:directory",
149
+ description: "Emit events when a directory content changes.",
150
+ declarationSource: import.meta.url,
151
+ fetch: (request) => {
152
+ if (!autoreload) {
153
+ return null;
154
+ }
155
+ return new WebSocketResponse((websocket) => {
156
+ const directoryRelativeUrl = request.params.directory;
157
+ const requestedUrl = FILE_AND_SERVER_URLS_CONVERTER.asFileUrl(
158
+ directoryRelativeUrl,
159
+ rootDirectoryUrl,
160
+ );
161
+ const closestDirectoryUrl =
162
+ getFirstExistingDirectoryUrl(requestedUrl);
163
+ const sendMessage = (message) => {
164
+ websocket.send(JSON.stringify(message));
165
+ };
166
+ const generateItems = () => {
167
+ const firstExistingDirectoryUrl = getFirstExistingDirectoryUrl(
168
+ requestedUrl,
169
+ rootDirectoryUrl,
170
+ );
171
+ const items = getDirectoryContentItems({
172
+ serverRootDirectoryUrl: rootDirectoryUrl,
173
+ mainFilePath,
174
+ requestedUrl,
175
+ firstExistingDirectoryUrl,
176
+ });
177
+ return items;
178
+ };
163
179
 
164
- const unwatch = registerDirectoryLifecycle(closestDirectoryUrl, {
165
- added: ({ relativeUrl }) => {
166
- sendMessage({
167
- type: "change",
168
- reason: `${relativeUrl} added`,
169
- items: generateItems(),
180
+ const unwatch = registerDirectoryLifecycle(closestDirectoryUrl, {
181
+ added: ({ relativeUrl }) => {
182
+ sendMessage({
183
+ type: "change",
184
+ reason: `${relativeUrl} added`,
185
+ items: generateItems(),
186
+ });
187
+ },
188
+ updated: ({ relativeUrl }) => {
189
+ sendMessage({
190
+ type: "change",
191
+ reason: `${relativeUrl} updated`,
192
+ items: generateItems(),
193
+ });
194
+ },
195
+ removed: ({ relativeUrl }) => {
196
+ sendMessage({
197
+ type: "change",
198
+ reason: `${relativeUrl} removed`,
199
+ items: generateItems(),
200
+ });
201
+ },
202
+ watchPatterns: getDirectoryWatchPatterns(
203
+ closestDirectoryUrl,
204
+ closestDirectoryUrl,
205
+ {
206
+ sourceFilesConfig,
207
+ },
208
+ ),
209
+ });
210
+ return () => {
211
+ unwatch();
212
+ };
170
213
  });
171
214
  },
172
- updated: ({ relativeUrl }) => {
173
- sendMessage({
174
- type: "change",
175
- reason: `${relativeUrl} updated`,
176
- items: generateItems(),
177
- });
178
- },
179
- removed: ({ relativeUrl }) => {
180
- sendMessage({
181
- type: "change",
182
- reason: `${relativeUrl} removed`,
183
- items: generateItems(),
184
- });
185
- },
186
- });
187
- websocket.signal.addEventListener("abort", () => {
188
- unwatch();
189
- });
190
- return true;
191
- },
215
+ },
216
+ ],
192
217
  };
193
218
  };
194
219
 
@@ -257,7 +282,7 @@ const generateDirectoryListingInjection = (
257
282
  );
258
283
  const websocketScheme = request.protocol === "https" ? "wss" : "ws";
259
284
  const { host } = new URL(request.url);
260
- const websocketUrl = `${websocketScheme}://${host}${directoryUrlRelativeToServer}`;
285
+ const websocketUrl = `${websocketScheme}://${host}/.internal/directory_content.websocket?directory=${encodeURIComponent(directoryUrlRelativeToServer)}`;
261
286
 
262
287
  const navItems = [];
263
288
  nav_items: {
@@ -17,20 +17,30 @@ export const jsenvPluginFsRedirection = ({
17
17
  name: "jsenv:fs_redirection",
18
18
  appliesDuring: "*",
19
19
  redirectReference: (reference) => {
20
+ if (reference.url === "file:///") {
21
+ return `ignore:file:///`;
22
+ }
23
+ if (reference.url === "file://") {
24
+ return `ignore:file://`;
25
+ }
26
+ // ignore all new URL second arg
27
+ if (reference.subtype === "new_url_second_arg") {
28
+ if (reference.original) {
29
+ return `ignore:${reference.original.specifier}`;
30
+ }
31
+ return `ignore:${reference.specifier}`;
32
+ }
20
33
  // http, https, data, about, ...
21
34
  if (!reference.url.startsWith("file:")) {
22
35
  return null;
23
36
  }
24
- if (reference.isInline) {
37
+ if (reference.original && !reference.original.url.startsWith("file:")) {
25
38
  return null;
26
39
  }
27
- if (reference.url === "file:///" || reference.url === "file://") {
28
- return `ignore:file:///`;
29
- }
30
- // ignore all new URL second arg
31
- if (reference.subtype === "new_url_second_arg") {
32
- return `ignore:${reference.url}`;
40
+ if (reference.isInline) {
41
+ return null;
33
42
  }
43
+
34
44
  if (
35
45
  reference.specifierPathname.endsWith(`/${directoryContentMagicName}`)
36
46
  ) {
@@ -13,6 +13,9 @@ export const jsenvPluginProtocolFile = ({
13
13
  magicDirectoryIndex,
14
14
  preserveSymlinks,
15
15
  directoryListing,
16
+ rootDirectoryUrl,
17
+ mainFilePath,
18
+ sourceFilesConfig,
16
19
  }) => {
17
20
  return [
18
21
  jsenvPluginFsRedirection({
@@ -73,6 +76,9 @@ export const jsenvPluginProtocolFile = ({
73
76
  jsenvPluginDirectoryListing({
74
77
  ...directoryListing,
75
78
  directoryContentMagicName,
79
+ rootDirectoryUrl,
80
+ mainFilePath,
81
+ sourceFilesConfig,
76
82
  }),
77
83
  ]
78
84
  : []),
@@ -1,7 +1,15 @@
1
1
  import { URL_META } from "@jsenv/url-meta";
2
+ import { setUrlExtension, urlToExtension, urlToFilename } from "@jsenv/urls";
2
3
  import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
3
4
 
4
5
  export const jsenvPluginProtocolHttp = ({ include }) => {
6
+ const prependIgnore = (reference) => {
7
+ if (reference.original) {
8
+ return `ignore:${reference.original.specifier}`;
9
+ }
10
+ return `ignore:${reference.specifier}`;
11
+ };
12
+
5
13
  if (include === false) {
6
14
  return {
7
15
  name: "jsenv:protocol_http",
@@ -10,7 +18,7 @@ export const jsenvPluginProtocolHttp = ({ include }) => {
10
18
  if (!reference.url.startsWith("http")) {
11
19
  return null;
12
20
  }
13
- return `ignore:${reference.url}`;
21
+ return prependIgnore(reference);
14
22
  },
15
23
  };
16
24
  }
@@ -33,7 +41,7 @@ export const jsenvPluginProtocolHttp = ({ include }) => {
33
41
  return null;
34
42
  }
35
43
  if (!shouldInclude(reference.url)) {
36
- return `ignore:${reference.url}`;
44
+ return prependIgnore(reference);
37
45
  }
38
46
  const outDirectoryUrl = reference.ownerUrlInfo.context.outDirectoryUrl;
39
47
  const urlObject = new URL(reference.url);
@@ -72,12 +80,34 @@ export const jsenvPluginProtocolHttp = ({ include }) => {
72
80
  if (isTextual) {
73
81
  content = await response.text();
74
82
  } else {
75
- content = await response.buffer;
83
+ content = Buffer.from(await response.arrayBuffer());
76
84
  }
85
+ // When fetching content from http it's possible to request something like
86
+ // "https://esm.sh/preact@10.23.1
87
+ // and receive content-type "application/javascript"
88
+ // if we do nothing, after build there will be a "preact@10.23.1" file without ".js" extension
89
+ // and the build server will serve this as "application/octet-stream".
90
+ // We want to build files to be compatible with any server and keep build server logic simple.
91
+ // -> We auto-append the extension corresponding to the content-type
92
+ let filenameHint;
93
+ const extension = urlToExtension(originalUrl);
94
+ if (extension === "") {
95
+ const wellKnownExtensionForThisContentType =
96
+ CONTENT_TYPE.toUrlExtension(contentType);
97
+ if (wellKnownExtensionForThisContentType) {
98
+ const urlWithExtension = setUrlExtension(
99
+ originalUrl,
100
+ wellKnownExtensionForThisContentType,
101
+ );
102
+ filenameHint = urlToFilename(urlWithExtension);
103
+ }
104
+ }
105
+
77
106
  return {
78
107
  content,
79
108
  contentType,
80
109
  contentLength: responseHeaders.get("content-length") || undefined,
110
+ filenameHint,
81
111
  };
82
112
  },
83
113
  };
@@ -7,7 +7,6 @@ import {
7
7
  getUrlForContentInsideHtml,
8
8
  parseHtml,
9
9
  parseSrcSet,
10
- removeHtmlNode,
11
10
  removeHtmlNodeText,
12
11
  setHtmlNodeAttributes,
13
12
  setHtmlNodeText,
@@ -63,22 +62,25 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
63
62
  // no importmap in this HTML file
64
63
  importmaps[htmlUrl] = null;
65
64
  }
66
- globalImportmap = Object.keys(importmaps).reduce((previous, url) => {
65
+ let importmapFinal = null;
66
+ for (const url of Object.keys(importmaps)) {
67
67
  const importmap = importmaps[url];
68
- if (!previous) {
69
- return importmap;
70
- }
71
68
  if (!importmap) {
72
- return previous;
69
+ continue;
73
70
  }
74
- return composeTwoImportMaps(previous, importmap);
75
- }, null);
71
+ if (!importmapFinal) {
72
+ importmapFinal = importmap;
73
+ continue;
74
+ }
75
+ importmapFinal = composeTwoImportMaps(importmapFinal, importmap);
76
+ }
77
+ globalImportmap = importmapFinal;
76
78
 
77
79
  importmapLoadingCount--;
78
80
  if (importmapLoadingCount === 0) {
79
- allImportmapLoadedCallbackSet.forEach((callback) => {
80
- callback();
81
- });
81
+ for (const allImportmapLoadedCallback of allImportmapLoadedCallbackSet) {
82
+ allImportmapLoadedCallback();
83
+ }
82
84
  allImportmapLoadedCallbackSet.clear();
83
85
  }
84
86
  };
@@ -103,6 +105,7 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
103
105
  },
104
106
  });
105
107
  if (fromMapping) {
108
+ reference.data.fromMapping = true;
106
109
  return result;
107
110
  }
108
111
  return null;
@@ -433,7 +436,7 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
433
436
  setHtmlNodeAttributes(scriptNode, {
434
437
  "src": undefined,
435
438
  "jsenv-inlined-by": "jsenv:html_reference_analysis",
436
- "inlined-from-src": src,
439
+ "inlined-from-src": importmapReference.url,
437
440
  });
438
441
  });
439
442
  });
@@ -471,16 +474,6 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
471
474
  });
472
475
  }
473
476
  }
474
- // once this plugin knows the importmap, it will use it
475
- // to map imports. These import specifiers will be normalized
476
- // by "formatReference" making the importmap presence useless.
477
- // In dev/test we keep importmap into the HTML to see it even if useless
478
- // Duing build we get rid of it
479
- if (urlInfo.context.build) {
480
- mutations.push(() => {
481
- removeHtmlNode(scriptNode);
482
- });
483
- }
484
477
  return;
485
478
  }
486
479
  const externalRef = visitSrc(scriptNode, {
@@ -1,10 +1,12 @@
1
1
  import { getUrlForContentInsideJs, parseJsUrls } from "@jsenv/ast";
2
+ import {
3
+ isWebWorkerEntryPointReference,
4
+ isWebWorkerUrlInfo,
5
+ } from "@jsenv/core/src/kitchen/web_workers.js";
2
6
  import { createMagicSource } from "@jsenv/sourcemap";
3
7
  import { urlToExtension } from "@jsenv/urls";
4
8
  import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
5
9
 
6
- import { isWebWorkerUrlInfo } from "@jsenv/core/src/kitchen/web_workers.js";
7
-
8
10
  export const jsenvPluginJsReferenceAnalysis = ({ inlineContent }) => {
9
11
  return [
10
12
  {
@@ -97,6 +99,34 @@ const parseAndTransformJsReferences = async (
97
99
  ) {
98
100
  externalReferenceInfo.expectedType = "js_module";
99
101
  }
102
+
103
+ let filenameHint;
104
+ if (
105
+ externalReferenceInfo.subtype === "import_dynamic" &&
106
+ isBareSpecifier(externalReferenceInfo.specifier)
107
+ ) {
108
+ filenameHint = `${externalReferenceInfo.specifier}.js`;
109
+ }
110
+
111
+ let isEntryPoint;
112
+ let isDynamicEntryPoint;
113
+ if (
114
+ isNodeJs &&
115
+ (externalReferenceInfo.type === "js_url" ||
116
+ externalReferenceInfo.subtype === "import_meta_resolve")
117
+ ) {
118
+ isEntryPoint = true;
119
+ isDynamicEntryPoint = true;
120
+ } else if (
121
+ isWebWorkerEntryPointReference({
122
+ subtype: externalReferenceInfo.subtype,
123
+ expectedSubtype: externalReferenceInfo.expectedSubtype,
124
+ })
125
+ ) {
126
+ isEntryPoint = true;
127
+ } else {
128
+ isEntryPoint = false;
129
+ }
100
130
  const reference = urlInfo.dependencies.found({
101
131
  type: externalReferenceInfo.type,
102
132
  subtype: externalReferenceInfo.subtype,
@@ -119,7 +149,11 @@ const parseAndTransformJsReferences = async (
119
149
  importAttributes: externalReferenceInfo.importAttributes,
120
150
  isSideEffectImport: externalReferenceInfo.isSideEffectImport,
121
151
  astInfo: externalReferenceInfo.astInfo,
152
+ isEntryPoint,
153
+ isDynamicEntryPoint,
154
+ filenameHint,
122
155
  });
156
+
123
157
  parallelActions.push(async () => {
124
158
  await reference.readGeneratedSpecifier();
125
159
  const replacement = reference.generatedSpecifier;
@@ -158,3 +192,20 @@ const parseAndTransformJsReferences = async (
158
192
  const { content, sourcemap } = magicSource.toContentAndSourcemap();
159
193
  return { content, sourcemap };
160
194
  };
195
+
196
+ const isBareSpecifier = (specifier) => {
197
+ if (
198
+ specifier[0] === "/" ||
199
+ specifier.startsWith("./") ||
200
+ specifier.startsWith("../")
201
+ ) {
202
+ return false;
203
+ }
204
+ try {
205
+ // eslint-disable-next-line no-new
206
+ new URL(specifier);
207
+ return false;
208
+ } catch {
209
+ return true;
210
+ }
211
+ };
@@ -3,43 +3,50 @@ import { createNodeEsmResolver } from "./node_esm_resolver.js";
3
3
  export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
4
4
  let nodeEsmResolverDefault;
5
5
  const resolvers = {};
6
- Object.keys(resolutionConfig).forEach((urlType) => {
7
- const config = resolutionConfig[urlType];
8
- if (config === true) {
9
- resolvers[urlType] = (...args) => nodeEsmResolverDefault(...args);
10
- } else if (config === false) {
11
- resolvers[urlType] = () => null;
12
- } else if (typeof config === "object") {
13
- const { runtimeCompat, packageConditions, preservesSymlink, ...rest } =
14
- config;
15
- const unexpectedKeys = Object.keys(rest);
16
- if (unexpectedKeys.length) {
17
- throw new TypeError(
18
- `${unexpectedKeys.join(
19
- ",",
20
- )}: there is no such configuration on "${urlType}"`,
21
- );
22
- }
23
- resolvers[urlType] = createNodeEsmResolver({
24
- runtimeCompat,
25
- packageConditions,
26
- preservesSymlink,
27
- });
28
- } else {
29
- throw new TypeError(
30
- `config must be true, false or an object, got ${config} on "${urlType}"`,
31
- );
32
- }
33
- });
34
6
 
35
7
  return {
36
8
  name: "jsenv:node_esm_resolution",
37
9
  appliesDuring: "*",
38
- init: ({ runtimeCompat }) => {
10
+ init: (kitchenContext) => {
39
11
  nodeEsmResolverDefault = createNodeEsmResolver({
40
- runtimeCompat,
12
+ build: kitchenContext.build,
13
+ runtimeCompat: kitchenContext.runtimeCompat,
41
14
  preservesSymlink: true,
42
15
  });
16
+ Object.keys(resolutionConfig).forEach((urlType) => {
17
+ const config = resolutionConfig[urlType];
18
+ if (config === true) {
19
+ resolvers[urlType] = (...args) => nodeEsmResolverDefault(...args);
20
+ } else if (config === false) {
21
+ resolvers[urlType] = () => null;
22
+ } else if (typeof config === "object") {
23
+ const {
24
+ runtimeCompat,
25
+ packageConditions,
26
+ preservesSymlink,
27
+ ...rest
28
+ } = config;
29
+ const unexpectedKeys = Object.keys(rest);
30
+ if (unexpectedKeys.length) {
31
+ throw new TypeError(
32
+ `${unexpectedKeys.join(
33
+ ",",
34
+ )}: there is no such configuration on "${urlType}"`,
35
+ );
36
+ }
37
+ resolvers[urlType] = createNodeEsmResolver({
38
+ build: kitchenContext.build,
39
+ runtimeCompat,
40
+ packageConditions,
41
+ preservesSymlink,
42
+ });
43
+ } else {
44
+ throw new TypeError(
45
+ `config must be true, false or an object, got ${config} on "${urlType}"`,
46
+ );
47
+ }
48
+ });
49
+
43
50
  if (resolvers.js_module === undefined) {
44
51
  resolvers.js_module = nodeEsmResolverDefault;
45
52
  }
@@ -16,6 +16,7 @@ import {
16
16
  import { readFileSync } from "node:fs";
17
17
 
18
18
  export const createNodeEsmResolver = ({
19
+ build,
19
20
  runtimeCompat,
20
21
  packageConditions,
21
22
  preservesSymlink,
@@ -23,7 +24,7 @@ export const createNodeEsmResolver = ({
23
24
  const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node");
24
25
  // https://nodejs.org/api/esm.html#resolver-algorithm-specification
25
26
  packageConditions = packageConditions || [
26
- ...readCustomConditionsFromProcessArgs(),
27
+ ...(build ? [] : readCustomConditionsFromProcessArgs()),
27
28
  nodeRuntimeEnabled ? "node" : "browser",
28
29
  "import",
29
30
  ];
@@ -34,11 +35,7 @@ export const createNodeEsmResolver = ({
34
35
  }
35
36
  const { ownerUrlInfo } = reference;
36
37
  if (reference.specifierPathname[0] === "/") {
37
- const url = new URL(
38
- reference.specifier.slice(1),
39
- ownerUrlInfo.context.rootDirectoryUrl,
40
- );
41
- return url;
38
+ return null; // let it to jsenv_web_resolution
42
39
  }
43
40
  let parentUrl;
44
41
  if (reference.baseUrl) {
@@ -49,8 +46,7 @@ export const createNodeEsmResolver = ({
49
46
  parentUrl = ownerUrlInfo.url;
50
47
  }
51
48
  if (!parentUrl.startsWith("file:")) {
52
- const url = new URL(reference.specifier, parentUrl);
53
- return url;
49
+ return null; // let it to jsenv_web_resolution
54
50
  }
55
51
  const { url, type, packageDirectoryUrl } = applyNodeEsmResolution({
56
52
  conditions: packageConditions,
@@ -5,18 +5,20 @@ export const jsenvPluginWebResolution = () => {
5
5
  resolveReference: (reference) => {
6
6
  const { ownerUrlInfo } = reference;
7
7
  if (reference.specifierPathname[0] === "/") {
8
- const url = new URL(
9
- reference.specifier.slice(1),
10
- ownerUrlInfo.context.rootDirectoryUrl,
11
- );
8
+ const resource = reference.specifier;
9
+ if (ownerUrlInfo.originalUrl?.startsWith("http")) {
10
+ return new URL(resource, ownerUrlInfo.originalUrl);
11
+ }
12
+ const url = new URL(resource.slice(1), ownerUrlInfo.entryUrlInfo.url);
12
13
  return url;
13
14
  }
14
15
  // baseUrl happens second argument to new URL() is different from
15
16
  // import.meta.url or document.currentScript.src
16
17
  const parentUrl =
17
- reference.baseUrl || ownerUrlInfo.context.dev
18
+ reference.baseUrl ||
19
+ (ownerUrlInfo.context.dev
18
20
  ? ownerUrlInfo.url
19
- : ownerUrlInfo.originalUrl || ownerUrlInfo.url;
21
+ : ownerUrlInfo.originalUrl || ownerUrlInfo.url);
20
22
  const url = new URL(reference.specifier, parentUrl);
21
23
  return url;
22
24
  },
@@ -271,7 +271,7 @@ connection_using_websocket: {
271
271
  websocketUrl,
272
272
  {
273
273
  logs,
274
- protocols = ["jsenv"],
274
+ protocols = [],
275
275
  useEventsToManageConnection = true,
276
276
  retry = false,
277
277
  retryAfter = 1000,
@@ -439,7 +439,7 @@ const serverEventsInterface = {
439
439
  listenEvents: () => {},
440
440
  setup: ({ logs }) => {
441
441
  const websocketScheme = self.location.protocol === "https:" ? "wss" : "ws";
442
- const websocketUrl = `${websocketScheme}://${self.location.host}${self.location.pathname}${self.location.search}`;
442
+ const websocketUrl = `${websocketScheme}://${self.location.host}/.internal/events.websocket`;
443
443
  const websocketConnection = createWebSocketConnection(websocketUrl, {
444
444
  logs,
445
445
  retry: true,