@jsenv/core 25.2.1 → 25.4.1

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 (112) hide show
  1. package/dist/browser_runtime/asset-manifest.json +2 -2
  2. package/dist/browser_runtime/{browser_runtime_91c5a3b8.js → browser_runtime_0e3396a1.js} +15 -16
  3. package/dist/browser_runtime/{browser_runtime_91c5a3b8.js.map → browser_runtime_0e3396a1.js.map} +13 -13
  4. package/dist/build_manifest.js +5 -5
  5. package/dist/compile_proxy/asset-manifest.json +2 -2
  6. package/dist/compile_proxy/compile_proxy.html__inline__20_f4285042.js.map +343 -0
  7. package/dist/compile_proxy/{compile_proxy_8dfaee51.html → compile_proxy_ab528227.html} +358 -397
  8. package/dist/redirector/asset-manifest.json +2 -2
  9. package/dist/redirector/redirector.html__inline__12_404b8295.js.map +349 -0
  10. package/dist/redirector/{redirector_3e9a97b9.html → redirector_6df2620a.html} +369 -407
  11. package/dist/toolbar/asset-manifest.json +2 -2
  12. package/dist/toolbar/toolbar.main_279b3a68.js.map +764 -0
  13. package/dist/toolbar/{toolbar_361afb84.html → toolbar_0a91ca3b.html} +1509 -1583
  14. package/dist/toolbar_injector/asset-manifest.json +2 -2
  15. package/dist/toolbar_injector/{toolbar_injector_fac1e995.js → toolbar_injector_34f6ad8e.js} +18 -15
  16. package/dist/toolbar_injector/{toolbar_injector_fac1e995.js.map → toolbar_injector_34f6ad8e.js.map} +15 -15
  17. package/package.json +16 -12
  18. package/readme.md +54 -54
  19. package/src/buildProject.js +31 -26
  20. package/src/dev_server.js +111 -92
  21. package/src/execute.js +3 -8
  22. package/src/internal/browser_launcher/{browser_runtime_report.js → browser_runtime_profile.js} +21 -15
  23. package/src/internal/browser_launcher/executeHtmlFile.js +22 -14
  24. package/src/internal/browser_launcher/from_playwright.js +6 -4
  25. package/src/internal/browser_runtime/browser_runtime.js +12 -14
  26. package/src/internal/browser_runtime/createBrowserRuntime.js +7 -6
  27. package/src/internal/browser_utils/fetchAndEvalUsingFetch.js +1 -1
  28. package/src/internal/browser_utils/fetchJson.js +1 -1
  29. package/src/internal/browser_utils/{fetch-browser.js → fetch_browser.js} +0 -2
  30. package/src/internal/building/buildUsingRollup.js +41 -57
  31. package/src/internal/building/html/parseHtmlRessource.js +2 -1
  32. package/src/internal/building/rollup_plugin_jsenv.js +28 -8
  33. package/src/internal/compiling/babel_parse_error.js +9 -0
  34. package/src/internal/{babel_plugin_transform_import_meta.js → compiling/babel_plugin_transform_import_meta.js} +58 -9
  35. package/src/internal/compiling/compileFile.js +2 -2
  36. package/src/internal/compiling/compileHtml.js +1 -1
  37. package/src/internal/compiling/createCompiledFileService.js +25 -74
  38. package/src/internal/compiling/js-compilation-service/transformJs.js +153 -23
  39. package/src/internal/compiling/jsenvCompilerForHtml.js +29 -32
  40. package/src/internal/compiling/jsenvCompilerForImportmap.js +2 -2
  41. package/src/internal/compiling/jsenvCompilerForJavaScript.js +2 -4
  42. package/src/internal/compiling/jsenv_directory/comparison_utils.js +24 -0
  43. package/src/internal/compiling/{compile-directory/compile-asset.js → jsenv_directory/compile_asset.js} +0 -0
  44. package/src/internal/compiling/jsenv_directory/compile_context.js +68 -0
  45. package/src/internal/compiling/jsenv_directory/compile_profile.js +218 -0
  46. package/src/internal/compiling/{compile-directory/createLockRegistry.js → jsenv_directory/file_lock_registry.js} +0 -0
  47. package/src/internal/compiling/{compile-directory/createLockRegistry.test.js → jsenv_directory/file_lock_registry.test.js} +2 -1
  48. package/src/internal/compiling/{compile-directory → jsenv_directory}/fs-optimized-for-cache.js +0 -0
  49. package/src/internal/compiling/{compile-directory → jsenv_directory}/getOrGenerateCompiledFile.js +2 -2
  50. package/src/internal/compiling/jsenv_directory/jsenv_directory.js +174 -0
  51. package/src/internal/compiling/{compile-directory → jsenv_directory}/updateMeta.js +1 -1
  52. package/src/internal/compiling/{compile-directory → jsenv_directory}/validateCache.js +0 -0
  53. package/src/internal/compiling/sse_service/sse_service.js +369 -0
  54. package/src/internal/compiling/startCompileServer.js +157 -804
  55. package/src/internal/compiling/transformResultToCompilationResult.js +2 -2
  56. package/src/internal/dev_server/exploring/exploring.js +10 -8
  57. package/src/internal/dev_server/toolbar/compilation/toolbar.compilation.js +92 -78
  58. package/src/internal/dev_server/toolbar/settings/toolbar.settings.js +13 -0
  59. package/src/internal/dev_server/toolbar/toolbar.html +46 -16
  60. package/src/internal/dev_server/toolbar/toolbar.injector.js +17 -15
  61. package/src/internal/dev_server/toolbar/toolbar.main.js +12 -16
  62. package/src/internal/executing/executeConcurrently.js +1 -1
  63. package/src/internal/executing/executePlan.js +2 -3
  64. package/src/internal/{generateGroupMap → features}/babel_plugins_compatibility.js +8 -8
  65. package/src/internal/features/browser_feature_detection/browser_feature_detect_dynamic_import.js +20 -0
  66. package/src/internal/features/browser_feature_detection/browser_feature_detect_import_assertions_css.js +23 -0
  67. package/src/internal/features/browser_feature_detection/browser_feature_detect_import_assertions_json.js +25 -0
  68. package/src/internal/features/browser_feature_detection/browser_feature_detect_importmap.js +37 -0
  69. package/src/internal/features/browser_feature_detection/browser_feature_detect_new_stylesheet.js +9 -0
  70. package/src/internal/features/browser_feature_detection/browser_feature_detect_top_level_await.js +14 -0
  71. package/src/internal/features/browser_feature_detection/browser_feature_detection.js +89 -0
  72. package/src/internal/{browser_feature_detection → features/browser_feature_detection}/compile_proxy.html +1 -1
  73. package/src/internal/features/browser_feature_detection/execute_with_script_module.js +24 -0
  74. package/src/internal/features/features_compat_from_runtime.js +38 -0
  75. package/src/internal/features/features_compat_from_runtime_support.js +31 -0
  76. package/src/internal/{generateGroupMap → features}/features_compatibility.js +3 -3
  77. package/src/internal/{node_feature_detection → features/node_feature_detection}/feature_detect_dynamic_import.mjs +0 -0
  78. package/src/internal/{node_feature_detection → features/node_feature_detection}/feature_detect_top_level_await.mjs +0 -0
  79. package/src/internal/{node_feature_detection/nodeSupportsDynamicImport.js → features/node_feature_detection/node_feature_detect_dynamic_import.js} +0 -0
  80. package/src/internal/{node_feature_detection/nodeSupportsTopLevelAwait.js → features/node_feature_detection/node_feature_detect_top_level_await.js} +0 -0
  81. package/src/internal/features/node_feature_detection/node_feature_detection.js +66 -0
  82. package/src/internal/jsenv_remote_directory.js +1 -1
  83. package/src/internal/node_launcher/createControllableNodeProcess.js +4 -3
  84. package/src/internal/node_launcher/node_runtime_report.js +15 -9
  85. package/src/internal/node_runtime/fetchSource.js +2 -4
  86. package/src/internal/node_runtime/node_execution_systemjs.js +2 -5
  87. package/src/internal/redirector/redirector.html +40 -0
  88. package/src/internal/{generateGroupMap/jsenvRuntimeSupport.js → runtime_support/jsenv_runtime_support.js} +0 -0
  89. package/src/internal/{generateGroupMap → runtime_support}/runtime_support.js +0 -0
  90. package/src/internal/url_conversion.js +12 -17
  91. package/src/launchNode.js +29 -41
  92. package/dist/compile_proxy/compile_proxy.html__inline__20_809f35f7.js.map +0 -392
  93. package/dist/redirector/redirector.html__inline__15_e391410e.js.map +0 -397
  94. package/dist/toolbar/toolbar.main_6c1b3d82.js.map +0 -802
  95. package/src/internal/CONSTANTS.js +0 -11
  96. package/src/internal/browser_feature_detection/browser_feature_detection.js +0 -274
  97. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +0 -242
  98. package/src/internal/dev_server/redirector/redirector.html +0 -48
  99. package/src/internal/generateGroupMap/generateGroupMap.js +0 -65
  100. package/src/internal/generateGroupMap/one_runtime_compat.js +0 -38
  101. package/src/internal/generateGroupMap/runtime_compat.js +0 -34
  102. package/src/internal/generateGroupMap/runtime_compat_composition.js +0 -76
  103. package/src/internal/generateGroupMap/shake_babel_plugin_map.js +0 -21
  104. package/src/internal/integrity/integrity_algorithms.js +0 -26
  105. package/src/internal/integrity/integrity_parsing.js +0 -50
  106. package/src/internal/integrity/integrity_update.js +0 -23
  107. package/src/internal/integrity/integrity_validation.js +0 -49
  108. package/src/internal/node_feature_detection/node_feature_detection.js +0 -117
  109. package/src/internal/node_runtime/detectNode.js +0 -3
  110. package/src/internal/runtime/computeCompileIdFromGroupId.js +0 -30
  111. package/src/internal/runtime/resolveGroup.js +0 -13
  112. package/src/internal/runtime/resolveRuntimeGroup.js +0 -11
