@jsenv/core 27.0.0-alpha.5 → 27.0.0-alpha.50

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 (146) hide show
  1. package/dist/event_source_client.js +545 -0
  2. package/dist/event_source_client.js.map +187 -0
  3. package/dist/html_supervisor_installer.js +1236 -0
  4. package/dist/html_supervisor_installer.js.map +337 -0
  5. package/dist/html_supervisor_setup.js +95 -0
  6. package/dist/html_supervisor_setup.js.map +57 -0
  7. package/dist/import_meta_hot.js +86 -0
  8. package/dist/import_meta_hot.js.map +42 -0
  9. package/main.js +8 -1
  10. package/package.json +22 -21
  11. package/readme.md +4 -12
  12. package/src/build/build.js +749 -437
  13. package/src/build/build_urls_generator.js +56 -22
  14. package/src/build/graph_utils.js +31 -0
  15. package/src/build/{inject_version_mappings.js → inject_global_version_mappings.js} +33 -15
  16. package/src/build/inject_service_worker_urls.js +79 -0
  17. package/src/build/resync_ressource_hints.js +68 -0
  18. package/src/build/start_build_server.js +255 -0
  19. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +2 -2
  20. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -1
  21. package/src/dev/start_dev_server.js +136 -30
  22. package/src/execute/execute.js +31 -6
  23. package/src/execute/run.js +19 -56
  24. package/src/execute/runtimes/browsers/from_playwright.js +201 -147
  25. package/src/execute/runtimes/node/controllable_file.mjs +26 -10
  26. package/src/execute/runtimes/node/node_execution_performance.js +67 -0
  27. package/src/execute/runtimes/node/node_process.js +280 -39
  28. package/src/jsenv_root_directory_url.js +1 -0
  29. package/src/omega/{runtime_support/default_runtime_support.js → compat/default_runtime_compat.js} +3 -5
  30. package/src/omega/{runtime_support/features_compatibility.js → compat/features_compats.js} +66 -4
  31. package/src/omega/compat/runtime_compat.js +50 -0
  32. package/src/omega/errors.js +51 -58
  33. package/src/omega/fetched_content_compliance.js +24 -0
  34. package/src/omega/file_url_converter.js +8 -50
  35. package/src/omega/kitchen.js +482 -292
  36. package/src/omega/omega_server.js +2 -3
  37. package/src/omega/server/file_service.js +38 -25
  38. package/src/omega/server/user_agent.js +4 -2
  39. package/src/omega/url_graph/url_graph_load.js +14 -7
  40. package/src/omega/url_graph/url_graph_report.js +17 -15
  41. package/src/omega/url_graph/url_info_transformations.js +26 -9
  42. package/src/omega/url_graph.js +91 -16
  43. package/src/omega/web_workers.js +42 -0
  44. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/autoreload_preference.js +0 -0
  45. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/event_source_client.js +2 -2
  46. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/reload.js +0 -0
  47. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/url_helpers.js +0 -0
  48. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +46 -0
  49. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +204 -0
  50. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +27 -0
  51. package/src/plugins/autoreload/jsenv_plugin_hmr.js +35 -0
  52. package/src/plugins/bundling/css/bundle_css.js +21 -0
  53. package/src/plugins/bundling/js_classic_workers/bundle_js_classic_workers.js +13 -0
  54. package/src/{build/plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js → plugins/bundling/js_module/bundle_js_module.js} +150 -79
  55. package/src/plugins/bundling/jsenv_plugin_bundling.js +54 -0
  56. package/src/{omega/core_plugins → plugins}/commonjs_globals/jsenv_plugin_commonjs_globals.js +54 -41
  57. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +66 -0
  58. package/src/{omega/core_plugins → plugins}/filesystem_magic/jsenv_plugin_filesystem_magic.js +8 -5
  59. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_document.js +0 -0
  60. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_notification.js +0 -0
  61. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +242 -0
  62. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +79 -0
  63. package/src/{omega/core_plugins → plugins}/html_supervisor/client/perf_browser.js +0 -0
  64. package/src/{omega/core_plugins → plugins}/html_supervisor/client/uneval_exception.js +0 -0
  65. package/src/{omega/core_plugins → plugins}/html_supervisor/jsenv_plugin_html_supervisor.js +83 -58
  66. package/src/plugins/http_urls/jsenv_plugin_http_urls.js +12 -0
  67. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/babel_plugin_metadata_import_meta_hot.js +4 -5
  68. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/client/import_meta_hot.js +3 -1
  69. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/html_hot_dependencies.js +2 -2
  70. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +105 -0
  71. package/src/{omega/core_plugins → plugins}/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +33 -8
  72. package/src/plugins/import_meta_url/client/import_meta_url_browser.js +52 -0
  73. package/src/plugins/import_meta_url/client/import_meta_url_commonjs.mjs +9 -0
  74. package/src/{omega/core_plugins → plugins}/importmap/jsenv_plugin_importmap.js +39 -33
  75. package/src/plugins/inject_globals/jsenv_plugin_inject_globals.js +67 -0
  76. package/src/{omega/core_plugins → plugins}/inline/client/inline_content.js +0 -0
  77. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_data_urls.js +18 -14
  78. package/src/{omega/core_plugins/inline/jsenv_plugin_js_and_css_inside_html.js → plugins/inline/jsenv_plugin_html_inline_content.js} +65 -44
  79. package/src/plugins/inline/jsenv_plugin_inline.js +36 -0
  80. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_inline_query_param.js +6 -6
  81. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +296 -0
  82. package/src/plugins/leading_slash/jsenv_plugin_leading_slash.js +13 -0
  83. package/src/plugins/minification/css/minify_css.js +9 -0
  84. package/src/plugins/minification/html/minify_html.js +15 -0
  85. package/src/{build/plugins/minify_js/jsenv_plugin_minify_js.js → plugins/minification/js/minify_js.js} +6 -22
  86. package/src/plugins/minification/jsenv_plugin_minification.js +78 -0
  87. package/src/plugins/minification/json/minify_json.js +8 -0
  88. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +146 -0
  89. package/src/{omega → plugins}/plugin_controller.js +42 -11
  90. package/src/plugins/plugins.js +91 -0
  91. package/src/plugins/transpilation/as_js_classic/client/s.js +808 -0
  92. package/src/plugins/transpilation/as_js_classic/client/s.js.md +1 -0
  93. package/src/plugins/transpilation/as_js_classic/helpers/babel_plugin_transform_import_meta_url.js +47 -0
  94. package/src/plugins/transpilation/as_js_classic/helpers/systemjs_old.js +43 -0
  95. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +183 -0
  96. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_script_type_module_as_classic.js +256 -0
  97. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_workers_type_module_as_classic.js +55 -0
  98. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +0 -0
  99. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/client/global_this.js +0 -0
  100. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +0 -0
  101. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_structure.js +4 -22
  102. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugins_compatibility.js +0 -0
  103. package/src/{omega/core_plugins → plugins/transpilation}/babel/jsenv_plugin_babel.js +37 -21
  104. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +30 -6
  105. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/.eslintrc.cjs +0 -0
  106. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/new_stylesheet.js +0 -0
  107. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +0 -0
  108. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/client/regenerator_runtime.js +0 -0
  109. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +18 -0
  110. package/src/plugins/transpilation/fetch_original_url_info.js +30 -0
  111. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +181 -0
  112. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +70 -0
  113. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +44 -0
  114. package/src/plugins/url_analysis/css/css_urls.js +42 -0
  115. package/src/plugins/url_analysis/html/html_urls.js +273 -0
  116. package/src/plugins/url_analysis/js/js_urls.js +67 -0
  117. package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +18 -0
  118. package/src/plugins/url_analysis/webmanifest/webmanifest_urls.js +17 -0
  119. package/src/{omega/core_plugins → plugins}/url_resolution/jsenv_plugin_url_resolution.js +12 -5
  120. package/src/{omega/core_plugins → plugins}/url_version/jsenv_plugin_url_version.js +12 -15
  121. package/src/test/execute_plan.js +30 -18
  122. package/src/test/execute_test_plan.js +23 -8
  123. package/src/test/logs_file_execution.js +8 -7
  124. package/src/build/plugins/minify_html/jsenv_plugin_minify_html.js +0 -30
  125. package/src/dev/plugins/autoreload/client/event_source_connection.js +0 -195
  126. package/src/dev/plugins/autoreload/jsenv_plugin_autoreload.js +0 -374
  127. package/src/dev/plugins/autoreload/sse_service.js +0 -149
  128. package/src/execute/runtimes/node/controlled_process.js +0 -316
  129. package/src/omega/core_plugins/file_urls/jsenv_plugin_file_urls.js +0 -67
  130. package/src/omega/core_plugins/html_supervisor/client/html_supervisor_installer.js +0 -168
  131. package/src/omega/core_plugins/html_supervisor/client/html_supervisor_setup.js +0 -77
  132. package/src/omega/core_plugins/import_assertions/helpers/babel_plugin_metadata_import_assertions.js +0 -98
  133. package/src/omega/core_plugins/import_assertions/helpers/json_module.js +0 -12
  134. package/src/omega/core_plugins/import_assertions/helpers/text_module.js +0 -6
  135. package/src/omega/core_plugins/import_assertions/jsenv_plugin_import_assertions.js +0 -211
  136. package/src/omega/core_plugins/inline/jsenv_plugin_inline.js +0 -13
  137. package/src/omega/core_plugins/inline/jsenv_plugin_new_inline_content.js +0 -210
  138. package/src/omega/core_plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -12
  139. package/src/omega/core_plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +0 -77
  140. package/src/omega/core_plugins.js +0 -39
  141. package/src/omega/runtime_support/runtime_support.js +0 -20
  142. package/src/omega/url_mentions/css_url_mentions.js +0 -63
  143. package/src/omega/url_mentions/html_url_mentions.js +0 -185
  144. package/src/omega/url_mentions/js_module_url_mentions.js +0 -91
  145. package/src/omega/url_mentions/parse_url_mentions.js +0 -37
  146. package/src/omega/url_mentions/worker_classic_url_mentions.js +0 -37
