@jsenv/core 27.0.0-alpha.4 → 27.0.0-alpha.42

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 (141) hide show
  1. package/dist/event_source_client.js +549 -0
  2. package/dist/event_source_client.js.map +188 -0
  3. package/dist/html_supervisor_installer.js +1145 -0
  4. package/dist/html_supervisor_installer.js.map +322 -0
  5. package/dist/html_supervisor_setup.js +92 -0
  6. package/dist/html_supervisor_setup.js.map +57 -0
  7. package/main.js +8 -1
  8. package/package.json +24 -24
  9. package/readme.md +4 -12
  10. package/src/build/build.js +731 -433
  11. package/src/build/build_urls_generator.js +55 -21
  12. package/src/build/graph_utils.js +31 -0
  13. package/src/build/{inject_version_mappings.js → inject_global_version_mappings.js} +33 -15
  14. package/src/build/inject_service_worker_urls.js +79 -0
  15. package/src/build/resync_ressource_hints.js +68 -0
  16. package/src/build/start_build_server.js +205 -0
  17. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +2 -2
  18. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -1
  19. package/src/dev/start_dev_server.js +58 -26
  20. package/src/execute/execute.js +30 -4
  21. package/src/execute/run.js +19 -56
  22. package/src/execute/runtimes/browsers/from_playwright.js +201 -147
  23. package/src/execute/runtimes/node/controllable_file.mjs +26 -10
  24. package/src/execute/runtimes/node/node_execution_performance.js +67 -0
  25. package/src/execute/runtimes/node/node_process.js +280 -39
  26. package/src/jsenv_root_directory_url.js +1 -0
  27. package/src/omega/{runtime_support/default_runtime_support.js → compat/default_runtime_compat.js} +3 -5
  28. package/src/omega/{runtime_support/features_compatibility.js → compat/features_compats.js} +66 -4
  29. package/src/omega/compat/runtime_compat.js +50 -0
  30. package/src/omega/errors.js +51 -58
  31. package/src/omega/fetched_content_compliance.js +24 -0
  32. package/src/omega/file_url_converter.js +8 -50
  33. package/src/omega/kitchen.js +414 -285
  34. package/src/omega/server/file_service.js +21 -22
  35. package/src/omega/url_graph/url_graph_load.js +14 -7
  36. package/src/omega/url_graph/url_graph_report.js +17 -15
  37. package/src/omega/url_graph/url_info_transformations.js +17 -5
  38. package/src/omega/url_graph.js +33 -10
  39. package/src/omega/web_workers.js +42 -0
  40. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/autoreload_preference.js +0 -0
  41. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/event_source_client.js +2 -2
  42. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/reload.js +0 -0
  43. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/url_helpers.js +0 -0
  44. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +46 -0
  45. package/src/{dev/plugins/autoreload/jsenv_plugin_autoreload.js → plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js} +31 -172
  46. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +27 -0
  47. package/src/plugins/autoreload/jsenv_plugin_hmr.js +35 -0
  48. package/src/plugins/bundling/css/bundle_css.js +21 -0
  49. package/src/plugins/bundling/js_classic_workers/bundle_js_classic_workers.js +13 -0
  50. package/src/{build/plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js → plugins/bundling/js_module/bundle_js_module.js} +120 -79
  51. package/src/plugins/bundling/jsenv_plugin_bundling.js +51 -0
  52. package/src/{omega/core_plugins → plugins}/commonjs_globals/jsenv_plugin_commonjs_globals.js +54 -41
  53. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +66 -0
  54. package/src/{omega/core_plugins → plugins}/filesystem_magic/jsenv_plugin_filesystem_magic.js +8 -5
  55. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_document.js +0 -0
  56. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_notification.js +0 -0
  57. package/src/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_installer.js +3 -2
  58. package/src/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_setup.js +0 -0
  59. package/src/{omega/core_plugins → plugins}/html_supervisor/client/perf_browser.js +0 -0
  60. package/src/{omega/core_plugins → plugins}/html_supervisor/client/uneval_exception.js +0 -0
  61. package/src/{omega/core_plugins → plugins}/html_supervisor/jsenv_plugin_html_supervisor.js +52 -55
  62. package/src/plugins/http_urls/jsenv_plugin_http_urls.js +12 -0
  63. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/babel_plugin_metadata_import_meta_hot.js +4 -5
  64. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/client/import_meta_hot.js +3 -1
  65. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/html_hot_dependencies.js +2 -2
  66. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +101 -0
  67. package/src/{omega/core_plugins → plugins}/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +33 -8
  68. package/src/plugins/import_meta_url/client/import_meta_url_browser.js +52 -0
  69. package/src/plugins/import_meta_url/client/import_meta_url_commonjs.mjs +9 -0
  70. package/src/{omega/core_plugins → plugins}/importmap/jsenv_plugin_importmap.js +37 -31
  71. package/src/plugins/inject_globals/jsenv_plugin_inject_globals.js +67 -0
  72. package/src/{omega/core_plugins → plugins}/inline/client/inline_content.js +0 -0
  73. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_data_urls.js +18 -14
  74. package/src/{omega/core_plugins/inline/jsenv_plugin_js_and_css_inside_html.js → plugins/inline/jsenv_plugin_html_inline_content.js} +61 -40
  75. package/src/plugins/inline/jsenv_plugin_inline.js +36 -0
  76. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_inline_query_param.js +6 -6
  77. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +296 -0
  78. package/src/plugins/leading_slash/jsenv_plugin_leading_slash.js +13 -0
  79. package/src/plugins/minification/css/minify_css.js +9 -0
  80. package/src/plugins/minification/html/minify_html.js +15 -0
  81. package/src/{build/plugins/minify_js/jsenv_plugin_minify_js.js → plugins/minification/js/minify_js.js} +6 -22
  82. package/src/plugins/minification/jsenv_plugin_minification.js +78 -0
  83. package/src/plugins/minification/json/minify_json.js +8 -0
  84. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +146 -0
  85. package/src/{omega → plugins}/plugin_controller.js +39 -11
  86. package/src/plugins/plugins.js +89 -0
  87. package/src/plugins/transpilation/as_js_classic/client/s.js +808 -0
  88. package/src/plugins/transpilation/as_js_classic/client/s.js.md +1 -0
  89. package/src/plugins/transpilation/as_js_classic/helpers/babel_plugin_transform_import_meta_url.js +47 -0
  90. package/src/plugins/transpilation/as_js_classic/helpers/systemjs_old.js +43 -0
  91. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +183 -0
  92. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_script_type_module_as_classic.js +256 -0
  93. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_workers_type_module_as_classic.js +93 -0
  94. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +0 -0
  95. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/client/global_this.js +0 -0
  96. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +0 -0
  97. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_structure.js +4 -22
  98. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugins_compatibility.js +0 -0
  99. package/src/{omega/core_plugins → plugins/transpilation}/babel/jsenv_plugin_babel.js +37 -21
  100. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +30 -6
  101. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/.eslintrc.cjs +0 -0
  102. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/new_stylesheet.js +0 -0
  103. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +0 -0
  104. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/client/regenerator_runtime.js +0 -0
  105. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +18 -0
  106. package/src/plugins/transpilation/fetch_original_url_info.js +30 -0
  107. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +200 -0
  108. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +70 -0
  109. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +44 -0
  110. package/src/plugins/url_references/css/css_urls.js +49 -0
  111. package/src/plugins/url_references/html/html_urls.js +273 -0
  112. package/src/plugins/url_references/js/js_urls.js +43 -0
  113. package/src/plugins/url_references/jsenv_plugin_imports_analysis.js +46 -0
  114. package/src/plugins/url_references/jsenv_plugin_url_analysis.js +18 -0
  115. package/src/plugins/url_references/jsenv_plugin_url_references.js +6 -0
  116. package/src/plugins/url_references/webmanifest/webmanifest_urls.js +17 -0
  117. package/src/{omega/core_plugins → plugins}/url_resolution/jsenv_plugin_url_resolution.js +12 -5
  118. package/src/{omega/core_plugins → plugins}/url_version/jsenv_plugin_url_version.js +12 -15
  119. package/src/test/execute_plan.js +28 -11
  120. package/src/test/execute_test_plan.js +23 -8
  121. package/src/test/logs_file_execution.js +8 -7
  122. package/src/build/plugins/minify_html/jsenv_plugin_minify_html.js +0 -30
  123. package/src/dev/plugins/autoreload/client/event_source_connection.js +0 -195
  124. package/src/dev/plugins/autoreload/sse_service.js +0 -149
  125. package/src/execute/runtimes/node/controlled_process.js +0 -316
  126. package/src/omega/core_plugins/file_urls/jsenv_plugin_file_urls.js +0 -67
  127. package/src/omega/core_plugins/import_assertions/helpers/babel_plugin_metadata_import_assertions.js +0 -98
  128. package/src/omega/core_plugins/import_assertions/helpers/json_module.js +0 -12
  129. package/src/omega/core_plugins/import_assertions/helpers/text_module.js +0 -6
  130. package/src/omega/core_plugins/import_assertions/jsenv_plugin_import_assertions.js +0 -211
  131. package/src/omega/core_plugins/inline/jsenv_plugin_inline.js +0 -13
  132. package/src/omega/core_plugins/inline/jsenv_plugin_new_inline_content.js +0 -210
  133. package/src/omega/core_plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -12
  134. package/src/omega/core_plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +0 -77
  135. package/src/omega/core_plugins.js +0 -39
  136. package/src/omega/runtime_support/runtime_support.js +0 -20
  137. package/src/omega/url_mentions/css_url_mentions.js +0 -63
  138. package/src/omega/url_mentions/html_url_mentions.js +0 -185
  139. package/src/omega/url_mentions/js_module_url_mentions.js +0 -91
  140. package/src/omega/url_mentions/parse_url_mentions.js +0 -37
  141. 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 { jsenvPluginUrlReferences } from "../plugins/url_references/jsenv_plugin_url_references.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,335 @@ ${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: true,
