@jsenv/core 41.4.5 → 41.4.7

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,6 +3,7 @@ import { asUrlWithoutSearch, urlToRelativeUrl } from "@jsenv/urls";
3
3
  import {
4
4
  INJECTIONS,
5
5
  isPlaceholderInjection,
6
+ readInjectionValue,
6
7
  } from "../../kitchen/url_graph/url_info_injections.js";
7
8
 
8
9
  export const jsenvPluginInjections = (rawAssociations) => {
@@ -18,6 +19,7 @@ export const jsenvPluginInjections = (rawAssociations) => {
18
19
  }
19
20
  return null;
20
21
  };
22
+ const injectionsForUrlInfoMap = new WeakMap();
21
23
  let getInjections = null;
22
24
 
23
25
  return {
@@ -36,8 +38,8 @@ export const jsenvPluginInjections = (rawAssociations) => {
36
38
  });
37
39
  return injectionsGetter;
38
40
  };
39
- // an url written by an injection cannot be resolved during reference analysis;
40
- // errors use this to tell the file holds injections and suggest "jsenv-ignore"
41
+ // errors and the build read this to know an unresolved url may come from
42
+ // an injection, and word what they report accordingly
41
43
  context.hasInjections = (url) => {
42
44
  return Boolean(findInjectionsGetterForUrl(url));
43
45
  };
@@ -91,16 +93,61 @@ export const jsenvPluginInjections = (rawAssociations) => {
91
93
  contentInjections: defaultInjections,
92
94
  };
93
95
  }
96
+ injectionsForUrlInfoMap.set(urlInfo, injections);
94
97
  return {
95
- contentInjections: {
96
- ...defaultInjections,
97
- ...injections,
98
- },
98
+ contentInjections: { ...defaultInjections, ...injections },
99
99
  };
100
100
  },
101
+ // The content still holds the placeholders when references are analyzed: they are
102
+ // replaced at the very end, once the type of every inline content is known.
103
+ // A specifier must not wait for that, it would resolve "__BACKEND_URL__/users/me"
104
+ // as a file sitting next to the document. Only the placeholder is resolved here,
105
+ // then the specifier goes to whoever resolves this kind of url (node esm, web, ...)
106
+ resolveReference: (reference) => {
107
+ const { ownerUrlInfo } = reference;
108
+ const injections = injectionsForUrlInfoMap.get(ownerUrlInfo);
109
+ if (!injections) {
110
+ return null;
111
+ }
112
+ const { specifier, keySet } = injectIntoSpecifier(
113
+ reference.specifier,
114
+ injections,
115
+ );
116
+ if (keySet.size === 0) {
117
+ return null;
118
+ }
119
+ reference.specifier = specifier;
120
+ for (const key of keySet) {
121
+ // rewriting the specifier takes the placeholder out of the content,
122
+ // so the injection step must not expect to find it there
123
+ ownerUrlInfo.contentInjectionUsedKeySet.add(key);
124
+ }
125
+ return null;
126
+ },
101
127
  };
102
128
  };
103
129
 
130
+ const injectIntoSpecifier = (specifier, injections) => {
131
+ let specifierInjected = specifier;
132
+ const keySet = new Set();
133
+ for (const key of Object.keys(injections)) {
134
+ if (!specifierInjected.includes(key)) {
135
+ continue;
136
+ }
137
+ const injection = injections[key];
138
+ if (!isPlaceholderInjection(injection)) {
139
+ continue;
140
+ }
141
+ const value = readInjectionValue(injection);
142
+ if (typeof value !== "string") {
143
+ continue;
144
+ }
145
+ specifierInjected = specifierInjected.replaceAll(key, value);
146
+ keySet.add(key);
147
+ }
148
+ return { specifier: specifierInjected, keySet };
149
+ };
150
+
104
151
  // What a file inlines (a <script> or a <style> inside html) is authored in that file
105
152
  // and inherits its injections, with two adjustments:
106
153
  // - a global belongs to the file itself, injecting it into each inline content would
@@ -87,8 +87,10 @@ export const getCorePlugins = ({
87
87
  ...(packageBundle
88
88
  ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
89
89
  : []),
90
- jsenvPluginReferenceAnalysis(referenceAnalysis),
90
+ // before reference analysis: an url written by an injection must hold its
91
+ // final value when references are analyzed
91
92
  jsenvPluginInjections(injections),
93
+ jsenvPluginReferenceAnalysis(referenceAnalysis),
92
94
  jsenvPluginTranspilation(transpilation),
93
95
  // "jsenvPluginInlining" must be very soon because all other plugins will react differently once they see the file is inlined
94
96
  ...(inlining ? [jsenvPluginInlining()] : []),
@@ -6,6 +6,7 @@ import {
6
6
  import { createMagicSource } from "@jsenv/sourcemap";
7
7
  import { urlToExtension } from "@jsenv/urls";
8
8
  import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
9
+ import { isBareSpecifier } from "../../../helpers/bare_specifier.js";
9
10
 
10
11
  export const jsenvPluginJsReferenceAnalysis = ({ inlineContent }) => {
11
12
  return [
@@ -192,20 +193,3 @@ const parseAndTransformJsReferences = async (
192
193
  const { content, sourcemap } = magicSource.toContentAndSourcemap();
193
194
  return { content, sourcemap };
194
195
  };
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
- };
@@ -14,6 +14,7 @@ import {
14
14
  import { URL_META } from "@jsenv/url-meta";
15
15
  import { urlToBasename, urlToExtension } from "@jsenv/urls";
16
16
  import { readFileSync } from "node:fs";
17
+ import { isBareSpecifier } from "../../helpers/bare_specifier.js";
17
18
 
18
19
  export const createNodeEsmResolver = ({
19
20
  packageDirectory,
@@ -487,20 +488,3 @@ const createResolverWithFallbackOnError = (mainResolver, fallbackResolver) => {
487
488
  }
488
489
  };
489
490
  };
490
-
491
- const isBareSpecifier = (specifier) => {
492
- if (
493
- specifier[0] === "/" ||
494
- specifier.startsWith("./") ||
495
- specifier.startsWith("../")
496
- ) {
497
- return false;
498
- }
499
- try {
500
- // eslint-disable-next-line no-new
501
- new URL(specifier);
502
- return false;
503
- } catch {
504
- return true;
505
- }
506
- };
@@ -9,7 +9,13 @@ export const jsenvPluginWebResolution = () => {
9
9
  if (ownerUrlInfo.originalUrl?.startsWith("http")) {
10
10
  return new URL(resource, ownerUrlInfo.originalUrl);
11
11
  }
12
- const url = new URL(resource.slice(1), ownerUrlInfo.entryUrlInfo.url);
12
+ // "/x" is the web meaning of a root-relative url: the root of what is
13
+ // served, which is the source directory — not the directory the entry
14
+ // point happens to sit in.
15
+ const url = new URL(
16
+ resource.slice(1),
17
+ ownerUrlInfo.context.rootDirectoryUrl,
18
+ );
13
19
  return url;
14
20
  }
15
21
  // baseUrl happens second argument to new URL() is different from