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

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.56",
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,6 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
223
224
  })
224
225
  })
225
226
  const addToBundlerIfAny = (rawUrlInfo) => {
226
- // if (rawUrlInfo.dependencies.size === 0) {
227
- // return
228
- // }
229
227
  const bundler = bundlers[rawUrlInfo.type]
230
228
  if (bundler) {
231
229
  bundler.urlInfos.push(rawUrlInfo)
@@ -253,6 +251,21 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
253
251
  }
254
252
  addToBundlerIfAny(dependencyUrlInfo)
255
253
  })
254
+ rawUrlInfo.references.forEach((reference) => {
255
+ if (
256
+ reference.isRessourceHint &&
257
+ reference.expectedType === "js_module"
258
+ ) {
259
+ const referencedUrlInfo = rawGraph.getUrlInfo(reference.url)
260
+ if (
261
+ referencedUrlInfo &&
262
+ // something else than the ressource hint is using this url
263
+ referencedUrlInfo.dependents.size > 0
264
+ ) {
265
+ addToBundlerIfAny(referencedUrlInfo)
266
+ }
267
+ }
268
+ })
256
269
  return
257
270
  }
258
271
  }
@@ -361,25 +374,10 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
361
374
  if (urlRedirectedByBundle) {
362
375
  return urlRedirectedByBundle
363
376
  }
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
377
  const urlRedirected = rawUrlRedirections[url]
380
378
  return urlRedirected || url
381
379
  },
382
- normalizeUrl: (reference) => {
380
+ redirectUrl: (reference) => {
383
381
  if (!reference.url.startsWith("file:")) {
384
382
  return null
385
383
  }
@@ -732,12 +730,12 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
732
730
  buildInlineContents[buildRelativeUrl] = urlInfo.content
733
731
  } else {
734
732
  buildFileContents[buildRelativeUrl] = urlInfo.content
733
+ const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
734
+ urlInfo.url,
735
+ buildDirectoryUrl,
736
+ )
737
+ buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
735
738
  }
736
- const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
737
- urlInfo.url,
738
- buildDirectoryUrl,
739
- )
740
- buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
741
739
  })
742
740
  if (writeOnFileSystem) {
743
741
  if (buildDirectoryClean) {
@@ -860,11 +858,13 @@ const applyUrlVersioning = async ({
860
858
  })
861
859
  urlInfo.data.version = versionGenerator.generate()
862
860
 
863
- urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
864
- buildUrl: urlInfo.url,
865
- version: urlInfo.data.version,
866
- versioningMethod,
867
- })
861
+ urlInfo.data.versionedUrl = normalizeUrl(
862
+ injectVersionIntoBuildUrl({
863
+ buildUrl: urlInfo.url,
864
+ version: urlInfo.data.version,
865
+ versioningMethod,
866
+ }),
867
+ )
868
868
  })
869
869
  const versionMappings = {}
870
870
  const usedVersionMappings = []