340
+ runtimeCompat,
292
341
  plugins: [
293
- jsenvPluginInline(),
342
+ jsenvPluginUrlReferences(),
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
  }
511
+ // remove eventual search params and hash
512
+ const urlUntilPathname = asUrlUntilPathname(reference.generatedUrl)
392
513
  // 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
514
+ // but to keep things simple let's keep the "absolutely relative" to baseUrl for now
394
515
  const specifier = `${baseUrl}${urlToRelativeUrl(
395
- reference.url,
516
+ urlUntilPathname,
396
517
  buildDirectoryUrl,
397
518
  )}`
398
- buildUrls[specifier] = reference.url
519
+ buildUrls[specifier] = reference.generatedUrl
399
520
  return specifier
400
521
  },
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,
522
+ fetchUrlContent: async (finalUrlInfo, context) => {
523
+ if (!finalUrlInfo.url.startsWith("file:")) {
524
+ return { external: true }
411
525
  }
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
- }),
526
+ const fromBundleOrRawGraph = (url) => {
527
+ const bundleUrlInfo = bundleUrlInfos[url]
528
+ if (bundleUrlInfo) {
529
+ return bundleUrlInfo
422
530
  }
423
- },
531
+ const rawUrl = rawUrls[url] || url
532
+ const rawUrlInfo = rawGraph.getUrlInfo(rawUrl)
533
+ if (!rawUrlInfo) {
534
+ const originalBuildUrl = buildUrlRedirections[url]
535
+ if (originalBuildUrl) {
536
+ return fromBundleOrRawGraph(originalBuildUrl)
537
+ }
538
+ throw new Error(`Cannot find url`)
539
+ }
540
+ if (rawUrlInfo.isInline) {
541
+ // Inline content, such as <script> inside html, is transformed during the previous phase.
542
+ // If we read the inline content it would be considered as the original content.
543
+ // - It could be "fixed" by taking into account sourcemap and consider sourcemap sources
544
+ // as the original content.
545
+ // - But it would not work when sourcemap are not generated
546
+ // - would be a bit slower
547
+ // - So instead of reading the inline content directly, we search into raw graph
548
+ // to get "originalContent" and "sourcemap"
549
+ finalUrlInfo.type = rawUrlInfo.type
550
+ finalUrlInfo.subtype = rawUrlInfo.subtype
551
+ return rawUrlInfo
552
+ }
553
+ return rawUrlInfo
554
+ }
555
+ // reference injected during "postbuild":
556
+ // - happens for "as_js_classic" injecting "s.js"
557
+ if (context.reference.injected) {
558
+ const [ref, rawUrlInfo] = rawGraphKitchen.injectReference({
559
+ type: context.reference.type,
560
+ expectedType: context.reference.expectedType,
561
+ expectedSubtype: context.reference.expectedSubtype,
562
+ parentUrl: rawUrls[context.reference.parentUrl],
563
+ specifier: context.reference.specifier,
564
+ injected: true,
565
+ })
566
+ await rawGraphKitchen.cook({
567
+ reference: ref,
568
+ urlInfo: rawUrlInfo,
569
+ })
570
+ return rawUrlInfo
571
+ }
572
+ // reference updated during "postbuild":
573
+ // - happens for "as_js_classic"
574
+ if (context.reference.original) {
575
+ return fromBundleOrRawGraph(context.reference.original.url)
576
+ }
577
+ return fromBundleOrRawGraph(finalUrlInfo.url)
424
578
  },
