@jsenv/core 27.0.0-alpha.51 → 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.51",
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.1",
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"
@@ -29,6 +30,7 @@ import {
29
30
  parseHtmlString,
30
31
  stringifyHtmlAst,
31
32
  } from "@jsenv/utils/html_ast/html_ast.js"
33
+ import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js"
32
34
 
33
35
  import { jsenvPluginUrlAnalysis } from "../plugins/url_analysis/jsenv_plugin_url_analysis.js"
34
36
  import { jsenvPluginInline } from "../plugins/inline/jsenv_plugin_inline.js"
@@ -38,7 +40,6 @@ import { getCorePlugins } from "../plugins/plugins.js"
38
40
  import { createKitchen } from "../omega/kitchen.js"
39
41
  import { loadUrlGraph } from "../omega/url_graph/url_graph_load.js"
40
42
  import { createUrlGraphSummary } from "../omega/url_graph/url_graph_report.js"
41
- import { sortUrlGraphByDependencies } from "../omega/url_graph/url_graph_sort.js"
42
43
  import { isWebWorkerEntryPointReference } from "../omega/web_workers.js"
43
44
 
44
45
  import { GRAPH } from "./graph_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
  }
@@ -618,6 +626,7 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
618
626
  urlGraph: finalGraph,
619
627
  kitchen: finalGraphKitchen,
620
628
  outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