@@ -68,6 +68,7 @@ export const startBuildServer = async ({
68
68
  buildDirectoryUrl = assertAndNormalizeDirectoryUrl(buildDirectoryUrl)
69
69
 
70
70
  const reloadableProcess = await initReloadableProcess({
71
+ signal,
71
72
  handleSIGINT,
72
73
  ...(buildServerAutoreload
73
74
  ? {
@@ -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
 
@@ -206,10 +192,7 @@ export const createKitchen = ({
206
192
  Object.keys(returnValue).forEach((key) => {
207
193
  referenceUrlObject.searchParams.set(key, returnValue[key])
208
194
  })
209
- reference.generatedUrl = referenceUrlObject.href.replace(
210
- /[=](?=&|$)/g,
211
- "",
212
- )
195
+ reference.generatedUrl = normalizeUrl(referenceUrlObject.href)
213
196
  },
214
197
  )
215
198
  const returnValue = pluginController.callHooksUntil(
@@ -281,12 +264,13 @@ export const createKitchen = ({
281
264
  return
282
265
  }
283
266
  try {
284
- const returnValue = await pluginController.callAsyncHooksUntil(
285
- "fetchUrlContent",
286
- urlInfo,
287
- context,
288
- )
289
- if (!returnValue) {
267
+ const fetchUrlContentReturnValue =
268
+ await pluginController.callAsyncHooksUntil(
269
+ "fetchUrlContent",
270
+ urlInfo,
271
+ context,
272
+ )
273
+ if (!fetchUrlContentReturnValue) {
290
274
  logger.warn(
291
275
  createDetailedMessage(
292
276
  `no plugin has handled the url during "fetchUrlContent" hook -> consider url as external (ignore it)`,
@@ -299,7 +283,7 @@ export const createKitchen = ({
299
283
  urlInfo.external = true
300
284
  return
301
285
  }
302
- if (returnValue.external) {
286
+ if (fetchUrlContentReturnValue.external) {
303
287
  urlInfo.external = true
304
288
  return
305
289
  }
@@ -312,7 +296,7 @@ export const createKitchen = ({
312
296
  content,
313
297
  sourcemap,
314
298
  filename,
315
- } = returnValue
299
+ } = fetchUrlContentReturnValue
316
300
  urlInfo.type =
317
301
  type ||
318
302
  reference.expectedType ||
@@ -667,8 +651,6 @@ export const createKitchen = ({
667
651
  const prepareEntryPoint = (params) => {
668
652
  const entryReference = createReference(params)
669
653
  const entryUrlInfo = resolveReference(entryReference)
670
- // I should likely delete urlInfo.sourcemap
671
- // otherwise it is reused when page is reloaded
672
654
  return [entryReference, entryUrlInfo]
673
655
  }
674
656
 
@@ -20,6 +20,11 @@ export const createFileService = ({
20
20
  urlGraph,
21
21
  scenario,
22
22
  }
23
+ const augmentResponseContext = {
24
+ rootDirectoryUrl,
25
+ urlGraph,
26
+ scenario,
27
+ }
23
28
 
24
29
  const getResponse = async (request) => {
25
30
  // serve file inside ".jsenv" directory
@@ -58,9 +63,19 @@ export const createFileService = ({
58
63
  reference.parentUrl,
59
64
  )
60
65
  try {
61
- // urlInfo objects are reused, they must be "reset" before cooking then again
62
- if (!urlInfo.isInline && !urlInfo.type === "sourcemap") {
63
- urlGraph.resetUrlInfo(urlInfo)
66
+ // urlInfo objects are reused, they must be "reset" before cooking them again
67
+ if (
68
+ urlInfo.contentEtag &&
69
+ !urlInfo.isInline &&
70
+ urlInfo.type !== "sourcemap"
71
+ ) {
72
+ urlInfo.sourcemap = null
73
+ urlInfo.sourcemapReference = null
74
+ urlInfo.content = null
75
+ urlInfo.originalContent = null
76
+ urlInfo.type = null
77
+ urlInfo.subtype = null
78
+ urlInfo.timing = {}
64
79
  }
65
80
  const { runtimeName, runtimeVersion } = parseUserAgentHeader(
66
81
  request.headers["user-agent"],
@@ -92,7 +107,7 @@ export const createFileService = ({
92
107
  kitchen.pluginController.callHooks(
93
108
  "augmentResponse",
94
109
  { reference, urlInfo },
95
- {},
110
+ augmentResponseContext,
96
111
  (returnValue) => {
97
112
  response = composeTwoResponses(response, returnValue)
98
113
  },
@@ -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:
@@ -15,16 +15,6 @@ export const createUrlGraph = ({
15
15
  }
16
16
  }
17
17
  }
18
- const resetUrlInfo = (urlInfo) => {
19
- urlInfo.sourcemap = null
20
- urlInfo.sourcemapReference = null
21
- urlInfo.content = null
22
- urlInfo.originalContent = null
23
- urlInfo.type = null
24
- urlInfo.subtype = null
25
- urlInfo.data = {}
26
- urlInfo.timing = {}
27
- }
28
18
 
29
19
  const reuseOrCreateUrlInfo = (url) => {
30
20
  const existingUrlInfo = urlInfos[url]
@@ -160,7 +150,6 @@ export const createUrlGraph = ({
160
150
  reuseOrCreateUrlInfo,
161
151
  getUrlInfo,
162
152
  deleteUrlInfo,
163
- resetUrlInfo,
164
153
  inferReference,
165
154
  findDependent,
166
155
  updateReferences,
@@ -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
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  isFileSystemPath,
3
3
  normalizeStructuredMetaMap,
4
+ urlIsInsideOf,
4
5
  urlToMeta,
5
6
  } from "@jsenv/filesystem"
6
7
  import { createDetailedMessage } from "@jsenv/logger"
@@ -8,6 +9,12 @@ import { createDetailedMessage } from "@jsenv/logger"
8
9
  import { applyRollupPlugins } from "@jsenv/utils/js_ast/apply_rollup_plugins.js"
9
10
  import { sourcemapConverter } from "@jsenv/utils/sourcemap/sourcemap_converter.js"
10
11
  import { fileUrlConverter } from "@jsenv/core/src/omega/file_url_converter.js"
12
+ import { babelHelperNameFromUrl } from "@jsenv/babel-plugins"
13
+
14
+ const jsenvBabelPluginDirectoryUrl = new URL(
15
+ "../../transpilation/babel/",
16
+ import.meta.url,
17
+ ).href
11
18
 
12
19
  export const bundleJsModule = async ({
13
20
  jsModuleUrlInfos,
@@ -216,6 +223,23 @@ const rollupPluginJsenv = ({
216
223
  const name = nameFromUrlInfo || `${chunkInfo.name}.js`
217
224
  return insideJs ? `js/${name}` : `${name}`
218
225
  },
226
+ manualChunks: (id) => {
227
+ const fileUrl = fileUrlConverter.asFileUrl(id)
228
+ if (
229
+ fileUrl.endsWith(
230
+ "babel-plugin-transform-async-to-promises/helpers.mjs",
231
+ )
232
+ ) {
233
+ return "babel_helpers"
234
+ }
235
+ if (babelHelperNameFromUrl(fileUrl)) {
236
+ return "babel_helpers"
237
+ }
238
+ if (urlIsInsideOf(fileUrl, jsenvBabelPluginDirectoryUrl)) {
239
+ return "babel_helpers"
240
+ }
241
+ return null
242
+ },
219
243
  // https://rollupjs.org/guide/en/#outputpaths
220
244
  // paths: (id) => {
221
245
  // return id
@@ -247,19 +271,8 @@ const rollupPluginJsenv = ({
247
271
  code: urlInfo.content,
248
272
  map: urlInfo.sourcemap
249
273
  ? sourcemapConverter.toFilePaths(urlInfo.sourcemap)
250
- : urlInfo.sourcemap,
251
- }
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
274
+ : null,
261
275
  }
262
- return null
263
276
  },
264
277
  }
265
278
  }
@@ -0,0 +1,34 @@
1
+ export const jsenvPluginCacheControl = () => {
2
+ return {
3
+ name: "jsenv:cache_control",
4
+ appliesDuring: {
5
+ dev: true,
6
+ test: true,
7
+ },
8
+ augmentResponse: ({ reference }, context) => {
9
+ if (context.scenario === "dev") {
10
+ // During dev, all files are put into browser cache for 1 hour because:
11
+ // 1: Browser cache is a temporary directory created by playwright
12
+ // 2: We assume source files won't be modified while tests are running
13
+ return {
14
+ headers: {
15
+ "cache-control": `private,max-age=3600,immutable`,
16
+ },
17
+ }
18
+ }
19
+ if (
20
+ reference.searchParams.has("v") &&
21
+ !reference.searchParams.has("hmr")
22
+ ) {
23
+ return {
24
+ headers: {
25
+ "cache-control": `private,max-age=${SECONDS_IN_30_DAYS},immutable`,
26
+ },
27
+ }
28
+ }
29
+ return null
30
+ },
31
+ }
32
+ }
33
+
34
+ const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30
@@ -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
@@ -17,6 +17,7 @@ import { jsenvPluginBundling } from "./bundling/jsenv_plugin_bundling.js"
17
17
  import { jsenvPluginMinification } from "./minification/jsenv_plugin_minification.js"
18
18
  import { jsenvPluginImportMetaHot } from "./import_meta_hot/jsenv_plugin_import_meta_hot.js"
19
19
  import { jsenvPluginAutoreload } from "./autoreload/jsenv_plugin_autoreload.js"
20
+ import { jsenvPluginCacheControl } from "./cache_control/jsenv_plugin_cache_control.js"
20
21
 
21
22
  export const getCorePlugins = ({
22
23
  rootDirectoryUrl,
@@ -68,7 +69,6 @@ export const getCorePlugins = ({
68
69
  jsenvPluginInjectGlobals(injectedGlobals),
69
70
  jsenvPluginCommonJsGlobals(),
70
71
  jsenvPluginImportMetaScenarios(),
71
- // jsenvPluginWorkers(),
72
72
 
73
73
  jsenvPluginBundling(bundling),
74
74
  jsenvPluginMinification(minification),
@@ -87,5 +87,6 @@ export const getCorePlugins = ({
87
87
  }),
88
88
  ]
89
89
  : []),
90
+ jsenvPluginCacheControl(),
90
91
  ]
91
92
  }
@@ -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
@@ -1,7 +1,12 @@
1
1
  import { getBabelHelperFileUrl, requireBabelPlugin } from "@jsenv/babel-plugins"
2
2
  import { babelPluginCompatMap } from "./babel_plugins_compatibility.js"
3
3
 
4
- export const getBaseBabelPluginStructure = ({ url, isSupported }) => {
4
+ export const getBaseBabelPluginStructure = ({
5
+ url,
6
+ isSupported,
7
+ // isJsModule,
8
+ // getImportSpecifier,
9
+ }) => {
5
10
  const isBabelPluginNeeded = (babelPluginName) => {
6
11
  return !isSupported(babelPluginCompatMap[babelPluginName])
7
12
  }
@@ -35,6 +40,12 @@ export const getBaseBabelPluginStructure = ({ url, isSupported }) => {
35
40
  requireBabelPlugin("babel-plugin-transform-async-to-promises"),
36
41
  {
37
42
  topLevelAwait: "ignore", // will be handled by "jsenv:top_level_await" plugin
43
+ externalHelpers: false,
44
+ // enable once https://github.com/rpetrich/babel-plugin-transform-async-to-promises/pull/83
45
+ // externalHelpers: isJsModule,
46
+ // externalHelpersPath: isJsModule ? getImportSpecifier(
47
+ // "babel-plugin-transform-async-to-promises/helpers.mjs",
48
+ // ) : null
38
49
  },
39
50
  ]
40
51
  }
@@ -33,27 +33,29 @@ export const jsenvPluginBabel = ({ getCustomBabelPlugins } = {}) => {
33
33
  )
34
34
  }
35
35
 
36
- const { referenceUtils } = context
37
36
  const isSupported = (feature) =>
38
37
  RUNTIME_COMPAT.isSupported(clientRuntimeCompat, feature)
38
+ const getImportSpecifier = (clientFileUrl) => {
39
+ const [reference] = context.referenceUtils.inject({
40
+ type: "js_import_export",
41
+ expectedType: "js_module",
42
+ specifier: clientFileUrl,
43
+ })
44
+ return JSON.parse(reference.generatedSpecifier)
45
+ }
46
+
39
47
  const babelPluginStructure = getBaseBabelPluginStructure({
40
48
  url: urlInfo.url,
41
49
  isSupported,
42
50
  isWorkerContext,
51
+ isJsModule,
52
+ getImportSpecifier,
43
53
  })
44
54
  if (getCustomBabelPlugins) {
45
55
  Object.assign(babelPluginStructure, getCustomBabelPlugins(context))
46
56
  }
47
57
 
48
58
  if (isJsModule) {
49
- const getImportSpecifier = (clientFileUrl) => {
50
- const [reference] = referenceUtils.inject({
51
- type: "js_import_export",
52
- expectedType: "js_module",
53
- specifier: clientFileUrl,
54
- })
55
- return JSON.parse(reference.generatedSpecifier)
56
- }
57
59
  if (!isSupported("global_this")) {
58
60
  babelPluginStructure["global-this-as-jsenv-import"] = [
59
61
  babelPluginGlobalThisAsJsenvImport,
@@ -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
@@ -24,6 +24,16 @@ export const jsenvPluginTopLevelAwait = () => {
24
24
  // Maybe we could pass target: "es6" when we support arrow function
25
25
  // https://github.com/rpetrich/babel-plugin-transform-async-to-promises/blob/92755ff8c943c97596523e586b5fa515c2e99326/async-to-promises.ts#L55
26
26
  topLevelAwait: "simple",
27
+ // enable once https://github.com/rpetrich/babel-plugin-transform-async-to-promises/pull/83
28
+ // externalHelpers: true,
29
+ // externalHelpersPath: JSON.parse(
30
+ // context.referenceUtils.inject({
31
+ // type: "js_import_export",
32
+ // expectedType: "js_module",
33
+ // specifier:
34
+ // "babel-plugin-transform-async-to-promises/helpers.mjs",
35
+ // })[0],
36
+ // ),
27
37
  },
28
38
  ],
29
39
  ],
@@ -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"]
@@ -1,8 +1,8 @@
1
- export const jsenvPluginUrlVersion = ({ longTermCache = true } = {}) => {
1
+ export const jsenvPluginUrlVersion = () => {
2
2
  return {
3
3
  name: "jsenv:url_version",
4
- appliesDuring: "*", // maybe only during dev?
5
- normalizeUrl: (reference) => {
4
+ appliesDuring: "*",
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
@@ -24,24 +24,5 @@ export const jsenvPluginUrlVersion = ({ longTermCache = true } = {}) => {
24
24
  v: reference.data.version,
25
25
  }
26
26
  },
27
- augmentResponse: ({ reference }) => {
28
- if (!longTermCache) {
29
- return null
30
- }
31
- if (!reference.searchParams.has("v")) {
32
- return null
33
- }
34
- if (reference.searchParams.has("hmr")) {
35
- return null
36
- }
37
- // When url is versioned put it in browser cache for 30 days
38
- return {
39
- headers: {
40
- "cache-control": `private,max-age=${SECONDS_IN_30_DAYS},immutable`,
41
- },
42
- }
43
- },
44
27
  }
45
28
  }
46
-
47
- const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30