@jsenv/core 27.1.0 → 27.2.0

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 (33) hide show
  1. package/dist/controllable_child_process.mjs +139 -0
  2. package/dist/controllable_worker_thread.mjs +103 -0
  3. package/dist/js/execute_using_dynamic_import.js +169 -0
  4. package/dist/js/v8_coverage.js +539 -0
  5. package/dist/main.js +575 -804
  6. package/package.json +8 -7
  7. package/src/build/build.js +9 -12
  8. package/src/build/build_urls_generator.js +1 -1
  9. package/src/build/inject_global_version_mappings.js +3 -2
  10. package/src/build/inject_service_worker_urls.js +1 -2
  11. package/src/execute/run.js +50 -68
  12. package/src/execute/runtimes/browsers/from_playwright.js +13 -8
  13. package/src/execute/runtimes/node/{controllable_file.mjs → controllable_child_process.mjs} +18 -50
  14. package/src/execute/runtimes/node/controllable_worker_thread.mjs +103 -0
  15. package/src/execute/runtimes/node/execute_using_dynamic_import.js +49 -0
  16. package/src/execute/runtimes/node/exit_codes.js +9 -0
  17. package/src/execute/runtimes/node/{node_process.js → node_child_process.js} +56 -50
  18. package/src/execute/runtimes/node/node_worker_thread.js +268 -25
  19. package/src/execute/runtimes/node/profiler_v8_coverage.js +56 -0
  20. package/src/main.js +3 -1
  21. package/src/omega/kitchen.js +19 -6
  22. package/src/omega/server/file_service.js +2 -2
  23. package/src/omega/url_graph/url_graph_load.js +0 -1
  24. package/src/omega/url_graph.js +1 -0
  25. package/src/plugins/bundling/js_module/bundle_js_module.js +2 -5
  26. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +18 -15
  27. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +2 -1
  28. package/src/test/coverage/report_to_coverage.js +16 -19
  29. package/src/test/coverage/v8_coverage.js +26 -0
  30. package/src/test/coverage/{v8_coverage_from_directory.js → v8_coverage_node_directory.js} +22 -26
  31. package/src/test/execute_plan.js +92 -91
  32. package/src/test/execute_test_plan.js +15 -13
  33. package/dist/js/controllable_file.mjs +0 -227
@@ -0,0 +1,9 @@
1
+ // https://nodejs.org/api/process.html#process_signal_events
2
+ const SIGINT_SIGNAL_NUMBER = 2
3
+ const SIGABORT_SIGNAL_NUMBER = 6
4
+ const SIGTERM_SIGNAL_NUMBER = 15
5
+ export const EXIT_CODES = {
6
+ SIGINT: 128 + SIGINT_SIGNAL_NUMBER,
7
+ SIGABORT: 128 + SIGABORT_SIGNAL_NUMBER,
8
+ SIGTERM: 128 + SIGTERM_SIGNAL_NUMBER,
9
+ }
@@ -1,29 +1,30 @@
1
1
  import { fork } from "node:child_process"
2
+ import { fileURLToPath } from "node:url"
2
3
  import {
3
4
  Abort,
4
5
  raceCallbacks,
5
6
  createCallbackListNotifiedOnce,
6
7
  } from "@jsenv/abort"
7
- import { uneval } from "@jsenv/uneval"
8
- import { urlToFileSystemPath } from "@jsenv/urls"
9
8
  import { createDetailedMessage } from "@jsenv/log"
10
9
  import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
11
10
 
12
11
  import { createChildExecOptions } from "./child_exec_options.js"
13
12
  import { ExecOptions } from "./exec_options.js"
14
13
  import { killProcessTree } from "./kill_process_tree.js"
14
+ import { EXIT_CODES } from "./exit_codes.js"
15
15
 
