@jsenv/core 27.0.0-alpha.8 → 27.0.0-alpha.80

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