@jsenv/core 23.2.2 → 23.4.2

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 (56) hide show
  1. package/{license → LICENSE} +0 -0
  2. package/package.json +13 -14
  3. package/readme.md +4 -4
  4. package/src/buildProject.js +12 -13
  5. package/src/execute.js +92 -93
  6. package/src/executeTestPlan.js +9 -8
  7. package/src/internal/browser-launcher/executeHtmlFile.js +26 -23
  8. package/src/internal/building/buildUsingRollup.js +3 -4
  9. package/src/internal/building/build_logs.js +7 -6
  10. package/src/internal/building/createJsenvRollupPlugin.js +9 -14
  11. package/src/internal/building/url_trace.js +3 -4
  12. package/src/internal/compiling/createCompiledFileService.js +21 -6
  13. package/src/internal/compiling/startCompileServer.js +55 -46
  14. package/src/internal/executing/coverage/babel_plugin_instrument.js +1 -0
  15. package/src/internal/executing/coverage/reportToCoverage.js +147 -120
  16. package/src/internal/executing/{coverage → coverage_empty}/createEmptyCoverage.js +0 -0
  17. package/src/internal/executing/coverage_empty/list_files_not_covered.js +20 -0
  18. package/src/internal/executing/{coverage → coverage_empty}/relativeUrlToEmptyCoverage.js +3 -4
  19. package/src/internal/executing/{coverage/generateCoverageHtmlDirectory.js → coverage_reporter/coverage_reporter_html_directory.js} +11 -4
  20. package/src/internal/executing/{coverage/generateCoverageJsonFile.js → coverage_reporter/coverage_reporter_json_file.js} +0 -0
  21. package/src/internal/executing/{coverage/generateCoverageTextLog.js → coverage_reporter/coverage_reporter_text_log.js} +4 -2
  22. package/src/internal/executing/{coverage → coverage_reporter}/istanbulCoverageMapFromCoverage.js +0 -0
  23. package/src/internal/executing/{coverage/normalizeIstanbulCoverage.js → coverage_utils/file_by_file_coverage.js} +9 -7
  24. package/src/internal/executing/coverage_utils/istanbul_coverage_composition.js +28 -0
  25. package/src/internal/executing/coverage_utils/v8_and_istanbul.js +38 -0
  26. package/src/internal/executing/coverage_utils/v8_coverage_composition.js +23 -0
  27. package/src/internal/executing/coverage_utils/v8_coverage_from_directory.js +65 -0
  28. package/src/internal/executing/coverage_utils/v8_coverage_to_istanbul.js +90 -0
  29. package/src/internal/executing/createSummaryLog.js +15 -15
  30. package/src/internal/executing/executeConcurrently.js +92 -32
  31. package/src/internal/executing/executePlan.js +84 -81
  32. package/src/internal/executing/executionLogs.js +14 -18
  33. package/src/internal/executing/execution_colors.js +6 -12
  34. package/src/internal/executing/launchAndExecute.js +172 -169
  35. package/src/internal/node-launcher/createControllableNodeProcess.js +26 -23
  36. package/src/launchBrowser.js +72 -69
  37. package/src/launchNode.js +11 -99
  38. package/src/startExploring.js +2 -17
  39. package/src/abort/abortable.js +0 -172
  40. package/src/abort/callback_list.js +0 -64
  41. package/src/abort/callback_race.js +0 -34
  42. package/src/abort/cleaner.js +0 -22
  43. package/src/abort/main.js +0 -32
  44. package/src/abort/process_teardown_events.js +0 -59
  45. package/src/internal/createCallbackList.js +0 -21
  46. package/src/internal/executing/coverage/composeIstanbulCoverages.js +0 -108
  47. package/src/internal/executing/coverage/composeV8Coverages.js +0 -20
  48. package/src/internal/executing/coverage/istanbulCoverageFromCoverages.js +0 -43
  49. package/src/internal/executing/coverage/istanbulCoverageFromV8Coverage.js +0 -79
  50. package/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js +0 -40
  51. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +0 -67
  52. package/src/internal/executing/logUtils.js +0 -30
  53. package/src/internal/executing/writeLog.js +0 -106
  54. package/src/internal/executing/writeLog.test-manual.js +0 -62
  55. package/src/internal/logs/log_style.js +0 -40
  56. package/src/signal/signal.js +0 -65