16
- const NODE_CONTROLLABLE_FILE_URL = new URL(
17
- "./controllable_file.mjs",
16
+ const CONTROLLABLE_CHILD_PROCESS_URL = new URL(
17
+ "./controllable_child_process.mjs?entry_point",
18
18
  import.meta.url,
19
19
  ).href
20
20
 
21
- export const nodeProcess = {
22
- name: "node",
21
+ export const nodeChildProcess = {
22
+ type: "node",
23
+ name: "node_child_process",
23
24
  version: process.version.slice(1),
24
25
  }
25
26
 
26
- nodeProcess.run = async ({
27
+ nodeChildProcess.run = async ({
27
28
  signal = new AbortController().signal,
28
29
  logger,
29
30
  logProcessCommand = false,
@@ -35,14 +36,15 @@ nodeProcess.run = async ({
35
36
  stopSignal,
36
37
  onConsole,
37
38
 
38
- collectCoverage = false,
39
- coverageForceIstanbul,
39
+ coverageEnabled = false,
40
+ coverageConfig,
41
+ coverageMethodForNodeJs,
40
42
  collectPerformance,
41
43
 
44
+ env,
42
45
  debugPort,
43
46
  debugMode,
44
47
  debugModeInheritBreak,
45
- env,
46
48
  inheritProcessEnv = true,
47
49
  commandLineOptions = [],
48
50
  stdin = "pipe",
@@ -54,12 +56,9 @@ nodeProcess.run = async ({
54
56
  }
55
57
  env = {
56
58
  ...env,
57
- COVERAGE_ENABLED: collectCoverage,
58
59
  JSENV: true,
59
60
  }
60
- if (coverageForceIstanbul) {
61
- // if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
62
- // into the child_process
61
+ if (coverageMethodForNodeJs !== "NODE_V8_COVERAGE") {
63
62
  env.NODE_V8_COVERAGE = ""
64
63
  }
65
64
  commandLineOptions = [
@@ -87,11 +86,11 @@ nodeProcess.run = async ({
87
86
  ...env,
88
87
  }
89
88
  logger[logProcessCommand ? "info" : "debug"](
90
- `${process.argv[0]} ${execArgv.join(" ")} ${urlToFileSystemPath(
91
- NODE_CONTROLLABLE_FILE_URL,
89
+ `${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(
90
+ CONTROLLABLE_CHILD_PROCESS_URL,
92
91
  )}`,
93
92
  )
94
- const childProcess = fork(urlToFileSystemPath(NODE_CONTROLLABLE_FILE_URL), {
93
+ const childProcess = fork(fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL), {
95
94
  execArgv,
96
95
  // silent: true
97
96
  stdio: ["pipe", "pipe", "pipe", "ipc"],
@@ -114,9 +113,9 @@ nodeProcess.run = async ({
114
113
  childProcess.stderr.pipe(stderr)
115
114
  }
116
115
  const childProcessReadyPromise = new Promise((resolve) => {
117
- onceProcessMessage(childProcess, "ready", resolve)
116
+ onceChildProcessMessage(childProcess, "ready", resolve)
118
117
  })
119
- const removeOutputListener = installProcessOutputListener(
118
+ const removeOutputListener = installChildProcessOutputListener(
120
119
  childProcess,
121
120
  ({ type, text }) => {
122
121
  onConsole({ type, text })
@@ -171,15 +170,15 @@ nodeProcess.run = async ({
171
170
  // },
172
171
  // https://nodejs.org/api/child_process.html#child_process_event_error
173
172
  error: (cb) => {
174
- return onceProcessEvent(childProcess, "error", cb)
173
+ return onceChildProcessEvent(childProcess, "error", cb)
175
174
  },
176
175
  exit: (cb) => {
177
- return onceProcessEvent(childProcess, "exit", (code, signal) => {
176
+ return onceChildProcessEvent(childProcess, "exit", (code, signal) => {
178
177
  cb({ code, signal })
179
178
  })
180
179
  },
181
180
  response: (cb) => {
182
- onceProcessMessage(childProcess, "action-result", cb)
181
+ return onceChildProcessMessage(childProcess, "action-result", cb)
183
182
  },
184
183
  },
185
184
  resolve,
@@ -189,11 +188,19 @@ nodeProcess.run = async ({
189
188
  actionOperation.throwIfAborted()
190
189
  await childProcessReadyPromise
191
190
  actionOperation.throwIfAborted()
192
- await sendToProcess(childProcess, "action", {
193
- actionType: "execute-using-dynamic-import",
194
- actionParams: {
195
- fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
196
- collectPerformance,
191
+ await sendToChildProcess(childProcess, {
192
+ type: "action",
193
+ data: {
194
+ actionType: "execute-using-dynamic-import",
195
+ actionParams: {
196
+ rootDirectoryUrl,
197
+ fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
198
+ collectPerformance,
199
+ coverageEnabled,
200
+ coverageConfig,
201
+ coverageMethodForNodeJs,
202
+ exitAfterAction: true,
203
+ },
197
204
  },
198
205
  })
199
206
  const winner = await winnerPromise
@@ -224,9 +231,9 @@ nodeProcess.run = async ({
224
231
  if (
225
232
  code === null ||
226
233
  code === 0 ||
227
- code === SIGINT_EXIT_CODE ||
228
- code === SIGTERM_EXIT_CODE ||
229
- code === SIGABORT_EXIT_CODE
234
+ code === EXIT_CODES.SIGINT ||
235
+ code === EXIT_CODES.SIGTERM ||
236
+ code === EXIT_CODES.SIGABORT
230
237
  ) {
231
238
  return {
232
239
  status: "errored",
@@ -275,13 +282,6 @@ nodeProcess.run = async ({
275
282
  return result
276
283
  }
277
284
 
278
- // https://nodejs.org/api/process.html#process_signal_events
279
- const SIGINT_SIGNAL_NUMBER = 2
280
- const SIGABORT_SIGNAL_NUMBER = 6
281
- const SIGTERM_SIGNAL_NUMBER = 15
282
- const SIGINT_EXIT_CODE = 128 + SIGINT_SIGNAL_NUMBER
283
- const SIGABORT_EXIT_CODE = 128 + SIGABORT_SIGNAL_NUMBER
284
- const SIGTERM_EXIT_CODE = 128 + SIGTERM_SIGNAL_NUMBER
285
285
  // http://man7.org/linux/man-pages/man7/signal.7.html
286
286
  // https:// github.com/nodejs/node/blob/1d9511127c419ec116b3ddf5fc7a59e8f0f1c1e4/lib/internal/child_process.js#L472
287
287
  const GRACEFUL_STOP_SIGNAL = "SIGTERM"
@@ -290,20 +290,26 @@ const STOP_SIGNAL = "SIGKILL"
290
290
  // but I'm not sure and it changes nothing so just use SIGKILL
291
291
  const GRACEFUL_STOP_FAILED_SIGNAL = "SIGKILL"
292
292
 
293
- const sendToProcess = async (childProcess, type, data) => {
294
- const source = uneval(data, { functionAllowed: true })
293
+ const sendToChildProcess = async (childProcess, { type, data }) => {
295
294
  return new Promise((resolve, reject) => {
296
- childProcess.send({ type, data: source }, (error) => {
297
- if (error) {
298
- reject(error)
299
- } else {
300
- resolve()
301
- }
302
- })
295
+ childProcess.send(
296
+ {
297
+ jsenv: true,
298
+ type,
299
+ data,
300
+ },
301
+ (error) => {
302
+ if (error) {
303
+ reject(error)
304
+ } else {
305
+ resolve()
306
+ }
307
+ },
308
+ )
303
309
  })
304
310
  }
305
311
 
306
- const installProcessOutputListener = (childProcess, callback) => {
312
+ const installChildProcessOutputListener = (childProcess, callback) => {
307
313
  // beware that we may receive ansi output here, should not be a problem but keep that in mind
308
314
  const stdoutDataCallback = (chunk) => {
309
315
  callback({ type: "log", text: String(chunk) })
@@ -319,9 +325,9 @@ const installProcessOutputListener = (childProcess, callback) => {
319
325
  }
320
326
  }
321
327
 
322
- const onceProcessMessage = (childProcess, type, callback) => {
328
+ const onceChildProcessMessage = (childProcess, type, callback) => {
323
329
  const onmessage = (message) => {
324
- if (message.type === type) {
330
+ if (message && message.jsenv && message.type === type) {
325
331
  childProcess.removeListener("message", onmessage)
326
332
  // eslint-disable-next-line no-eval
327
333
  callback(message.data ? eval(`(${message.data})`) : "")
@@ -333,7 +339,7 @@ const onceProcessMessage = (childProcess, type, callback) => {
333
339
  }
334
340
  }
335
341
 
336
- const onceProcessEvent = (childProcess, type, callback) => {
342
+ const onceChildProcessEvent = (childProcess, type, callback) => {
337
343
  childProcess.once(type, callback)
338
344
  return () => {
339
345
  childProcess.removeListener(type, callback)
@@ -1,28 +1,271 @@
1
1
  // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/fork.js#L23
2
2
  // https://nodejs.org/api/worker_threads.html
3
3
  // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/worker/base.js
4
- // import { Worker } from "node:worker_threads"
5
-
6
- // export const nodeWorkerThread = {
7
- // name: "node_worker",
8
- // version: process.version.slice(1),
9
- // launch: async ({ env }) => {
10
- // // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
11
- // const worker = new Worker(workerPath, {
12
- // argv: options.workerArgv,
13
- // env: {
14
- // NODE_ENV: "test",
15
- // JSENV: true,
16
- // ...process.env,
17
- // },
18
- // execArgv: [...execArgv, ...additionalExecArgv],
19
- // workerData: {
20
- // options,
21
- // },
22
- // trackUnmanagedFds: true,
23
- // stdin: true,
24
- // stdout: true,
25
- // stderr: true,
26
- // })
27
- // },
28
- // }
4
+ import { Worker } from "node:worker_threads"
5
+ import { fileURLToPath } from "node:url"
6
+ import {
7
+ Abort,
8
+ createCallbackListNotifiedOnce,
9
+ raceCallbacks,
10
+ } from "@jsenv/abort"
11
+ import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
12
+
13
+ import { createChildExecOptions } from "./child_exec_options.js"
14
+ import { ExecOptions } from "./exec_options.js"
15
+ import { EXIT_CODES } from "./exit_codes.js"
16
+
17
+ const CONTROLLABLE_WORKER_THREAD_URL = new URL(
18
+ "./controllable_worker_thread.mjs?entry_point",
19
+ import.meta.url,
20
+ ).href
21
+
22
+ export const nodeWorkerThread = {
23
+ type: "node",
24
+ name: "node_worker_thread",
25
+ version: process.version.slice(1),
26
+ }
27
+
28
+ nodeWorkerThread.run = async ({
29
+ signal = new AbortController().signal,
30
+ // logger,
31
+ rootDirectoryUrl,
32
+ fileRelativeUrl,
33
+
34
+ keepRunning,
35
+ stopSignal,
36
+ onConsole,
37
+
38
+ coverageConfig,
39
+ coverageMethodForNodeJs,
40
+ coverageEnabled = false,
41
+ collectPerformance,
42
+
43
+ env,
44
+ debugPort,
45
+ debugMode,
46
+ debugModeInheritBreak,
47
+ inheritProcessEnv = true,
48
+ commandLineOptions = [],
49
+ }) => {
50
+ if (env !== undefined && typeof env !== "object") {
51
+ throw new TypeError(`env must be an object, got ${env}`)
52
+ }
53
+ env = {
54
+ ...env,
55
+ JSENV: true,
56
+ }
57
+ if (coverageMethodForNodeJs !== "NODE_V8_COVERAGE") {
58
+ env.NODE_V8_COVERAGE = ""
59
+ }
60
+
61
+ const workerThreadExecOptions = await createChildExecOptions({
62
+ signal,
63
+ debugPort,
64
+ debugMode,
65
+ debugModeInheritBreak,
66
+ })
67
+ const execArgvForWorkerThread = ExecOptions.toExecArgv({
68
+ ...workerThreadExecOptions,
69
+ ...ExecOptions.fromExecArgv(commandLineOptions),
70
+ })
71
+ const envForWorkerThread = {
72
+ ...(inheritProcessEnv ? process.env : {}),
73
+ ...env,
74
+ }
75
+
76
+ const cleanupCallbackList = createCallbackListNotifiedOnce()
77
+ const cleanup = async (reason) => {
78
+ await cleanupCallbackList.notify({ reason })
79
+ }
80
+ const actionOperation = Abort.startOperation()
81
+ actionOperation.addAbortSignal(signal)
82
+ // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
83
+ const workerThread = new Worker(
84
+ fileURLToPath(CONTROLLABLE_WORKER_THREAD_URL),
85
+ {
86
+ env: envForWorkerThread,
87
+ execArgv: execArgvForWorkerThread,
88
+ // workerData: { options },
89
+ // trackUnmanagedFds: true,
90
+ stdin: true,
91
+ stdout: true,
92
+ stderr: true,
93
+ },
94
+ )
95
+ const removeOutputListener = installWorkerThreadOutputListener(
96
+ workerThread,
97
+ ({ type, text }) => {
98
+ onConsole({ type, text })
99
+ },
100
+ )
101
+ const workerThreadReadyPromise = new Promise((resolve) => {
102
+ onceWorkerThreadMessage(workerThread, "ready", resolve)
103
+ })
104
+
105
+ const stop = memoize(async () => {
106
+ await workerThreadReadyPromise
107
+ await workerThread.terminate()
108
+ })
109
+
110
+ const winnerPromise = new Promise((resolve) => {
111
+ raceCallbacks(
112
+ {
113
+ aborted: (cb) => {
114
+ return actionOperation.addAbortCallback(cb)
115
+ },
116
+ error: (cb) => {
117
+ return onceWorkerThreadEvent(workerThread, "error", cb)
118
+ },
119
+ exit: (cb) => {
120
+ return onceWorkerThreadEvent(workerThread, "exit", (code, signal) => {
121
+ cb({ code, signal })
122
+ })
123
+ },
124
+ response: (cb) => {
125
+ return onceWorkerThreadMessage(workerThread, "action-result", cb)
126
+ },
127
+ },
128
+ resolve,
129
+ )
130
+ })
131
+
132
+ const getResult = async () => {
133
+ actionOperation.throwIfAborted()
134
+ await workerThreadReadyPromise
135
+ actionOperation.throwIfAborted()
136
+ await sendToWorkerThread(workerThread, {
137
+ type: "action",
138
+ data: {
139
+ actionType: "execute-using-dynamic-import",
140
+ actionParams: {
141
+ rootDirectoryUrl,
142
+ fileUrl: new URL(fileRelativeUrl, rootDirectoryUrl).href,
143
+ collectPerformance,
144
+ coverageEnabled,
145
+ coverageConfig,
146
+ coverageMethodForNodeJs,
147
+ exitAfterAction: true,
148
+ },
149
+ },
150
+ })
151
+ const winner = await winnerPromise
152
+ if (winner.name === "aborted") {
153
+ return {
154
+ status: "aborted",
155
+ }
156
+ }
157
+ if (winner.name === "error") {
158
+ const error = winner.data
159
+ removeOutputListener()
160
+ return {
161
+ status: "errored",
162
+ error,
163
+ }
164
+ }
165
+ if (winner.name === "exit") {
166
+ const { code } = winner.data
167
+ await cleanup("process exit")
168
+ if (code === 12) {
169
+ return {
170
+ status: "errored",
171
+ error: new Error(
172
+ `node process exited with 12 (the forked child process wanted to use a non-available port for debug)`,
173
+ ),
174
+ }
175
+ }
176
+ if (
177
+ code === null ||
178
+ code === 0 ||
179
+ code === EXIT_CODES.SIGINT ||
180
+ code === EXIT_CODES.SIGTERM ||
181
+ code === EXIT_CODES.SIGABORT
182
+ ) {
183
+ return {
184
+ status: "errored",
185
+ error: new Error(`node worker thread exited during execution`),
186
+ }
187
+ }
188
+ // process.exit(1) in child process or process.exitCode = 1 + process.exit()
189
+ // means there was an error even if we don't know exactly what.
190
+ return {
191
+ status: "errored",
192
+ error: new Error(
193
+ `node worker thread exited with code ${code} during execution`,
194
+ ),
195
+ }
196
+ }
197
+ const { status, value } = winner.data
198
+ if (status === "action-failed") {
199
+ return {
200
+ status: "errored",
201
+ error: value,
202
+ }
203
+ }
204
+ const { namespace, performance, coverage } = value
205
+ return {
206
+ status: "completed",
207
+ namespace,
208
+ performance,
209
+ coverage,
210
+ }
211
+ }
212
+
213
+ let result
214
+ try {
215
+ result = await getResult()
216
+ } catch (e) {
217
+ result = {
218
+ status: "errored",
219
+ error: e,
220
+ }
221
+ }
222
+
223
+ if (keepRunning) {
224
+ stopSignal.notify = stop
225
+ } else {
226
+ await stop()
227
+ }
228
+ await actionOperation.end()
229
+ return result
230
+ }
231
+
232
+ const installWorkerThreadOutputListener = (workerThread, callback) => {
233
+ // beware that we may receive ansi output here, should not be a problem but keep that in mind
234
+ const stdoutDataCallback = (chunk) => {
235
+ callback({ type: "log", text: String(chunk) })
236
+ }
237
+ workerThread.stdout.on("data", stdoutDataCallback)
238
+ const stdErrorDataCallback = (chunk) => {
239
+ callback({ type: "error", text: String(chunk) })
240
+ }
241
+ workerThread.stderr.on("data", stdErrorDataCallback)
242
+ return () => {
243
+ workerThread.stdout.removeListener("data", stdoutDataCallback)
244
+ workerThread.stderr.removeListener("data", stdoutDataCallback)
245
+ }
246
+ }
247
+
248
+ const sendToWorkerThread = (worker, { type, data }) => {
249
+ worker.postMessage({ jsenv: true, type, data })
250
+ }
251
+
252
+ const onceWorkerThreadMessage = (workerThread, type, callback) => {
253
+ const onmessage = (message) => {
254
+ if (message && message.jsenv && message.type === type) {
255
+ workerThread.removeListener("message", onmessage)
256
+ // eslint-disable-next-line no-eval
257
+ callback(message.data ? eval(`(${message.data})`) : undefined)
258
+ }
259
+ }
260
+ workerThread.on("message", onmessage)
261
+ return () => {
262
+ workerThread.removeListener("message", onmessage)
263
+ }
264
+ }
265
+
266
+ const onceWorkerThreadEvent = (worker, type, callback) => {
267
+ worker.once(type, callback)
268
+ return () => {
269
+ worker.removeListener(type, callback)
270
+ }
271
+ }
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Calling Profiler.startPreciseCoverage DO NOT propagate to
3
+ * subprocesses (new Worker or child_process.fork())
4
+ * So the best solution remains NODE_V8_COVERAGE
5
+ * This profiler strategy remains useful when:
6
+ * - As fallback when NODE_V8_COVERAGE is not configured
7
+ * - If explicitely enabled with coverageMethodForNodeJs: 'Profiler'
8
+ * - Used by jsenv during automated tests about coverage
9
+ * - Anyone prefering this approach over NODE_V8_COVERAGE and assuming
10
+ * it will not fork subprocess or don't care if coverage is missed for this code
11
+ * - https://v8.dev/blog/javascript-code-coverage#for-embedders
12
+ * - https://github.com/nodejs/node/issues/28283
13
+ * - https://vanilla.aslushnikov.com/?Profiler.startPreciseCoverage
14
+ */
15
+
16
+ import { Session } from "node:inspector"
17
+
18
+ export const startJsCoverage = async ({
19
+ callCount = true,
20
+ detailed = true,
21
+ } = {}) => {
22
+ const session = new Session()
23
+ session.connect()
24
+ const postSession = (action, options) => {
25
+ const promise = new Promise((resolve, reject) => {
26
+ session.post(action, options, (error, data) => {
27
+ if (error) {
28
+ reject(error)
29
+ } else {
30
+ resolve(data)
31
+ }
32
+ })
33
+ })
34
+ return promise
35
+ }
36
+
37
+ await postSession("Profiler.enable")
38
+ await postSession("Profiler.startPreciseCoverage", { callCount, detailed })
39
+
40
+ const takeJsCoverage = async () => {
41
+ const coverage = await postSession("Profiler.takePreciseCoverage")
42
+ return coverage
43
+ }
44
+
45
+ const stopJsCoverage = async () => {
46
+ const coverage = await takeJsCoverage()
47
+ await postSession("Profiler.stopPreciseCoverage")
48
+ await postSession("Profiler.disable")
49
+ return coverage
50
+ }
51
+
52
+ return {
53
+ takeJsCoverage,
54
+ stopJsCoverage,
55
+ }
56
+ }
package/src/main.js CHANGED
@@ -2,6 +2,7 @@
2
2
  export { startDevServer } from "./dev/start_dev_server.js"
3
3
  // test
4
4
  export { executeTestPlan } from "./test/execute_test_plan.js"
5
+ // runtimes (used to execute tests)
5
6
  export {
6
7
  chromium,
7
8
  chromiumIsolatedTab,
@@ -14,7 +15,8 @@ export {
14
15
  webkit,
15
16
  webkitIsolatedTab,
16
17
  } from "./execute/runtimes/browsers/webkit.js"
17
- export { nodeProcess } from "./execute/runtimes/node/node_process.js"
18
+ export { nodeChildProcess } from "./execute/runtimes/node/node_child_process.js"
19
+ export { nodeWorkerThread } from "./execute/runtimes/node/node_worker_thread.js"
18
20
  // build
19
21
  export { build } from "./build/build.js"
20
22
  export { startBuildServer } from "./build/start_build_server.js"
@@ -86,6 +86,7 @@ export const createKitchen = ({
86
86
  baseUrl,
87
87
  isOriginalPosition,
88
88
  shouldHandle,
89
+ isEntryPoint = false,
89
90
  isInline = false,
90
91
  injected = false,
91
92
  isRessourceHint = false,
@@ -122,6 +123,7 @@ export const createKitchen = ({
122
123
  baseUrl,
123
124
  isOriginalPosition,
124
125
  shouldHandle,
126
+ isEntryPoint,
125
127
  isInline,
126
128
  injected,
127
129
  isRessourceHint,
@@ -138,6 +140,7 @@ export const createKitchen = ({
138
140
  reference.next = newReference
139
141
  newReference.prev = reference
140
142
  newReference.original = reference.original || reference
143
+ // newReference.isEntryPoint = reference.isEntryPoint
141
144
  }
142
145
  const resolveReference = (reference) => {
143
146
  try {
@@ -166,12 +169,16 @@ export const createKitchen = ({
166
169
  },
167
170
  )
168
171
 
169
- const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
170
- applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext)
171
-
172
172
  const referenceUrlObject = new URL(reference.url)
173
173
  reference.searchParams = referenceUrlObject.searchParams
174
174
  reference.generatedUrl = reference.url
175
+ if (reference.searchParams.has("entry_point")) {
176
+ reference.isEntryPoint = true
177
+ }
178
+
179
+ const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url)
180
+ applyReferenceEffectsOnUrlInfo(reference, urlInfo, kitchenContext)
181
+
175
182
  // This hook must touch reference.generatedUrl, NOT reference.url
176
183
  // And this is because this hook inject query params used to:
177
184
  // - bypass browser cache (?v)
@@ -286,6 +293,7 @@ export const createKitchen = ({
286
293
  status = 200,
287
294
  headers = {},
288
295
  body,
296
+ isEntryPoint,
289
297
  } = fetchUrlContentReturnValue
290
298
  if (status !== 200) {
291
299
  throw new Error(`unexpected status, ${status}`)
@@ -319,6 +327,9 @@ export const createKitchen = ({
319
327
  if (data) {
320
328
  Object.assign(urlInfo.data, data)
321
329
  }
330
+ if (typeof isEntryPoint === "boolean") {
331
+ urlInfo.isEntryPoint = isEntryPoint
332
+ }
322
333
  if (filename) {
323
334
  urlInfo.filename = filename
324
335
  }
@@ -620,6 +631,7 @@ export const createKitchen = ({
620
631
 
621
632
  const prepareEntryPoint = (params) => {
622
633
  const entryReference = createReference(params)
634
+ entryReference.isEntryPoint = true
623
635
  const entryUrlInfo = resolveReference(entryReference)
624
636
  return [entryReference, entryUrlInfo]
625
637
  }
@@ -718,6 +730,10 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
718
730
  }
719
731
  urlInfo.originalUrl = urlInfo.originalUrl || reference.url
720
732
 
733
+ if (reference.isEntryPoint || isWebWorkerEntryPointReference(reference)) {
734
+ urlInfo.isEntryPoint = true
735
+ }
736
+
721
737
  Object.assign(urlInfo.data, reference.data)
722
738
  Object.assign(urlInfo.timing, reference.timing)
723
739
  if (reference.injected) {
@@ -746,9 +762,6 @@ const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
746
762
  : reference.content
747
763
  urlInfo.content = reference.content
748
764
  }
749
- if (isWebWorkerEntryPointReference(reference)) {
750
- urlInfo.data.isWebWorkerEntryPoint = true
751
- }
752
765
  }
753
766
 
754
767
  const adjustUrlSite = (urlInfo, { urlGraph, url, line, column }) => {