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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/event_source_client.js +549 -0
  2. package/dist/event_source_client.js.map +188 -0
  3. package/dist/html_supervisor_installer.js +1145 -0
  4. package/dist/html_supervisor_installer.js.map +322 -0
  5. package/dist/html_supervisor_setup.js +92 -0
  6. package/dist/html_supervisor_setup.js.map +57 -0
  7. package/main.js +8 -1
  8. package/package.json +22 -21
  9. package/readme.md +4 -12
  10. package/src/build/build.js +731 -433
  11. package/src/build/build_urls_generator.js +55 -21
  12. package/src/build/graph_utils.js +31 -0
  13. package/src/build/{inject_version_mappings.js → inject_global_version_mappings.js} +33 -15
  14. package/src/build/inject_service_worker_urls.js +79 -0
  15. package/src/build/resync_ressource_hints.js +68 -0
  16. package/src/build/start_build_server.js +205 -0
  17. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +2 -2
  18. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -1
  19. package/src/dev/start_dev_server.js +58 -26
  20. package/src/execute/execute.js +30 -4
  21. package/src/execute/run.js +19 -56
  22. package/src/execute/runtimes/browsers/from_playwright.js +201 -147
  23. package/src/execute/runtimes/node/controllable_file.mjs +26 -10
  24. package/src/execute/runtimes/node/node_execution_performance.js +67 -0
  25. package/src/execute/runtimes/node/node_process.js +280 -39
  26. package/src/jsenv_root_directory_url.js +1 -0
  27. package/src/omega/{runtime_support/default_runtime_support.js → compat/default_runtime_compat.js} +3 -5
  28. package/src/omega/{runtime_support/features_compatibility.js → compat/features_compats.js} +66 -4
  29. package/src/omega/compat/runtime_compat.js +50 -0
  30. package/src/omega/errors.js +51 -58
  31. package/src/omega/fetched_content_compliance.js +24 -0
  32. package/src/omega/file_url_converter.js +8 -50
  33. package/src/omega/kitchen.js +414 -285
  34. package/src/omega/server/file_service.js +21 -22
  35. package/src/omega/url_graph/url_graph_load.js +14 -7
  36. package/src/omega/url_graph/url_graph_report.js +17 -15
  37. package/src/omega/url_graph/url_info_transformations.js +17 -5
  38. package/src/omega/url_graph.js +33 -10
  39. package/src/omega/web_workers.js +42 -0
  40. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/autoreload_preference.js +0 -0
  41. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/event_source_client.js +2 -2
  42. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/reload.js +0 -0
  43. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/url_helpers.js +0 -0
  44. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +46 -0
  45. package/src/{dev/plugins/autoreload/jsenv_plugin_autoreload.js → plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js} +31 -172
  46. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +27 -0
  47. package/src/plugins/autoreload/jsenv_plugin_hmr.js +35 -0
  48. package/src/plugins/bundling/css/bundle_css.js +21 -0
  49. package/src/plugins/bundling/js_classic_workers/bundle_js_classic_workers.js +13 -0
  50. package/src/{build/plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js → plugins/bundling/js_module/bundle_js_module.js} +120 -79
  51. package/src/plugins/bundling/jsenv_plugin_bundling.js +51 -0
  52. package/src/{omega/core_plugins → plugins}/commonjs_globals/jsenv_plugin_commonjs_globals.js +54 -41
  53. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +66 -0
  54. package/src/{omega/core_plugins → plugins}/filesystem_magic/jsenv_plugin_filesystem_magic.js +8 -5
  55. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_document.js +0 -0
  56. package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_notification.js +0 -0
  57. package/src/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_installer.js +3 -2
  58. package/src/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_setup.js +0 -0
  59. package/src/{omega/core_plugins → plugins}/html_supervisor/client/perf_browser.js +0 -0
  60. package/src/{omega/core_plugins → plugins}/html_supervisor/client/uneval_exception.js +0 -0
  61. package/src/{omega/core_plugins → plugins}/html_supervisor/jsenv_plugin_html_supervisor.js +52 -55
  62. package/src/plugins/http_urls/jsenv_plugin_http_urls.js +12 -0
  63. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/babel_plugin_metadata_import_meta_hot.js +4 -5
  64. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/client/import_meta_hot.js +3 -1
  65. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/html_hot_dependencies.js +2 -2
  66. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +101 -0
  67. package/src/{omega/core_plugins → plugins}/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +33 -8
  68. package/src/plugins/import_meta_url/client/import_meta_url_browser.js +52 -0
  69. package/src/plugins/import_meta_url/client/import_meta_url_commonjs.mjs +9 -0
  70. package/src/{omega/core_plugins → plugins}/importmap/jsenv_plugin_importmap.js +37 -31
  71. package/src/plugins/inject_globals/jsenv_plugin_inject_globals.js +67 -0
  72. package/src/{omega/core_plugins → plugins}/inline/client/inline_content.js +0 -0
  73. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_data_urls.js +18 -14
  74. package/src/{omega/core_plugins/inline/jsenv_plugin_js_and_css_inside_html.js → plugins/inline/jsenv_plugin_html_inline_content.js} +61 -40
  75. package/src/plugins/inline/jsenv_plugin_inline.js +36 -0
  76. package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_inline_query_param.js +6 -6
  77. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +296 -0
  78. package/src/plugins/leading_slash/jsenv_plugin_leading_slash.js +13 -0
  79. package/src/plugins/minification/css/minify_css.js +9 -0
  80. package/src/plugins/minification/html/minify_html.js +15 -0
  81. package/src/{build/plugins/minify_js/jsenv_plugin_minify_js.js → plugins/minification/js/minify_js.js} +6 -22
  82. package/src/plugins/minification/jsenv_plugin_minification.js +78 -0
  83. package/src/plugins/minification/json/minify_json.js +8 -0
  84. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +146 -0
  85. package/src/{omega → plugins}/plugin_controller.js +39 -11
  86. package/src/plugins/plugins.js +89 -0
  87. package/src/plugins/transpilation/as_js_classic/client/s.js +808 -0
  88. package/src/plugins/transpilation/as_js_classic/client/s.js.md +1 -0
  89. package/src/plugins/transpilation/as_js_classic/helpers/babel_plugin_transform_import_meta_url.js +47 -0
  90. package/src/plugins/transpilation/as_js_classic/helpers/systemjs_old.js +43 -0
  91. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +183 -0
  92. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_script_type_module_as_classic.js +256 -0
  93. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_workers_type_module_as_classic.js +93 -0
  94. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +0 -0
  95. package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/client/global_this.js +0 -0
  96. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +0 -0
  97. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_structure.js +4 -22
  98. package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugins_compatibility.js +0 -0
  99. package/src/{omega/core_plugins → plugins/transpilation}/babel/jsenv_plugin_babel.js +37 -21
  100. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +30 -6
  101. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/.eslintrc.cjs +0 -0
  102. package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/new_stylesheet.js +0 -0
  103. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +0 -0
  104. package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/client/regenerator_runtime.js +0 -0
  105. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +18 -0
  106. package/src/plugins/transpilation/fetch_original_url_info.js +30 -0
  107. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +200 -0
  108. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +70 -0
  109. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +44 -0
  110. package/src/plugins/url_references/css/css_urls.js +49 -0
  111. package/src/plugins/url_references/html/html_urls.js +273 -0
  112. package/src/plugins/url_references/js/js_urls.js +43 -0
  113. package/src/plugins/url_references/jsenv_plugin_imports_analysis.js +46 -0
  114. package/src/plugins/url_references/jsenv_plugin_url_analysis.js +18 -0
  115. package/src/plugins/url_references/jsenv_plugin_url_references.js +6 -0
  116. package/src/plugins/url_references/webmanifest/webmanifest_urls.js +17 -0
  117. package/src/{omega/core_plugins → plugins}/url_resolution/jsenv_plugin_url_resolution.js +12 -5
  118. package/src/{omega/core_plugins → plugins}/url_version/jsenv_plugin_url_version.js +12 -15
  119. package/src/test/execute_plan.js +28 -11
  120. package/src/test/execute_test_plan.js +23 -8
  121. package/src/test/logs_file_execution.js +8 -7
  122. package/src/build/plugins/minify_html/jsenv_plugin_minify_html.js +0 -30
  123. package/src/dev/plugins/autoreload/client/event_source_connection.js +0 -195
  124. package/src/dev/plugins/autoreload/sse_service.js +0 -149
  125. package/src/execute/runtimes/node/controlled_process.js +0 -316
  126. package/src/omega/core_plugins/file_urls/jsenv_plugin_file_urls.js +0 -67
  127. package/src/omega/core_plugins/import_assertions/helpers/babel_plugin_metadata_import_assertions.js +0 -98
  128. package/src/omega/core_plugins/import_assertions/helpers/json_module.js +0 -12
  129. package/src/omega/core_plugins/import_assertions/helpers/text_module.js +0 -6
  130. package/src/omega/core_plugins/import_assertions/jsenv_plugin_import_assertions.js +0 -211
  131. package/src/omega/core_plugins/inline/jsenv_plugin_inline.js +0 -13
  132. package/src/omega/core_plugins/inline/jsenv_plugin_new_inline_content.js +0 -210
  133. package/src/omega/core_plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -12
  134. package/src/omega/core_plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +0 -77
  135. package/src/omega/core_plugins.js +0 -39
  136. package/src/omega/runtime_support/runtime_support.js +0 -20
  137. package/src/omega/url_mentions/css_url_mentions.js +0 -63
  138. package/src/omega/url_mentions/html_url_mentions.js +0 -185
  139. package/src/omega/url_mentions/js_module_url_mentions.js +0 -91
  140. package/src/omega/url_mentions/parse_url_mentions.js +0 -37
  141. package/src/omega/url_mentions/worker_classic_url_mentions.js +0 -37