@@ -21,70 +21,101 @@ import { createTaskLog } from "@jsenv/utils/logs/task_log.js"
21
21
  import {
22
22
  injectQueryParams,
23
23
  setUrlFilename,
24
+ asUrlUntilPathname,
24
25
  } from "@jsenv/utils/urls/url_utils.js"
25
- import { createUrlVersionGenerator } from "@jsenv/utils/urls/url_version_generator.js"
26
+ import { createVersionGenerator } from "@jsenv/utils/versioning/version_generator.js"
26
27
  import { generateSourcemapUrl } from "@jsenv/utils/sourcemap/sourcemap_utils.js"
27
28
  import {
28
29
  parseHtmlString,
29
30
  stringifyHtmlAst,
30
31
  } from "@jsenv/utils/html_ast/html_ast.js"
31
32
 
32
- import { defaultRuntimeSupport } from "../omega/runtime_support/default_runtime_support.js"
33
- import { jsenvPluginInline } from "../omega/core_plugins/inline/jsenv_plugin_inline.js"
33
+ import { jsenvPluginUrlAnalysis } from "../plugins/url_analysis/jsenv_plugin_url_analysis.js"
34
+ import { jsenvPluginInline } from "../plugins/inline/jsenv_plugin_inline.js"
35
+ import { jsenvPluginAsJsClassic } from "../plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js"
34
36
  import { createUrlGraph } from "../omega/url_graph.js"
35
- import { getCorePlugins } from "../omega/core_plugins.js"
37
+ import { getCorePlugins } from "../plugins/plugins.js"
36
38
  import { createKitchen } from "../omega/kitchen.js"
37
39
  import { loadUrlGraph } from "../omega/url_graph/url_graph_load.js"
38
40
  import { createUrlGraphSummary } from "../omega/url_graph/url_graph_report.js"
39
41
  import { sortUrlGraphByDependencies } from "../omega/url_graph/url_graph_sort.js"
42
+ import { isWebWorkerEntryPointReference } from "../omega/web_workers.js"
40
43
 
44
+ import { GRAPH } from "./graph_utils.js"
41
45
  import { createBuilUrlsGenerator } from "./build_urls_generator.js"
42
- import { injectVersionMappings } from "./inject_version_mappings.js"
43
- import { jsenvPluginBundleJsModule } from "./plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js"
44
- import { jsenvPluginMinifyJs } from "./plugins/minify_js/jsenv_plugin_minify_js.js"
45
- import { jsenvPluginMinifyHtml } from "./plugins/minify_html/jsenv_plugin_minify_html.js"
46
+ import { injectGlobalVersionMapping } from "./inject_global_version_mappings.js"
47
+ import { injectServiceWorkerUrls } from "./inject_service_worker_urls.js"
48
+ import { resyncRessourceHints } from "./resync_ressource_hints.js"
46
49
 
