@jsenv/core 27.0.0-alpha.82 → 27.0.0-alpha.85

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 (76) hide show
  1. package/dist/js/event_source_client.js +208 -4
  2. package/dist/js/s.js +2 -2
  3. package/dist/main.js +1430 -615
  4. package/dist/s.js +2 -2
  5. package/dist/s.js.map +2 -1
  6. package/package.json +6 -2
  7. package/src/build/build.js +5 -8
  8. package/src/build/build_urls_generator.js +1 -2
  9. package/src/build/inject_global_version_mappings.js +4 -4
  10. package/src/build/inject_service_worker_urls.js +2 -2
  11. package/src/build/resync_ressource_hints.js +17 -18
  12. package/src/build/start_build_server.js +33 -26
  13. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +1 -2
  14. package/src/dev/plugins/toolbar/client/util/fetching.js +1 -1
  15. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -3
  16. package/src/dev/start_dev_server.js +38 -30
  17. package/src/execute/runtimes/browsers/from_playwright.js +5 -4
  18. package/src/execute/runtimes/node/node_process.js +2 -2
  19. package/src/helpers/command/command.js +73 -0
  20. package/src/helpers/event_source/event_source.js +197 -0
  21. package/src/helpers/event_source/sse_service.js +53 -0
  22. package/src/helpers/worker_reload.js +57 -0
  23. package/src/omega/compat/runtime_compat.js +2 -1
  24. package/src/omega/kitchen.js +4 -1
  25. package/src/omega/server/user_agent.js +2 -1
  26. package/src/omega/url_graph/sort_by_dependencies.js +27 -0
  27. package/src/omega/url_graph/url_info_transformations.js +24 -14
  28. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +1 -1
  29. package/src/plugins/autoreload/dev_sse/client/reload.js +6 -3
  30. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +3 -3
  31. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +1 -1
  32. package/src/plugins/bundling/css/bundle_css.js +4 -4
  33. package/src/plugins/bundling/js_module/bundle_js_module.js +86 -67
  34. package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
  35. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -5
  36. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +62 -74
  37. package/src/plugins/import_meta_hot/html_hot_dependencies.js +9 -15
  38. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +3 -3
  39. package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -2
  40. package/src/plugins/importmap/jsenv_plugin_importmap.js +25 -27
  41. package/src/plugins/inject_globals/inject_globals.js +4 -4
  42. package/src/plugins/inline/jsenv_plugin_data_urls.js +1 -1
  43. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +41 -43
  44. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +4 -4
  45. package/src/plugins/minification/css/minify_css.js +1 -1
  46. package/src/plugins/transpilation/as_js_classic/client/s.js +2 -2
  47. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +2 -4
  48. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +45 -67
  49. package/src/plugins/transpilation/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +2 -3
  50. package/src/plugins/transpilation/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +3 -4
  51. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -1
  52. package/src/plugins/transpilation/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +2 -3
  53. package/src/plugins/transpilation/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +2 -3
  54. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +1 -1
  55. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +1 -1
  56. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +2 -1
  57. package/src/plugins/url_analysis/css/css_urls.js +2 -3
  58. package/src/plugins/url_analysis/html/html_urls.js +98 -113
  59. package/src/plugins/url_analysis/js/js_urls.js +3 -2
  60. package/src/test/coverage/babel_plugin_instrument.js +82 -0
  61. package/src/test/coverage/coverage_reporter_html_directory.js +36 -0
  62. package/src/test/coverage/coverage_reporter_json_file.js +22 -0
  63. package/src/test/coverage/coverage_reporter_text_log.js +19 -0
  64. package/src/test/coverage/empty_coverage_factory.js +52 -0
  65. package/src/test/coverage/file_by_file_coverage.js +25 -0
  66. package/src/test/coverage/istanbul_coverage_composition.js +28 -0
  67. package/src/test/coverage/istanbul_coverage_map_from_coverage.js +16 -0
  68. package/src/test/coverage/list_files_not_covered.js +15 -0
  69. package/src/test/coverage/missing_coverage.js +41 -0
  70. package/src/test/coverage/report_to_coverage.js +196 -0
  71. package/src/test/coverage/v8_and_istanbul.js +37 -0
  72. package/src/test/coverage/v8_coverage_composition.js +24 -0
  73. package/src/test/coverage/v8_coverage_from_directory.js +87 -0
  74. package/src/test/coverage/v8_coverage_to_istanbul.js +99 -0
  75. package/src/test/execute_plan.js +2 -2
  76. package/src/test/execute_test_plan.js +3 -3