@@ -0,0 +1,296 @@
1
+ import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js"
2
+ import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
3
+ import { JS_QUOTES } from "@jsenv/utils/string/js_quotes.js"
4
+ import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
5
+ import { generateInlineContentUrl } from "@jsenv/utils/urls/inline_content_url_generator.js"
6
+
7
+ export const jsenvPluginJsInlineContent = ({ allowEscapeForVersioning }) => {
8
+ const parseAndTransformInlineContentCalls = async (urlInfo, context) => {
9
+ const inlineContentInfos = await parseJsInlineContentInfos({
10
+ js: urlInfo.content,
11
+ url: (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
12
+ isJsModule: urlInfo.type === "js_module",
13
+ })
14
+ if (inlineContentInfos.length === 0) {
15
+ return null
16
+ }
17
+ const magicSource = createMagicSource(urlInfo.content)
18
+ await inlineContentInfos.reduce(async (previous, inlineContentInfo) => {
19
+ await previous
20
+ const inlineUrl = generateInlineContentUrl({
21
+ url: urlInfo.url,
22
+ extension: CONTENT_TYPE.asFileExtension(inlineContentInfo.contentType),
23
+ line: inlineContentInfo.line,
24
+ column: inlineContentInfo.column,
25
+ lineEnd: inlineContentInfo.lineEnd,
26
+ columnEnd: inlineContentInfo.columnEnd,
27
+ })
28
+ let { quote } = inlineContentInfo
29
+ if (
30
+ quote === "`" &&
31
+ !context.isSupportedOnCurrentClients("template_literals")
32
+ ) {
33
+ // if quote is "`" and template literals are not supported
34
+ // we'll use a regular string (single or double quote)
35
+ // when rendering the string
36
+ quote = JS_QUOTES.pickBest(inlineContentInfo.content)
37
+ }
38
+ const [inlineReference, inlineUrlInfo] =
39
+ context.referenceUtils.foundInline({
40
+ type: "js_inline_content",
41
+ subtype: inlineContentInfo.type, // "new_blob_first_arg", "new_inline_content_first_arg", "json_parse_first_arg"
42
+ isOriginalPosition: urlInfo.content === urlInfo.originalContent,
43
+ line: inlineContentInfo.line,
44
+ column: inlineContentInfo.column,
45
+ specifier: inlineUrl,
46
+ contentType: inlineContentInfo.contentType,
47
+ content: inlineContentInfo.content,
48
+ })
49
+ inlineUrlInfo.jsQuote = quote
50
+ inlineReference.escape = (value) =>
51
+ JS_QUOTES.escapeSpecialChars(value.slice(1, -1), { quote })
52
+ await context.cook({
53
+ reference: inlineReference,
54
+ urlInfo: inlineUrlInfo,
55
+ })
56
+ magicSource.replace({
57
+ start: inlineContentInfo.start,
58
+ end: inlineContentInfo.end,
59
+ replacement: JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
60
+ quote,
61
+ allowEscapeForVersioning,
62
+ }),
63
+ })
64
+ }, Promise.resolve())
65
+ return magicSource.toContentAndSourcemap()
66
+ }
67
+
68
+ return {
69
+ name: "jsenv:js_inline_content",
70
+ appliesDuring: "*",
71
+ transformUrlContent: {
72
+ js_classic: parseAndTransformInlineContentCalls,
73
+ js_module: parseAndTransformInlineContentCalls,
74
+ },
75
+ }
76
+ }
77
+
78
+ const parseJsInlineContentInfos = async ({ js, url, isJsModule }) => {
79
+ if (
80
+ !js.includes("InlineContent") &&
81
+ !js.includes("new Blob(") &&
82
+ !js.includes("JSON.parse(")
83
+ ) {
84
+ return []
85
+ }
86
+ const { metadata } = await applyBabelPlugins({
87
+ babelPlugins: [babelPluginMetadataInlineContents],
88
+ urlInfo: {
89
+ url,
90
+ type: isJsModule ? "js_module" : "js_classic",
91
+ content: js,
92
+ },
93
+ })
94
+ return metadata.inlineContentInfos
95
+ }
96
+
97
+ const babelPluginMetadataInlineContents = () => {
98
+ return {
99
+ name: "metadata-inline-contents",
100
+ visitor: {
101
+ Program: (programPath, state) => {
102
+ const inlineContentInfos = []
103
+ const onInlineContentInfo = (inlineContentInfo) => {
104
+ inlineContentInfos.push(inlineContentInfo)
105
+ }
106
+ programPath.traverse({
107
+ NewExpression: (path) => {
108
+ if (isNewInlineContentCall(path)) {
109
+ analyzeNewInlineContentCall(path.node, {
110
+ onInlineContentInfo,
111
+ })
112
+ return
113
+ }
114
+ if (isNewBlobCall(path.node)) {
115
+ analyzeNewBlobCall(path.node, {
116
+ onInlineContentInfo,
117
+ })
118
+ return
119
+ }
120
+ },
121
+ CallExpression: (path) => {
122
+ const node = path.node
123
+ if (isJSONParseCall(node)) {
124
+ analyzeJsonParseCall(node, {
125
+ onInlineContentInfo,
126
+ })
127
+ }
128
+ },
129
+ })
130
+ state.file.metadata.inlineContentInfos = inlineContentInfos
131
+ },
132
+ },
133
+ }
134
+ }
135
+
136
+ const isNewInlineContentCall = (path) => {
137
+ const node = path.node
138
+ if (node.callee.type === "Identifier") {
139
+ // terser rename import to use a shorter name
140
+ const name = getOriginalName(path, node.callee.name)
141
+ return name === "InlineContent"
142
+ }
143
+ if (node.callee.id && node.callee.id.type === "Identifier") {
144
+ const name = getOriginalName(path, node.callee.id.name)
145
+ return name === "InlineContent"
146
+ }
147
+ return false
148
+ }
149
+ const analyzeNewInlineContentCall = (node, { onInlineContentInfo }) => {
150
+ analyzeArguments({
151
+ node,
152
+ onInlineContentInfo,
153
+ nodeHoldingContent: node.arguments[0],
154
+ type: "new_inline_content_first_arg",
155
+ })
156
+ }
157
+
158
+ const isNewBlobCall = (node) => {
159
+ return node.callee.type === "Identifier" && node.callee.name === "Blob"
160
+ }
161
+ const analyzeNewBlobCall = (node, { onInlineContentInfo }) => {
162
+ const firstArg = node.arguments[0]
163
+ if (firstArg.type !== "ArrayExpression") {
164
+ return
165
+ }
166
+ if (firstArg.elements.length !== 1) {
167
+ return
168
+ }
169
+ analyzeArguments({
170
+ node,
171
+ onInlineContentInfo,
172
+ nodeHoldingContent: firstArg.elements[0],
173
+ type: "new_blob_first_arg",
174
+ })
175
+ }
176
+
177
+ const analyzeArguments = ({
178
+ node,
179
+ onInlineContentInfo,
180
+ nodeHoldingContent,
181
+ type,
182
+ }) => {
183
+ if (node.arguments.length !== 2) {
184
+ return
185
+ }
186
+ const [, secondArg] = node.arguments
187
+ const typePropertyNode = getTypePropertyNode(secondArg)
188
+ if (!typePropertyNode) {
189
+ return
190
+ }
191
+ const typePropertyValueNode = typePropertyNode.value
192
+ if (typePropertyValueNode.type !== "StringLiteral") {
193
+ return
194
+ }
195
+ const contentType = typePropertyValueNode.value
196
+ const contentDetails = extractContentDetails(nodeHoldingContent)
197
+ if (contentDetails) {
198
+ onInlineContentInfo({
199
+ node: nodeHoldingContent,
200
+ ...getNodePosition(nodeHoldingContent),
201
+ type,
202
+ contentType,
203
+ ...contentDetails,
204
+ })
205
+ }
206
+ }
207
+ const extractContentDetails = (node) => {
208
+ if (node.type === "StringLiteral") {
209
+ return {
210
+ nodeType: "StringLiteral",
211
+ quote: node.extra.raw[0],
212
+ content: node.value,
213
+ }
214
+ }
215
+ if (node.type === "TemplateLiteral") {
216
+ const quasis = node.quasis
217
+ if (quasis.length !== 1) {
218
+ return null
219
+ }
220
+ const templateElementNode = quasis[0]
221
+ return {
222
+ nodeType: "TemplateLiteral",
223
+ quote: "`",
224
+ content: templateElementNode.value.cooked,
225
+ }
226
+ }
227
+ return null
228
+ }
229
+
230
+ const isJSONParseCall = (node) => {
231
+ const callee = node.callee
232
+ return (
233
+ callee.type === "MemberExpression" &&
234
+ callee.object.type === "Identifier" &&
235
+ callee.object.name === "JSON" &&
236
+ callee.property.type === "Identifier" &&
237
+ callee.property.name === "parse"
238
+ )
239
+ }
240
+ const analyzeJsonParseCall = (node, { onInlineContentInfo }) => {
241
+ const firstArgNode = node.arguments[0]
242
+ const contentDetails = extractContentDetails(firstArgNode)
243
+ if (contentDetails) {
244
+ onInlineContentInfo({
245
+ node: firstArgNode,
246
+ ...getNodePosition(firstArgNode),
247
+ type: "json_parse_first_arg",
248
+ contentType: "application/json",
249
+ ...contentDetails,
250
+ })
251
+ }
252
+ }
253
+
254
+ const getNodePosition = (node) => {
255
+ return {
256
+ start: node.start,
257
+ end: node.end,
258
+ line: node.loc.start.line,
259
+ column: node.loc.start.column,
260
+ lineEnd: node.loc.end.line,
261
+ columnEnd: node.loc.end.column,
262
+ }
263
+ }
264
+ const getOriginalName = (path, name) => {
265
+ const binding = path.scope.getBinding(name)
266
+ if (!binding) {
267
+ return name
268
+ }
269
+ if (binding.path.type === "ImportSpecifier") {
270
+ const importedName = binding.path.node.imported.name
271
+ if (name === importedName) {
272
+ return name
273
+ }
274
+ return getOriginalName(path, importedName)
275
+ }
276
+ if (binding.path.type === "VariableDeclarator") {
277
+ if (binding.path.node.init.type === "Identifier") {
278
+ const previousName = binding.path.node.init.name
279
+ return getOriginalName(path, previousName)
280
+ }
281
+ }
282
+ return name
283
+ }
284
+ const getTypePropertyNode = (node) => {
285
+ if (node.type !== "ObjectExpression") {
286
+ return null
287
+ }
288
+ const { properties } = node
289
+ return properties.find((property) => {
290
+ return (
291
+ property.type === "ObjectProperty" &&
292
+ property.key.type === "Identifier" &&
293
+ property.key.name === "type"
294
+ )
295
+ })
296
+ }
@@ -0,0 +1,13 @@
1
+ export const jsenvPluginLeadingSlash = () => {
2
+ return {
3
+ name: "jsenv:leading_slash",
4
+ appliesDuring: "*",
5
+ resolveUrl: (reference, context) => {
6
+ if (reference.specifier[0] !== "/") {
7
+ return null
8
+ }
9
+ return new URL(reference.specifier.slice(1), context.rootDirectoryUrl)
10
+ .href
11
+ },
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { minifyWithParcel } from "@jsenv/utils/css_ast/parcel_css.js"
2
+
3
+ export const minifyCss = ({ cssUrlInfo, context }) => {
4
+ const { code, map } = minifyWithParcel(cssUrlInfo, context)
5
+ return {
6
+ content: String(code),
7
+ sourcemap: map,
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ import { createRequire } from "node:module"
2
+
3
+ const require = createRequire(import.meta.url)
4
+
5
+ // https://github.com/kangax/html-minifier#options-quick-reference
6
+ export const minifyHtml = ({ htmlUrlInfo, options } = {}) => {
7
+ const { collapseWhitespace = true, removeComments = true } = options
8
+
9
+ const { minify } = require("html-minifier")
10
+ const htmlMinified = minify(htmlUrlInfo.content, {
11
+ collapseWhitespace,
12
+ removeComments,
13
+ })
14
+ return htmlMinified
15
+ }
@@ -1,28 +1,11 @@
1
1
  // https://github.com/terser-js/terser#minify-options
2
2
 
3
- export const jsenvPluginMinifyJs = () => {
4
- const applyTerser = async (urlInfo) => {
5
- return minifyJs({
6
- url: urlInfo.url,
7
- content: urlInfo.content,
8
- sourcemap: urlInfo.sourcemap,
9
- isJsModule: urlInfo.type === "js_module",
10
- })
11
- }
12
-
13
- return {
14
- name: "jsenv:minify_js",
15
- appliesDuring: {
16
- build: true,
17
- },
18
- optimize: {
19
- js_classic: applyTerser,
20
- js_module: applyTerser,
21
- },
22
- }
23
- }
3
+ export const minifyJs = async ({ jsUrlInfo, options }) => {
4
+ const url = jsUrlInfo.url
5
+ const content = jsUrlInfo.content
6
+ const sourcemap = jsUrlInfo.sourcemap
7
+ const isJsModule = jsUrlInfo.type === "js_module"
24
8
 
25
- const minifyJs = async ({ url, content, sourcemap, isJsModule }) => {
26
9
  const { minify } = await import("terser")
27
10
  const terserResult = await minify(
28
11
  {
@@ -38,6 +21,7 @@ const minifyJs = async ({ url, content, sourcemap, isJsModule }) => {
38
21
  // We need to preserve "new InlineContent()" calls to be able to recognize them
39
22
  // after minification in order to version urls inside inline content text
40
23
  keep_fnames: /InlineContent/,
24
+ ...options,
41
25
  },
42
26
  )
43
27
  return {
@@ -0,0 +1,78 @@
1
+ import { minifyHtml } from "./html/minify_html.js"
2
+ import { minifyCss } from "./css/minify_css.js"
3
+ import { minifyJs } from "./js/minify_js.js"
4
+ import { minifyJson } from "./json/minify_json.js"
5
+
6
+ export const jsenvPluginMinification = (minification) => {
7
+ if (typeof minification === "boolean") {
8
+ minification = {
9
+ html: minification,
10
+ css: minification,
11
+ js_classic: minification,
12
+ js_module: minification,
13
+ json: minification,
14
+ svg: minification,
15
+ }
16
+ } else if (typeof minification !== "object") {
17
+ throw new Error(
18
+ `minification must be a boolean or an object, got ${minification}`,
19
+ )
20
+ }
21
+ Object.keys(minification).forEach((key) => {
22
+ if (minification[key] === true) minification[key] = {}
23
+ })
24
+ const htmlOptimizer = minification.html
25
+ ? (urlInfo, context) =>
26
+ minifyHtml({
27
+ htmlUrlInfo: urlInfo,
28
+ context,
29
+ options: minification.html,
30
+ })
31
+ : null
32
+ const jsonOptimizer = minification.json
33
+ ? (urlInfo, context) =>
34
+ minifyJson({
35
+ jsonUrlInfo: urlInfo,
36
+ context,
37
+ options: minification.json,
38
+ })
39
+ : null
40
+
41
+ return {
42
+ name: "jsenv:minification",
43
+ appliesDuring: {
44
+ build: true,
45
+ },
46
+ optimizeUrlContent: {
47
+ html: htmlOptimizer,
48
+ svg: htmlOptimizer,
49
+ css: minification.css
50
+ ? (urlInfo, context) =>
51
+ minifyCss({
52
+ cssUrlInfo: urlInfo,
53
+ context,
54
+ options: minification.css,
55
+ })
56
+ : null,
57
+ js_classic: minification.js_classic
58
+ ? (urlInfo, context) =>
59
+ minifyJs({
60
+ jsUrlInfo: urlInfo,
61
+ context,
62
+ options: minification.js_classic,
63
+ })
64
+ : null,
65
+ js_module: minification.js_module
66
+ ? (urlInfo, context) =>
67
+ minifyJs({
68
+ jsUrlInfo: urlInfo,
69
+ context,
70
+ options: minification.js_module,
71
+ })
72
+ : null,
73
+ json: jsonOptimizer,
74
+ importmap: jsonOptimizer,
75
+ webmanifest: jsonOptimizer,
76
+ },
77
+ }
78
+ }
@@ -0,0 +1,8 @@
1
+ export const minifyJson = ({ jsonUrlInfo }) => {
2
+ const { content } = jsonUrlInfo
3
+ if (content.startsWith("{\n")) {
4
+ const jsonWithoutWhitespaces = JSON.stringify(JSON.parse(content))
5
+ return jsonWithoutWhitespaces
6
+ }
7
+ return null
8
+ }
@@ -0,0 +1,146 @@
1
+ /*
2
+ * - should I restore eventual search params lost during node esm resolution
3
+ * - what about symlinks?
4
+ * It feels like I should apply symlink (when we don't want to preserve them)
5
+ * once a file:/// url is found, regardless
6
+ * if that comes from node resolution or anything else (not even magic resolution)
7
+ * it should likely be an other plugin happening after the others
8
+ */
9
+
10
+ import { registerFileLifecycle } from "@jsenv/filesystem"
11
+
12
+ import {
13
+ applyNodeEsmResolution,
14
+ defaultLookupPackageScope,
15
+ defaultReadPackageJson,
16
+ } from "@jsenv/node-esm-resolution"
17
+
18
+ export const jsenvPluginNodeEsmResolution = ({
19
+ rootDirectoryUrl,
20
+ // https://nodejs.org/api/esm.html#resolver-algorithm-specification
21
+ packageConditions = ["browser", "import"],
22
+ filesInvalidatingCache = ["package.json", "package-lock.json"],
23
+ }) => {
24
+ const packageScopesCache = new Map()
25
+ const lookupPackageScope = (url) => {
26
+ const fromCache = packageScopesCache.get(url)
27
+ if (fromCache) {
28
+ return fromCache
29
+ }
30
+ const packageScope = defaultLookupPackageScope(url)
31
+ packageScopesCache.set(url, packageScope)
32
+ return packageScope
33
+ }
34
+ const packageJsonsCache = new Map()
35
+ const readPackageJson = (url) => {
36
+ const fromCache = packageJsonsCache.get(url)
37
+ if (fromCache) {
38
+ return fromCache
39
+ }
40
+ const packageJson = defaultReadPackageJson(url)
41
+ packageJsonsCache.set(url, packageJson)
42
+ return packageJson
43
+ }
44
+
45
+ const unregisters = []
46
+ filesInvalidatingCache.forEach((file) => {
47
+ const unregister = registerFileLifecycle(new URL(file, rootDirectoryUrl), {
48
+ added: () => {
49
+ packageScopesCache.clear()
50
+ packageJsonsCache.clear()
51
+ },
52
+ updated: () => {
53
+ packageScopesCache.clear()
54
+ packageJsonsCache.clear()
55
+ },
56
+ removed: () => {
57
+ packageScopesCache.clear()
58
+ packageJsonsCache.clear()
59
+ },
60
+ keepProcessAlive: false,
61
+ })
62
+ unregisters.push(unregister)
63
+ })
64
+
65
+ return [
66
+ jsenvPluginNodeEsmResolver({
67
+ packageConditions,
68
+ lookupPackageScope,
69
+ readPackageJson,
70
+ }),
71
+ jsenvPluginNodeModulesVersionInUrls({
72
+ lookupPackageScope,
73
+ readPackageJson,
74
+ }),
75
+ ]
76
+ }
77
+
78
+ const jsenvPluginNodeEsmResolver = ({
79
+ packageConditions,
80
+ lookupPackageScope,
81
+ readPackageJson,
82
+ }) => {
83
+ return {
84
+ name: "jsenv:node_esm_resolve",
85
+ appliesDuring: "*",
86
+ resolveUrl: {
87
+ js_import_export: (reference) => {
88
+ const { parentUrl, specifier } = reference
89
+ const { url } = applyNodeEsmResolution({
90
+ conditions: packageConditions,
91
+ parentUrl,
92
+ specifier,
93
+ lookupPackageScope,
94
+ readPackageJson,
95
+ })
96
+ return url
97
+ },
98
+ },
99
+ fetchUrlContent: (urlInfo) => {
100
+ if (urlInfo.url.startsWith("file:///@ignore/")) {
101
+ return {
102
+ content: "export default {}",
103
+ }
104
+ }
105
+ return null
106
+ },
107
+ }
108
+ }
109
+
110
+ const jsenvPluginNodeModulesVersionInUrls = ({
111
+ lookupPackageScope,
112
+ readPackageJson,
113
+ }) => {
114
+ return {
115
+ name: "jsenv:node_modules_version_in_urls",
116
+ appliesDuring: {
117
+ dev: true,
118
+ test: true,
119
+ },
120
+ transformUrlSearchParams: (reference, context) => {
121
+ if (!reference.url.startsWith("file:")) {
122
+ return null
123
+ }
124
+ // without this check a file inside a project without package.json
125
+ // could be considered as a node module if there is a ancestor package.json
126
+ // but we want to version only node modules
127
+ if (!reference.url.includes("/node_modules/")) {
128
+ return null
129
+ }
130
+ if (reference.searchParams.has("v")) {
131
+ return null
132
+ }
133
+ const packageUrl = lookupPackageScope(reference.url)
134
+ if (!packageUrl) {
135
+ return null
136
+ }
137
+ if (packageUrl === context.rootDirectoryUrl) {
138
+ return null
139
+ }
140
+ const packageVersion = readPackageJson(packageUrl).version
141
+ return {
142
+ v: packageVersion,
143
+ }
144
+ },
145
+ }
146
+ }