@jsenv/core 27.0.0-alpha.53 → 27.0.0-alpha.54

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "27.0.0-alpha.53",
3
+ "version": "27.0.0-alpha.54",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -68,7 +68,7 @@
68
68
  "@jsenv/node-esm-resolution": "0.0.6",
69
69
  "@jsenv/server": "12.6.2",
70
70
  "@jsenv/uneval": "1.6.0",
71
- "@jsenv/utils": "1.7.2",
71
+ "@jsenv/utils": "1.7.3",
72
72
  "construct-style-sheets-polyfill": "3.1.0",
73
73
  "cssnano": "5.1.7",
74
74
  "cssnano-preset-default": "5.2.7",
@@ -22,6 +22,7 @@ import {
22
22
  injectQueryParams,
23
23
  setUrlFilename,
24
24
  asUrlUntilPathname,
25
+ normalizeUrl,
25
26
  } from "@jsenv/utils/urls/url_utils.js"
26
27
  import { createVersionGenerator } from "@jsenv/utils/versioning/version_generator.js"
27
28
  import { generateSourcemapUrl } from "@jsenv/utils/sourcemap/sourcemap_utils.js"
@@ -223,9 +224,16 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
223
224
  })
224
225
  })
225
226
  const addToBundlerIfAny = (rawUrlInfo) => {
226
- // if (rawUrlInfo.dependencies.size === 0) {
227
- // return
228
- // }
227
+ if (
228
+ // entry point must be given to the bundler (rollup)
229
+ // so that the bundler know it's an entry point, even if it has no dependency
230
+ // this way the bundler can share code properly and avoid inlining an entry point
231
+ // if it's used by an other entry point
232
+ !rawUrlInfo.data.isEntryPoint &&
233
+ rawUrlInfo.dependencies.size === 0
234
+ ) {
235
+ return
236
+ }
229
237
  const bundler = bundlers[rawUrlInfo.type]
230
238
  if (bundler) {
231
239
  bundler.urlInfos.push(rawUrlInfo)
@@ -253,6 +261,21 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
253
261
  }
254
262
  addToBundlerIfAny(dependencyUrlInfo)
255
263
  })
264
+ rawUrlInfo.references.forEach((reference) => {
265
+ if (
266
+ reference.isRessourceHint &&
267
+ reference.expectedType === "js_module"
268
+ ) {
269
+ const referencedUrlInfo = rawGraph.getUrlInfo(reference.url)
270
+ if (
271
+ referencedUrlInfo &&
272
+ // something else than the ressource hint is using this url
273
+ referencedUrlInfo.dependents.size > 0
274
+ ) {
275
+ addToBundlerIfAny(referencedUrlInfo)
276
+ }
277
+ }
278
+ })
256
279
  return
257
280
  }
258
281
  }
@@ -361,25 +384,10 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
361
384
  if (urlRedirectedByBundle) {
362
385
  return urlRedirectedByBundle
363
386
  }
364
- const parentIsFromBundle = Boolean(
365
- bundleUrlInfos[reference.parentUrl],
366
- )
367
- // urls inside css bundled by parcel
368
- // contains url relative to the bundle file (which is considered inside build directory)
369
- // if the file is not itself a bundle file it must be resolved against
370
- // the original css url
371
- if (
372
- parentIsFromBundle &&
373
- !bundleUrlInfos[url] &&
374
- urlIsInsideOf(url, buildDirectoryUrl)
375
- ) {
376
- const parentRawUrl = rawUrls[reference.parentUrl]
377
- url = new URL(reference.specifier, parentRawUrl).href
378
- }
379
387
  const urlRedirected = rawUrlRedirections[url]
380
388
  return urlRedirected || url
381
389
  },