629
+ skipRessourceHint: true,
621
630
  startLoading: (cookEntryFile) => {
622
631
  entryUrls.forEach((entryUrl) => {
623
632
  const [, postBuildEntryUrlInfo] = cookEntryFile({
@@ -698,7 +707,7 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
698
707
  urlInfo.dependents.size === 0
699
708
  ) {
700
709
  cleanupActions.push(() => {
701
- delete finalGraph.urlInfos[urlInfo.url]
710
+ finalGraph.deleteUrlInfo(urlInfo.url)
702
711
  })
703
712
  }
704
713
  })
@@ -783,7 +792,7 @@ const applyUrlVersioning = async ({
783
792
  }) => {
784
793
  const versioningTask = createTaskLog(logger, "inject version in urls")
785
794
  try {
786
- const urlsSorted = sortUrlGraphByDependencies(finalGraph)
795
+ const urlsSorted = sortByDependencies(finalGraph.urlInfos)
787
796
  urlsSorted.forEach((url) => {
788
797
  if (url.startsWith("data:")) {
789
798
  return
@@ -859,11 +868,13 @@ const applyUrlVersioning = async ({
859
868
  })
860
869
  urlInfo.data.version = versionGenerator.generate()
861
870
 
862
- urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
863
- buildUrl: urlInfo.url,
864
- version: urlInfo.data.version,
865
- versioningMethod,
866
- })
871
+ urlInfo.data.versionedUrl = normalizeUrl(
872
+ injectVersionIntoBuildUrl({
873
+ buildUrl: urlInfo.url,
874
+ version: urlInfo.data.version,
875
+ versioningMethod,
876
+ }),
877
+ )
867
878
  })
868
879
  const versionMappings = {}
869
880
  const usedVersionMappings = []
@@ -969,6 +980,7 @@ const applyUrlVersioning = async ({
969
980
  await loadUrlGraph({
970
981
  urlGraph: finalGraph,
971
982
  kitchen: versioningKitchen,
983
+ skipRessourceHint: true,
972
984
  startLoading: (cookEntryFile) => {
973
985
  postBuildEntryUrls.forEach((postBuildEntryUrl) => {
974
986
  cookEntryFile({
@@ -56,8 +56,8 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
56
56
  // To keep in mind: if you have "user.jsx" and "user.js" AND both file are not bundled
57
57
  // you end up with "dist/js/user.js" and "dist/js/user2.js"
58
58
  const extensionMappings = {
59
- ".ts": ".js",
60
59
  ".jsx": ".js",
60
+ ".ts": ".js",
61
61
  ".tsx": ".js",
62
62
  }
63
63
 
@@ -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,29 +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
+ const normalizedReturnValue = normalizeUrl(returnValue)
166
+ if (normalizedReturnValue === reference.url) {
165
167
  return
166
168
  }
167
169
  const previousReference = { ...reference }
168
- reference.url = returnValue
170
+ reference.url = normalizedReturnValue
169
171
  mutateReference(previousReference, reference)
170
172
  },
171
173
  )
172
- // force a last normalization on url search params
173
- // some plugin use URLSearchParams to alter the url search params
174
- // which can result into "file:///file.css?css_module"
175
- // becoming "file:///file.css?css_module="
176
- // we want to get rid of the "=" and consider it's the same url
177
- if (
178
- // disable on data urls (would mess up base64 encoding)
179
- !reference.url.startsWith("data:")
180
- ) {
181
- reference.url = reference.url.replace(/[=](?=&|$)/g, "")
182
- }
174
+
183
175
  const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
184
176
  applyReferenceEffectsOnUrlInfo(reference, urlInfo, baseContext)
185
177
 
@@ -275,12 +267,13 @@ export const createKitchen = ({
275
267
  return
276
268
  }
277
269
  try {
278
- const returnValue = await pluginController.callAsyncHooksUntil(
279
- "fetchUrlContent",
280
- urlInfo,
281
- context,
282
- )
283
- if (!returnValue) {
270
+ const fetchUrlContentReturnValue =
271
+ await pluginController.callAsyncHooksUntil(
272
+ "fetchUrlContent",
273
+ urlInfo,
274
+ context,
275
+ )
276
+ if (!fetchUrlContentReturnValue) {
284
277
  logger.warn(
285
278
  createDetailedMessage(
286
279
  `no plugin has handled the url during "fetchUrlContent" hook -> consider url as external (ignore it)`,
@@ -293,7 +286,7 @@ export const createKitchen = ({
293
286
  urlInfo.external = true
294
287
  return
295
288
  }
296
- if (returnValue.external) {
289
+ if (fetchUrlContentReturnValue.external) {
297
290
  urlInfo.external = true
298
291
  return
299
292
  }
@@ -306,7 +299,7 @@ export const createKitchen = ({
306
299
  content,
307
300
  sourcemap,
308
301
  filename,
309
- } = returnValue
302
+ } = fetchUrlContentReturnValue
310
303
  urlInfo.type =
311
304
  type ||
312
305
  reference.expectedType ||
@@ -33,6 +33,13 @@ export const loadUrlGraph = async ({
33
33
  })
34
34
  const { references } = urlInfo
35
35
  references.forEach((reference) => {
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) {
41
+ return
42
+ }
36
43
  // we use reference.generatedUrl to mimic what a browser would do:
37
44
  // do a fetch to the specifier as found in the file
38
45
  const referencedUrlInfo = urlGraph.reuseOrCreateUrlInfo(
@@ -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,21 +1,140 @@
1
- import { bundleWithParcel } from "@jsenv/utils/css_ast/parcel_css.js"
1
+ /*
2
+ * Each @import found in css is replaced by the file content
3
+ * - There is no need to worry about urls (such as background-image: url())
4
+ * because they are absolute (file://*) and will be made relative again by jsenv build
5
+ * - The sourcemap are not generated but ideally they should be
6
+ * It can be quite challenging, see "bundle_sourcemap.js"
7
+ */
2
8
 
9
+ import { applyPostCss } from "@jsenv/utils/css_ast/apply_post_css.js"
10
+ import { postCssPluginUrlVisitor } from "@jsenv/utils/css_ast/postcss_plugin_url_visitor.js"
11
+ import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
12
+ import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js"
13
+
14
+ // Do not use until https://github.com/parcel-bundler/parcel-css/issues/181
3
15
  export const bundleCss = async ({ cssUrlInfos, context }) => {
4
16
  const bundledCssUrlInfos = {}
17
+ const cssBundleInfos = await performCssBundling({
18
+ cssEntryUrlInfos: cssUrlInfos,
19
+ context,
20
+ })
5
21
  cssUrlInfos.forEach((cssUrlInfo) => {
6
- const { code, map } = bundleWithParcel(cssUrlInfo, context)
7
- const content = String(code)
8
- const sourcemap = map
9
- // here we need to replace css urls to ensure
10
- // all urls targets the correct stuff
11
22
  bundledCssUrlInfos[cssUrlInfo.url] = {
12
23
  data: {
13
24
  generatedBy: "parcel",
14
25
  },
15
26
  contentType: "text/css",
16
- content,
17
- sourcemap,
27
+ content: cssBundleInfos[cssUrlInfo.url].bundleContent,
18
28
  }
19
29
  })
20
30
  return bundledCssUrlInfos
21
31
  }
32
+
33
+ const performCssBundling = async ({ cssEntryUrlInfos, context }) => {
34
+ const cssBundleInfos = await loadCssUrls({
35
+ cssEntryUrlInfos,
36
+ context,
37
+ })
38
+ const cssUrlsSorted = sortByDependencies(cssBundleInfos)
39
+ cssUrlsSorted.forEach((cssUrl) => {
40
+ const cssBundleInfo = cssBundleInfos[cssUrl]
41
+ const magicSource = createMagicSource(cssBundleInfo.content)
42
+ cssBundleInfo.cssUrls.forEach((cssUrl) => {
43
+ if (cssUrl.type === "@import") {
44
+ magicSource.replace({
45
+ start: cssUrl.atRuleStart,
46
+ end: cssUrl.atRuleEnd,
47
+ replacement: cssBundleInfos[cssUrl.url].bundleContent,
48
+ })
49
+ }
50
+ })
51
+ const { content } = magicSource.toContentAndSourcemap()
52
+ cssBundleInfo.bundleContent = content.trim()
53
+ })
54
+ return cssBundleInfos
55
+ }
56
+
57
+ const parseCssUrls = async ({ css, url }) => {
58
+ const cssUrls = []
59
+ await applyPostCss({
60
+ sourcemaps: false,
61
+ plugins: [
62
+ postCssPluginUrlVisitor({
63
+ urlVisitor: ({
64
+ type,
65
+ specifier,
66
+ specifierStart,
67
+ specifierEnd,
68
+ atRuleStart,
69
+ atRuleEnd,
70
+ }) => {
71
+ cssUrls.push({
72
+ type,
73
+ url: new URL(specifier, url).href,
74
+ specifierStart,
75
+ specifierEnd,
76
+ atRuleStart,
77
+ atRuleEnd,
78
+ })
79
+ },
80
+ }),
81
+ ],
82
+ url,
83
+ content: css,
84
+ })
85
+
86
+ return cssUrls
87
+ }
88
+
89
+ const loadCssUrls = async ({ cssEntryUrlInfos, context }) => {
90
+ const cssBundleInfos = {}
91
+ const promises = []
92
+ const promiseMap = new Map()
93
+
94
+ const load = (cssUrlInfo) => {
95
+ const promiseFromData = promiseMap.get(cssUrlInfo.url)
96
+ if (promiseFromData) return promiseFromData
97
+ const promise = _load(cssUrlInfo)
98
+ promises.push(promise)
99
+ promiseMap.set(cssUrlInfo.url, promise)
100
+ return promise
101
+ }
102
+
103
+ const _load = async (cssUrlInfo) => {
104
+ const cssUrls = await parseCssUrls({
105
+ css: cssUrlInfo.content,
106
+ url: cssUrlInfo.url,
107
+ })
108
+ const cssBundleInfo = {
109
+ content: cssUrlInfo.content,
110
+ cssUrls,
111
+ dependencies: [],
112
+ }
113
+ cssBundleInfos[cssUrlInfo.url] = cssBundleInfo
114
+ cssUrls.forEach((cssUrl) => {
115
+ if (cssUrl.type === "@import") {
116
+ cssBundleInfo.dependencies.push(cssUrl.url)
117
+ const importedCssUrlInfo = context.urlGraph.getUrlInfo(cssUrl.url)
118
+ load(importedCssUrlInfo)
119
+ }
120
+ })
121
+ }
122
+
123
+ cssEntryUrlInfos.forEach((cssEntryUrlInfo) => {
124
+ load(cssEntryUrlInfo)
125
+ })
126
+
127
+ const waitAll = async () => {
128
+ if (promises.length === 0) {
129
+ return
130
+ }
131
+ const promisesToWait = promises.slice()
132
+ promises.length = 0
133
+ await Promise.all(promisesToWait)
134
+ await waitAll()
135
+ }
136
+ await waitAll()
137
+ promiseMap.clear()
138
+
139
+ return cssBundleInfos
140
+ }
@@ -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
@@ -274,8 +274,9 @@ const getOriginalName = (path, name) => {
274
274
  return getOriginalName(path, importedName)
275
275
  }
276
276
  if (binding.path.type === "VariableDeclarator") {
277
- if (binding.path.node.init.type === "Identifier") {
278
- const previousName = binding.path.node.init.name
277
+ const { init } = binding.path.node
278
+ if (init && init.type === "Identifier") {
279
+ const previousName = init.name
279
280
  return getOriginalName(path, previousName)
280
281
  }
281
282
  }
@@ -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
- ) {
61
- 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
- ) {
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
+ }
68
81
  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({
@@ -119,7 +127,15 @@ const asJsClassic = ({ systemJsInjection, systemJsClientFileUrl }) => {
119
127
 
120
128
  const generateJsClassicFilename = (url) => {
121
129
  const filename = urlToFilename(url)
122
- const [basename, extension] = splitFileExtension(filename)
130
+ let [basename, extension] = splitFileExtension(filename)
131
+ const { searchParams } = new URL(url)
132
+ if (
133
+ searchParams.has("as_json_module") ||
134
+ searchParams.has("as_css_module") ||
135
+ searchParams.has("as_text_module")
136
+ ) {
137
+ extension = ".js"
138
+ }
123
139
  return `${basename}.es5${extension}`
124
140
  }
125
141
 
@@ -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
@@ -27,16 +27,16 @@ export const jsenvPluginImportAssertions = () => {
27
27
  end: reference.assertNode.end,
28
28
  })
29
29
  }
30
-
31
- return injectQueryParams(reference.url, {
30
+ const newUrl = injectQueryParams(reference.url, {
32
31
  [searchParam]: "",
33
32
  })
33
+ return newUrl
34
34
  }
35
35
 
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
@@ -13,19 +13,26 @@ export const parseAndTransformCssUrls = async (urlInfo, context) => {
13
13
  sourcemaps: false,
14
14
  plugins: [
15
15
  postCssPluginUrlVisitor({
16
- urlVisitor: ({ type, specifier, line, column, start, end }) => {
16
+ urlVisitor: ({
17
+ type,
18
+ specifier,
19
+ specifierStart,
20
+ specifierEnd,
21
+ specifierLine,
22
+ specifierColumn,
23
+ }) => {
17
24
  const [reference] = context.referenceUtils.found({
18
25
  type: `css_${type}`,
19
26
  specifier,
20
- specifierStart: start,
21
- specifierEnd: end,
22
- specifierLine: line,
23
- specifierColumn: column,
27
+ specifierStart,
28
+ specifierEnd,
29
+ specifierLine,
30
+ specifierColumn,
24
31
  })
25
32
  actions.push(async () => {
26
33
  magicSource.replace({
27
- start,
28
- end,
34
+ start: specifierStart,
35
+ end: specifierEnd,
29
36
  replacement: await context.referenceUtils.readGeneratedSpecifier(
30
37
  reference,
31
38
  ),
@@ -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
@@ -1,29 +0,0 @@
1
- export const sortUrlGraphByDependencies = (urlGraph) => {
2
- const { urlInfos } = urlGraph
3
-
4
- const visited = []
5
- const sorted = []
6
- const circular = []
7
- const visit = (url) => {
8
- const isSorted = sorted.includes(url)
9
- if (isSorted) {
10
- return
11
- }
12
- const isVisited = visited.includes(url)
13
- if (isVisited) {
14
- circular.push(url)
15
- sorted.push(url)
16
- } else {
17
- visited.push(url)
18
- urlInfos[url].dependencies.forEach((dependencyUrl) => {
19
- visit(dependencyUrl, url)
20
- })
21
- sorted.push(url)
22
- }
23
- }
24
- Object.keys(urlInfos).forEach((url) => {
25
- visit(url)
26
- })
27
- sorted.circular = circular
28
- return sorted
29
- }