@@ -1,12 +1,14 @@
1
1
  // https://github.com/microsoft/playwright/blob/master/docs/api.md
2
2
 
3
3
  import { createDetailedMessage } from "@jsenv/logger"
4
-
5
4
  import {
6
- Abortable,
5
+ Abort,
6
+ createCallbackListNotifiedOnce,
7
+ createCallbackList,
7
8
  raceProcessTeardownEvents,
8
- } from "@jsenv/core/src/abort/main.js"
9
- import { createSignal } from "@jsenv/core/src/signal/signal.js"
9
+ } from "@jsenv/abort"
10
+ import { memoize } from "@jsenv/filesystem"
11
+
10
12
  import { fetchUrl } from "./internal/fetchUrl.js"
11
13
  import { validateResponse } from "./internal/response_validation.js"
12
14
  import { trackPageToNotify } from "./internal/browser-launcher/trackPageToNotify.js"
@@ -35,7 +37,7 @@ chromiumRuntime.launch = async ({
35
37
  collectPerformance,
36
38
  measurePerformance,
37
39
  collectCoverage,
38
- coverageConfig,
40
+ coverageIgnorePredicate,
39
41
  coverageForceIstanbul,
40
42
 
41
43
  headless = true,
@@ -45,7 +47,9 @@ chromiumRuntime.launch = async ({
45
47
  stopOnExit = true,
46
48
  share = false,
47
49
  }) => {
48
- const launchBrowserOperation = Abortable.fromSignal(signal)
50
+ const launchBrowserOperation = Abort.startOperation()
51
+ launchBrowserOperation.addAbortSignal(signal)
52
+
49
53
  const sharingToken = share
50
54
  ? chromiumSharing.getSharingToken({
51
55
  chromiumExecutablePath,
@@ -54,9 +58,7 @@ chromiumRuntime.launch = async ({
54
58
  debugPort,
55
59
  })
56
60
  : chromiumSharing.getUniqueSharingToken()
57
-
58
61
  if (!sharingToken.isUsed()) {
59
- Abortable.throwIfAborted(launchBrowserOperation)
60
62
  const { chromium } = await import("playwright")
61
63
  const launchOperation = launchBrowser("chromium", {
62
64
  browserClass: chromium,
@@ -77,10 +79,9 @@ chromiumRuntime.launch = async ({
77
79
  sharingToken.setSharedValue(launchOperation)
78
80
  }
79
81
 
80
- const [launchOperation, stopUsingBrowser] = sharingToken.useSharedValue()
81
- launchBrowserOperation.cleaner.addCallback(stopUsingBrowser)
82
-
83
- const browser = await launchOperation
82
+ const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
83
+ launchBrowserOperation.addEndCallback(stopUsingBrowser)
84
+ const browser = await browserPromise
84
85
 
85
86
  if (debug) {
86
87
  // https://github.com/puppeteer/puppeteer/blob/v2.0.0/docs/api.md#browserwsendpoint
@@ -105,7 +106,6 @@ chromiumRuntime.launch = async ({
105
106
  }
106
107
 
107
108
  const browserHooks = browserToRuntimeHooks(browser, {
108
- launchBrowserOperation,
109
109
  browserServerLogLevel,
110
110
 
111
111
  projectDirectoryUrl,
@@ -115,7 +115,7 @@ chromiumRuntime.launch = async ({
115
115
  collectPerformance,
116
116
  measurePerformance,
117
117
  collectCoverage,
118
- coverageConfig,
118
+ coverageIgnorePredicate,
119
119
  coverageForceIstanbul,
120
120
  coveragePlaywrightAPIAvailable: true,
121
121
  })
@@ -151,20 +151,20 @@ firefoxRuntime.launch = async ({
151
151
  collectPerformance,
152
152
  measurePerformance,
153
153
  collectCoverage,
154
- coverageConfig,
154
+ coverageIgnorePredicate,
155
155
  coverageForceIstanbul,
156
156
 
157
157
  headless = true,
158
158
  stopOnExit = true,
159
159
  share = false,
160
160
  }) => {
161
- const launchBrowserOperation = Abortable.fromSignal(signal)
161
+ const launchBrowserOperation = Abort.startOperation()
162
+ launchBrowserOperation.addAbortSignal(signal)
163
+
162
164
  const sharingToken = share
163
165
  ? firefoxSharing.getSharingToken({ firefoxExecutablePath, headless })
164
166
  : firefoxSharing.getUniqueSharingToken()
165
-
166
167
  if (!sharingToken.isUsed()) {
167
- Abortable.throwIfAborted(launchBrowserOperation)
168
168
  const { firefox } = await import("playwright")
169
169
  const launchOperation = launchBrowser("firefox", {
170
170
  browserClass: firefox,
@@ -179,10 +179,9 @@ firefoxRuntime.launch = async ({
179
179
  sharingToken.setSharedValue(launchOperation)
180
180
  }
181
181
 
182
- const [launchOperation, stopUsingBrowser] = sharingToken.useSharedValue()
183
- launchBrowserOperation.cleaner.addCallback(stopUsingBrowser)
184
-
185
- const browser = await launchOperation
182
+ const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
183
+ launchBrowserOperation.addEndCallback(stopUsingBrowser)
184
+ const browser = await browserPromise
186
185
 
187
186
  const browserHooks = browserToRuntimeHooks(browser, {
188
187
  launchBrowserOperation,
@@ -195,7 +194,7 @@ firefoxRuntime.launch = async ({
195
194
  collectPerformance,
196
195
  measurePerformance,
197
196
  collectCoverage,
198
- coverageConfig,
197
+ coverageIgnorePredicate,
199
198
  coverageForceIstanbul,
200
199
  })
201
200
 
@@ -230,20 +229,21 @@ webkitRuntime.launch = async ({
230
229
  collectPerformance,
231
230
  measurePerformance,
232
231
  collectCoverage,
233
- coverageConfig,
232
+ coverageIgnorePredicate,
234
233
  coverageForceIstanbul,
235
234
 
236
235
  headless = true,
237
236
  stopOnExit = true,
238
237
  share = false,
239
238
  }) => {
240
- const launchBrowserOperation = Abortable.fromSignal(signal)
239
+ const launchBrowserOperation = Abort.startOperation()
240
+ launchBrowserOperation.addAbortSignal(signal)
241
+
241
242
  const sharingToken = share
242
243
  ? webkitSharing.getSharingToken({ webkitExecutablePath, headless })
243
244
  : webkitSharing.getUniqueSharingToken()
244
245
 
245
246
  if (!sharingToken.isUsed()) {
246
- Abortable.throwIfAborted(launchBrowserOperation)
247
247
  const { webkit } = await import("playwright")
248
248
  const launchOperation = launchBrowser("webkit", {
249
249
  browserClass: webkit,
@@ -257,10 +257,9 @@ webkitRuntime.launch = async ({
257
257
  sharingToken.setSharedValue(launchOperation)
258
258
  }
259
259
 
260
- const [launchOperation, stopUsingBrowser] = sharingToken.useSharedValue()
261
- launchBrowserOperation.cleaner.addCallback(stopUsingBrowser)
262
-
263
- const browser = await launchOperation
260
+ const [browserPromise, stopUsingBrowser] = sharingToken.useSharedValue()
261
+ launchBrowserOperation.addEndCallback(stopUsingBrowser)
262
+ const browser = await browserPromise
264
263
 
265
264
  const browserHooks = browserToRuntimeHooks(browser, {
266
265
  launchBrowserOperation,
@@ -273,7 +272,7 @@ webkitRuntime.launch = async ({
273
272
  collectPerformance,
274
273
  measurePerformance,
275
274
  collectCoverage,
276
- coverageConfig,
275
+ coverageIgnorePredicate,
277
276
  coverageForceIstanbul,
278
277
  ignoreErrorHook: (error) => {
279
278
  // we catch error during execution but safari throw unhandled rejection
@@ -314,8 +313,8 @@ const launchBrowser = async (
314
313
  { launchBrowserOperation, browserClass, options, stopOnExit },
315
314
  ) => {
316
315
  if (stopOnExit) {
317
- Abortable.effect(launchBrowserOperation, (cb) =>
318
- raceProcessTeardownEvents(
316
+ launchBrowserOperation.addAbortSource((abort) => {
317
+ return raceProcessTeardownEvents(
319
318
  {
320
319
  SIGHUP: true,
321
320
  SIGTERM: true,
@@ -323,9 +322,9 @@ const launchBrowser = async (
323
322
  beforeExit: true,
324
323
  exit: true,
325
324
  },
326
- cb,
327
- ),
328
- )
325
+ abort,
326
+ )
327
+ })
329
328
  }
330
329
 
331
330
  try {
@@ -337,19 +336,16 @@ const launchBrowser = async (
337
336
  handleSIGTERM: false,
338
337
  handleSIGHUP: false,
339
338
  })
340
- launchBrowserOperation.cleaner.addCallback(async () => {
341
- await stopBrowser(browser)
342
- })
343
- if (launchBrowserOperation.signal.aborted) {
344
- await launchBrowserOperation.cleaner.clean()
345
- Abortable.throwIfAborted(launchBrowserOperation)
346
- }
339
+ launchBrowserOperation.throwIfAborted()
347
340
  return browser
348
341
  } catch (e) {
349
342
  if (launchBrowserOperation.signal.aborted && isTargetClosedError(e)) {
350
- return e
343
+ // rethrow the abort error
344
+ launchBrowserOperation.throwIfAborted()
351
345
  }
352
346
  throw e
347
+ } finally {
348
+ await launchBrowserOperation.end()
353
349
  }
354
350
  }
355
351
 
@@ -375,8 +371,6 @@ const stopBrowser = async (browser) => {
375
371
  const browserToRuntimeHooks = (
376
372
  browser,
377
373
  {
378
- launchBrowserOperation,
379
-
380
374
  projectDirectoryUrl,
381
375
  compileServerOrigin,
382
376
  outDirectoryRelativeUrl,
@@ -384,33 +378,47 @@ const browserToRuntimeHooks = (
384
378
  collectPerformance,
385
379
  measurePerformance,
386
380
  collectCoverage,
387
- coverageConfig,
381
+ coverageIgnorePredicate,
388
382
  coverageForceIstanbul,
389
383
  coveragePlaywrightAPIAvailable = false,
390
384
  ignoreErrorHook = () => false,
391
385
  transformErrorHook = (error) => error,
392
386
  },
393
387
  ) => {
394
- const stoppedSignal = createSignal({ once: true })
388
+ const stopCallbackList = createCallbackListNotifiedOnce()
389
+ const stoppedCallbackList = createCallbackListNotifiedOnce()
390
+ const stop = memoize(async (reason) => {
391
+ await stopCallbackList.notify({ reason })
392
+ stoppedCallbackList.notify({ reason })
393
+ return { graceful: false }
394
+ })
395
+
395
396
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-disconnected
396
- browser.on("disconnected", stoppedSignal.emit)
397
+ browser.on("disconnected", () => {
398
+ stop()
399
+ })
400
+
401
+ stopCallbackList.add(async () => {
402
+ await stopBrowser(browser)
403
+ })
397
404
 
398
- const errorSignal = createSignal()
405
+ const errorCallbackList = createCallbackList()
399
406
 
400
- const outputSignal = createSignal()
407
+ const outputCallbackList = createCallbackList()
401
408
 
402
409
  const execute = async ({
403
410
  signal,
404
411
  fileRelativeUrl,
405
412
  ignoreHTTPSErrors = true, // we mostly use self signed certificates during tests
406
413
  }) => {
407
- const executeOperation = Abortable.fromSignal(signal)
408
- Abortable.throwIfAborted(executeOperation)
414
+ const executeOperation = Abort.startOperation()
415
+ executeOperation.addAbortSignal(signal)
416
+ executeOperation.throwIfAborted()
409
417
  // open a tab to execute to the file
410
418
  const browserContext = await browser.newContext({ ignoreHTTPSErrors })
411
- Abortable.throwIfAborted(executeOperation)
419
+ executeOperation.throwIfAborted()
412
420
  const page = await browserContext.newPage()
413
- launchBrowserOperation.cleaner.addCallback(async () => {
421
+ executeOperation.addEndCallback(async () => {
414
422
  try {
415
423
  await browserContext.close()
416
424
  } catch (e) {
@@ -425,15 +433,15 @@ const browserToRuntimeHooks = (
425
433
  onError: (error) => {
426
434
  error = transformErrorHook(error)
427
435
  if (!ignoreErrorHook(error)) {
428
- errorSignal.emit(error)
436
+ errorCallbackList.notify(error)
429
437
  }
430
438
  },
431
- onConsole: outputSignal.emit,
439
+ onConsole: outputCallbackList.notify,
432
440
  })
433
- launchBrowserOperation.cleaner.addCallback(stopTrackingToNotify)
441
+ stoppedCallbackList.add(stopTrackingToNotify)
434
442
 
435
443
  const result = await executeHtmlFile(fileRelativeUrl, {
436
- launchBrowserOperation,
444
+ executeOperation,
437
445
 
438
446
  projectDirectoryUrl,
439
447
  compileServerOrigin,
@@ -443,25 +451,20 @@ const browserToRuntimeHooks = (
443
451
  measurePerformance,
444
452
  collectPerformance,
445
453
  collectCoverage,
446
- coverageConfig,
447
454
  coverageForceIstanbul,
448
455
  coveragePlaywrightAPIAvailable,
456
+ coverageIgnorePredicate,
449
457
  transformErrorHook,
450
458
  })
451
459
  return result
452
460
  }
453
461
 
454
- Abortable.cleanOnAbort(launchBrowserOperation)
455
-
456
462
  return {
457
- stoppedSignal,
458
- errorSignal,
459
- outputSignal,
463
+ stoppedCallbackList,
464
+ errorCallbackList,
465
+ outputCallbackList,
460
466
  execute,
461
- stop: async (reason) => {
462
- await launchBrowserOperation.cleaner.clean(reason)
463
- return { graceful: false }
464
- },
467
+ stop,
465
468
  }
466
469
  }
467
470
 
package/src/launchNode.js CHANGED
@@ -1,18 +1,9 @@
1
1
  import { Script } from "node:vm"
2
- import cuid from "cuid"
3
2
  import { loggerToLogLevel } from "@jsenv/logger"
4
- import {
5
- writeDirectory,
6
- resolveUrl,
7
- urlToFileSystemPath,
8
- removeFileSystemNode,
9
- moveDirectoryContent,
10
- } from "@jsenv/filesystem"
11
3
 
12
4
  import { jsenvCoreDirectoryUrl } from "@jsenv/core/src/internal/jsenvCoreDirectoryUrl.js"
13
5
  import { escapeRegexpSpecialCharacters } from "./internal/escapeRegexpSpecialCharacters.js"
14
6
  import { createControllableNodeProcess } from "./internal/node-launcher/createControllableNodeProcess.js"
15
- import { v8CoverageFromNodeV8Directory } from "./internal/executing/coverage/v8CoverageFromNodeV8Directory.js"
16
7
 
17
8
  export const nodeRuntime = {
18
9
  name: "node",
@@ -30,8 +21,8 @@ nodeRuntime.launch = async ({
30
21
  measurePerformance,
31
22
  collectPerformance,
32
23
  collectCoverage = false,
33
- coverageConfig,
34
24
  coverageForceIstanbul,
25
+ coverageConfig,
35
26
 
36
27
  debugPort,
37
28
  debugMode,
@@ -69,59 +60,10 @@ nodeRuntime.launch = async ({
69
60
  JSENV: true,
70
61
  }
71
62
 
72
- let finalizeExecutionResult
73
-
74
- if (collectCoverage) {
75
- // v8 coverage is written in a directoy and auto propagate to subprocesses
76
- // through process.env.NODE_V8_COVERAGE.
77
-
78
- if (coverageForceIstanbul) {
79
- // if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
80
- // into the child_process
81
- env.NODE_V8_COVERAGE = ""
82
- }
83
- // } else if (process.env.NODE_V8_COVERAGE) {
84
- // // The V8_COVERAGE was already set by a parent process or command line.
85
- // // It's the caller that is interested into coverage, it's not anymore this script
86
- // // responsability to set process.env.NODE_V8_COVERAGE nor to read
87
- // // coverage files in the v8 directory.
88
- // }
89
- // In fact it is so that it's possible to go coverageception:
90
- // jsenv collect coverage for tests
91
- // which are testing that coverage can be collected for tests
92
- // this is possible because we overriding the child process NODE_V8_COVERAGE
93
- else {
94
- const NODE_V8_COVERAGE = await getNodeV8CoverageDir({
95
- projectDirectoryUrl,
96
- })
97
- env.NODE_V8_COVERAGE = NODE_V8_COVERAGE
98
-
99
- // the v8 coverage directory is available once the child process is disconnected
100
- finalizeExecutionResult = async (executionResult) => {
101
- if (
102
- executionResult.status === "timedout" ||
103
- executionResult.status === "aborted"
104
- ) {
105
- return executionResult
106
- }
107
-
108
- const coverage = await ensureV8CoverageDirClean(async () => {
109
- // prefer istanbul if available
110
- if (executionResult.coverage) {
111
- return executionResult.coverage
112
- }
113
-
114
- const v8Coverage = await v8CoverageFromNodeV8Directory({
115
- projectDirectoryUrl,
116
- NODE_V8_COVERAGE,
117
- coverageConfig,
118
- })
119
- return v8Coverage
120
- }, NODE_V8_COVERAGE)
121
- executionResult.coverage = coverage
122
- return executionResult
123
- }
124
- }
63
+ if (coverageForceIstanbul) {
64
+ // if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
65
+ // into the child_process
66
+ env.NODE_V8_COVERAGE = ""
125
67
  }
126
68
 
127
69
  commandLineOptions = [
@@ -132,9 +74,9 @@ nodeRuntime.launch = async ({
132
74
  const logLevel = loggerToLogLevel(logger)
133
75
  const {
134
76
  execArgv,
135
- stoppedSignal,
136
- errorSignal,
137
- outputSignal,
77
+ stoppedCallbackList,
78
+ errorCallbackList,
79
+ outputCallbackList,
138
80
  stop,
139
81
  requestActionOnChildProcess,
140
82
  } = await createControllableNodeProcess({
@@ -193,44 +135,14 @@ nodeRuntime.launch = async ({
193
135
  // because process.env is very big
194
136
  // env,
195
137
  },
196
- stoppedSignal,
197
- errorSignal,
198
- outputSignal,
138
+ stoppedCallbackList,
139
+ errorCallbackList,
140
+ outputCallbackList,
199
141
  stop,
200
142
  execute,
201
- finalizeExecutionResult,
202
143
  }
203
144
  }
204
145
 
205
- const ensureV8CoverageDirClean = async (fn, NODE_V8_COVERAGE) => {
206
- try {
207
- return await fn()
208
- } finally {
209
- if (process.env.NODE_V8_COVERAGE === NODE_V8_COVERAGE) {
210
- // do not try to remove or copy coverage
211
- } else if (process.env.NODE_V8_COVERAGE) {
212
- await moveDirectoryContent({
213
- from: NODE_V8_COVERAGE,
214
- to: process.env.NODE_V8_COVERAGE,
215
- })
216
- await removeFileSystemNode(NODE_V8_COVERAGE, {})
217
- } else {
218
- await removeFileSystemNode(NODE_V8_COVERAGE, {
219
- recursive: true,
220
- })
221
- }
222
- }
223
- }
224
-
225
- const getNodeV8CoverageDir = async ({ projectDirectoryUrl }) => {
226
- const v8CoverageDirectory = resolveUrl(
227
- `./coverage-v8/${cuid()}`,
228
- projectDirectoryUrl,
229
- )
230
- await writeDirectory(v8CoverageDirectory, { allowUseless: true })
231
- return urlToFileSystemPath(v8CoverageDirectory)
232
- }
233
-
234
146
  const transformExecutionResult = (
235
147
  executionResult,
236
148
  { compileServerOrigin, projectDirectoryUrl },
@@ -4,10 +4,6 @@ import {
4
4
  urlToRelativeUrl,
5
5
  } from "@jsenv/filesystem"
6
6
 
7
- import {
8
- Abortable,
9
- raceProcessTeardownEvents,
10
- } from "@jsenv/core/src/abort/main.js"
11
7
  import { jsenvCoreDirectoryUrl } from "./internal/jsenvCoreDirectoryUrl.js"
12
8
  import {
13
9
  assertProjectDirectoryUrl,
@@ -60,18 +56,6 @@ export const startExploring = async ({
60
56
  projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
61
57
  await assertProjectDirectoryExists({ projectDirectoryUrl })
62
58
 
63
- const exploringServerOperation = Abortable.fromSignal(signal)
64
- if (handleSIGINT) {
65
- Abortable.effect(exploringServerOperation, (cb) =>
66
- raceProcessTeardownEvents(
67
- {
68
- SIGINT: true,
69
- },
70
- cb,
71
- ),
72
- )
73
- }
74
-
75
59
  const outDirectoryRelativeUrl = computeOutDirectoryRelativeUrl({
76
60
  projectDirectoryUrl,
77
61
  jsenvDirectoryRelativeUrl,
@@ -95,7 +79,8 @@ export const startExploring = async ({
95
79
  })
96
80
 
97
81
  const compileServer = await startCompileServer({
98
- signal: exploringServerOperation.signal,
82
+ signal,
83
+ handleSIGINT,
99
84
  keepProcessAlive,
100
85
 
101
86
  projectDirectoryUrl,
@@ -1,172 +0,0 @@
1
- /*
2
- * https://github.com/whatwg/dom/issues/920
3
- */
4
-
5
- import { createCleaner } from "./cleaner.js"
6
- import { raceCallbacks } from "./callback_race.js"
7
- import { createCallbackList } from "./callback_list.js"
8
-
9
- export const Abortable = {
10
- throwIfAborted: (operation) => {
11
- if (operation.signal.aborted) {
12
- const error = new Error(`The operation was aborted`)
13
- error.name = "AbortError"
14
- error.type = "aborted"
15
- throw error
16
- }
17
- },
18
-
19
- isAbortError: (error) => {
20
- return error.name === "AbortError"
21
- },
22
-
23
- start: () => {
24
- const abortController = new AbortController()
25
- const abort = (value) => abortController.abort(value)
26
- const signal = abortController.signal
27
- const cleaner = createCleaner()
28
-
29
- // abortCallbackList is used to ignore the max listeners warning from Node.js
30
- // this warning is useful but becomes problematic when it's expected
31
- // (a function doing 20 http call in parallel)
32
- // To be 100% sure we don't have memory leak, only Abortable.asyncCallback
33
- // uses abortCallbackList to know when something is aborted
34
- const abortCallbackList = createCallbackList()
35
- signal.onabort = () => {
36
- const callbacks = abortCallbackList.copy()
37
- abortCallbackList.clear()
38
- callbacks.forEach((callback) => {
39
- callback()
40
- })
41
- }
42
- cleaner.addCallback(() => {
43
- abortCallbackList.clear()
44
- })
45
-
46
- const operation = {
47
- abort,
48
- signal,
49
- abortCallbackList,
50
- cleaner,
51
- }
52
-
53
- return operation
54
- },
55
-
56
- fromSignal: (signal) => {
57
- const operation = Abortable.start()
58
- Abortable.followSignal(operation, signal)
59
- return operation
60
- },
61
-
62
- followSignal: (operation, signal, cleanup = cleanupNoop) => {
63
- if (operation.signal.aborted) {
64
- return () => {}
65
- }
66
-
67
- if (signal.aborted) {
68
- operation.abort()
69
- return () => {}
70
- }
71
-
72
- return raceCallbacks(
73
- {
74
- parent_abort: (cb) => {
75
- return operation.abortCallbackList.add(cb)
76
- },
77
- child_abort: (cb) => {
78
- return addEventListener(signal, "abort", cb)
79
- },
80
- cleaned: (cb) => {
81
- return operation.cleaner.addCallback(cb)
82
- },
83
- },
84
- (winner) => {
85
- const raceEffects = {
86
- parent_abort: () => {
87
- // Nothing to do, exists to remove
88
- // - "abort" event listener on parent
89
- // - "abort" event listener on child
90
- cleanup()
91
- },
92
- child_abort: () => {
93
- operation.abort()
94
- },
95
- cleaned: () => {
96
- // Nothing to do, exists to remove
97
- // - "abort" event listener on parent
98
- // - "abort" event listener on child
99
- cleanup()
100
- },
101
- }
102
- raceEffects[winner.name](winner.value)
103
- },
104
- )
105
- },
106
-
107
- effect: (operation, effect) => {
108
- const abortController = new AbortController()
109
- const returnValue = effect((value) => abortController.abort(value))
110
- const cleanup =
111
- typeof returnValue === "function" ? returnValue : cleanupNoop
112
- const signal = abortController.signal
113
-
114
- Abortable.followSignal(operation, signal, cleanup)
115
- return {
116
- signal,
117
- cleanup,
118
- }
119
- },
120
-
121
- timeout: (operation, ms) => {
122
- return Abortable.effect(operation, (abort) => {
123
- const timeoutId = setTimeout(abort, ms)
124
- return () => {
125
- clearTimeout(timeoutId)
126
- }
127
- })
128
- },
129
-
130
- cleanOnAbort: (operation) => {
131
- const removeAbortEventListener = addEventListener(
132
- operation.signal,
133
- "abort",
134
- () => {
135
- removeAbortEventListener()
136
- operation.cleaner.clean("operation aborted")
137
- },
138
- )
139
- return removeAbortEventListener
140
- },
141
-
142
- // Provide a signal to the callback
143
- // this signal won't inherit the current signal max listeners
144
- asyncCallback: async (operation, asyncFunction) => {
145
- Abortable.throwIfAborted(operation)
146
-
147
- const abortController = new AbortController()
148
- const signal = abortController.signal
149
-
150
- const removeOperationAbortCallback = operation.abortCallbackList.add(() => {
151
- abortController.abort()
152
- })
153
-
154
- try {
155
- const value = await asyncFunction(signal)
156
- removeOperationAbortCallback()
157
- return value
158
- } catch (e) {
159
- removeOperationAbortCallback()
160
- throw e
161
- }
162
- },
163
- }
164
-
165
- const cleanupNoop = () => {}
166
-
167
- const addEventListener = (target, eventName, cb) => {
168
- target.addEventListener(eventName, cb)
169
- return () => {
170
- target.removeEventListener(eventName, cb)
171
- }
172
- }