382
- normalizeUrl: (reference) => {
390
+ redirectUrl: (reference) => {
383
391
  if (!reference.url.startsWith("file:")) {
384
392
  return null
385
393
  }
@@ -860,11 +868,13 @@ const applyUrlVersioning = async ({
860
868
  })
861
869
  urlInfo.data.version = versionGenerator.generate()
862
870
 
863
- urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
864
- buildUrl: urlInfo.url,
865
- version: urlInfo.data.version,
866
- versioningMethod,
867
- })
871
+ urlInfo.data.versionedUrl = normalizeUrl(
872
+ injectVersionIntoBuildUrl({
873
+ buildUrl: urlInfo.url,
874
+ version: urlInfo.data.version,
875
+ versioningMethod,
876
+ }),
877
+ )
868
878
  })
869
879
  const versionMappings = {}
870
880
  const usedVersionMappings = []
@@ -280,6 +280,9 @@ export const createRuntimeFromPlaywright = ({
280
280
  /* eslint-disable no-undef */
281
281
  /* istanbul ignore next */
282
282
  () => {
283
+ if (!window.__html_supervisor__) {
284
+ throw new Error(`window.__html_supervisor__ not found`)
285
+ }
283
286
  return window.__html_supervisor__.getScriptExecutionResults()
284
287
  },
285
288
  /* eslint-enable no-undef */
@@ -10,7 +10,7 @@ import { createDetailedMessage } from "@jsenv/logger"
10
10
 
11
11
  import { stringifyUrlSite } from "@jsenv/utils/urls/url_trace.js"
12
12
  import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js"
13
- import { setUrlFilename } from "@jsenv/utils/urls/url_utils.js"
13
+ import { normalizeUrl, setUrlFilename } from "@jsenv/utils/urls/url_utils.js"
14
14
 
15
15
  import { createPluginController } from "../plugins/plugin_controller.js"
16
16
  import { createUrlInfoTransformer } from "./url_graph/url_info_transformations.js"
@@ -142,7 +142,7 @@ export const createKitchen = ({
142
142
  }
143
143
  const resolveReference = (reference) => {
144
144
  try {
145
- const resolvedUrl = pluginController.callHooksUntil(
145
+ let resolvedUrl = pluginController.callHooksUntil(
146
146
  "resolveUrl",
147
147
  reference,
148
148
  baseContext,
@@ -150,6 +150,7 @@ export const createKitchen = ({
150
150
  if (!resolvedUrl) {
151
151
  throw new Error(`NO_RESOLVE`)
152
152
  }
153
+ resolvedUrl = normalizeUrl(resolvedUrl)
153
154
  reference.url = resolvedUrl
154
155
  if (reference.external) {
155
156
  reference.generatedUrl = resolvedUrl
@@ -157,35 +158,20 @@ export const createKitchen = ({
157
158
  return urlGraph.reuseOrCreateUrlInfo(reference.url)
158
159
  }
159
160
  pluginController.callHooks(
160
- "normalizeUrl",
161
+ "redirectUrl",
161
162
  reference,
162
163
  baseContext,
163
164
  (returnValue) => {
164
- if (returnValue === reference.url) {
165
- return
166
- }
167
- const normalizedReturnValue = returnValue.startsWith("data:")
168
- ? returnValue
169
- : returnValue.replace(/[=](?=&|$)/g, "")
165
+ const normalizedReturnValue = normalizeUrl(returnValue)
170
166
  if (normalizedReturnValue === reference.url) {
171
167
  return
172
168
  }
173
169
  const previousReference = { ...reference }
174
- reference.url = returnValue
170
+ reference.url = normalizedReturnValue
175
171
  mutateReference(previousReference, reference)
176
172
  },
177
173
  )
178
- // force a last normalization on url search params
179
- // some plugin use URLSearchParams to alter the url search params
180
- // which can result into "file:///file.css?css_module"
181
- // becoming "file:///file.css?css_module="
182
- // we want to get rid of the "=" and consider it's the same url
183
- if (
184
- // disable on data urls (would mess up base64 encoding)
185
- !reference.url.startsWith("data:")
186
- ) {
187
- reference.url = reference.url.replace(/[=](?=&|$)/g, "")
188
- }
174
+
189
175
  const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
190
176
  applyReferenceEffectsOnUrlInfo(reference, urlInfo, baseContext)
191
177
 
@@ -281,12 +267,13 @@ export const createKitchen = ({
281
267
  return
282
268
  }
283
269
  try {
284
- const returnValue = await pluginController.callAsyncHooksUntil(
285
- "fetchUrlContent",
286
- urlInfo,
287
- context,
288
- )
289
- if (!returnValue) {
270
+ const fetchUrlContentReturnValue =
271
+ await pluginController.callAsyncHooksUntil(
272
+ "fetchUrlContent",
273
+ urlInfo,
274
+ context,
275
+ )
276
+ if (!fetchUrlContentReturnValue) {
290
277
  logger.warn(
291
278
  createDetailedMessage(
292
279
  `no plugin has handled the url during "fetchUrlContent" hook -> consider url as external (ignore it)`,
@@ -299,7 +286,7 @@ export const createKitchen = ({
299
286
  urlInfo.external = true
300
287
  return
301
288
  }
302
- if (returnValue.external) {
289
+ if (fetchUrlContentReturnValue.external) {
303
290
  urlInfo.external = true
304
291
  return
305
292
  }
@@ -312,7 +299,7 @@ export const createKitchen = ({
312
299
  content,
313
300
  sourcemap,
314
301
  filename,
315
- } = returnValue
302
+ } = fetchUrlContentReturnValue
316
303
  urlInfo.type =
317
304
  type ||
318
305
  reference.expectedType ||
@@ -6,7 +6,6 @@ export const loadUrlGraph = async ({
6
6
  startLoading,
7
7
  outDirectoryUrl,
8
8
  clientRuntimeCompat,
9
- skipRessourceHint = false,
10
9
  }) => {
11
10
  if (outDirectoryUrl) {
12
11
  await ensureEmptyDirectory(outDirectoryUrl)
@@ -34,7 +33,11 @@ export const loadUrlGraph = async ({
34
33
  })
35
34
  const { references } = urlInfo
36
35
  references.forEach((reference) => {
37
- if (skipRessourceHint && reference.isRessourceHint) {
36
+ // we don't cook ressource hints
37
+ // because they might refer to ressource that will be modified during build
38
+ // It also means something else have to reference that url in order to cook it
39
+ // so that the preload is deleted by "resync_ressource_hints.js" otherwise
40
+ if (reference.isRessourceHint) {
38
41
  return
39
42
  }
40
43
  // we use reference.generatedUrl to mimic what a browser would do:
@@ -2,7 +2,7 @@ export const jsenvPluginHmr = () => {
2
2
  return {
3
3
  name: "jsenv:hmr",
4
4
  appliesDuring: { dev: true },
5
- normalizeUrl: (reference) => {
5
+ redirectUrl: (reference) => {
6
6
  const urlObject = new URL(reference.url)
7
7
  if (!urlObject.searchParams.has("hmr")) {
8
8
  reference.data.hmr = false
@@ -247,20 +247,9 @@ const rollupPluginJsenv = ({
247
247
  code: urlInfo.content,
248
248
  map: urlInfo.sourcemap
249
249
  ? sourcemapConverter.toFilePaths(urlInfo.sourcemap)
250
- : urlInfo.sourcemap,
250
+ : null,
251
251
  }
252
252
  },
253
- // resolveFileUrl: ({ moduleId }) => {
254
- // return `${fileUrlConverter.asFileUrl(moduleId)}`
255
- // },
256
- renderChunk: (code, chunkInfo) => {
257
- const { facadeModuleId } = chunkInfo
258
- if (!facadeModuleId) {
259
- // happens for inline module scripts for instance
260
- return null
261
- }
262
- return null
263
- },
264
253
  }
265
254
  }
266
255
 
@@ -13,7 +13,7 @@ export const jsenvPluginFileSystemMagic = ({
13
13
  return {
14
14
  name: "jsenv:filesystem_magic",
15
15
  appliesDuring: "*",
16
- normalizeUrl: (reference) => {
16
+ redirectUrl: (reference) => {
17
17
  // http, https, data, about, etc
18
18
  if (!reference.url.startsWith("file:")) {
19
19
  return null
@@ -5,7 +5,7 @@ export const createPluginController = ({
5
5
  scenario,
6
6
  hooks = [
7
7
  "resolveUrl",
8
- "normalizeUrl",
8
+ "redirectUrl",
9
9
  "fetchUrlContent",
10
10
  "transformUrlContent",
11
11
  "transformUrlSearchParams",
@@ -226,7 +226,7 @@ const assertAndNormalizeReturnValue = (hookName, returnValue) => {
226
226
  const returnValueAssertions = [
227
227
  {
228
228
  name: "url_assertion",
229
- appliesTo: ["resolveUrl", "normalizeUrl"],
229
+ appliesTo: ["resolveUrl", "redirectUrl"],
230
230
  assertion: (valueReturned) => {
231
231
  if (valueReturned instanceof URL) {
232
232
  return valueReturned.href
@@ -44,34 +44,42 @@ export const jsenvPluginAsJsClassic = ({ systemJsInjection }) => {
44
44
  }
45
45
 
46
46
  const asJsClassic = ({ systemJsInjection, systemJsClientFileUrl }) => {
47
+ const propagateJsClassicSearchParam = (reference, context) => {
48
+ const parentUrlInfo = context.urlGraph.getUrlInfo(reference.parentUrl)
49
+ if (
50
+ !parentUrlInfo ||
51
+ !new URL(parentUrlInfo.url).searchParams.has("as_js_classic")
52
+ ) {
53
+ return null
54
+ }
55
+ const urlTransformed = injectQueryParams(reference.url, {
56
+ as_js_classic: "",
57
+ })
58
+ reference.filename = generateJsClassicFilename(reference.url)
59
+ return urlTransformed
60
+ }
61
+
47
62
  return {
48
63
  name: "jsenv:as_js_classic",
49
64
  appliesDuring: "*",
50
65
  // forward ?as_js_classic to referenced urls
51
- normalizeUrl: (reference, context) => {
52
- // We want to propagate transformation of js module to js classic
53
- // but only for import specifier (static/dynamic import + re-export)
54
- // All other references won't get the ?as_js_classic
55
- // otherwise we could try to transform inline ressources, specifiers inside new URL(). ...
56
- if (
57
- reference.type !== "js_import_export" &&
58
- reference.subtype !== "system_register_arg" &&
59
- reference.subtype !== "system_import_arg"
60
- ) {
66
+ redirectUrl: {
67
+ // We want to propagate transformation of js module to js classic to:
68
+ // - import specifier (static/dynamic import + re-export)
69
+ // - url specifier when inside System.register/_context.import()
70
+ // (because it's the transpiled equivalent of static and dynamic imports)
71
+ // And not other references otherwise we could try to transform inline ressources
72
+ // or specifiers inside new URL()...
73
+ js_import_export: propagateJsClassicSearchParam,
74
+ js_url_specifier: (reference, context) => {
75
+ if (
76
+ reference.subtype === "system_register_arg" ||
77
+ reference.subtype === "system_import_arg"
78
+ ) {
79
+ return propagateJsClassicSearchParam(reference, context)
80
+ }
61
81
  return null
62
- }
63
- const parentUrlInfo = context.urlGraph.getUrlInfo(reference.parentUrl)
64
- if (
65
- !parentUrlInfo ||
66
- !new URL(parentUrlInfo.url).searchParams.has("as_js_classic")
67
- ) {
68
- return null
69
- }
70
- const urlTransformed = injectQueryParams(reference.url, {
71
- as_js_classic: "",
72
- })
73
- reference.filename = generateJsClassicFilename(reference.url)
74
- return urlTransformed
82
+ },
75
83
  },
76
84
  fetchUrlContent: async (urlInfo, context) => {
77
85
  const originalUrlInfo = await fetchOriginalUrlInfo({
@@ -98,7 +98,16 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
98
98
  })
99
99
 
100
100
  const jsModuleUrls = []
101
- const getReferenceAsJsClassic = async (reference) => {
101
+ const getReferenceAsJsClassic = async (
102
+ reference,
103
+ {
104
+ // we don't cook ressource hints
105
+ // because they might refer to ressource that will be modified during build
106
+ // It also means something else HAVE to reference that url in order to cook it
107
+ // so that the preload is deleted by "resync_ressource_hints.js" otherwise
108
+ cookIt = false,
109
+ } = {},
110
+ ) => {
102
111
  const [newReference, newUrlInfo] = context.referenceUtils.update(
103
112
  reference,
104
113
  {
@@ -112,6 +121,8 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
112
121
  const jsModuleUrl = newUrlInfo.url
113
122
  if (!jsModuleUrls.includes(jsModuleUrl)) {
114
123
  jsModuleUrls.push(newUrlInfo.url)
124
+ }
125
+ if (cookIt) {
115
126
  // during dev it means js modules will be cooked before server sends the HTML
116
127
  // it's ok because:
117
128
  // - during dev script_type_module are supported (dev use a recent browser)
@@ -141,9 +152,9 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
141
152
  // but it's unlikely to happen and people should use "modulepreload" in that case anyway
142
153
  if (expectedScriptType === "module") {
143
154
  actions.push(async () => {
144
- const [newReference] = await getReferenceAsJsClassic(
145
- context.referenceUtils.findByGeneratedSpecifier(href),
146
- )
155
+ const reference =
156
+ context.referenceUtils.findByGeneratedSpecifier(href)
157
+ const [newReference] = await getReferenceAsJsClassic(reference)
147
158
  assignHtmlNodeAttributes(preloadAsScriptNode, {
148
159
  href: newReference.generatedSpecifier,
149
160
  })
@@ -158,9 +169,9 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
158
169
  )
159
170
  const href = hrefAttribute.value
160
171
  actions.push(async () => {
161
- const [newReference] = await getReferenceAsJsClassic(
162
- context.referenceUtils.findByGeneratedSpecifier(href),
163
- )
172
+ const reference =
173
+ context.referenceUtils.findByGeneratedSpecifier(href)
174
+ const [newReference] = await getReferenceAsJsClassic(reference)
164
175
  assignHtmlNodeAttributes(modulePreloadNode, {
165
176
  rel: "preload",
166
177
  as: "script",
@@ -176,9 +187,11 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
176
187
  if (srcAttribute) {
177
188
  actions.push(async () => {
178
189
  const specifier = srcAttribute.value
179
- const [newReference] = await getReferenceAsJsClassic(
180
- context.referenceUtils.findByGeneratedSpecifier(specifier),
181
- )
190
+ const reference =
191
+ context.referenceUtils.findByGeneratedSpecifier(specifier)
192
+ const [newReference] = await getReferenceAsJsClassic(reference, {
193
+ cookIt: true,
194
+ })
182
195
  removeHtmlNodeAttributeByName(moduleScriptNode, "type")
183
196
  srcAttribute.value = newReference.generatedSpecifier
184
197
  })
@@ -214,6 +227,7 @@ export const jsenvPluginScriptTypeModuleAsClassic = ({
214
227
  })
215
228
  const [, newUrlInfo] = await getReferenceAsJsClassic(
216
229
  inlineReference,
230
+ { cookIt: true },
217
231
  )
218
232
  removeHtmlNodeAttributeByName(moduleScriptNode, "type")
219
233
  setHtmlNodeGeneratedText(moduleScriptNode, {
@@ -21,7 +21,7 @@ export const jsenvPluginWorkersTypeModuleAsClassic = ({
21
21
  return {
22
22
  name: "jsenv:workers_type_module_as_classic",
23
23
  appliesDuring: "*",
24
- normalizeUrl: {
24
+ redirectUrl: {
25
25
  js_url_specifier: (reference, context) => {
26
26
  if (reference.expectedType !== "js_module") {
27
27
  return null
@@ -36,7 +36,7 @@ export const jsenvPluginImportAssertions = () => {
36
36
  const importAssertions = {
37
37
  name: "jsenv:import_assertions",
38
38
  appliesDuring: "*",
39
- normalizeUrl: {
39
+ redirectUrl: {
40
40
  js_import_export: (reference, context) => {
41
41
  if (!reference.assert) {
42
42
  return null
@@ -96,8 +96,8 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
96
96
  ...readFetchMetas(node),
97
97
  })
98
98
  }
99
- const onNode = (node) => {
100
- if (node.nodeName === "link") {
99
+ const visitors = {
100
+ link: (node) => {
101
101
  const relAttribute = getHtmlNodeAttributeByName(node, "rel")
102
102
  const rel = relAttribute ? relAttribute.value : undefined
103
103
  const typeAttribute = getHtmlNodeAttributeByName(node, "type")
@@ -114,13 +114,9 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
114
114
  stylesheet: "css",
115
115
  }[rel],
116
116
  })
117
- return
118
- }
119
- // if (node.nodeName === "style") {
120
- // // styles.push(node)
121
- // return
122
- // }
123
- if (node.nodeName === "script") {
117
+ },
118
+ // style: () => {},
119
+ script: (node) => {
124
120
  const typeAttributeNode = getHtmlNodeAttributeByName(node, "type")
125
121
  visitAttributeAsUrlSpecifier({
126
122
  type: "script_src",
@@ -133,23 +129,22 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
133
129
  node,
134
130
  attributeName: "src",
135
131
  })
136
- return
137
- }
138
- if (node.nodeName === "a") {
132
+ },
133
+ a: (node) => {
139
134
  visitAttributeAsUrlSpecifier({
140
135
  type: "a_href",
141
136
  node,
142
137
  attributeName: "href",
143
138
  })
144
- }
145
- if (node.nodeName === "iframe") {
139
+ },
140
+ iframe: (node) => {
146
141
  visitAttributeAsUrlSpecifier({
147
142
  type: "iframe_src",
148
143
  node,
149
144
  attributeName: "src",
150
145
  })
151
- }
152
- if (node.nodeName === "img") {
146
+ },
147
+ img: (node) => {
153
148
  visitAttributeAsUrlSpecifier({
154
149
  type: "img_src",
155
150
  node,
@@ -159,9 +154,8 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
159
154
  type: "img_srcset",
160
155
  node,
161
156
  })
162
- return
163
- }
164
- if (node.nodeName === "source") {
157
+ },
158
+ souce: (node) => {
165
159
  visitAttributeAsUrlSpecifier({
166
160
  type: "source_src",
167
161
  node,
@@ -171,25 +165,22 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
171
165
  type: "source_srcset",
172
166
  node,
173
167
  })
174
- return
175
- }
168
+ },
176
169
  // svg <image> tag
177
- if (node.nodeName === "image") {
170
+ image: (node) => {
178
171
  visitAttributeAsUrlSpecifier({
179
172
  type: "image_href",
180
173
  node,
181
174
  attributeName: "href",
182
175
  })
183
- return
184
- }
185
- if (node.nodeName === "use") {
176
+ },
177
+ use: (node) => {
186
178
  visitAttributeAsUrlSpecifier({
187
179
  type: "use_href",
188
180
  node,
189
181
  attributeName: "href",
190
182
  })
191
- return
192
- }
183
+ },
193
184
  }
194
185
  const visitAttributeAsUrlSpecifier = ({
195
186
  type,
@@ -252,7 +243,12 @@ const visitHtmlUrls = ({ url, htmlAst, onUrl }) => {
252
243
  })
253
244
  }
254
245
  }
255
- visitHtmlAst(htmlAst, onNode)
246
+ visitHtmlAst(htmlAst, (node) => {
247
+ const visitor = visitors[node.nodeName]
248
+ if (visitor) {
249
+ visitor(node)
250
+ }
251
+ })
256
252
  }
257
253
 
258
254
  const crossOriginCompatibleTagNames = ["script", "link", "img", "source"]
@@ -2,7 +2,7 @@ export const jsenvPluginUrlVersion = ({ longTermCache = true } = {}) => {
2
2
  return {
3
3
  name: "jsenv:url_version",
4
4
  appliesDuring: "*", // maybe only during dev?
5
- normalizeUrl: (reference) => {
5
+ redirectUrl: (reference) => {
6
6
  // "v" search param goal is to enable long-term cache
7
7
  // for server response headers
8
8
  // it is also used by hmr to bypass browser cache