@jsenv/core 23.1.1 → 23.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 (84) hide show
  1. package/dist/jsenv_browser_system.js +18 -82
  2. package/dist/jsenv_browser_system.js.map +11 -20
  3. package/dist/jsenv_compile_proxy.js +18 -82
  4. package/dist/jsenv_compile_proxy.js.map +11 -21
  5. package/dist/jsenv_exploring_index.js +127 -274
  6. package/dist/jsenv_exploring_index.js.map +76 -90
  7. package/dist/jsenv_exploring_redirector.js +21 -89
  8. package/dist/jsenv_exploring_redirector.js.map +13 -25
  9. package/dist/jsenv_toolbar.js +85 -149
  10. package/dist/jsenv_toolbar.js.map +51 -62
  11. package/dist/jsenv_toolbar_injector.js +185 -231
  12. package/dist/jsenv_toolbar_injector.js.map +30 -42
  13. package/{LICENSE → license} +0 -0
  14. package/package.json +7 -10
  15. package/src/abort/abortable.js +172 -0
  16. package/src/abort/callback_list.js +64 -0
  17. package/src/abort/callback_race.js +34 -0
  18. package/src/abort/cleaner.js +22 -0
  19. package/src/abort/main.js +32 -0
  20. package/src/abort/process_teardown_events.js +59 -0
  21. package/src/buildProject.js +132 -123
  22. package/src/execute.js +108 -107
  23. package/src/executeTestPlan.js +101 -121
  24. package/src/importUsingChildProcess.js +2 -1
  25. package/src/internal/browser-launcher/executeHtmlFile.js +22 -10
  26. package/src/internal/browser-utils/fetch-browser.js +4 -29
  27. package/src/internal/browser-utils/fetchUsingXHR.js +5 -7
  28. package/src/internal/building/buildUsingRollup.js +60 -24
  29. package/src/internal/building/createJsenvRollupPlugin.js +13 -31
  30. package/src/internal/building/ressource_builder.js +3 -6
  31. package/src/internal/building/sourcemap_loader.js +4 -5
  32. package/src/internal/building/url_fetcher.js +2 -5
  33. package/src/internal/building/url_loader.js +3 -6
  34. package/src/internal/compiling/compileFile.js +1 -2
  35. package/src/internal/compiling/createCompiledFileService.js +10 -10
  36. package/src/internal/compiling/jsenvCompilerForHtml.js +21 -7
  37. package/src/internal/compiling/jsenvCompilerForJavaScript.js +2 -0
  38. package/src/internal/compiling/startCompileServer.js +82 -134
  39. package/src/internal/executing/coverage/relativeUrlToEmptyCoverage.js +19 -30
  40. package/src/internal/executing/coverage/reportToCoverage.js +44 -24
  41. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +2 -15
  42. package/src/internal/executing/createSummaryLog.js +47 -34
  43. package/src/internal/executing/executeConcurrently.js +89 -47
  44. package/src/internal/executing/executePlan.js +33 -7
  45. package/src/internal/executing/executionLogs.js +25 -28
  46. package/src/internal/executing/execution_colors.js +15 -0
  47. package/src/internal/executing/generateExecutionSteps.js +3 -2
  48. package/src/internal/executing/launchAndExecute.js +213 -257
  49. package/src/internal/exploring/fetchExploringJson.js +3 -4
  50. package/src/internal/fetchUrl.js +6 -2
  51. package/src/internal/logs/log_style.js +16 -28
  52. package/src/internal/logs/msAsDuration.js +1 -1
  53. package/src/internal/node-launcher/createChildProcessOptions.js +4 -5
  54. package/src/internal/node-launcher/createControllableNodeProcess.js +117 -229
  55. package/src/internal/node-launcher/kill_process_tree.js +76 -0
  56. package/src/internal/node-launcher/nodeControllableFile.mjs +16 -10
  57. package/src/internal/{promise_track_race.js → promise_race.js} +2 -2
  58. package/src/internal/toolbar/execution/toolbar.execution.js +4 -0
  59. package/src/internal/toolbar/toolbar.html +157 -61
  60. package/src/internal/toolbar/toolbar.injector.js +8 -0
  61. package/src/internal/toolbar/util/animation.js +3 -7
  62. package/src/internal/toolbar/util/fetching.js +1 -30
  63. package/src/jsenvServiceWorkerFinalizer.js +1 -2
  64. package/src/launchBrowser.js +127 -127
  65. package/src/launchNode.js +32 -17
  66. package/src/playwright_browser_versions.js +3 -3
  67. package/src/requireUsingChildProcess.js +2 -1
  68. package/src/signal/signal.js +65 -0
  69. package/src/startExploring.js +71 -71
  70. package/src/internal/executeJsenvAsyncFunction.js +0 -34
  71. package/src/internal/toolbar/animation/toolbar-movie-icon.svg +0 -15
  72. package/src/internal/toolbar/compilation/flask.svg +0 -7
  73. package/src/internal/toolbar/compilation/info.svg +0 -9
  74. package/src/internal/toolbar/compilation/loupe.svg +0 -11
  75. package/src/internal/toolbar/compilation/toolbar_compilation.svg +0 -11
  76. package/src/internal/toolbar/eventsource/toolbar-power-icon.svg +0 -10
  77. package/src/internal/toolbar/eventsource/toolbar-power-off-icon.svg +0 -10
  78. package/src/internal/toolbar/responsive/toolbar-dots-icon.svg +0 -10
  79. package/src/internal/toolbar/settings/toolbar-settings-icon.svg +0 -9
  80. package/src/internal/toolbar/theme/toolbar-palette-icon.svg +0 -10
  81. package/src/internal/toolbar/toolbar-cross-icon.svg +0 -10
  82. package/src/internal/toolbar/toolbar-loading-icon.svg +0 -102
  83. package/src/internal/toolbar/toolbar-notif-icon.svg +0 -9
  84. package/src/internal/trackRessources.js +0 -23