@@ -1,12 +1,13 @@
1
- import { parentPort } from "node:worker_threads"
2
-
1
+ import { findFreePort } from "@jsenv/server"
3
2
  import {
4
3
  assertAndNormalizeDirectoryUrl,
5
4
  registerDirectoryLifecycle,
6
5
  } from "@jsenv/filesystem"
6
+ import { Abort, raceProcessTeardownEvents } from "@jsenv/abort"
7
7
  import { createLogger, loggerToLevels, createTaskLog } from "@jsenv/log"
8
8
  import { getCallerPosition } from "@jsenv/urls"
9
- import { initReloadableProcess } from "@jsenv/utils/process_reload/process_reload.js"
9
+
10
+ import { createReloadableWorker } from "@jsenv/core/src/helpers/worker_reload.js"
10
11
  import { getCorePlugins } from "@jsenv/core/src/plugins/plugins.js"
11
12
  import { createUrlGraph } from "@jsenv/core/src/omega/url_graph.js"
12
13
  import { createKitchen } from "@jsenv/core/src/omega/kitchen.js"
@@ -17,7 +18,7 @@ import { jsenvPluginExplorer } from "./plugins/explorer/jsenv_plugin_explorer.js
17
18
 
18
19
  export const startDevServer = async ({
19
20
  signal = new AbortController().signal,
20
- handleSIGINT,
21
+ handleSIGINT = true,
21
22
  logLevel = "info",
22
23
  omegaServerLogLevel = "warn",
23
24
  port = 3456,
@@ -37,10 +38,9 @@ export const startDevServer = async ({
37
38
  devServerMainFile = getCallerPosition().url,
38
39
  // force disable server autoreload when this code is executed:
39
40
  // - inside a forked child process
40
- // - inside a worker thread
41
- // (because node cluster won't work)
41
+ // - debugged by vscode
42
+ // otherwise we get net:ERR_CONNECTION_REFUSED
42
43
  devServerAutoreload = typeof process.send !== "function" &&
43
- !parentPort &&
44
44
  !process.env.VSCODE_INSPECTOR_OPTIONS,
45
45
  clientFiles = {
46
46
  "./src/": true,
@@ -80,28 +80,30 @@ export const startDevServer = async ({
80
80
  }) => {
81
81
  const logger = createLogger({ logLevel })
82
82
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
83
- const reloadableProcess = await initReloadableProcess({
84
- signal,
85
- handleSIGINT,
86
- ...(devServerAutoreload
87
- ? {
88
- enabled: true,
89
- logLevel: "warn",
90
- fileToRestart: devServerMainFile,
91
- }
92
- : {
93
- enabled: false,
94
- }),
95
- })
96
- if (reloadableProcess.isPrimary) {
83
+ const operation = Abort.startOperation()
84
+ operation.addAbortSignal(signal)
85
+ if (handleSIGINT) {
86
+ operation.addAbortSource((abort) => {
87
+ return raceProcessTeardownEvents(
88
+ {
89
+ SIGINT: true,
90
+ },
91
+ abort,
92
+ )
93
+ })
94
+ }
95
+ if (port === 0) {
96
+ port = await findFreePort(port, { signal: operation.signal })
97
+ }
98
+
99
+ const reloadableWorker = createReloadableWorker(devServerMainFile)
100
+ if (devServerAutoreload && reloadableWorker.isPrimary) {
97
101
  const devServerFileChangeCallback = ({ relativeUrl, event }) => {
98
102
  const url = new URL(relativeUrl, rootDirectoryUrl).href
99
- if (devServerAutoreload) {
100
- logger.info(`file ${event} ${url} -> restarting server...`)
101
- reloadableProcess.reload()
102
- }
103
+ logger.info(`file ${event} ${url} -> restarting server...`)
104
+ reloadableWorker.reload()
103
105
  }
104
- const unregisterDevServerFilesWatcher = registerDirectoryLifecycle(
106
+ const stopWatchingDevServerFiles = registerDirectoryLifecycle(
105
107
  rootDirectoryUrl,
106
108
  {
107
109
  watchPatterns: {
@@ -122,14 +124,20 @@ export const startDevServer = async ({
122
124
  },
123
125
  },
124
126
  )
125
- signal.addEventListener("abort", () => {
126
- unregisterDevServerFilesWatcher()
127
+ operation.addAbortCallback(() => {
128
+ stopWatchingDevServerFiles()
129
+ reloadableWorker.terminate()
127
130
  })
131
+
132
+ const worker = await reloadableWorker.load()
133
+ if (!keepProcessAlive) {
134
+ worker.unref()
135
+ }
128
136
  return {
129
137
  origin: `${protocol}://127.0.0.1:${port}`,
130
138
  stop: () => {
131
- unregisterDevServerFilesWatcher()
132
- reloadableProcess.stop()
139
+ stopWatchingDevServerFiles()
140
+ reloadableWorker.terminate()
133
141
  },
134
142
  }
135
143
  }
@@ -8,10 +8,11 @@ import {
8
8
  raceCallbacks,
9
9
  } from "@jsenv/abort"
10
10
  import { moveUrl } from "@jsenv/urls"
11
- import { memoize } from "@jsenv/utils/memoize/memoize.js"
12
- import { filterV8Coverage } from "@jsenv/utils/coverage/v8_coverage_from_directory.js"
13
- import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/utils/coverage/istanbul_coverage_composition.js"
14
- import { escapeRegexpSpecialChars } from "@jsenv/utils/string/escape_regexp_special_chars.js"
11
+ import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
12
+ import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js"
13
+
14
+ import { filterV8Coverage } from "@jsenv/core/src/test/coverage/v8_coverage_from_directory.js"
15
+ import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/test/coverage/istanbul_coverage_composition.js"
15
16
 
16
17
  export const createRuntimeFromPlaywright = ({
17
18
  browserName,
@@ -5,10 +5,10 @@ import {
5
5
  createCallbackListNotifiedOnce,
6
6
  } from "@jsenv/abort"
7
7
  import { uneval } from "@jsenv/uneval"
8
-
9
8
  import { urlToFileSystemPath } from "@jsenv/urls"
10
9
  import { createDetailedMessage } from "@jsenv/log"
11
- import { memoize } from "@jsenv/utils/memoize/memoize.js"
10
+ import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
11
+
12
12
  import { createChildExecOptions } from "./child_exec_options.js"
13
13
  import { ExecOptions } from "./exec_options.js"
14
14
  import { killProcessTree } from "./kill_process_tree.js"
@@ -0,0 +1,73 @@
1
+ import { exec } from "node:child_process"
2
+ import { createDetailedMessage, createLogger, UNICODE } from "@jsenv/log"
3
+
4
+ export const executeCommand = (
5
+ command,
6
+ {
7
+ logLevel = "info",
8
+ signal = new AbortController().signal,
9
+ onStdout = () => {},
10
+ onStderr = () => {},
11
+ cwd,
12
+ env,
13
+ timeout,
14
+ } = {},
15
+ ) => {
16
+ const logger = createLogger({ logLevel })
17
+
18
+ return new Promise((resolve, reject) => {
19
+ logger.debug(`${UNICODE.COMMAND} ${command}`)
20
+ const commandProcess = exec(command, {
21
+ signal,
22
+ cwd:
23
+ cwd && typeof cwd === "string" && cwd.startsWith("file:")
24
+ ? new URL(cwd)
25
+ : cwd,
26
+ env,
27
+ timeout,
28
+ silent: true,
29
+ })
30
+ commandProcess.on("error", (error) => {
31
+ if (error && error.code === "ETIMEDOUT") {
32
+ logger.error(`timeout after ${timeout} ms`)
33
+ reject(error)
34
+ } else {
35
+ reject(error)
36
+ }
37
+ })
38
+ const stdoutDatas = []
39
+ commandProcess.stdout.on("data", (data) => {
40
+ stdoutDatas.push(data)
41
+ logger.debug(data)
42
+ onStderr(data)
43
+ })
44
+ let stderrDatas = []
45
+ commandProcess.stderr.on("data", (data) => {
46
+ stderrDatas.push(data)
47
+ logger.debug(data)
48
+ onStdout(data)
49
+ })
50
+ if (commandProcess.stdin) {
51
+ commandProcess.stdin.on("error", (error) => {
52
+ reject(error)
53
+ })
54
+ }
55
+ commandProcess.on("exit", (exitCode, signal) => {
56
+ if (signal) {
57
+ reject(new Error(`killed with ${signal}`))
58
+ }
59
+ if (exitCode) {
60
+ reject(
61
+ new Error(
62
+ createDetailedMessage(`failed with exit code ${exitCode}`, {
63
+ "command stderr": stderrDatas.join(""),
64
+ // "command stdout": stdoutDatas.join(""),
65
+ }),
66
+ ),
67
+ )
68
+ return
69
+ }
70
+ resolve({ exitCode, signal })
71
+ })
72
+ })
73
+ }
@@ -0,0 +1,197 @@
1
+ /* eslint-env browser */
2
+
3
+ export const createEventSourceConnection = (
4
+ eventSourceUrl,
5
+ events = {},
6
+ { retryMaxAttempt = Infinity, retryAllocatedMs = Infinity, lastEventId } = {},
7
+ ) => {
8
+ const { EventSource } = window
9
+ if (typeof EventSource !== "function") {
10
+ return () => {}
11
+ }
12
+
13
+ const eventSourceOrigin = new URL(eventSourceUrl).origin
14
+ Object.keys(events).forEach((eventName) => {
15
+ const eventCallback = events[eventName]
16
+ events[eventName] = (e) => {
17
+ if (e.origin === eventSourceOrigin) {
18
+ if (e.lastEventId) {
19
+ lastEventId = e.lastEventId
20
+ }
21
+ eventCallback(e)
22
+ }
23
+ }
24
+ })
25
+
26
+ const status = {
27
+ value: "default",
28
+ goTo: (value) => {
29
+ if (value === status.value) {
30
+ return
31
+ }
32
+ status.value = value
33
+ status.onchange()
34
+ },
35
+ onchange: () => {},
36
+ }
37
+ let _disconnect = () => {}
38
+
39
+ const attemptConnection = (url) => {
40
+ const eventSource = new EventSource(url, {
41
+ withCredentials: true,
42
+ })
43
+ _disconnect = () => {
44
+ if (status.value !== "connecting" && status.value !== "connected") {
45
+ console.warn(
46
+ `disconnect() ignored because connection is ${status.value}`,
47
+ )
48
+ return
49
+ }
50
+ eventSource.onerror = undefined
51
+ eventSource.close()
52
+ Object.keys(events).forEach((eventName) => {
53
+ eventSource.removeEventListener(eventName, events[eventName])
54
+ })
55
+ status.goTo("disconnected")
56
+ }
57
+ let retryCount = 0
58
+ let firstRetryMs = Date.now()
59
+ eventSource.onerror = (errorEvent) => {
60
+ if (errorEvent.target.readyState === EventSource.CONNECTING) {
61
+ if (retryCount > retryMaxAttempt) {
62
+ console.info(`could not connect after ${retryMaxAttempt} attempt`)
63
+ _disconnect()
64
+ return
65
+ }
66
+
67
+ if (retryCount === 0) {
68
+ firstRetryMs = Date.now()
69
+ } else {
70
+ const allRetryDuration = Date.now() - firstRetryMs
71
+ if (retryAllocatedMs && allRetryDuration > retryAllocatedMs) {
72
+ console.info(
73
+ `could not connect in less than ${retryAllocatedMs} ms`,
74
+ )
75
+ _disconnect()
76
+ return
77
+ }
78
+ }
79
+
80
+ retryCount++
81
+ status.goTo("connecting")
82
+ return
83
+ }
84
+
85
+ if (errorEvent.target.readyState === EventSource.CLOSED) {
86
+ _disconnect()
87
+ return
88
+ }
89
+ }
90
+ eventSource.onopen = () => {
91
+ status.goTo("connected")
92
+ }
93
+ Object.keys(events).forEach((eventName) => {
94
+ eventSource.addEventListener(eventName, events[eventName])
95
+ })
96
+ if (!events.hasOwnProperty("welcome")) {
97
+ eventSource.addEventListener("welcome", (e) => {
98
+ if (e.origin === eventSourceOrigin && e.lastEventId) {
99
+ lastEventId = e.lastEventId
100
+ }
101
+ })
102
+ }
103
+ status.goTo("connecting")
104
+ }
105
+
106
+ let connect = () => {
107
+ attemptConnection(eventSourceUrl)
108
+ connect = () => {
109
+ attemptConnection(
110
+ lastEventId
111
+ ? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
112
+ : eventSourceUrl,
113
+ )
114
+ }
115
+ }
116
+
117
+ const removePageUnloadListener = listenPageUnload(() => {
118
+ if (status.value === "connecting" || status.value === "connected") {
119
+ _disconnect()
120
+ }
121
+ })
122
+
123
+ const destroy = () => {
124
+ removePageUnloadListener()
125
+ _disconnect()
126
+ }
127
+
128
+ return {
129
+ status,
130
+ connect,
131
+ disconnect: () => _disconnect(),
132
+ destroy,
133
+ }
134
+ }
135
+
136
+ const addLastEventIdIntoUrlSearchParams = (url, lastEventId) => {
137
+ if (url.indexOf("?") === -1) {
138
+ url += "?"
139
+ } else {
140
+ url += "&"
141
+ }
142
+ return `${url}last-event-id=${encodeURIComponent(lastEventId)}`
143
+ }
144
+
145
+ // const listenPageMightFreeze = (callback) => {
146
+ // const removePageHideListener = listenEvent(window, "pagehide", (pageHideEvent) => {
147
+ // if (pageHideEvent.persisted === true) {
148
+ // callback(pageHideEvent)
149
+ // }
150
+ // })
151
+ // return removePageHideListener
152
+ // }
153
+
154
+ // const listenPageFreeze = (callback) => {
155
+ // const removeFreezeListener = listenEvent(document, "freeze", (freezeEvent) => {
156
+ // callback(freezeEvent)
157
+ // })
158
+ // return removeFreezeListener
159
+ // }
160
+
161
+ // const listenPageIsRestored = (callback) => {
162
+ // const removeResumeListener = listenEvent(document, "resume", (resumeEvent) => {
163
+ // removePageshowListener()
164
+ // callback(resumeEvent)
165
+ // })
166
+ // const removePageshowListener = listenEvent(window, "pageshow", (pageshowEvent) => {
167
+ // if (pageshowEvent.persisted === true) {
168
+ // removePageshowListener()
169
+ // removeResumeListener()
170
+ // callback(pageshowEvent)
171
+ // }
172
+ // })
173
+ // return () => {
174
+ // removeResumeListener()
175
+ // removePageshowListener()
176
+ // }
177
+ // }
178
+
179
+ const listenPageUnload = (callback) => {
180
+ const removePageHideListener = listenEvent(
181
+ window,
182
+ "pagehide",
183
+ (pageHideEvent) => {
184
+ if (pageHideEvent.persisted !== true) {
185
+ callback(pageHideEvent)
186
+ }
187
+ },
188
+ )
189
+ return removePageHideListener
190
+ }
191
+
192
+ const listenEvent = (emitter, event, callback) => {
193
+ emitter.addEventListener(event, callback)
194
+ return () => {
195
+ emitter.removeEventListener(event, callback)
196
+ }
197
+ }
@@ -0,0 +1,53 @@
1
+ import { createSSERoom } from "@jsenv/server"
2
+ import { createCallbackListNotifiedOnce } from "@jsenv/abort"
3
+
4
+ export const createSSEService = ({ serverEventCallbackList }) => {
5
+ const destroyCallbackList = createCallbackListNotifiedOnce()
6
+
7
+ const cache = []
8
+ const sseRoomLimit = 100
9
+ const getOrCreateSSERoom = (request) => {
10
+ const htmlFileRelativeUrl = request.ressource.slice(1)
11
+ const cacheEntry = cache.find(
12
+ (cacheEntryCandidate) =>
13
+ cacheEntryCandidate.htmlFileRelativeUrl === htmlFileRelativeUrl,
14
+ )
15
+ if (cacheEntry) {
16
+ return cacheEntry.sseRoom
17
+ }
18
+ const sseRoom = createSSERoom({
19
+ retryDuration: 2000,
20
+ historyLength: 100,
21
+ welcomeEventEnabled: true,
22
+ effect: () => {
23
+ return serverEventCallbackList.add((event) => {
24
+ sseRoom.sendEvent(event)
25
+ })
26
+ },
27
+ })
28
+ const removeSSECleanupCallback = destroyCallbackList.add(() => {
29
+ removeSSECleanupCallback()
30
+ sseRoom.close()
31
+ })
32
+ cache.push({
33
+ htmlFileRelativeUrl,
34
+ sseRoom,
35
+ cleanup: () => {
36
+ removeSSECleanupCallback()
37
+ sseRoom.close()
38
+ },
39
+ })
40
+ if (cache.length >= sseRoomLimit) {
41
+ const firstCacheEntry = cache.shift()
42
+ firstCacheEntry.cleanup()
43
+ }
44
+ return sseRoom
45
+ }
46
+
47
+ return {
48
+ getOrCreateSSERoom,
49
+ destroy: () => {
50
+ destroyCallbackList.notify()
51
+ },
52
+ }
53
+ }
@@ -0,0 +1,57 @@
1
+ import { fileURLToPath } from "node:url"
2
+ import { Worker, workerData } from "node:worker_threads"
3
+
4
+ // https://nodejs.org/api/worker_threads.html
5
+ export const createReloadableWorker = (workerFileUrl, options = {}) => {
6
+ const workerFilePath = fileURLToPath(workerFileUrl)
7
+ const isPrimary = !workerData || workerData.workerFilePath !== workerFilePath
8
+ let worker
9
+
10
+ const terminate = async () => {
11
+ if (worker) {
12
+ let _worker = worker
13
+ worker = null
14
+ const exitPromise = new Promise((resolve) => {
15
+ _worker.once("exit", resolve)
16
+ })
17
+ _worker.terminate()
18
+ await exitPromise
19
+ }
20
+ }
21
+
22
+ const load = async () => {
23
+ if (!isPrimary) {
24
+ throw new Error(`worker can be loaded from primary file only`)
25
+ }
26
+ worker = new Worker(workerFilePath, {
27
+ ...options,
28
+ workerData: {
29
+ ...options.workerData,
30
+ workerFilePath,
31
+ },
32
+ })
33
+
34
+ worker.once("error", (error) => {
35
+ console.error(error)
36
+ })
37
+ await new Promise((resolve) => {
38
+ worker.once("online", resolve)
39
+ })
40
+ worker.once("exit", () => {
41
+ worker = null
42
+ })
43
+ return worker
44
+ }
45
+
46
+ const reload = async () => {
47
+ await terminate()
48
+ await load()
49
+ }
50
+
51
+ return {
52
+ isPrimary,
53
+ load,
54
+ reload,
55
+ terminate,
56
+ }
57
+ }
@@ -1,4 +1,5 @@
1
- import { findHighestVersion } from "@jsenv/utils/semantic_versioning/highest_version.js"
1
+ import { findHighestVersion } from "@jsenv/utils/src/semantic_versioning/highest_version.js"
2
+
2
3
  import { featureCompats } from "./features_compats.js"
3
4
 
4
5
  export const RUNTIME_COMPAT = {
@@ -8,7 +8,8 @@ import {
8
8
  } from "@jsenv/urls"
9
9
  import { writeFileSync, ensureWindowsDriveLetter } from "@jsenv/filesystem"
10
10
  import { createDetailedMessage } from "@jsenv/log"
11
- import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js"
11
+ import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js"
12
+
12
13
  import { createPluginController } from "../plugins/plugin_controller.js"
13
14
  import { urlSpecifierEncoding } from "./url_specifier_encoding.js"
14
15
  import { createUrlInfoTransformer } from "./url_graph/url_info_transformations.js"
@@ -711,6 +712,8 @@ const memoizeCook = (cook) => {
711
712
  const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
712
713
  if (reference.shouldHandle) {
713
714
  urlInfo.shouldHandle = true
715
+ } else {
716
+ urlInfo.shouldHandle = false
714
717
  }
715
718
  urlInfo.originalUrl = urlInfo.originalUrl || reference.url
716
719
 
@@ -1,4 +1,5 @@
1
- import { memoizeByFirstArgument } from "@jsenv/utils/memoize/memoize_by_first_argument.js"
1
+ import { memoizeByFirstArgument } from "@jsenv/utils/src/memoize/memoize_by_first_argument.js"
2
+
2
3
  import { requireFromJsenv } from "@jsenv/core/src/require_from_jsenv.js"
3
4
 
4
5
  export const parseUserAgentHeader = memoizeByFirstArgument((userAgent) => {
@@ -0,0 +1,27 @@
1
+ export const sortByDependencies = (nodes) => {
2
+ const visited = []
3
+ const sorted = []
4
+ const circular = []
5
+ const visit = (url) => {
6
+ const isSorted = sorted.includes(url)
7
+ if (isSorted) {
8
+ return
9
+ }
10
+ const isVisited = visited.includes(url)
11
+ if (isVisited) {
12
+ circular.push(url)
13
+ sorted.push(url)
14
+ } else {
15
+ visited.push(url)
16
+ nodes[url].dependencies.forEach((dependencyUrl) => {
17
+ visit(dependencyUrl, url)
18
+ })
19
+ sorted.push(url)
20
+ }
21
+ }
22
+ Object.keys(nodes).forEach((url) => {
23
+ visit(url)
24
+ })
25
+ sorted.circular = circular
26
+ return sorted
27
+ }
@@ -1,12 +1,12 @@
1
+ import { pathToFileURL } from "node:url"
1
2
  import { bufferToEtag } from "@jsenv/filesystem"
2
- import { urlToRelativeUrl } from "@jsenv/urls"
3
-
4
- import { composeTwoSourcemaps } from "@jsenv/utils/sourcemap/sourcemap_composition_v3.js"
3
+ import { urlToRelativeUrl, isFileSystemPath } from "@jsenv/urls"
5
4
  import {
5
+ composeTwoSourcemaps,
6
6
  SOURCEMAP,
7
- sourcemapToBase64Url,
8
- generateSourcemapUrl,
9
- } from "@jsenv/utils/sourcemap/sourcemap_utils.js"
7
+ generateSourcemapFileUrl,
8
+ generateSourcemapDataUrl,
9
+ } from "@jsenv/sourcemap"
10
10
 
11
11
  export const createUrlInfoTransformer = ({
12
12
  logger,
@@ -23,18 +23,25 @@ export const createUrlInfoTransformer = ({
23
23
  sourcemaps === "programmatic"
24
24
 
25
25
  const normalizeSourcemap = (urlInfo, sourcemap) => {
26
+ let { sources } = sourcemap
27
+ if (sources) {
28
+ sources = sources.map((source) => {
29
+ if (source && isFileSystemPath(source)) {
30
+ return String(pathToFileURL(source))
31
+ }
32
+ return source
33
+ })
34
+ }
26
35
  const wantSourcesContent =
27
36
  // for inline content (<script> insdide html)
28
37
  // chrome won't be able to fetch the file as it does not exists
29
38
  // so sourcemap must contain sources
30
39
  sourcemapsSourcesContent ||
31
40
  urlInfo.isInline ||
32
- (sourcemap.sources &&
33
- sourcemap.sources.some(
34
- (source) => !source || !source.startsWith("file:"),
35
- ))
36
- if (sourcemap.sources && sourcemap.sources.length > 1) {
37
- sourcemap.sources = sourcemap.sources.map(
41
+ (sources &&
42
+ sources.some((source) => !source || !source.startsWith("file:")))
43
+ if (sources && sources.length > 1) {
44
+ sourcemap.sources = sources.map(
38
45
  (source) => new URL(source, urlInfo.originalUrl).href,
39
46
  )
40
47
  if (!wantSourcesContent) {
@@ -65,7 +72,9 @@ export const createUrlInfoTransformer = ({
65
72
  // when jsenv is done cooking the file
66
73
  // during build it's urlInfo.url to be inside the build
67
74
  // but otherwise it's generatedUrl to be inside .jsenv/ directory
68
- urlInfo.sourcemapGeneratedUrl = generateSourcemapUrl(urlInfo.generatedUrl)
75
+ urlInfo.sourcemapGeneratedUrl = generateSourcemapFileUrl(
76
+ urlInfo.generatedUrl,
77
+ )
69
78
  const [sourcemapReference, sourcemapUrlInfo] = injectSourcemapPlaceholder({
70
79
  urlInfo,
71
80
  specifier: urlInfo.sourcemapGeneratedUrl,
@@ -154,7 +163,8 @@ export const createUrlInfoTransformer = ({
154
163
  }
155
164
  sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ")
156
165
  if (sourcemaps === "inline") {
157
- sourcemapReference.generatedSpecifier = sourcemapToBase64Url(sourcemap)
166
+ sourcemapReference.generatedSpecifier =
167
+ generateSourcemapDataUrl(sourcemap)
158
168
  }
159
169
  if (sourcemaps === "file" || sourcemaps === "inline") {
160
170
  urlInfo.content = SOURCEMAP.writeComment({
@@ -1,4 +1,4 @@
1
- import { createEventSourceConnection } from "@jsenv/utils/event_source/event_source.js"
1
+ import { createEventSourceConnection } from "@jsenv/core/src/helpers/event_source/event_source.js"
2
2
  import { urlHotMetas } from "../../../import_meta_hot/client/import_meta_hot.js"
3
3
  import {
4
4
  isAutoreloadEnabled,