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

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