package/src/execute.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import {
2
- createCancellationToken,
3
- composeCancellationToken,
4
- } from "@jsenv/cancellation"
5
-
2
+ Abortable,
3
+ raceProcessTeardownEvents,
4
+ } from "@jsenv/core/src/abort/main.js"
6
5
  import { normalizeRuntimeSupport } from "@jsenv/core/src/internal/generateGroupMap/runtime_support.js"
7
- import { executeJsenvAsyncFunction } from "./internal/executeJsenvAsyncFunction.js"
8
6
  import {
9
7
  assertProjectDirectoryUrl,
10
8
  assertProjectDirectoryExists,
@@ -13,11 +11,12 @@ import { startCompileServer } from "./internal/compiling/startCompileServer.js"
13
11
  import { launchAndExecute } from "./internal/executing/launchAndExecute.js"
14
12
 
15
13
  export const execute = async ({
14
+ signal = new AbortController().signal,
15
+ handleSIGINT = true,
16
+
16
17
  logLevel = "warn",
17
18
  compileServerLogLevel = logLevel,
18
19
  launchAndExecuteLogLevel = logLevel,
19
- cancellationToken = createCancellationToken(),
20
- cancelOnSIGINT = true,
21
20
 
22
21
  projectDirectoryUrl,
23
22
  jsenvDirectoryRelativeUrl,
@@ -59,119 +58,121 @@ export const execute = async ({
59
58
  runtimeStartedCallback,
60
59
  runtimeStoppedCallback,
61
60
  runtimeErrorAfterExecutionCallback,
62
- runtimeDisconnectCallback,
61
+ runtimeDisconnectCallback = () => {},
63
62
  }) => {
64
- const jsenvExecutionFunction = async ({ jsenvCancellationToken }) => {
65
- cancellationToken = composeCancellationToken(
66
- cancellationToken,
67
- jsenvCancellationToken,
63
+ projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
64
+ await assertProjectDirectoryExists({ projectDirectoryUrl })
65
+
66
+ if (typeof fileRelativeUrl !== "string") {
67
+ throw new TypeError(
68
+ `fileRelativeUrl must be a string, got ${fileRelativeUrl}`,
68
69
  )
70
+ }
71
+ fileRelativeUrl = fileRelativeUrl.replace(/\\/g, "/")
69
72
 
70
- projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
71
- await assertProjectDirectoryExists({ projectDirectoryUrl })
73
+ if (typeof runtime !== "object") {
74
+ throw new TypeError(`runtime must be an object, got ${runtime}`)
75
+ }
76
+ if (typeof runtime.launch !== "function") {
77
+ throw new TypeError(
78
+ `runtime.launch must be a function, got ${runtime.launch}`,
79
+ )
80
+ }
72
81
 
73
- if (typeof fileRelativeUrl !== "string") {
74
- throw new TypeError(
75
- `fileRelativeUrl must be a string, got ${fileRelativeUrl}`,
76
- )
77
- }
78
- fileRelativeUrl = fileRelativeUrl.replace(/\\/g, "/")
82
+ const executeOperation = Abortable.fromSignal(signal)
83
+ if (handleSIGINT) {
84
+ Abortable.effect(executeOperation, (cb) =>
85
+ raceProcessTeardownEvents(
86
+ {
87
+ SIGINT: true,
88
+ },
89
+ cb,
90
+ ),
91
+ )
92
+ }
79
93
 
80
- if (typeof runtime !== "object") {
81
- throw new TypeError(`runtime must be an object, got ${runtime}`)
82
- }
83
- if (typeof runtime.launch !== "function") {
84
- throw new TypeError(
85
- `runtime.launch must be a function, got ${runtime.launch}`,
86
- )
87
- }
94
+ const {
95
+ outDirectoryRelativeUrl,
96
+ origin: compileServerOrigin,
97
+ stop,
98
+ } = await startCompileServer({
99
+ signal: executeOperation.signal,
100
+ compileServerLogLevel,
101
+
102
+ projectDirectoryUrl,
103
+ jsenvDirectoryRelativeUrl,
104
+ jsenvDirectoryClean,
105
+ outDirectoryName: "out-dev",
106
+
107
+ importDefaultExtension,
108
+
109
+ compileServerProtocol,
110
+ compileServerPrivateKey,
111
+ compileServerCertificate,
112
+ compileServerIp,
113
+ compileServerPort,
114
+ babelPluginMap,
115
+ customCompilers,
116
+ runtimeSupport: normalizeRuntimeSupport({
117
+ [runtime.name]: runtime.version,
118
+ }),
119
+ compileServerCanReadFromFilesystem,
120
+ compileServerCanWriteOnFilesystem,
121
+ })
122
+ executeOperation.cleaner.addCallback(() => {
123
+ return stop()
124
+ })
88
125
 
89
- const {
90
- outDirectoryRelativeUrl,
91
- origin: compileServerOrigin,
92
- stop,
93
- } = await startCompileServer({
94
- cancellationToken,
95
- compileServerLogLevel,
126
+ const result = await launchAndExecute({
127
+ signal: executeOperation.signal,
128
+ launchAndExecuteLogLevel,
96
129
 
130
+ runtime,
131
+ runtimeParams: {
97
132
  projectDirectoryUrl,
98
- jsenvDirectoryRelativeUrl,
99
- jsenvDirectoryClean,
100
- outDirectoryName: "out-dev",
101
-
102
- importDefaultExtension,
103
-
104
- compileServerProtocol,
105
- compileServerPrivateKey,
106
- compileServerCertificate,
107
- compileServerIp,
108
- compileServerPort,
109
- babelPluginMap,
110
- customCompilers,
111
- runtimeSupport: normalizeRuntimeSupport({
112
- [runtime.name]: runtime.version,
113
- }),
114
- compileServerCanReadFromFilesystem,
115
- compileServerCanWriteOnFilesystem,
116
- })
117
-
118
- const result = await launchAndExecute({
119
- launchAndExecuteLogLevel,
120
- cancellationToken,
121
-
122
- runtime,
123
- runtimeParams: {
124
- projectDirectoryUrl,
125
- compileServerOrigin,
126
- outDirectoryRelativeUrl,
127
- ...runtimeParams,
128
- },
129
- executeParams: {
130
- fileRelativeUrl,
131
- },
132
-
133
- allocatedMs,
134
- measureDuration,
135
- mirrorConsole,
136
- captureConsole,
137
- collectRuntimeName,
138
- collectRuntimeVersion,
139
- inheritCoverage,
140
- collectCoverage,
141
- measurePerformance,
142
- collectPerformance,
143
-
144
- stopAfterExecute,
145
- stopAfterExecuteReason,
146
- gracefulStopAllocatedMs,
147
-
148
- runtimeConsoleCallback,
149
- runtimeStartedCallback,
150
- runtimeStoppedCallback,
151
- runtimeErrorAfterExecutionCallback,
152
- runtimeDisconnectCallback,
153
- })
154
-
155
- stop("single-execution-done")
156
-
157
- if (collectCompileServerInfo) {
158
- result.outDirectoryRelativeUrl = outDirectoryRelativeUrl
159
- result.compileServerOrigin = compileServerOrigin
160
- }
161
-
162
- return result
163
- }
164
-
165
- const executionPromise = executeJsenvAsyncFunction(jsenvExecutionFunction, {
166
- cancelOnSIGINT,
133
+ compileServerOrigin,
134
+ outDirectoryRelativeUrl,
135
+ ...runtimeParams,
136
+ },
137
+ executeParams: {
138
+ fileRelativeUrl,
139
+ },
140
+
141
+ allocatedMs,
142
+ measureDuration,
143
+ mirrorConsole,
144
+ captureConsole,
145
+ collectRuntimeName,
146
+ collectRuntimeVersion,
147
+ inheritCoverage,
148
+ collectCoverage,
149
+ measurePerformance,
150
+ collectPerformance,
151
+
152
+ stopAfterExecute,
153
+ stopAfterExecuteReason,
154
+ gracefulStopAllocatedMs,
155
+
156
+ runtimeConsoleCallback,
157
+ runtimeStartedCallback,
158
+ runtimeStoppedCallback,
159
+ runtimeErrorAfterExecutionCallback,
160
+ runtimeDisconnectCallback: () => {
161
+ // stop server when runtime disconnects
162
+ executeOperation.cleaner.clean("runtime-stopped")
163
+ runtimeDisconnectCallback()
164
+ },
167
165
  })
168
166
 
169
- if (ignoreError) {
170
- return executionPromise
167
+ if (collectCompileServerInfo) {
168
+ result.outDirectoryRelativeUrl = outDirectoryRelativeUrl
169
+ result.compileServerOrigin = compileServerOrigin
171
170
  }
172
171
 
173
- const result = await executionPromise
174
172
  if (result.status === "errored") {
173
+ if (ignoreError) {
174
+ return result
175
+ }
175
176
  /*
176
177
  Warning: when node launched with --unhandled-rejections=strict, despites
177
178
  this promise being rejected by throw result.error node will compltely ignore it.
@@ -1,8 +1,3 @@
1
- /* eslint-disable import/max-dependencies */
2
- import {
3
- createCancellationToken,
4
- composeCancellationToken,
5
- } from "@jsenv/cancellation"
6
1
  import {
7
2
  normalizeStructuredMetaMap,
8
3
  urlToFileSystemPath,
@@ -13,7 +8,6 @@ import {
13
8
  } from "@jsenv/filesystem"
14
9
  import { createLogger, createDetailedMessage } from "@jsenv/logger"
15
10
 
16
- import { executeJsenvAsyncFunction } from "./internal/executeJsenvAsyncFunction.js"
17
11
  import {
18
12
  assertProjectDirectoryUrl,
19
13
  assertProjectDirectoryExists,
@@ -26,11 +20,11 @@ import { generateCoverageTextLog } from "./internal/executing/coverage/generateC
26
20
  import { jsenvCoverageConfig } from "./jsenvCoverageConfig.js"
27
21
 
28
22
  export const executeTestPlan = async ({
23
+ signal = new AbortController().signal,
24
+ handleSIGINT = true,
29
25
  logLevel = "info",
30
26
  compileServerLogLevel = "warn",
31
27
  launchAndExecuteLogLevel = "warn",
32
- cancellationToken = createCancellationToken(),
33
- cancelOnSIGINT = false,
34
28
 
35
29
  projectDirectoryUrl,
36
30
  jsenvDirectoryRelativeUrl,
@@ -40,7 +34,7 @@ export const executeTestPlan = async ({
40
34
  testPlan,
41
35
  defaultMsAllocatedPerExecution,
42
36
 
43
- concurrencyLimit,
37
+ maxExecutionsInParallel,
44
38
 
45
39
  completedExecutionLogAbbreviation = false,
46
40
  completedExecutionLogMerging = false,
@@ -80,117 +74,107 @@ export const executeTestPlan = async ({
80
74
  customCompilers,
81
75
  jsenvDirectoryClean,
82
76
  }) => {
83
- const jsenvExecuteTestPlanFunction = async ({ jsenvCancellationToken }) => {
84
- cancellationToken = composeCancellationToken(
85
- cancellationToken,
86
- jsenvCancellationToken,
87
- )
88
-
89
- const logger = createLogger({ logLevel })
77
+ const logger = createLogger({ logLevel })
90
78
 
91
- cancellationToken.register((cancelError) => {
92
- if (cancelError.reason === "process SIGINT") {
93
- logger.info(`process SIGINT -> cancelling test execution`)
94
- }
95
- })
79
+ projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
80
+ await assertProjectDirectoryExists({ projectDirectoryUrl })
96
81
 
97
- projectDirectoryUrl = assertProjectDirectoryUrl({ projectDirectoryUrl })
98
- await assertProjectDirectoryExists({ projectDirectoryUrl })
82
+ if (typeof testPlan !== "object") {
83
+ throw new Error(`testPlan must be an object, got ${testPlan}`)
84
+ }
99
85
 
100
- if (typeof testPlan !== "object") {
101
- throw new Error(`testPlan must be an object, got ${testPlan}`)
86
+ if (coverage) {
87
+ if (typeof coverageConfig !== "object") {
88
+ throw new TypeError(
89
+ `coverageConfig must be an object, got ${coverageConfig}`,
90
+ )
102
91
  }
103
-
104
- if (coverage) {
105
- if (typeof coverageConfig !== "object") {
106
- throw new TypeError(
107
- `coverageConfig must be an object, got ${coverageConfig}`,
108
- )
109
- }
110
- if (Object.keys(coverageConfig).length === 0) {
111
- logger.warn(
112
- `coverageConfig is an empty object. Nothing will be instrumented for coverage so your coverage will be empty`,
113
- )
114
- }
115
- if (!coverageAndExecutionAllowed) {
116
- const structuredMetaMapForExecute = normalizeStructuredMetaMap(
117
- {
118
- execute: testPlan,
119
- },
120
- "file:///",
121
- )
122
- const structuredMetaMapForCover = normalizeStructuredMetaMap(
123
- {
124
- cover: coverageConfig,
125
- },
126
- "file:///",
92
+ if (Object.keys(coverageConfig).length === 0) {
93
+ logger.warn(
94
+ `coverageConfig is an empty object. Nothing will be instrumented for coverage so your coverage will be empty`,
95
+ )
96
+ }
97
+ if (!coverageAndExecutionAllowed) {
98
+ const structuredMetaMapForExecute = normalizeStructuredMetaMap(
99
+ {
100
+ execute: testPlan,
101
+ },
102
+ "file:///",
103
+ )
104
+ const structuredMetaMapForCover = normalizeStructuredMetaMap(
105
+ {
106
+ cover: coverageConfig,
107
+ },
108
+ "file:///",
109
+ )
110
+ const patternsMatchingCoverAndExecute = Object.keys(
111
+ structuredMetaMapForExecute.execute,
112
+ ).filter((testPlanPattern) => {
113
+ return urlToMeta({
114
+ url: testPlanPattern,
115
+ structuredMetaMap: structuredMetaMapForCover,
116
+ }).cover
117
+ })
118
+
119
+ if (patternsMatchingCoverAndExecute.length) {
120
+ // I think it is an error, it would be strange, for a given file
121
+ // to be both covered and executed
122
+ throw new Error(
123
+ createDetailedMessage(`some file will be both covered and executed`, {
124
+ patterns: patternsMatchingCoverAndExecute,
125
+ }),
127
126
  )
128
- const patternsMatchingCoverAndExecute = Object.keys(
129
- structuredMetaMapForExecute.execute,
130
- ).filter((testPlanPattern) => {
131
- return urlToMeta({
132
- url: testPlanPattern,
133
- structuredMetaMap: structuredMetaMapForCover,
134
- }).cover
135
- })
136
-
137
- if (patternsMatchingCoverAndExecute.length) {
138
- // I think it is an error, it would be strange, for a given file
139
- // to be both covered and executed
140
- throw new Error(
141
- createDetailedMessage(
142
- `some file will be both covered and executed`,
143
- {
144
- patterns: patternsMatchingCoverAndExecute,
145
- },
146
- ),
147
- )
148
- }
149
127
  }
150
128
  }
129
+ }
151
130
 
152
- const result = await executePlan(testPlan, {
153
- logger,
154
- compileServerLogLevel,
155
- launchAndExecuteLogLevel,
156
- cancellationToken,
157
-
158
- projectDirectoryUrl,
159
- jsenvDirectoryRelativeUrl,
160
-
161
- importResolutionMethod,
162
- importDefaultExtension,
163
-
164
- defaultMsAllocatedPerExecution,
165
- concurrencyLimit,
166
- completedExecutionLogMerging,
167
- completedExecutionLogAbbreviation,
168
- logSummary,
169
- measureGlobalDuration,
170
-
171
- coverage,
172
- coverageConfig,
173
- coverageIncludeMissing,
174
- coverageForceIstanbul,
175
- coverageV8MergeConflictIsExpected,
176
-
177
- jsenvDirectoryClean,
178
- compileServerProtocol,
179
- compileServerPrivateKey,
180
- compileServerCertificate,
181
- compileServerIp,
182
- compileServerPort,
183
- compileServerCanReadFromFilesystem,
184
- compileServerCanWriteOnFilesystem,
185
- babelPluginMap,
186
- babelConfigFileUrl,
187
- customCompilers,
188
- })
189
-
190
- if (updateProcessExitCode && !executionIsPassed(result)) {
191
- process.exitCode = 1
192
- }
131
+ const result = await executePlan(testPlan, {
132
+ signal,
133
+ handleSIGINT,
134
+
135
+ logger,
136
+ compileServerLogLevel,
137
+ launchAndExecuteLogLevel,
138
+
139
+ projectDirectoryUrl,
140
+ jsenvDirectoryRelativeUrl,
141
+
142
+ importResolutionMethod,
143
+ importDefaultExtension,
144
+
145
+ defaultMsAllocatedPerExecution,
146
+ maxExecutionsInParallel,
147
+ completedExecutionLogMerging,
148
+ completedExecutionLogAbbreviation,
149
+ logSummary,
150
+ measureGlobalDuration,
151
+
152
+ coverage,
153
+ coverageConfig,
154
+ coverageIncludeMissing,
155
+ coverageForceIstanbul,
156
+ coverageV8MergeConflictIsExpected,
157
+
158
+ jsenvDirectoryClean,
159
+ compileServerProtocol,
160
+ compileServerPrivateKey,
161
+ compileServerCertificate,
162
+ compileServerIp,
163
+ compileServerPort,
164
+ compileServerCanReadFromFilesystem,
165
+ compileServerCanWriteOnFilesystem,
166
+ babelPluginMap,
167
+ babelConfigFileUrl,
168
+ customCompilers,
169
+ })
193
170
 
171
+ if (updateProcessExitCode && !executionIsPassed(result)) {
172
+ process.exitCode = 1
173
+ }
174
+
175
+ const planCoverage = result.planCoverage
176
+ // planCoverage can be null when execution is abortes
177
+ if (planCoverage) {
194
178
  const promises = []
195
179
  // keep this one first because it does ensureEmptyDirectory
196
180
  // and in case coverage json file gets written in the same directory
@@ -208,7 +192,7 @@ export const executeTestPlan = async ({
208
192
  )
209
193
  }
210
194
  promises.push(
211
- generateCoverageHtmlDirectory(result.planCoverage, {
195
+ generateCoverageHtmlDirectory(planCoverage, {
212
196
  projectDirectoryUrl,
213
197
  coverageHtmlDirectoryRelativeUrl,
214
198
  }),
@@ -235,15 +219,11 @@ export const executeTestPlan = async ({
235
219
  )
236
220
  }
237
221
  await Promise.all(promises)
238
-
239
- return {
240
- testPlanSummary: result.planSummary,
241
- testPlanReport: result.planReport,
242
- testPlanCoverage: result.planCoverage,
243
- }
244
222
  }
245
223
 
246
- return executeJsenvAsyncFunction(jsenvExecuteTestPlanFunction, {
247
- cancelOnSIGINT,
248
- })
224
+ return {
225
+ testPlanSummary: result.planSummary,
226
+ testPlanReport: result.planReport,
227
+ testPlanCoverage: planCoverage,
228
+ }
249
229
  }
@@ -36,10 +36,11 @@ export const importUsingChildProcess = async (
36
36
 
37
37
  return {
38
38
  ...controllableNodeProcess,
39
- execute: async () => {
39
+ execute: async ({ signal }) => {
40
40
  try {
41
41
  const namespace =
42
42
  await controllableNodeProcess.requestActionOnChildProcess({
43
+ signal,
43
44
  actionType: "execute-using-import",
44
45
  actionParams: { fileUrl },
45
46
  })
@@ -5,6 +5,7 @@ import {
5
5
  urlToExtension,
6
6
  } from "@jsenv/filesystem"
7
7
 
8
+ import { Abortable } from "@jsenv/core/src/abort/main.js"
8
9
  import { jsenvCompileProxyHtmlFileInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
9
10
  import { v8CoverageFromAllV8Coverages } from "@jsenv/core/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js"
10
11
  import { composeIstanbulCoverages } from "@jsenv/core/src/internal/executing/coverage/composeIstanbulCoverages.js"
@@ -14,7 +15,7 @@ import { escapeRegexpSpecialCharacters } from "../escapeRegexpSpecialCharacters.
14
15
  export const executeHtmlFile = async (
15
16
  fileRelativeUrl,
16
17
  {
17
- cancellationToken,
18
+ launchBrowserOperation,
18
19
  projectDirectoryUrl,
19
20
  compileServerOrigin,
20
21
  outDirectoryRelativeUrl,
@@ -45,11 +46,13 @@ export const executeHtmlFile = async (
45
46
  compileProxyProjectRelativeUrl,
46
47
  compileServerOrigin,
47
48
  )
49
+ Abortable.throwIfAborted(launchBrowserOperation)
48
50
  await page.goto(compileProxyClientUrl)
49
51
 
50
52
  const coverageHandledFromOutside =
51
53
  coveragePlaywrightAPIAvailable && !coverageForceIstanbul
52
54
 
55
+ Abortable.throwIfAborted(launchBrowserOperation)
53
56
  const browserRuntimeFeaturesReport = await page.evaluate(
54
57
  /* istanbul ignore next */
55
58
  ({ coverageHandledFromOutside }) => {
@@ -65,6 +68,7 @@ export const executeHtmlFile = async (
65
68
  try {
66
69
  let executionResult
67
70
  const { canAvoidCompilation, compileId } = browserRuntimeFeaturesReport
71
+ Abortable.throwIfAborted(launchBrowserOperation)
68
72
  if (canAvoidCompilation) {
69
73
  executionResult = await executeSource({
70
74
  projectDirectoryUrl,
@@ -114,19 +118,27 @@ export const executeHtmlFile = async (
114
118
  }
115
119
 
116
120
  return executionResult
117
- } catch (e) {
118
- // if browser is closed due to cancellation
121
+ } catch (error) {
122
+ // if browser is closed due to abort
119
123
  // before it is able to finish evaluate we can safely ignore
120
- // and rethrow with current cancelError
121
- if (
122
- e.message.match(/^Protocol error \(.*?\): Target closed/) &&
123
- cancellationToken.cancellationRequested
124
- ) {
125
- cancellationToken.throwIfRequested()
124
+ // and rethrow with current abort error
125
+ if (launchBrowserOperation.signal.aborted && isBrowserClosedError(error)) {
126
+ Abortable.throwIfAborted(launchBrowserOperation)
126
127
  }
128
+ throw error
129
+ }
130
+ }
127
131
 
128
- throw e
132
+ const isBrowserClosedError = (error) => {
133
+ if (error.message.match(/browserContext.newPage: Browser closed/)) {
134
+ return true
129
135
  }
136
+
137
+ if (error.message.match(/^Protocol error \(.*?\): Target closed/)) {
138
+ return true
139
+ }
140
+
141
+ return false
130
142
  }
131
143
 
132
144
  const executeSource = async ({
@@ -1,36 +1,11 @@
1
- import { createCancellationToken } from "@jsenv/cancellation/main.browser.js"
2
1
  import { fetchUsingXHR } from "./fetchUsingXHR.js"
3
2
 
4
- const fetchNative = async (
5
- url,
6
- {
7
- cancellationToken = createCancellationToken(),
8
- mode = "cors",
9
- ...options
10
- } = {},
11
- ) => {
12
- const abortController = new AbortController()
13
-
14
- let cancelError
15
- cancellationToken.register((reason) => {
16
- cancelError = reason
17
- abortController.abort(reason)
3
+ const fetchNative = async (url, { mode = "cors", ...options } = {}) => {
4
+ const response = await window.fetch(url, {
5
+ mode,
6
+ ...options,
18
7
  })
19
8
 
20
- let response
21
- try {
22
- response = await window.fetch(url, {
23
- signal: abortController.signal,
24
- mode,
25
- ...options,
26
- })
27
- } catch (e) {
28
- if (cancelError && e.name === "AbortError") {
29
- throw cancelError
30
- }
31
- throw e
32
- }
33
-
34
9
  return {
35
10
  url: response.url,
36
11
  status: response.status,
@@ -1,13 +1,9 @@
1
- import { createCancellationToken } from "@jsenv/cancellation/main.browser.js"
2
1
  import { createDetailedMessage } from "@jsenv/logger"
3
- // ideally we should do some window.fetch detection (ensuring it has cancellation) and accordingly
4
- // fallback to this polyfill (or even use an existing polyfill would be better)
5
- // https://github.com/github/fetch/blob/master/fetch.js
6
2
 
7
3
  export const fetchUsingXHR = async (
8
4
  url,
9
5
  {
10
- cancellationToken = createCancellationToken(),
6
+ signal,
11
7
  method = "GET",
12
8
  credentials = "same-origin",
13
9
  headers = {},
@@ -52,9 +48,11 @@ export const fetchUsingXHR = async (
52
48
  bodyPromise.resolve()
53
49
  }
54
50
 
55
- cancellationToken.register((cancelError) => {
51
+ signal.addEventListener("abort", () => {
56
52
  xhr.abort()
57
- failure(cancelError)
53
+ const abortError = new Error("aborted")
54
+ abortError.name = "AbortError"
55
+ failure(abortError)
58
56
  })
59
57
 
60
58
  xhr.onreadystatechange = () => {