@jsenv/core 27.0.0-alpha.6 → 27.0.0-alpha.60

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