425
579
  },
426
580
  {
427
581
  name: "jsenv:optimize",
428
582
  appliesDuring: { build: true },
429
- transform: async (urlInfo, context) => {
430
- if (optimizeHooks.length) {
583
+ finalizeUrlContent: async (urlInfo, context) => {
584
+ if (optimizeUrlContentHooks.length) {
431
585
  await rawGraphKitchen.pluginController.callAsyncHooks(
432
- "optimize",
586
+ "optimizeUrlContent",
433
587
  urlInfo,
434
588
  context,
435
589
  async (optimizeReturnValue) => {
@@ -443,17 +597,24 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
443
597
  },
444
598
  },
445
599
  ],
446
- scenario: "build",
447
- sourcemaps,
448
600
  })
449
601
  const buildTask = createTaskLog(logger, "build")
602
+ const postBuildEntryUrls = []
450
603
  try {
451
604
  await loadUrlGraph({
452
605
  urlGraph: finalGraph,
453
606
  kitchen: finalGraphKitchen,
454
607
  outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
455
- runtimeSupport,
456
- startLoading: loadEntryFiles,
608
+ startLoading: (cookEntryFile) => {
609
+ entryUrls.forEach((entryUrl) => {
610
+ const [, postBuildEntryUrlInfo] = cookEntryFile({
611
+ trace: `entryPoint`,
612
+ type: "entry_point",
613
+ specifier: entryUrl,
614
+ })
615
+ postBuildEntryUrls.push(postBuildEntryUrlInfo.url)
616
+ })
617
+ },
457
618
  })
458
619
  } catch (e) {
459
620
  buildTask.fail()
@@ -465,217 +626,105 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
465
626
  `graph urls pre-versioning:
466
627
  ${Object.keys(finalGraph.urlInfos).join("\n")}`,
467
628
  )
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()
629
+ if (versioning) {
630
+ await applyUrlVersioning({
631
+ logger,
632
+ buildDirectoryUrl,
633
+ rawUrls,
634
+ buildUrls,
635
+ baseUrl,
636
+ postBuildEntryUrls,
637
+ sourcemaps,
638
+ runtimeCompat,
639
+ rawGraph,
640
+ finalGraph,
641
+ finalGraphKitchen,
642
+ lineBreakNormalization,
643
+ versioningMethod,
644
+ })
644
645
  }
645
-
646
- const buildFileContents = {}
647
- const buildInlineFileContents = {}
648
- const buildManifest = {}
649
- Object.keys(finalGraph.urlInfos).forEach((url) => {
650
- if (!url.startsWith("file:")) {
646
+ GRAPH.forEach(finalGraph, (urlInfo) => {
647
+ if (!urlInfo.url.startsWith("file:")) {
651
648
  return
652
649
  }
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`)
650
+ if (urlInfo.external) {
651
+ return
659
652
  }
660
- const buildRelativeUrl = urlToRelativeUrl(buildUrl, buildDirectoryUrl)
661
- if (buildUrlInfo.isInline) {
662
- buildInlineFileContents[buildRelativeUrl] = buildUrlInfo.content
663
- } else {
664
- buildFileContents[buildRelativeUrl] = buildUrlInfo.content
653
+ if (urlInfo.type === "html") {
654
+ const htmlAst = parseHtmlString(urlInfo.content, {
655
+ storeOriginalPositions: false,
656
+ })
657
+ urlInfo.content = stringifyHtmlAst(htmlAst, {
658
+ removeOriginalPositionAttributes: true,
659
+ })
665
660
  }
666
- if (useVersionedUrl) {
667
- const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
668
- buildUrlInfo.url,
669
- buildDirectoryUrl,
670
- )
671
- buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
661
+ const version = urlInfo.data.version
662
+ const useVersionedUrl = version && canUseVersionedUrl(urlInfo, finalGraph)
663
+ const buildUrl = useVersionedUrl ? urlInfo.data.versionedUrl : urlInfo.url
664
+ const buildUrlSpecifier = Object.keys(buildUrls).find(
665
+ (key) => buildUrls[key] === buildUrl,
666
+ )
667
+ urlInfo.data.buildUrl = buildUrl
668
+ urlInfo.data.buildUrlIsVersioned = useVersionedUrl
669
+ urlInfo.data.buildUrlSpecifier = buildUrlSpecifier
670
+ })
671
+ await resyncRessourceHints({
672
+ finalGraphKitchen,
673
+ finalGraph,
674
+ rawUrls,
675
+ buildUrls,
676
+ })
677
+ const cleanupActions = []
678
+ GRAPH.forEach(finalGraph, (urlInfo) => {
679
+ // nothing uses this url anymore
680
+ // - versioning update inline content
681
+ // - file converted for import assertion of js_classic conversion
682
+ if (
683
+ !urlInfo.data.isEntryPoint &&
684
+ urlInfo.type !== "sourcemap" &&
685
+ urlInfo.dependents.size === 0
686
+ ) {
687
+ cleanupActions.push(() => {
688
+ delete finalGraph.urlInfos[urlInfo.url]
689
+ })
672
690
  }
673
691
  })
692
+ cleanupActions.forEach((cleanupAction) => cleanupAction())
693
+ await injectServiceWorkerUrls({
694
+ finalGraphKitchen,
695
+ finalGraph,
696
+ lineBreakNormalization,
697
+ })
674
698
  logger.debug(
675
699
  `graph urls post-versioning:
676
700
  ${Object.keys(finalGraph.urlInfos).join("\n")}`,
677
701
  )
678
702
 
703
+ const buildManifest = {}
704
+ const buildFileContents = {}
705
+ const buildInlineContents = {}
706
+ GRAPH.forEach(finalGraph, (urlInfo) => {
707
+ if (urlInfo.external) {
708
+ return
709
+ }
710
+ if (urlInfo.url.startsWith("data:")) {
711
+ return
712
+ }
713
+ const buildRelativeUrl = urlToRelativeUrl(
714
+ urlInfo.data.buildUrl,
715
+ buildDirectoryUrl,
716
+ )
717
+ if (urlInfo.isInline) {
718
+ buildInlineContents[buildRelativeUrl] = urlInfo.content
719
+ } else {
720
+ buildFileContents[buildRelativeUrl] = urlInfo.content
721
+ }
722
+ const buildRelativeUrlWithoutVersioning = urlToRelativeUrl(
723
+ urlInfo.url,
724
+ buildDirectoryUrl,
725
+ )
726
+ buildManifest[buildRelativeUrlWithoutVersioning] = buildRelativeUrl
727
+ })
679
728
  if (writeOnFileSystem) {
680
729
  if (buildDirectoryClean) {
681
730
  await ensureEmptyDirectory(buildDirectoryUrl)
@@ -689,17 +738,253 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
689
738
  )
690
739
  }),
691
740
  )
741
+ if (versioning && assetManifest && Object.keys(buildManifest).length) {
742
+ await writeFile(
743
+ new URL(assetManifestFileRelativeUrl, buildDirectoryUrl),
744
+ JSON.stringify(buildManifest, null, " "),
745
+ )
746
+ }
692
747
  }
693
748
  logger.info(createUrlGraphSummary(finalGraph, { title: "build files" }))
694
749
  return {
695
750
  buildFileContents,
696
- buildInlineFileContents,
751
+ buildInlineContents,
697
752
  buildManifest,
698
753
  }
699
754
  }
700
755
 
701
- const injectVersionIntoBuildUrl = ({ buildUrl, version, versioning }) => {
702
- if (versioning === "search_param") {
756
+ const applyUrlVersioning = async ({
757
+ logger,
758
+ buildDirectoryUrl,
759
+ rawUrls,
760
+ buildUrls,
761
+ baseUrl,
762
+ postBuildEntryUrls,
763
+ sourcemaps,
764
+ runtimeCompat,
765
+ rawGraph,
766
+ finalGraph,
767
+ finalGraphKitchen,
768
+ lineBreakNormalization,
769
+ versioningMethod,
770
+ }) => {
771
+ const versioningTask = createTaskLog(logger, "inject version in urls")
772
+ try {
773
+ const urlsSorted = sortUrlGraphByDependencies(finalGraph)
774
+ urlsSorted.forEach((url) => {
775
+ if (url.startsWith("data:")) {
776
+ return
777
+ }
778
+ const urlInfo = finalGraph.getUrlInfo(url)
779
+ if (urlInfo.type === "sourcemap") {
780
+ return
781
+ }
782
+ // ignore:
783
+ // - inline files:
784
+ // they are already taken into account in the file where they appear
785
+ // - external files
786
+ // we don't know their content
787
+ // - unused files without reference
788
+ // File updated such as style.css -> style.css.js or file.js->file.es5.js
789
+ // Are used at some point just to be discarded later because they need to be converted
790
+ // There is no need to version them and we could not because the file have been ignored
791
+ // so their content is unknown
792
+ if (urlInfo.isInline) {
793
+ return
794
+ }
795
+ if (urlInfo.external) {
796
+ return
797
+ }
798
+ if (!urlInfo.data.isEntryPoint && urlInfo.dependents.size === 0) {
799
+ return
800
+ }
801
+
802
+ const urlContent =
803
+ urlInfo.type === "html"
804
+ ? stringifyHtmlAst(
805
+ parseHtmlString(urlInfo.content, {
806
+ storeOriginalPositions: false,
807
+ }),
808
+ { removeOriginalPositionAttributes: true },
809
+ )
810
+ : urlInfo.content
811
+ const versionGenerator = createVersionGenerator()
812
+ versionGenerator.augmentWithContent({
813
+ content: urlContent,
814
+ contentType: urlInfo.contentType,
815
+ lineBreakNormalization,
816
+ })
817
+ urlInfo.dependencies.forEach((dependencyUrl) => {
818
+ // this dependency is inline (data:) or remote (http://, https://)
819
+ if (!dependencyUrl.startsWith("file:")) {
820
+ return
821
+ }
822
+ const dependencyUrlInfo = finalGraph.getUrlInfo(dependencyUrl)
823
+ if (
824
+ // this content is part of the file, no need to take into account twice
825
+ dependencyUrlInfo.isInline ||
826
+ // this dependency content is not known
827
+ dependencyUrlInfo.external
828
+ ) {
829
+ return
830
+ }
831
+ if (dependencyUrlInfo.data.version) {
832
+ versionGenerator.augmentWithDependencyVersion(
833
+ dependencyUrlInfo.data.version,
834
+ )
835
+ } else {
836
+ // because all dependencies are know, if the dependency has no version
837
+ // it means there is a circular dependency between this file
838
+ // and it's dependency
839
+ // in that case we'll use the dependency content
840
+ versionGenerator.augmentWithContent({
841
+ content: dependencyUrlInfo.content,
842
+ contentType: dependencyUrlInfo.contentType,
843
+ lineBreakNormalization,
844
+ })
845
+ }
846
+ })
847
+ urlInfo.data.version = versionGenerator.generate()
848
+
849
+ urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
850
+ buildUrl: urlInfo.url,
851
+ version: urlInfo.data.version,
852
+ versioningMethod,
853
+ })
854
+ })
855
+ const versionMappings = {}
856
+ const usedVersionMappings = []
857
+ const versioningKitchen = createKitchen({
858
+ logger,
859
+ rootDirectoryUrl: buildDirectoryUrl,
860
+ urlGraph: finalGraph,
861
+ scenario: "build",
862
+ sourcemaps,
863
+ runtimeCompat,
864
+ plugins: [
865
+ jsenvPluginUrlReferences(),
866
+ jsenvPluginInline({
867
+ fetchInlineUrls: false,
868
+ analyzeConvertedScripts: true, // to be able to version their urls
869
+ allowEscapeForVersioning: true,
870
+ }),
871
+ {
872
+ name: "jsenv:versioning",
873
+ appliesDuring: { build: true },
874
+ resolveUrl: (reference) => {
875
+ if (reference.specifier[0] === "#") {
876
+ reference.external = true
877
+ }
878
+ const buildUrl = buildUrls[reference.specifier]
879
+ if (buildUrl) {
880
+ return buildUrl
881
+ }
882
+ const url = new URL(reference.specifier, reference.parentUrl).href
883
+ return url
884
+ },
885
+ formatUrl: (reference) => {
886
+ if (reference.isInline) {
887
+ return null
888
+ }
889
+ // specifier comes from "normalize" hook done a bit earlier in this file
890
+ // we want to get back their build url to access their infos
891
+ const referencedUrlInfo = finalGraph.getUrlInfo(reference.url)
892
+ if (!canUseVersionedUrl(referencedUrlInfo)) {
893
+ return reference.specifier
894
+ }
895
+ // data:* urls and so on
896
+ if (!referencedUrlInfo.url.startsWith("file:")) {
897
+ return null
898
+ }
899
+ const versionedUrl = referencedUrlInfo.data.versionedUrl
900
+ if (!versionedUrl) {
901
+ // happens for sourcemap
902
+ return `${baseUrl}${urlToRelativeUrl(
903
+ referencedUrlInfo.url,
904
+ buildDirectoryUrl,
905
+ )}`
906
+ }
907
+ const versionedSpecifier = `${baseUrl}${urlToRelativeUrl(
908
+ versionedUrl,
909
+ buildDirectoryUrl,
910
+ )}`
911
+ versionMappings[reference.specifier] = versionedSpecifier
912
+ buildUrls[versionedSpecifier] = versionedUrl
913
+
914
+ const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
915
+ if (parentUrlInfo.jsQuote) {
916
+ // the url is inline inside js quotes
917
+ usedVersionMappings.push(reference.specifier)
918
+ return () =>
919
+ `${parentUrlInfo.jsQuote}+__v__(${JSON.stringify(
920
+ reference.specifier,
921
+ )})+${parentUrlInfo.jsQuote}`
922
+ }
923
+ if (
924
+ reference.type === "js_url_specifier" ||
925
+ reference.subtype === "import_dynamic"
926
+ ) {
927
+ usedVersionMappings.push(reference.specifier)
928
+ return () => `__v__(${JSON.stringify(reference.specifier)})`
929
+ }
930
+ return versionedSpecifier
931
+ },
932
+ fetchUrlContent: (versionedUrlInfo) => {
933
+ if (!versionedUrlInfo.url.startsWith("file:")) {
934
+ return { external: true }
935
+ }
936
+ if (versionedUrlInfo.isInline) {
937
+ const rawUrlInfo = rawGraph.getUrlInfo(
938
+ rawUrls[versionedUrlInfo.url],
939
+ )
940
+ const finalUrlInfo = finalGraph.getUrlInfo(versionedUrlInfo.url)
941
+ return {
942
+ originalContent: rawUrlInfo
943
+ ? rawUrlInfo.originalContent
944
+ : undefined,
945
+ sourcemap: finalUrlInfo ? finalUrlInfo.sourcemap : undefined,
946
+ contentType: versionedUrlInfo.contentType,
947
+ content: versionedUrlInfo.content,
948
+ }
949
+ }
950
+ return versionedUrlInfo
951
+ },
952
+ },
953
+ ],
954
+ })
955
+ await loadUrlGraph({
956
+ urlGraph: finalGraph,
957
+ kitchen: versioningKitchen,
958
+ startLoading: (cookEntryFile) => {
959
+ postBuildEntryUrls.forEach((postBuildEntryUrl) => {
960
+ cookEntryFile({
961
+ trace: `entryPoint`,
962
+ type: "entry_point",
963
+ specifier: postBuildEntryUrl,
964
+ })
965
+ })
966
+ },
967
+ })
968
+ if (usedVersionMappings.length) {
969
+ const versionMappingsNeeded = {}
970
+ usedVersionMappings.forEach((specifier) => {
971
+ versionMappingsNeeded[specifier] = versionMappings[specifier]
972
+ })
973
+ await injectGlobalVersionMapping({
974
+ finalGraphKitchen,
975
+ finalGraph,
976
+ versionMappings: versionMappingsNeeded,
977
+ })
978
+ }
979
+ } catch (e) {
980
+ versioningTask.fail()
981
+ throw e
982
+ }
983
+ versioningTask.done()
984
+ }
985
+
986
+ const injectVersionIntoBuildUrl = ({ buildUrl, version, versioningMethod }) => {
987
+ if (versioningMethod === "search_param") {
703
988
  return injectQueryParams(buildUrl, {
704
989
  v: version,
705
990
  })
@@ -735,3 +1020,16 @@ const assertEntryPoints = ({ entryPoints }) => {
735
1020
  }
736
1021
  })
737
1022
  }
1023
+
1024
+ const canUseVersionedUrl = (urlInfo) => {
1025
+ if (urlInfo.data.isEntryPoint) {
1026
+ return false
1027
+ }
1028
+ if (urlInfo.type === "webmanifest") {
1029
+ return false
1030
+ }
1031
+ if (urlInfo.subtype === "service_worker") {
1032
+ return !urlInfo.data.isWebWorkerEntryPoint
1033
+ }
1034
+ return true
1035
+ }