50
+ /**
51
+ * Generate an optimized version of source files into a directory
52
+ * @param {Object} buildParameters
53
+ * @param {string|url} buildParameters.rootDirectoryUrl
54
+ * Directory containing source files
55
+ * @param {string|url} buildParameters.buildDirectoryUrl
56
+ * Directory where optimized files will be written
57
+ * @param {object} buildParameters.entryPoints
58
+ * Describe entry point paths and control their names in the build directory
59
+ * @param {object} buildParameters.runtimeCompat
60
+ * Code generated will be compatible with these runtimes
61
+ * @param {string="/"} buildParameters.baseUrl
62
+ * All urls in build file contents are prefixed with this url
63
+ * @param {boolean|object} [buildParameters.minification=true]
64
+ * Minify build file contents
65
+ * @param {boolean} [buildParameters.versioning=true]
66
+ * Controls if url in build file contents are versioned
67
+ * @param {('search_param'|'filename')} [buildParameters.versioningMethod="search_param"]
68
+ * Controls how url are versioned
69
+ * @param {boolean|string} [buildParameters.sourcemaps=false]
70
+ * Generate sourcemaps in the build directory
71
+ * @return {Object} buildReturnValue
72
+ * @return {Object} buildReturnValue.buildFileContents
73
+ * Contains all build file paths relative to the build directory and their content
74
+ * @return {Object} buildReturnValue.buildInlineContents
75
+ * Contains content that is inline into build files
76
+ * @return {Object} buildReturnValue.buildManifest
77
+ * Map build file paths without versioning to versioned file paths
78
+ */
47
79
  export const build = async ({
48
80
  signal = new AbortController().signal,
49
81
  logLevel = "info",
50
82
  rootDirectoryUrl,
51
83
  buildDirectoryUrl,
52
84
  entryPoints = {},
53
- // for now it's here but I think preview will become an other script
54
- // that will just pass different options to build project
55
- // and this function will be agnostic about "preview" concept
56
- isPreview = false,
85
+
57
86
  plugins = [],
58
- htmlSupervisor,
87
+ sourcemaps = false,
59
88
  nodeEsmResolution,
60
89
  fileSystemMagicResolution,
61
- babel,
62
- runtimeSupport = defaultRuntimeSupport,
63
- sourcemaps = isPreview ? "file" : false,
64
-
90
+ injectedGlobals,
91
+ runtimeCompat,
92
+ transpilation = {},
65
93
  bundling = true,
66
- minify = true,
67
- versioning = "filename", // "filename", "search_param", "none"
94
+ minification = true,
95
+ versioning = true,
96
+ versioningMethod = "search_param", // "filename", "search_param"
68
97
  lineBreakNormalization = process.platform === "win32",
69
98
 
70
99
  writeOnFileSystem = true,
71
100
  buildDirectoryClean = true,
72
101
  baseUrl = "/",
102
+ assetManifest = true,
103
+ assetManifestFileRelativeUrl = "asset-manifest.json",
73
104
  }) => {
74
105
  const logger = createLogger({ logLevel })
75
106
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
76
107
  buildDirectoryUrl = assertAndNormalizeDirectoryUrl(buildDirectoryUrl)
77
108
  assertEntryPoints({ entryPoints })
78
- if (!["filename", "search_param", "none"].includes(versioning)) {
109
+ if (!["filename", "search_param"].includes(versioningMethod)) {
79
110
  throw new Error(
80
- `Unexpected "versioning": must be "filename", "search_param" or "none"; got ${versioning}`,
111
+ `Unexpected "versioningMethod": must be "filename", "search_param"; got ${versioning}`,
81
112
  )
82
113
  }
83
114
 
84
115
  const entryPointKeys = Object.keys(entryPoints)
85
116
  if (entryPointKeys.length === 1) {
86
117
  logger.info(`
87
- build ${entryPointKeys[0]}`)
118
+ build "${entryPointKeys[0]}"`)
88
119
  } else {
89
120
  logger.info(`
90
121
  build ${entryPointKeys.length} entry points`)
@@ -97,6 +128,9 @@ build ${entryPointKeys.length} entry points`)
97
128
  logger,
98
129
  rootDirectoryUrl,
99
130
  urlGraph: rawGraph,
131
+ scenario: "build",
132
+ sourcemaps,
133
+ runtimeCompat,
100
134
  plugins: [
101
135
  ...plugins,
102
136
  {
@@ -108,33 +142,39 @@ build ${entryPointKeys.length} entry points`)
108
142
  },
109
143
  },
110
144
  ...getCorePlugins({
111
- htmlSupervisor,
145
+ rootDirectoryUrl,
146
+ urlGraph: rawGraph,
147
+ scenario: "build",
148
+
112
149
  nodeEsmResolution,
113
150
  fileSystemMagicResolution,
114
- babel,
151
+ injectedGlobals,
152
+ transpilation: {
153
+ ...transpilation,
154
+ jsModuleAsJsClassic: false,
155
+ },
156
+ minification,
157
+ bundling,
115
158
  }),
116
- jsenvPluginBundleJsModule(),
117
- ...(minify ? [jsenvPluginMinifyJs(), jsenvPluginMinifyHtml()] : []),
118
159
  ],
119
- scenario: "build",
120
- sourcemaps,
121
160
  })
122
- const loadEntryFiles = (cookEntryFile) => {
123
- Object.keys(entryPoints).forEach((key) => {
124
- cookEntryFile({
125
- trace: `"${key}" in entryPoints parameter`,
126
- type: "entry_point",
127
- specifier: key,
128
- })
129
- })
130
- }
161
+ const entryUrls = []
131
162
  try {
132
163
  await loadUrlGraph({
133
164
  urlGraph: rawGraph,
134
165
  kitchen: rawGraphKitchen,
135
166
  outDirectoryUrl: new URL(`.jsenv/build/`, rootDirectoryUrl),
136
- runtimeSupport,
137
- startLoading: loadEntryFiles,
167
+ startLoading: (cookEntryFile) => {
168
+ Object.keys(entryPoints).forEach((key) => {
169
+ const [, entryUrlInfo] = cookEntryFile({
170
+ trace: `"${key}" in entryPoints parameter`,
171
+ type: "entry_point",
172
+ specifier: key,
173
+ })
174
+ entryUrls.push(entryUrlInfo.url)
175
+ entryUrlInfo.filename = entryPoints[key]
176
+ })
177
+ },
138
178
  })
139
179
  } catch (e) {
140
180
  prebuildTask.fail()
@@ -147,44 +187,53 @@ build ${entryPointKeys.length} entry points`)
147
187
  ${Object.keys(rawGraph.urlInfos).join("\n")}`,
148
188
  )
149
189
 
190
+ const buildUrlsGenerator = createBuilUrlsGenerator({
191
+ buildDirectoryUrl,
192
+ })
193
+ const rawUrls = {}
194
+ const buildUrls = {}
195
+ const rawUrlRedirections = {}
150
196
  const bundleUrlInfos = {}
151
- if (bundling) {
152
- const bundlers = {}
153
- rawGraphKitchen.pluginController.plugins.forEach((plugin) => {
154
- const bundle = plugin.bundle
155
- if (!bundle) {
197
+ const bundlers = {}
198
+ rawGraphKitchen.pluginController.plugins.forEach((plugin) => {
199
+ const bundle = plugin.bundle
200
+ if (!bundle) {
201
+ return
202
+ }
203
+ if (typeof bundle !== "object") {
204
+ throw new Error(
205
+ `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
206
+ )
207
+ }
208
+ Object.keys(bundle).forEach((type) => {
209
+ const bundleFunction = bundle[type]
210
+ if (!bundleFunction) {
156
211
  return
157
212
  }
158
- if (typeof bundle !== "object") {
159
- throw new Error(
160
- `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
161
- )
162
- }
163
- Object.keys(bundle).forEach((type) => {
164
- const bundlerForThatType = bundlers[type]
165
- if (bundlerForThatType) {
166
- // first plugin to define a bundle hook wins
167
- return
168
- }
169
- bundlers[type] = {
170
- plugin,
171
- bundleFunction: bundle[type],
172
- urlInfos: [],
173
- }
174
- })
175
- })
176
- const addToBundlerIfAny = (rawUrlInfo) => {
177
- const bundler = bundlers[rawUrlInfo.type]
178
- if (bundler) {
179
- bundler.urlInfos.push(rawUrlInfo)
213
+ const bundlerForThatType = bundlers[type]
214
+ if (bundlerForThatType) {
215
+ // first plugin to define a bundle hook wins
180
216
  return
181
217
  }
182
- }
183
- Object.keys(rawGraph.urlInfos).forEach((rawUrl) => {
184
- const rawUrlInfo = rawGraph.getUrlInfo(rawUrl)
185
- if (!rawUrlInfo.data.isEntryPoint) {
186
- return
218
+ bundlers[type] = {
219
+ plugin,
220
+ bundleFunction: bundle[type],
221
+ urlInfos: [],
187
222
  }
223
+ })
224
+ })
225
+ const addToBundlerIfAny = (rawUrlInfo) => {
226
+ // if (rawUrlInfo.dependencies.size === 0) {
227
+ // return
228
+ // }
229
+ const bundler = bundlers[rawUrlInfo.type]
230
+ if (bundler) {
231
+ bundler.urlInfos.push(rawUrlInfo)
232
+ return
233
+ }
234
+ }
235
+ GRAPH.forEach(rawGraph, (rawUrlInfo) => {
236
+ if (rawUrlInfo.data.isEntryPoint) {
188
237
  addToBundlerIfAny(rawUrlInfo)
189
238
  if (rawUrlInfo.type === "html") {
190
239
  rawUrlInfo.dependencies.forEach((dependencyUrl) => {
@@ -194,7 +243,8 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
194
243
  // bundle inline script type module deps
195
244
  dependencyUrlInfo.references.forEach((inlineScriptRef) => {
196
245
  if (inlineScriptRef.type === "js_import_export") {
197
- addToBundlerIfAny(rawGraph.getUrlInfo(inlineScriptRef.url))
246
+ const inlineUrlInfo = rawGraph.getUrlInfo(inlineScriptRef.url)
247
+ addToBundlerIfAny(inlineUrlInfo)
198
248
  }
199
249
  })
200
250
  }
@@ -205,231 +255,348 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
205
255
  })
206
256
  return
207
257
  }
208
- })
209
- await Object.keys(bundlers).reduce(async (previous, type) => {
210
- await previous
211
- const bundler = bundlers[type]
212
- const urlInfosToBundle = bundler.urlInfos
213
- if (urlInfosToBundle.length === 0) {
214
- return
215
- }
216
- const bundleTask = createTaskLog(logger, `bundle "${type}"`)
217
- try {
218
- const bundlerGeneratedUrlInfos =
219
- await rawGraphKitchen.pluginController.callAsyncHook(
220
- {
221
- plugin: bundler.plugin,
222
- hookName: "bundle",
223
- value: bundler.bundleFunction,
224
- },
225
- urlInfosToBundle,
226
- {
227
- signal,
228
- logger,
229
- rootDirectoryUrl,
230
- buildDirectoryUrl,
231
- urlGraph: rawGraph,
232
- runtimeSupport,
233
- sourcemaps,
234
- },
235
- )
236
- Object.keys(bundlerGeneratedUrlInfos).forEach((url) => {
237
- const bundleUrlInfo = bundlerGeneratedUrlInfos[url]
238
- const rawUrlInfo = rawGraph.getUrlInfo(url)
239
- bundleUrlInfos[url] = {
240
- type,
241
- ...bundleUrlInfo,
242
- data: {
243
- ...(rawUrlInfo ? rawUrlInfo.data : {}),
244
- ...bundleUrlInfo.data,
245
- fromBundle: true,
246
- },
247
- }
258
+ }
259
+ // File referenced with new URL('./file.js', import.meta.url)
260
+ // are entry points that can be bundled
261
+ // For instance we will bundle service worker/workers detected like this
262
+ if (rawUrlInfo.type === "js_module") {
263
+ rawUrlInfo.references.forEach((reference) => {
264
+ if (reference.type === "js_url_specifier") {
265
+ const urlInfo = rawGraph.getUrlInfo(reference.url)
266
+ addToBundlerIfAny(urlInfo)
267
+ }
268
+ })
269
+ }
270
+ })
271
+ const bundleUrlRedirections = {}
272
+ await Object.keys(bundlers).reduce(async (previous, type) => {
273
+ await previous
274
+ const bundler = bundlers[type]
275
+ const urlInfosToBundle = bundler.urlInfos
276
+ if (urlInfosToBundle.length === 0) {
277
+ return
278
+ }
279
+ const bundleTask = createTaskLog(logger, `bundle "${type}"`)
280
+ try {
281
+ const bundlerGeneratedUrlInfos =
282
+ await rawGraphKitchen.pluginController.callAsyncHook(
283
+ {
284
+ plugin: bundler.plugin,
285
+ hookName: "bundle",
286
+ value: bundler.bundleFunction,
287
+ },
288
+ urlInfosToBundle,
289
+ {
290
+ ...rawGraphKitchen.baseContext,
291
+ buildDirectoryUrl,
292
+ },
293
+ )
294
+ Object.keys(bundlerGeneratedUrlInfos).forEach((url) => {
295
+ const rawUrlInfo = rawGraph.getUrlInfo(url)
296
+ const bundlerGeneratedUrlInfo = bundlerGeneratedUrlInfos[url]
297
+ const bundleUrlInfo = {
298
+ type,
299
+ subtype: rawUrlInfo ? rawUrlInfo.subtype : undefined,
300
+ filename: rawUrlInfo ? rawUrlInfo.filename : undefined,
301
+ ...bundlerGeneratedUrlInfo,
302
+ data: {
303
+ ...(rawUrlInfo ? rawUrlInfo.data : {}),
304
+ ...bundlerGeneratedUrlInfo.data,
305
+ fromBundle: true,
306
+ },
307
+ }
308
+ const buildUrl = buildUrlsGenerator.generate(url, {
309
+ urlInfo: bundleUrlInfo,
248
310
  })
249
- } catch (e) {
250
- bundleTask.fail()
251
- throw e
252
- }
253
- bundleTask.done()
254
- }, Promise.resolve())
255
- }
311
+ rawUrlRedirections[url] = buildUrl
312
+ rawUrls[buildUrl] = url
313
+ bundleUrlInfos[buildUrl] = bundleUrlInfo
314
+ if (bundlerGeneratedUrlInfo.data.bundleRelativeUrl) {
315
+ const urlForBundler = new URL(
316
+ bundlerGeneratedUrlInfo.data.bundleRelativeUrl,
317
+ buildDirectoryUrl,
318
+ ).href
319
+ bundleUrlRedirections[urlForBundler] = buildUrl
320
+ }
321
+ })
322
+ } catch (e) {
323
+ bundleTask.fail()
324
+ throw e
325
+ }
326
+ bundleTask.done()
327
+ }, Promise.resolve())
256
328
 
257
- const buildUrlsGenerator = createBuilUrlsGenerator({
258
- buildDirectoryUrl,
259
- })
260
- const rawUrls = {}
261
- const buildUrls = {}
329
+ const buildUrlRedirections = {}
262
330
  const finalGraph = createUrlGraph()
263
- const optimizeHooks = rawGraphKitchen.pluginController.addHook("optimize")
331
+ const optimizeUrlContentHooks =
332
+ rawGraphKitchen.pluginController.addHook("optimizeUrlContent")
264
333
  const finalGraphKitchen = createKitchen({
265
334
  logger,
266
335
  rootDirectoryUrl,
267
336
  urlGraph: finalGraph,
268
- // Inline content, such as <script> inside html, is transformed during the previous phase.
269
- // If we read the inline content it would be considered as the original content.
270
- // - It could be "fixed" by taking into account sourcemap and consider sourcemap sources
271
- // as the original content.
272
- // - But it would not work when sourcemap are not generated
273
- // - would be a bit slower
274
- // - So instead of reading the inline content directly, we search into raw graph
275
- // to get "originalContent" and "sourcemap"
276
- loadInlineUrlInfos: (finalUrlInfo) => {
277
- const rawUrl = finalUrlInfo.data.rawUrl
278
- const bundleUrlInfo = bundleUrlInfos[rawUrl]
279
- const urlInfo = bundleUrlInfo || finalUrlInfo
280
- const rawUrlInfo = rawGraph.getUrlInfo(rawUrl)
281
- return {
282
- originalContent: rawUrlInfo ? rawUrlInfo.originalContent : undefined,
283
- sourcemap: bundleUrlInfo
284
- ? bundleUrlInfo.sourcemap
285
- : rawUrlInfo
286
- ? rawUrlInfo.sourcemap
287
- : undefined,
288
- contentType: urlInfo.contentType,
289
- content: urlInfo.content,
290
- }
291
- },
337
+ scenario: "build",
338
+ sourcemaps,
339
+ sourcemapsRelativeSources: !versioning,
340
+ runtimeCompat,
292
341
  plugins: [
293
- jsenvPluginInline(),
342
+ jsenvPluginUrlAnalysis(),
343
+ jsenvPluginAsJsClassic({
344
+ systemJsInjection: true,
345
+ }),
346
+ jsenvPluginInline({
347
+ fetchInlineUrls: false,
348
+ }),
294
349
  {
295
350
  name: "jsenv:postbuild",
296
351
  appliesDuring: { build: true },
297
- resolve: (reference) => {
298
- if (reference.specifier[0] === "/") {
299
- const url = new URL(reference.specifier.slice(1), rootDirectoryUrl)
300
- .href
301
- return url
352
+ resolveUrl: (reference) => {
353
+ if (reference.specifier[0] === "#") {
354
+ reference.external = true
302
355
  }
303
- const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
304
- if (parentUrlInfo && parentUrlInfo.data.fromBundle) {
305
- // code generated by rollup contains specifier relative
306
- // to the generated file.
307
- // This file does not exists yet we must resolve against the raw url, not the build url
308
- const parentRawUrl = parentUrlInfo.data.rawUrl
309
- const rawUrl = new URL(reference.specifier, parentRawUrl).href
310
- return rawUrl
356
+ let url =
357
+ reference.specifier[0] === "/"
358
+ ? new URL(reference.specifier.slice(1), buildDirectoryUrl).href
359
+ : new URL(reference.specifier, reference.parentUrl).href
360
+ const urlRedirectedByBundle = bundleUrlRedirections[url]
361
+ if (urlRedirectedByBundle) {
362
+ return urlRedirectedByBundle
311
363
  }
312
- return new URL(reference.specifier, reference.parentUrl).href
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
+ const urlRedirected = rawUrlRedirections[url]
380
+ return urlRedirected || url
313
381
  },
314
- normalize: (reference) => {
382
+ normalizeUrl: (reference) => {
315
383
  if (!reference.url.startsWith("file:")) {
316
384
  return null
317
385
  }
318
386
  // already a build url
319
387
  const rawUrl = rawUrls[reference.url]
320
388
  if (rawUrl) {
321
- reference.data.rawUrl = rawUrl
322
389
  return reference.url
323
390
  }
324
- const bundleUrlInfo = bundleUrlInfos[reference.url]
325
391
  // from rollup or postcss
392
+ const bundleUrlInfo = bundleUrlInfos[reference.url]
326
393
  if (bundleUrlInfo) {
327
- const buildUrl = buildUrlsGenerator.generate(
328
- reference.url,
329
- bundleUrlInfo,
330
- )
331
- reference.data.rawUrl = reference.url
332
- rawUrls[buildUrl] = reference.url
333
- return buildUrl
394
+ return reference.url
334
395
  }
335
- const rawUrlInfo = rawGraph.getUrlInfo(reference.url)
336
- // files from root directory but not given to rollup nor postcss
337
- if (rawUrlInfo) {
338
- const buildUrl = buildUrlsGenerator.generate(
339
- reference.url,
340
- rawUrlInfo,
341
- )
342
- reference.data.rawUrl = reference.url
343
- rawUrls[buildUrl] = reference.url
396
+ // from "js_module_as_js_classic":
397
+ // - injecting "?as_js_classic" for the first time
398
+ // - injecting "?as_js_classic" because the parentUrl has it
399
+ if (reference.original) {
400
+ const referenceOriginalUrl = reference.original.url
401
+ let originalBuildUrl
402
+ if (urlIsInsideOf(referenceOriginalUrl, buildDirectoryUrl)) {
403
+ originalBuildUrl = referenceOriginalUrl
404
+ } else {
405
+ originalBuildUrl = Object.keys(rawUrls).find(
406
+ (key) => rawUrls[key] === referenceOriginalUrl,
407
+ )
408
+ }
409
+ let rawUrl
410
+ if (urlIsInsideOf(reference.url, buildDirectoryUrl)) {
411
+ // rawUrl = rawUrls[reference.url] || reference.url
412
+ const originalBuildUrl =
413
+ buildUrlRedirections[referenceOriginalUrl]
414
+ rawUrl = originalBuildUrl
415
+ ? rawUrls[originalBuildUrl]
416
+ : reference.url
417
+ } else {
418
+ rawUrl = reference.url
419
+ }
420
+ // the url info do not exists yet (it will be created after this "normalize" hook)
421
+ // And the content will be generated when url is cooked by url graph loader.
422
+ // Here we just want to reserve an url for that file
423
+ const buildUrl = buildUrlsGenerator.generate(rawUrl, {
424
+ urlInfo: {
425
+ data: {
426
+ ...reference.data,
427
+ isWebWorkerEntryPoint:
428
+ isWebWorkerEntryPointReference(reference),
429
+ },
430
+ type: reference.expectedType,
431
+ subtype: reference.expectedSubtype,
432
+ filename: reference.filename,
433
+ },
434
+ })
435
+ buildUrlRedirections[originalBuildUrl] = buildUrl
436
+ rawUrls[buildUrl] = rawUrl
344
437
  return buildUrl
345
438
  }
346
439
  if (reference.isInline) {
347
- const rawUrl = Object.keys(rawGraph.urlInfos).find((url) => {
348
- const rawUrlInfo = rawGraph.urlInfos[url]
440
+ const rawUrlInfo = GRAPH.find(rawGraph, (rawUrlInfo) => {
349
441
  if (!rawUrlInfo.isInline) {
350
442
  return false
351
443
  }
352
444
  if (rawUrlInfo.content === reference.content) {
353
445
  return true
354
446
  }
447
+ if (rawUrlInfo.originalContent === reference.content) {
448
+ return true
449
+ }
355
450
  return false
356
451
  })
357
- if (!rawUrl) {
358
- throw new Error(`cannot find raw url`)
359
- }
360
- const rawUrlInfo = rawGraph.getUrlInfo(rawUrl)
361
452
  const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
362
- const buildUrl = buildUrlsGenerator.generate(
363
- reference.url,
364
- rawUrlInfo,
453
+ if (!rawUrlInfo) {
454
+ // generated during final graph
455
+ // (happens for JSON.parse injected for import assertions for instance)
456
+ // throw new Error(`cannot find raw url for "${reference.url}"`)
457
+ return reference.url
458
+ }
459
+ const buildUrl = buildUrlsGenerator.generate(reference.url, {
460
+ urlInfo: rawUrlInfo,
365
461
  parentUrlInfo,
366
- )
462
+ })
463
+ rawUrls[buildUrl] = rawUrlInfo.url
464
+ return buildUrl
465
+ }
466
+ // from "js_module_as_js_classic":
467
+ // - to inject "s.js"
468
+ if (reference.injected) {
469
+ const buildUrl = buildUrlsGenerator.generate(reference.url, {
470
+ urlInfo: {
471
+ data: {},
472
+ type: "js_classic",
473
+ },
474
+ })
367
475
  rawUrls[buildUrl] = reference.url
368
- reference.data.rawUrl = rawUrl
476
+ return buildUrl
477
+ }
478
+ const rawUrlInfo = rawGraph.getUrlInfo(reference.url)
479
+ // files from root directory but not given to rollup nor postcss
480
+ if (rawUrlInfo) {
481
+ const buildUrl = buildUrlsGenerator.generate(reference.url, {
482
+ urlInfo: rawUrlInfo,
483
+ })
484
+ rawUrls[buildUrl] = rawUrlInfo.url
369
485
  return buildUrl
370
486
  }
371
487
  if (reference.type === "sourcemap_comment") {
372
488
  // inherit parent build url
373
489
  return generateSourcemapUrl(reference.parentUrl)
374
490
  }
375
- // files generated during the final graph (sourcemaps)
491
+ // files generated during the final graph:
492
+ // - sourcemaps
376
493
  // const finalUrlInfo = finalGraph.getUrlInfo(url)
377
494
  const buildUrl = buildUrlsGenerator.generate(reference.url, {
378
- data: {},
379
- type: "asset",
495
+ urlInfo: {
496
+ data: {},
497
+ type: "asset",
498
+ },
380
499
  })
381
500
  return buildUrl
382
501
  },
383
- formatReferencedUrl: (reference) => {
384
- if (!reference.url.startsWith("file:")) {
502
+ formatUrl: (reference) => {
503
+ if (!reference.generatedUrl.startsWith("file:")) {
385
504
  return null
386
505
  }
387
- if (!urlIsInsideOf(reference.url, buildDirectoryUrl)) {
506
+ if (!urlIsInsideOf(reference.generatedUrl, buildDirectoryUrl)) {
388
507
  throw new Error(
389
508
  `urls should be inside build directory at this stage, found "${reference.url}"`,
390
509
  )
391
510
  }
392
- // if a file is in the same directory we could prefer the relative notation
393
- // but to keep things simple let's keep the notation relative to baseUrl for now
394
- const specifier = `${baseUrl}${urlToRelativeUrl(
395
- reference.url,
396
- buildDirectoryUrl,
397
- )}`
398
- buildUrls[specifier] = reference.url
511
+ // remove eventual search params and hash
512
+ const urlUntilPathname = asUrlUntilPathname(reference.generatedUrl)
513
+ let specifier
514
+ if (baseUrl === "./") {
515
+ const relativeUrl = urlToRelativeUrl(
516
+ urlUntilPathname,
517
+ reference.parentUrl === rootDirectoryUrl
518
+ ? buildDirectoryUrl
519
+ : reference.parentUrl,
520
+ )
521
+ // ensure "./" on relative url (otherwise it could be a "bare specifier")
522
+ specifier =
523
+ relativeUrl[0] === "." ? relativeUrl : `./${relativeUrl}`
524
+ } else {
525
+ // if a file is in the same directory we could prefer the relative notation
526
+ // but to keep things simple let's keep the "absolutely relative" to baseUrl for now
527
+ specifier = `${baseUrl}${urlToRelativeUrl(
528
+ urlUntilPathname,
529
+ buildDirectoryUrl,
530
+ )}`
531
+ }
532
+ buildUrls[specifier] = reference.generatedUrl
399
533
  return specifier
400
534
  },
401
- load: (finalUrlInfo) => {
402
- const rawUrl = finalUrlInfo.data.rawUrl
403
- const bundleUrlInfo = bundleUrlInfos[rawUrl]
404
- const urlInfo = bundleUrlInfo || rawGraph.getUrlInfo(rawUrl)
405
- return {
406
- data: bundleUrlInfo ? bundleUrlInfo.data : undefined,
407
- originalContent: urlInfo.originalContent,
408
- contentType: urlInfo.contentType,
409
- content: urlInfo.content,
410
- sourcemap: urlInfo.sourcemap,
535
+ fetchUrlContent: async (finalUrlInfo, context) => {
536
+ if (!finalUrlInfo.url.startsWith("file:")) {
537
+ return { external: true }
411
538
  }
412
- },
413
- transform: {
414
- html: (urlInfo) => {
415
- const htmlAst = parseHtmlString(urlInfo.content, {
416
- storeOriginalPositions: false,
417
- })
418
- return {
419
- content: stringifyHtmlAst(htmlAst, {
420
- removeOriginalPositionAttributes: true,
421
- }),
539
+ const fromBundleOrRawGraph = (url) => {
540
+ const bundleUrlInfo = bundleUrlInfos[url]
541
+ if (bundleUrlInfo) {
542
+ return bundleUrlInfo
422
543
  }
423
- },
544
+ const rawUrl = rawUrls[url] || url
545
+ const rawUrlInfo = rawGraph.getUrlInfo(rawUrl)
546
+ if (!rawUrlInfo) {
547
+ const originalBuildUrl = buildUrlRedirections[url]
548
+ if (originalBuildUrl) {
549
+ return fromBundleOrRawGraph(originalBuildUrl)
550
+ }
551
+ throw new Error(`Cannot find url`)
552
+ }
553
+ if (rawUrlInfo.isInline) {
554
+ // Inline content, such as <script> inside html, is transformed during the previous phase.
555
+ // If we read the inline content it would be considered as the original content.
556
+ // - It could be "fixed" by taking into account sourcemap and consider sourcemap sources
557
+ // as the original content.
558
+ // - But it would not work when sourcemap are not generated
559
+ // - would be a bit slower
560
+ // - So instead of reading the inline content directly, we search into raw graph
561
+ // to get "originalContent" and "sourcemap"
562
+ finalUrlInfo.type = rawUrlInfo.type
563
+ finalUrlInfo.subtype = rawUrlInfo.subtype
564
+ return rawUrlInfo
565
+ }
566
+ return rawUrlInfo
567
+ }
568
+ // reference injected during "postbuild":
569
+ // - happens for "as_js_classic" injecting "s.js"
570
+ if (context.reference.injected) {
571
+ const [ref, rawUrlInfo] = rawGraphKitchen.injectReference({
572
+ type: context.reference.type,
573
+ expectedType: context.reference.expectedType,
574
+ expectedSubtype: context.reference.expectedSubtype,
575
+ parentUrl: rawUrls[context.reference.parentUrl],
576
+ specifier: context.reference.specifier,
577
+ injected: true,
578
+ })
579
+ await rawGraphKitchen.cook({
580
+ reference: ref,
581
+ urlInfo: rawUrlInfo,
582
+ })
583
+ return rawUrlInfo
584
+ }
585
+ // reference updated during "postbuild":
586
+ // - happens for "as_js_classic"
587
+ if (context.reference.original) {
588
+ return fromBundleOrRawGraph(context.reference.original.url)
589
+ }
590
+ return fromBundleOrRawGraph(finalUrlInfo.url)
424
591
  },
425
592
  },
426
593
  {
427
594
  name: "jsenv:optimize",
428
595
  appliesDuring: { build: true },
429
- transform: async (urlInfo, context) => {
430
- if (optimizeHooks.length) {
596
+ finalizeUrlContent: async (urlInfo, context) => {
597
+ if (optimizeUrlContentHooks.length) {
431
598
  await rawGraphKitchen.pluginController.callAsyncHooks(
432
- "optimize",
599
+ "optimizeUrlContent",
433
600
  urlInfo,
434
601
  context,
435
602
  async (optimizeReturnValue) => {
@@ -443,17 +610,24 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
443
610
  },
444
611
  },
445
612
  ],
446
- scenario: "build",
447
- sourcemaps,
448
613
  })
449
614
  const buildTask = createTaskLog(logger, "build")
615
+ const postBuildEntryUrls = []
450
616
  try {
451
617
  await loadUrlGraph({
452
618
  urlGraph: finalGraph,
453
619
  kitchen: finalGraphKitchen,
454
620
  outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
455
- runtimeSupport,
456
- startLoading: loadEntryFiles,
621
+ startLoading: (cookEntryFile) => {
622
+ entryUrls.forEach((entryUrl) => {
623
+ const [, postBuildEntryUrlInfo] = cookEntryFile({
624
+ trace: `entryPoint`,
625
+ type: "entry_point",
626
+ specifier: entryUrl,
627
+ })
628
+ postBuildEntryUrls.push(postBuildEntryUrlInfo.url)
629
+ })
630
+ },
457
631
  })
458
632
  } catch (e) {
459
633
  buildTask.fail()
@@ -465,217 +639,105 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
465
639
  `graph urls pre-versioning:
466
640
  ${Object.keys(finalGraph.urlInfos).join("\n")}`,
467
641
  )
468
- if (versioning !== "none") {
469
- const versioningTask = createTaskLog(logger, "inject version in urls")
470
- try {
471
- const urlsSorted = sortUrlGraphByDependencies(finalGraph)
472
- urlsSorted.forEach((url) => {
473
- if (url.startsWith("data:")) {
474
- return
475
- }
476
- const urlInfo = finalGraph.getUrlInfo(url)
477
- if (urlInfo.type === "sourcemap") {
478
- return
479
- }
480
- if (urlInfo.isInline) {
481
- return
482
- }
483
- const urlVersionGenerator = createUrlVersionGenerator()
484
- urlVersionGenerator.augmentWithContent({
485
- content: urlInfo.content,
486
- contentType: urlInfo.contentType,
487
- lineBreakNormalization,
488
- })
489
- urlInfo.dependencies.forEach((dependencyUrl) => {
490
- const dependencyUrlInfo = finalGraph.getUrlInfo(dependencyUrl)
491
- if (dependencyUrlInfo.isInline) {
492
- // this content is part of the file, no need to take into account twice
493
- return
494
- }
495
- if (dependencyUrlInfo.data.version) {
496
- urlVersionGenerator.augmentWithDependencyVersion(
497
- dependencyUrlInfo.data.version,
498
- )
499
- } else {
500
- // because all dependencies are know, if the dependency has no version
501
- // it means there is a circular dependency between this file
502
- // and it's dependency
503
- // in that case we'll use the dependency content
504
- urlVersionGenerator.augmentWithContent({
505
- content: dependencyUrlInfo.content,
506
- contentType: dependencyUrlInfo.contentType,
507
- lineBreakNormalization,
508
- })
509
- }
510
- })
511
- urlInfo.data.version = urlVersionGenerator.generate()
512
- urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
513
- buildUrl: urlInfo.url,
514
- version: urlInfo.data.version,
515
- versioning,
516
- })
517
- })
518
- const versionMappings = {}
519
- const usedVersionMappings = []
520
- const versioningKitchen = createKitchen({
521
- logger,
522
- rootDirectoryUrl: buildDirectoryUrl,
523
- urlGraph: finalGraph,
524
- loadInlineUrlInfos: (versionedUrlInfo) => {
525
- const rawUrlInfo = rawGraph.getUrlInfo(versionedUrlInfo.data.rawUrl)
526
- const finalUrlInfo = finalGraph.getUrlInfo(versionedUrlInfo.url)
527
- return {
528
- originalContent: rawUrlInfo
529
- ? rawUrlInfo.originalContent
530
- : undefined,
531
- sourcemap: finalUrlInfo ? finalUrlInfo.sourcemap : undefined,
532
- contentType: versionedUrlInfo.contentType,
533
- content: versionedUrlInfo.content,
534
- }
535
- },
536
- plugins: [
537
- jsenvPluginInline({
538
- allowEscapeForVersioning: true,
539
- }),
540
- {
541
- name: "jsenv:versioning",
542
- appliesDuring: { build: true },
543
- resolve: ({ parentUrl, specifier }) => {
544
- const buildUrl = buildUrls[specifier]
545
- if (buildUrl) {
546
- return buildUrl
547
- }
548
- const url = new URL(specifier, parentUrl).href
549
- return url
550
- },
551
- formatReferencedUrl: (reference) => {
552
- if (reference.isInline) {
553
- return null
554
- }
555
- // specifier comes from "normalize" hook done a bit earlier in this file
556
- // we want to get back their build url to access their infos
557
- const referencedUrlInfo = finalGraph.getUrlInfo(reference.url)
558
- if (referencedUrlInfo.data.isEntryPoint) {
559
- return reference.specifier
560
- }
561
- // data:* urls and so on
562
- if (!referencedUrlInfo.url.startsWith("file:")) {
563
- return null
564
- }
565
- const versionedUrl = referencedUrlInfo.data.versionedUrl
566
- if (!versionedUrl) {
567
- // happens for sourcemap
568
- return `${baseUrl}${urlToRelativeUrl(
569
- referencedUrlInfo.url,
570
- buildDirectoryUrl,
571
- )}`
572
- }
573
- const versionedSpecifier = `${baseUrl}${urlToRelativeUrl(
574
- versionedUrl,
575
- buildDirectoryUrl,
576
- )}`
577
- versionMappings[reference.specifier] = versionedSpecifier
578
- const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
579
- if (parentUrlInfo.jsQuote) {
580
- // the url is inline inside js quotes
581
- usedVersionMappings.push(reference.specifier)
582
- return () =>
583
- `${parentUrlInfo.jsQuote}+__v__(${JSON.stringify(
584
- reference.specifier,
585
- )})+${parentUrlInfo.jsQuote}`
586
- }
587
- if (
588
- reference.type === "js_import_meta_url_pattern" ||
589
- reference.subtype === "import_dynamic"
590
- ) {
591
- usedVersionMappings.push(reference.specifier)
592
- return () => `__v__(${JSON.stringify(reference.specifier)})`
593
- }
594
- return versionedSpecifier
595
- },
596
- load: ({ url }) => {
597
- const urlInfo = finalGraph.getUrlInfo(url)
598
- return {
599
- originalContent: urlInfo.originalContent,
600
- contentType: urlInfo.contentType,
601
- content: urlInfo.content,
602
- sourcemap: urlInfo.sourcemap,
603
- }
604
- },
605
- },
606
- ],
607
- scenario: "build",
608
- sourcemaps,
609
- })
610
- // arrange state before reloading all files
611
- Object.keys(finalGraph.urlInfos).forEach((url) => {
612
- const urlInfo = finalGraph.urlInfos[url]
613
- urlInfo.data.promise = null
614
- })
615
- await loadUrlGraph({
616
- urlGraph: finalGraph,
617
- kitchen: versioningKitchen,
618
- runtimeSupport,
619
- startLoading: loadEntryFiles,
620
- })
621
- if (usedVersionMappings.length) {
622
- const versionMappingsNeeded = {}
623
- usedVersionMappings.forEach((specifier) => {
624
- versionMappingsNeeded[specifier] = versionMappings[specifier]
625
- })
626
- await Promise.all(
627
- Object.keys(finalGraph.urlInfos).map(async (buildUrl) => {
628
- const buildUrlInfo = finalGraph.getUrlInfo(buildUrl)
629
- if (!buildUrlInfo.data.isEntryPoint) {
630
- return
631
- }
632
- await injectVersionMappings(buildUrlInfo, {
633
- kitchen: finalGraphKitchen,
634
- versionMappings: versionMappingsNeeded,
635
- })
636
- }),
637
- )
638
- }
639
- } catch (e) {
640
- versioningTask.fail()
641
- throw e
642
- }
643
- versioningTask.done()
642
+ if (versioning) {
643
+ await applyUrlVersioning({
644
+ logger,
645
+ buildDirectoryUrl,
646
+ rawUrls,
647
+ buildUrls,
648
+ baseUrl,
649
+ postBuildEntryUrls,
650
+ sourcemaps,
651
+ runtimeCompat,
652
+ rawGraph,
653
+ finalGraph,
654
+ finalGraphKitchen,
655
+ lineBreakNormalization,
656
+ versioningMethod,
657
+ })
644
658
  }
645
-
646
- const buildFileContents = {}
647
- const buildInlineFileContents = {}
648
- const buildManifest = {}
649
- Object.keys(finalGraph.urlInfos).forEach((url) => {
650
- if (!url.startsWith("file:")) {
659
+ GRAPH.forEach(finalGraph, (urlInfo) => {
660
+ if (!urlInfo.url.startsWith("file:")) {
651
661
  return
652
662
  }
653
- const buildUrlInfo = finalGraph.getUrlInfo(url)
654
- const versionedUrl = buildUrlInfo.data.versionedUrl
655
- const useVersionedUrl = versionedUrl && !buildUrlInfo.data.isEntryPoint
656
- const buildUrl = useVersionedUrl ? versionedUrl : buildUrlInfo.url
657
- if (!urlIsInsideOf(buildUrl, buildDirectoryUrl)) {
658
- throw new Error(`build url outside build directory`)
663
+ if (urlInfo.external) {
664
+ return
659
665
  }
660
- const buildRelativeUrl = urlToRelativeUrl(buildUrl, buildDirectoryUrl)
661
- if (buildUrlInfo.isInline) {
662
- buildInlineFileContents[buildRelativeUrl] = buildUrlInfo.content
663
- } else {
664
- buildFileContents[buildRelativeUrl] = buildUrlInfo.content
666
+ if (urlInfo.type === "html") {
667
+ const htmlAst = parseHtmlString(urlInfo.content, {
668
+ storeOriginalPositions: false,
669
+ })
670
+ urlInfo.content = stringifyHtmlAst(htmlAst, {
671
+ removeOriginalPositionAttributes: true,
672
+ })
665
673
  }
666
- if (useVersionedUrl) {
667
- const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
668
- buildUrlInfo.url,
669
- buildDirectoryUrl,
670
- )
671
- buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
674
+ const version = urlInfo.data.version
675
+ const useVersionedUrl = version && canUseVersionedUrl(urlInfo, finalGraph)
676
+ const buildUrl = useVersionedUrl ? urlInfo.data.versionedUrl : urlInfo.url
677
+ const buildUrlSpecifier = Object.keys(buildUrls).find(
678
+ (key) => buildUrls[key] === buildUrl,
679
+ )
680
+ urlInfo.data.buildUrl = buildUrl
681
+ urlInfo.data.buildUrlIsVersioned = useVersionedUrl
682
+ urlInfo.data.buildUrlSpecifier = buildUrlSpecifier
683
+ })
684
+ await resyncRessourceHints({
685
+ finalGraphKitchen,
686
+ finalGraph,
687
+ rawUrls,
688
+ buildUrls,
689
+ })
690
+ const cleanupActions = []
691
+ GRAPH.forEach(finalGraph, (urlInfo) => {
692
+ // nothing uses this url anymore
693
+ // - versioning update inline content
694
+ // - file converted for import assertion of js_classic conversion
695
+ if (
696
+ !urlInfo.data.isEntryPoint &&
697
+ urlInfo.type !== "sourcemap" &&
698
+ urlInfo.dependents.size === 0
699
+ ) {
700
+ cleanupActions.push(() => {
701
+ delete finalGraph.urlInfos[urlInfo.url]
702
+ })
672
703
  }
673
704
  })
705
+ cleanupActions.forEach((cleanupAction) => cleanupAction())
706
+ await injectServiceWorkerUrls({
707
+ finalGraphKitchen,
708
+ finalGraph,
709
+ lineBreakNormalization,
710
+ })
674
711
  logger.debug(
675
712
  `graph urls post-versioning:
676
713
  ${Object.keys(finalGraph.urlInfos).join("\n")}`,
677
714
  )
678
715
 
716
+ const buildManifest = {}
717
+ const buildFileContents = {}
718
+ const buildInlineContents = {}
719
+ GRAPH.forEach(finalGraph, (urlInfo) => {
720
+ if (urlInfo.external) {
721
+ return
722
+ }
723
+ if (urlInfo.url.startsWith("data:")) {
724
+ return
725
+ }
726
+ const buildRelativeUrl = urlToRelativeUrl(
727
+ urlInfo.data.buildUrl,
728
+ buildDirectoryUrl,
729
+ )
730
+ if (urlInfo.isInline) {
731
+ buildInlineContents[buildRelativeUrl] = urlInfo.content
732
+ } else {
733
+ buildFileContents[buildRelativeUrl] = urlInfo.content
734
+ }
735
+ const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
736
+ urlInfo.url,
737
+ buildDirectoryUrl,
738
+ )
739
+ buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
740
+ })
679
741
  if (writeOnFileSystem) {
680
742
  if (buildDirectoryClean) {
681
743
  await ensureEmptyDirectory(buildDirectoryUrl)
@@ -689,17 +751,254 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
689
751
  )
690
752
  }),
691
753
  )
754
+ if (versioning && assetManifest && Object.keys(buildManifest).length) {
755
+ await writeFile(
756
+ new URL(assetManifestFileRelativeUrl, buildDirectoryUrl),
757
+ JSON.stringify(buildManifest, null, " "),
758
+ )
759
+ }
692
760
  }
693
761
  logger.info(createUrlGraphSummary(finalGraph, { title: "build files" }))
694
762
  return {
695
763
  buildFileContents,
696
- buildInlineFileContents,
764
+ buildInlineContents,
697
765
  buildManifest,
698
766
  }
699
767
  }
700
768
 
701
- const injectVersionIntoBuildUrl = ({ buildUrl, version, versioning }) => {
702
- if (versioning === "search_param") {
769
+ const applyUrlVersioning = async ({
770
+ logger,
771
+ buildDirectoryUrl,
772
+ rawUrls,
773
+ buildUrls,
774
+ baseUrl,
775
+ postBuildEntryUrls,
776
+ sourcemaps,
777
+ runtimeCompat,
778
+ rawGraph,
779
+ finalGraph,
780
+ finalGraphKitchen,
781
+ lineBreakNormalization,
782
+ versioningMethod,
783
+ }) => {
784
+ const versioningTask = createTaskLog(logger, "inject version in urls")
785
+ try {
786
+ const urlsSorted = sortUrlGraphByDependencies(finalGraph)
787
+ urlsSorted.forEach((url) => {
788
+ if (url.startsWith("data:")) {
789
+ return
790
+ }
791
+ const urlInfo = finalGraph.getUrlInfo(url)
792
+ if (urlInfo.type === "sourcemap") {
793
+ return
794
+ }
795
+ // ignore:
796
+ // - inline files:
797
+ // they are already taken into account in the file where they appear
798
+ // - external files
799
+ // we don't know their content
800
+ // - unused files without reference
801
+ // File updated such as style.css -> style.css.js or file.js->file.es5.js
802
+ // Are used at some point just to be discarded later because they need to be converted
803
+ // There is no need to version them and we could not because the file have been ignored
804
+ // so their content is unknown
805
+ if (urlInfo.isInline) {
806
+ return
807
+ }
808
+ if (urlInfo.external) {
809
+ return
810
+ }
811
+ if (!urlInfo.data.isEntryPoint && urlInfo.dependents.size === 0) {
812
+ return
813
+ }
814
+
815
+ const urlContent =
816
+ urlInfo.type === "html"
817
+ ? stringifyHtmlAst(
818
+ parseHtmlString(urlInfo.content, {
819
+ storeOriginalPositions: false,
820
+ }),
821
+ { removeOriginalPositionAttributes: true },
822
+ )
823
+ : urlInfo.content
824
+ const versionGenerator = createVersionGenerator()
825
+ versionGenerator.augmentWithContent({
826
+ content: urlContent,
827
+ contentType: urlInfo.contentType,
828
+ lineBreakNormalization,
829
+ })
830
+ urlInfo.dependencies.forEach((dependencyUrl) => {
831
+ // this dependency is inline (data:) or remote (http://, https://)
832
+ if (!dependencyUrl.startsWith("file:")) {
833
+ return
834
+ }
835
+ const dependencyUrlInfo = finalGraph.getUrlInfo(dependencyUrl)
836
+ if (
837
+ // this content is part of the file, no need to take into account twice
838
+ dependencyUrlInfo.isInline ||
839
+ // this dependency content is not known
840
+ dependencyUrlInfo.external
841
+ ) {
842
+ return
843
+ }
844
+ if (dependencyUrlInfo.data.version) {
845
+ versionGenerator.augmentWithDependencyVersion(
846
+ dependencyUrlInfo.data.version,
847
+ )
848
+ } else {
849
+ // because all dependencies are know, if the dependency has no version
850
+ // it means there is a circular dependency between this file
851
+ // and it's dependency
852
+ // in that case we'll use the dependency content
853
+ versionGenerator.augmentWithContent({
854
+ content: dependencyUrlInfo.content,
855
+ contentType: dependencyUrlInfo.contentType,
856
+ lineBreakNormalization,
857
+ })
858
+ }
859
+ })
860
+ urlInfo.data.version = versionGenerator.generate()
861
+
862
+ urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
863
+ buildUrl: urlInfo.url,
864
+ version: urlInfo.data.version,
865
+ versioningMethod,
866
+ })
867
+ })
868
+ const versionMappings = {}
869
+ const usedVersionMappings = []
870
+ const versioningKitchen = createKitchen({
871
+ logger,
872
+ rootDirectoryUrl: buildDirectoryUrl,
873
+ urlGraph: finalGraph,
874
+ scenario: "build",
875
+ sourcemaps,
876
+ sourcemapsRelativeSources: true,
877
+ runtimeCompat,
878
+ plugins: [
879
+ jsenvPluginUrlAnalysis(),
880
+ jsenvPluginInline({
881
+ fetchInlineUrls: false,
882
+ analyzeConvertedScripts: true, // to be able to version their urls
883
+ allowEscapeForVersioning: true,
884
+ }),
885
+ {
886
+ name: "jsenv:versioning",
887
+ appliesDuring: { build: true },
888
+ resolveUrl: (reference) => {
889
+ if (reference.specifier[0] === "#") {
890
+ reference.external = true
891
+ }
892
+ const buildUrl = buildUrls[reference.specifier]
893
+ if (buildUrl) {
894
+ return buildUrl
895
+ }
896
+ const url = new URL(reference.specifier, reference.parentUrl).href
897
+ return url
898
+ },
899
+ formatUrl: (reference) => {
900
+ if (reference.isInline) {
901
+ return null
902
+ }
903
+ // specifier comes from "normalize" hook done a bit earlier in this file
904
+ // we want to get back their build url to access their infos
905
+ const referencedUrlInfo = finalGraph.getUrlInfo(reference.url)
906
+ if (!canUseVersionedUrl(referencedUrlInfo)) {
907
+ return reference.specifier
908
+ }
909
+ // data:* urls and so on
910
+ if (!referencedUrlInfo.url.startsWith("file:")) {
911
+ return null
912
+ }
913
+ const versionedUrl = referencedUrlInfo.data.versionedUrl
914
+ if (!versionedUrl) {
915
+ // happens for sourcemap
916
+ return `${baseUrl}${urlToRelativeUrl(
917
+ referencedUrlInfo.url,
918
+ buildDirectoryUrl,
919
+ )}`
920
+ }
921
+ const versionedSpecifier = `${baseUrl}${urlToRelativeUrl(
922
+ versionedUrl,
923
+ buildDirectoryUrl,
924
+ )}`
925
+ versionMappings[reference.specifier] = versionedSpecifier
926
+ buildUrls[versionedSpecifier] = versionedUrl
927
+
928
+ const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
929
+ if (parentUrlInfo.jsQuote) {
930
+ // the url is inline inside js quotes
931
+ usedVersionMappings.push(reference.specifier)
932
+ return () =>
933
+ `${parentUrlInfo.jsQuote}+__v__(${JSON.stringify(
934
+ reference.specifier,
935
+ )})+${parentUrlInfo.jsQuote}`
936
+ }
937
+ if (
938
+ reference.type === "js_url_specifier" ||
939
+ reference.subtype === "import_dynamic"
940
+ ) {
941
+ usedVersionMappings.push(reference.specifier)
942
+ return () => `__v__(${JSON.stringify(reference.specifier)})`
943
+ }
944
+ return versionedSpecifier
945
+ },
946
+ fetchUrlContent: (versionedUrlInfo) => {
947
+ if (!versionedUrlInfo.url.startsWith("file:")) {
948
+ return { external: true }
949
+ }
950
+ if (versionedUrlInfo.isInline) {
951
+ const rawUrlInfo = rawGraph.getUrlInfo(
952
+ rawUrls[versionedUrlInfo.url],
953
+ )
954
+ const finalUrlInfo = finalGraph.getUrlInfo(versionedUrlInfo.url)
955
+ return {
956
+ originalContent: rawUrlInfo
957
+ ? rawUrlInfo.originalContent
958
+ : undefined,
959
+ sourcemap: finalUrlInfo ? finalUrlInfo.sourcemap : undefined,
960
+ contentType: versionedUrlInfo.contentType,
961
+ content: versionedUrlInfo.content,
962
+ }
963
+ }
964
+ return versionedUrlInfo
965
+ },
966
+ },
967
+ ],
968
+ })
969
+ await loadUrlGraph({
970
+ urlGraph: finalGraph,
971
+ kitchen: versioningKitchen,
972
+ startLoading: (cookEntryFile) => {
973
+ postBuildEntryUrls.forEach((postBuildEntryUrl) => {
974
+ cookEntryFile({
975
+ trace: `entryPoint`,
976
+ type: "entry_point",
977
+ specifier: postBuildEntryUrl,
978
+ })
979
+ })
980
+ },
981
+ })
982
+ if (usedVersionMappings.length) {
983
+ const versionMappingsNeeded = {}
984
+ usedVersionMappings.forEach((specifier) => {
985
+ versionMappingsNeeded[specifier] = versionMappings[specifier]
986
+ })
987
+ await injectGlobalVersionMapping({
988
+ finalGraphKitchen,
989
+ finalGraph,
990
+ versionMappings: versionMappingsNeeded,
991
+ })
992
+ }
993
+ } catch (e) {
994
+ versioningTask.fail()
995
+ throw e
996
+ }
997
+ versioningTask.done()
998
+ }
999
+
1000
+ const injectVersionIntoBuildUrl = ({ buildUrl, version, versioningMethod }) => {
1001
+ if (versioningMethod === "search_param") {
703
1002
  return injectQueryParams(buildUrl, {
704
1003
  v: version,
705
1004
  })
@@ -735,3 +1034,16 @@ const assertEntryPoints = ({ entryPoints }) => {
735
1034
  }
736
1035
  })
737
1036
  }
1037
+
1038
+ const canUseVersionedUrl = (urlInfo) => {
1039
+ if (urlInfo.data.isEntryPoint) {
1040
+ return false
1041
+ }
1042
+ if (urlInfo.type === "webmanifest") {
1043
+ return false
1044
+ }
1045
+ if (urlInfo.subtype === "service_worker") {
1046
+ return !urlInfo.data.isWebWorkerEntryPoint
1047
+ }
1048
+ return true
1049
+ }