@@ -0,0 +1,369 @@
1
+ import { createSSERoom } from "@jsenv/server"
2
+ import {
3
+ resolveUrl,
4
+ registerDirectoryLifecycle,
5
+ urlToExtension,
6
+ urlIsInsideOf,
7
+ urlToRelativeUrl,
8
+ } from "@jsenv/filesystem"
9
+ import { createLogger } from "@jsenv/logger"
10
+ import { createCallbackList } from "@jsenv/abort"
11
+
12
+ import { urlIsCompilationAsset } from "@jsenv/core/src/internal/compiling/jsenv_directory/compile_asset.js"
13
+
14
+ export const createSSEService = ({
15
+ projectDirectoryUrl,
16
+ jsenvDirectoryRelativeUrl,
17
+ livereloadSSE,
18
+ projectFileRequestedSignal,
19
+
20
+ serverStopCallbackList,
21
+ livereloadLogLevel,
22
+ livereloadWatchConfig,
23
+ }) => {
24
+ let handleSSEClientRequest
25
+ if (livereloadSSE) {
26
+ handleSSEClientRequest = createSSEServiceWithLivereload({
27
+ projectDirectoryUrl,
28
+ jsenvDirectoryRelativeUrl,
29
+ projectFileRequestedSignal,
30
+
31
+ serverStopCallbackList,
32
+ livereloadLogLevel,
33
+ livereloadWatchConfig,
34
+ })
35
+ } else {
36
+ const roomWhenLivereloadIsDisabled = createSSERoom()
37
+ roomWhenLivereloadIsDisabled.open()
38
+ handleSSEClientRequest = (request) => {
39
+ return roomWhenLivereloadIsDisabled.join(request)
40
+ }
41
+ }
42
+ return (request) => {
43
+ const { accept } = request.headers
44
+ if (!accept || !accept.includes("text/event-stream")) {
45
+ return null
46
+ }
47
+ return handleSSEClientRequest(request)
48
+ }
49
+ }
50
+
51
+ const createSSEServiceWithLivereload = ({
52
+ projectDirectoryUrl,
53
+ jsenvDirectoryRelativeUrl,
54
+
55
+ projectFileRequestedSignal,
56
+ serverStopCallbackList,
57
+ livereloadLogLevel,
58
+ livereloadWatchConfig,
59
+ }) => {
60
+ const livereloadLogger = createLogger({ logLevel: livereloadLogLevel })
61
+ const trackerMap = new Map()
62
+ const projectFileRequested = createCallbackList()
63
+ const projectFileModified = createCallbackList()
64
+ const projectFileRemoved = createCallbackList()
65
+ const projectFileAdded = createCallbackList()
66
+
67
+ /**
68
+ * projectFileRequestedCallback
69
+ * This function will be called by the compile server every time a file inside projectDirectory
70
+ * is requested so that we can build up the dependency tree of any file
71
+ *
72
+ */
73
+ projectFileRequestedSignal.onrequested = (relativeUrl, request) => {
74
+ if (relativeUrl[0] === "/") {
75
+ relativeUrl = relativeUrl.slice(1)
76
+ }
77
+ const url = `${projectDirectoryUrl}${relativeUrl}`
78
+ if (
79
+ // Do not watch sourcemap files
80
+ urlToExtension(url) === ".map" ||
81
+ // Do not watch compilation asset, watching source file is enough
82
+ urlIsCompilationAsset(url)
83
+ ) {
84
+ return
85
+ }
86
+ projectFileRequested.notify({ relativeUrl, request })
87
+ }
88
+ const watchDescription = {
89
+ ...livereloadWatchConfig,
90
+ [jsenvDirectoryRelativeUrl]: false,
91
+ }
92
+ // wait 100ms to actually start watching
93
+ // otherwise server starting is delayed by the filesystem scan done in
94
+ // registerDirectoryLifecycle
95
+ const timeout = setTimeout(() => {
96
+ const unregisterDirectoryLifecyle = registerDirectoryLifecycle(
97
+ projectDirectoryUrl,
98
+ {
99
+ watchDescription,
100
+ updated: ({ relativeUrl }) => {
101
+ projectFileModified.notify(relativeUrl)
102
+ },
103
+ removed: ({ relativeUrl }) => {
104
+ projectFileRemoved.notify(relativeUrl)
105
+ },
106
+ added: ({ relativeUrl }) => {
107
+ projectFileAdded.notify(relativeUrl)
108
+ },
109
+ keepProcessAlive: false,
110
+ recursive: true,
111
+ },
112
+ )
113
+ serverStopCallbackList.add(unregisterDirectoryLifecyle)
114
+ }, 100)
115
+ serverStopCallbackList.add(() => {
116
+ clearTimeout(timeout)
117
+ })
118
+
119
+ const startTrackingRoot = (rootFile) => {
120
+ stopTrackingRoot(rootFile)
121
+ const set = new Set()
122
+ set.add(rootFile)
123
+ const depInfo = {
124
+ set,
125
+ cleanup: [],
126
+ }
127
+ trackerMap.set(rootFile, depInfo)
128
+ return depInfo
129
+ }
130
+ const addStopTrackingCalback = (rootFile, callback) => {
131
+ trackerMap.get(rootFile).cleanup.push(callback)
132
+ }
133
+ const stopTrackingRoot = (rootFile) => {
134
+ const depInfo = trackerMap.get(rootFile)
135
+ if (depInfo) {
136
+ depInfo.cleanup.forEach((cb) => {
137
+ cb()
138
+ })
139
+ trackerMap.delete(rootFile)
140
+ }
141
+ }
142
+ const isDependencyOf = (file, rootFile) => {
143
+ const depInfo = trackerMap.get(rootFile)
144
+ return depInfo && depInfo.set.has(file)
145
+ }
146
+ const markAsDependencyOf = (file, rootFile) => {
147
+ trackerMap.get(rootFile).set.add(file)
148
+ }
149
+
150
+ // each time a file is requested for the first time its dependencySet is computed
151
+ projectFileRequested.add((requestInfo) => {
152
+ const rootRelativeUrl = requestInfo.relativeUrl
153
+ // for now no use case of livereloading on node.js
154
+ // and for browsers only html file can be main files
155
+ // this avoid collecting dependencies of non html files that will never be used
156
+ if (!rootRelativeUrl.endsWith(".html")) {
157
+ return
158
+ }
159
+
160
+ livereloadLogger.debug(`${rootRelativeUrl} requested -> start tracking it`)
161
+ // when a file is requested, always rebuild its dependency in case it has changed
162
+ // since the last time it was requested
163
+ startTrackingRoot(rootRelativeUrl)
164
+
165
+ const removeDependencyRequestedCallback = projectFileRequested.add(
166
+ ({ relativeUrl, request }) => {
167
+ if (isDependencyOf(relativeUrl, rootRelativeUrl)) {
168
+ return
169
+ }
170
+ const dependencyReport = reportDependency(
171
+ relativeUrl,
172
+ rootRelativeUrl,
173
+ request,
174
+ )
175
+ if (dependencyReport.dependency === false) {
176
+ livereloadLogger.debug(
177
+ `${relativeUrl} not a dependency of ${rootRelativeUrl} because ${dependencyReport.reason}`,
178
+ )
179
+ return
180
+ }
181
+ livereloadLogger.debug(
182
+ `${relativeUrl} is a dependency of ${rootRelativeUrl} because ${dependencyReport.reason}`,
183
+ )
184
+ markAsDependencyOf(relativeUrl, rootRelativeUrl)
185
+ },
186
+ )
187
+ addStopTrackingCalback(rootRelativeUrl, removeDependencyRequestedCallback)
188
+ const removeRootRemovedCallback = projectFileRemoved.add((relativeUrl) => {
189
+ if (relativeUrl === rootRelativeUrl) {
190
+ stopTrackingRoot(rootRelativeUrl)
191
+ livereloadLogger.debug(`${rootRelativeUrl} removed -> stop tracking it`)
192
+ }
193
+ })
194
+ addStopTrackingCalback(rootRelativeUrl, removeRootRemovedCallback)
195
+ })
196
+
197
+ /**
198
+ * trackMainAndDependencies
199
+ * This function is meant to be used to implement server sent events in order for a client to know
200
+ * when a given file or any of its dependencies changes in order to implement livereloading.
201
+ * At any time this function can be called with (mainRelativeUrl, { modified, removed, lastEventId })
202
+ * modified is called
203
+ * - immediatly if lastEventId is passed and mainRelativeUrl or any of its dependencies have
204
+ * changed since that event (last change is passed to modified if their is more than one change)
205
+ * - when mainRelativeUrl or any of its dependencies is modified
206
+ * removed is called
207
+ * - with same spec as modified but when a file is deleted from the filesystem instead of modified
208
+ */
209
+ const trackMainAndDependencies = (
210
+ mainRelativeUrl,
211
+ { modified, removed, added },
212
+ ) => {
213
+ livereloadLogger.debug(`track ${mainRelativeUrl} and its dependencies`)
214
+
215
+ const removeModifiedCallback = projectFileModified.add((relativeUrl) => {
216
+ if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
217
+ modified(relativeUrl)
218
+ }
219
+ })
220
+ const removeRemovedCallback = projectFileRemoved.add((relativeUrl) => {
221
+ if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
222
+ removed(relativeUrl)
223
+ }
224
+ })
225
+ const removeAddedCallback = projectFileAdded.add((relativeUrl) => {
226
+ if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
227
+ added(relativeUrl)
228
+ }
229
+ })
230
+
231
+ return () => {
232
+ livereloadLogger.debug(
233
+ `stop tracking ${mainRelativeUrl} and its dependencies.`,
234
+ )
235
+ removeModifiedCallback()
236
+ removeRemovedCallback()
237
+ removeAddedCallback()
238
+ }
239
+ }
240
+
241
+ const reportDependency = (relativeUrl, mainRelativeUrl, request) => {
242
+ if (relativeUrl === mainRelativeUrl) {
243
+ return {
244
+ dependency: true,
245
+ reason: "it's main",
246
+ }
247
+ }
248
+
249
+ if ("x-jsenv-execution-id" in request.headers) {
250
+ const executionId = request.headers["x-jsenv-execution-id"]
251
+ if (executionId === mainRelativeUrl) {
252
+ return {
253
+ dependency: true,
254
+ reason: "x-jsenv-execution-id request header",
255
+ }
256
+ }
257
+ return {
258
+ dependency: false,
259
+ reason: "x-jsenv-execution-id request header",
260
+ }
261
+ }
262
+
263
+ const { referer } = request.headers
264
+ if (referer) {
265
+ // here we know the referer is inside compileServer
266
+ const refererRelativeUrl = urlToOriginalRelativeUrl(
267
+ referer,
268
+ resolveUrl(jsenvDirectoryRelativeUrl, request.origin),
269
+ )
270
+ if (refererRelativeUrl) {
271
+ // search if referer (file requesting this one) is tracked as being a dependency of main file
272
+ // in that case because the importer is a dependency the importee is also a dependency
273
+ // eslint-disable-next-line no-unused-vars
274
+ for (const tracker of trackerMap) {
275
+ if (
276
+ tracker[0] === mainRelativeUrl &&
277
+ tracker[1].set.has(refererRelativeUrl)
278
+ ) {
279
+ return {
280
+ dependency: true,
281
+ reason: "referer is a dependency",
282
+ }
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ return {
289
+ dependency: true,
290
+ reason: "it was requested",
291
+ }
292
+ }
293
+
294
+ const cache = []
295
+ const sseRoomLimit = 100
296
+ const getOrCreateSSERoom = (mainFileRelativeUrl) => {
297
+ const cacheEntry = cache.find(
298
+ (cacheEntryCandidate) =>
299
+ cacheEntryCandidate.mainFileRelativeUrl === mainFileRelativeUrl,
300
+ )
301
+ if (cacheEntry) {
302
+ return cacheEntry.sseRoom
303
+ }
304
+
305
+ const sseRoom = createSSERoom({
306
+ retryDuration: 2000,
307
+ historyLength: 100,
308
+ welcomeEventEnabled: true,
309
+ })
310
+
311
+ // each time something is modified or removed we send event to the room
312
+ const stopTracking = trackMainAndDependencies(mainFileRelativeUrl, {
313
+ modified: (relativeUrl) => {
314
+ sseRoom.sendEvent({ type: "file-modified", data: relativeUrl })
315
+ },
316
+ removed: (relativeUrl) => {
317
+ sseRoom.sendEvent({ type: "file-removed", data: relativeUrl })
318
+ },
319
+ added: (relativeUrl) => {
320
+ sseRoom.sendEvent({ type: "file-added", data: relativeUrl })
321
+ },
322
+ })
323
+
324
+ const removeSSECleanupCallback = serverStopCallbackList.add(() => {
325
+ removeSSECleanupCallback()
326
+ sseRoom.close()
327
+ stopTracking()
328
+ })
329
+ cache.push({
330
+ mainFileRelativeUrl,
331
+ sseRoom,
332
+ cleanup: () => {
333
+ removeSSECleanupCallback()
334
+ sseRoom.close()
335
+ stopTracking()
336
+ },
337
+ })
338
+ if (cache.length >= sseRoomLimit) {
339
+ const firstCacheEntry = cache.shift()
340
+ firstCacheEntry.cleanup()
341
+ }
342
+ return sseRoom
343
+ }
344
+
345
+ return (request) => {
346
+ const requestUrl = resolveUrl(request.ressource, request.origin)
347
+ const outDirectoryServerUrl = resolveUrl(
348
+ jsenvDirectoryRelativeUrl,
349
+ request.origin,
350
+ )
351
+ const originalRelativeUrl = urlToOriginalRelativeUrl(
352
+ requestUrl,
353
+ outDirectoryServerUrl,
354
+ )
355
+ const room = getOrCreateSSERoom(originalRelativeUrl)
356
+ return room.join(request)
357
+ }
358
+ }
359
+
360
+ const urlToOriginalRelativeUrl = (url, outDirectoryRemoteUrl) => {
361
+ if (urlIsInsideOf(url, outDirectoryRemoteUrl)) {
362
+ const afterCompileDirectory = urlToRelativeUrl(url, outDirectoryRemoteUrl)
363
+ const fileRelativeUrl = afterCompileDirectory.slice(
364
+ afterCompileDirectory.indexOf("/") + 1,
365
+ )
366
+ return fileRelativeUrl
367
+ }
368
+ return new URL(url).pathname.slice(1)
369
+ }