@jsenv/core 23.0.9 → 23.1.4
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.
- package/dist/jsenv_browser_system.js +14 -2
- package/dist/jsenv_browser_system.js.map +4 -4
- package/dist/jsenv_toolbar.js +4 -0
- package/dist/jsenv_toolbar.js.map +2 -2
- package/dist/jsenv_toolbar_injector.js +129 -107
- package/dist/jsenv_toolbar_injector.js.map +4 -2
- package/package.json +4 -3
- package/src/execute.js +12 -0
- package/src/internal/compiling/compile-directory/updateMeta.js +5 -1
- package/src/internal/compiling/createCompiledFileService.js +6 -6
- package/src/internal/compiling/jsenvCompilerForHtml.js +21 -7
- package/src/internal/compiling/jsenvCompilerForJavaScript.js +2 -0
- package/src/internal/compiling/startCompileServer.js +32 -27
- package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +35 -18
- package/src/internal/executing/executionLogs.js +12 -3
- package/src/internal/executing/launchAndExecute.js +24 -15
- package/src/internal/logs/log_style.js +13 -1
- package/src/internal/node-launcher/createControllableNodeProcess.js +97 -45
- package/src/internal/node-launcher/nodeControllableFile.mjs +1 -1
- package/src/internal/runtime/createBrowserRuntime/createBrowserSystem.js +18 -3
- package/src/internal/toolbar/execution/toolbar.execution.js +4 -0
- package/src/internal/toolbar/toolbar.html +157 -61
- package/src/internal/toolbar/toolbar.injector.js +8 -0
- package/src/launchBrowser.js +67 -49
- package/src/launchNode.js +3 -7
- package/src/startExploring.js +4 -0
- package/src/internal/toolbar/animation/toolbar-movie-icon.svg +0 -15
- package/src/internal/toolbar/compilation/flask.svg +0 -7
- package/src/internal/toolbar/compilation/info.svg +0 -9
- package/src/internal/toolbar/compilation/loupe.svg +0 -11
- package/src/internal/toolbar/compilation/toolbar_compilation.svg +0 -11
- package/src/internal/toolbar/eventsource/toolbar-power-icon.svg +0 -10
- package/src/internal/toolbar/eventsource/toolbar-power-off-icon.svg +0 -10
- package/src/internal/toolbar/responsive/toolbar-dots-icon.svg +0 -10
- package/src/internal/toolbar/settings/toolbar-settings-icon.svg +0 -9
- package/src/internal/toolbar/theme/toolbar-palette-icon.svg +0 -10
- package/src/internal/toolbar/toolbar-cross-icon.svg +0 -10
- package/src/internal/toolbar/toolbar-loading-icon.svg +0 -102
- package/src/internal/toolbar/toolbar-notif-icon.svg +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { serveFile } from "@jsenv/server"
|
|
1
|
+
import { serveFile, nextService } from "@jsenv/server"
|
|
2
2
|
import {
|
|
3
3
|
resolveUrl,
|
|
4
4
|
resolveDirectoryUrl,
|
|
@@ -31,7 +31,6 @@ const jsenvCompilers = {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export const createCompiledFileService = ({
|
|
34
|
-
sourceFileService,
|
|
35
34
|
cancellationToken,
|
|
36
35
|
logger,
|
|
37
36
|
|
|
@@ -51,6 +50,7 @@ export const createCompiledFileService = ({
|
|
|
51
50
|
projectFileRequestedCallback,
|
|
52
51
|
useFilesystemAsCache,
|
|
53
52
|
compileCacheStrategy,
|
|
53
|
+
sourcemapMethod,
|
|
54
54
|
sourcemapExcludeSources,
|
|
55
55
|
}) => {
|
|
56
56
|
Object.keys(customCompilers).forEach((key) => {
|
|
@@ -80,7 +80,7 @@ export const createCompiledFileService = ({
|
|
|
80
80
|
|
|
81
81
|
// not inside compile directory -> nothing to compile
|
|
82
82
|
if (!requestCompileInfo.insideCompileDirectory) {
|
|
83
|
-
return
|
|
83
|
+
return null
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const { compileId, afterCompileId } = requestCompileInfo
|
|
@@ -110,7 +110,7 @@ export const createCompiledFileService = ({
|
|
|
110
110
|
|
|
111
111
|
// nothing after compileId, we don't know what to compile (not supposed to happen)
|
|
112
112
|
if (afterCompileId === "") {
|
|
113
|
-
return
|
|
113
|
+
return null
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
const originalFileRelativeUrl = afterCompileId
|
|
@@ -132,7 +132,7 @@ export const createCompiledFileService = ({
|
|
|
132
132
|
// we don't redirect otherwise it complexify ressource tracking
|
|
133
133
|
// and url resolution
|
|
134
134
|
if (!compiler) {
|
|
135
|
-
return
|
|
135
|
+
return nextService({
|
|
136
136
|
...request,
|
|
137
137
|
ressource: `/${originalFileRelativeUrl}`,
|
|
138
138
|
})
|
|
@@ -175,7 +175,7 @@ export const createCompiledFileService = ({
|
|
|
175
175
|
groupMap,
|
|
176
176
|
}),
|
|
177
177
|
|
|
178
|
-
sourcemapMethod
|
|
178
|
+
sourcemapMethod,
|
|
179
179
|
sourcemapExcludeSources,
|
|
180
180
|
jsenvToolbarInjection,
|
|
181
181
|
})
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
jsenvToolbarInjectorFileInfo,
|
|
7
7
|
} from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
8
8
|
import { getDefaultImportMap } from "@jsenv/core/src/internal/import-resolution/importmap-default.js"
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
setJavaScriptSourceMappingUrl,
|
|
11
|
+
sourcemapToBase64Url,
|
|
12
|
+
} from "../sourceMappingURLUtils.js"
|
|
10
13
|
import { transformJs } from "./js-compilation-service/transformJs.js"
|
|
11
14
|
import {
|
|
12
15
|
parseHtmlString,
|
|
@@ -41,6 +44,7 @@ export const compileHtml = async ({
|
|
|
41
44
|
babelPluginMap,
|
|
42
45
|
|
|
43
46
|
jsenvToolbarInjection,
|
|
47
|
+
sourcemapMethod,
|
|
44
48
|
}) => {
|
|
45
49
|
const jsenvBrowserBuildUrlRelativeToProject = urlToRelativeUrl(
|
|
46
50
|
jsenvBrowserSystemFileInfo.jsenvBuildUrl,
|
|
@@ -203,12 +207,22 @@ export const compileHtml = async ({
|
|
|
203
207
|
sourcemapFileUrl,
|
|
204
208
|
compiledUrl,
|
|
205
209
|
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
|
|
211
|
+
if (sourcemapMethod === "inline") {
|
|
212
|
+
code = setJavaScriptSourceMappingUrl(code, sourcemapToBase64Url(map))
|
|
213
|
+
} else {
|
|
214
|
+
// TODO: respect "sourcemapMethod" parameter
|
|
215
|
+
code = setJavaScriptSourceMappingUrl(
|
|
216
|
+
code,
|
|
217
|
+
sourcemapFileRelativePathForModule,
|
|
218
|
+
)
|
|
219
|
+
assets = [...assets, scriptAssetUrl, sourcemapFileUrl]
|
|
220
|
+
assetsContent = [
|
|
221
|
+
...assetsContent,
|
|
222
|
+
code,
|
|
223
|
+
JSON.stringify(map, null, " "),
|
|
224
|
+
]
|
|
225
|
+
}
|
|
212
226
|
}),
|
|
213
227
|
)
|
|
214
228
|
|
|
@@ -14,6 +14,7 @@ export const compileJavascript = async ({
|
|
|
14
14
|
importMetaFormat,
|
|
15
15
|
|
|
16
16
|
sourcemapExcludeSources,
|
|
17
|
+
sourcemapMethod,
|
|
17
18
|
}) => {
|
|
18
19
|
const transformResult = await transformJs({
|
|
19
20
|
code,
|
|
@@ -42,6 +43,7 @@ export const compileJavascript = async ({
|
|
|
42
43
|
compiledFileUrl: compiledUrl,
|
|
43
44
|
sourcemapFileUrl: `${compiledUrl}.map`,
|
|
44
45
|
sourcemapExcludeSources,
|
|
46
|
+
sourcemapMethod,
|
|
45
47
|
},
|
|
46
48
|
)
|
|
47
49
|
}
|
|
@@ -59,6 +59,7 @@ export const startCompileServer = async ({
|
|
|
59
59
|
jsenvDirectoryClean = false,
|
|
60
60
|
outDirectoryName = "out",
|
|
61
61
|
|
|
62
|
+
sourcemapMethod = "comment", // "inline" is also possible
|
|
62
63
|
sourcemapExcludeSources = false, // this should increase perf (no need to download source for browser)
|
|
63
64
|
compileServerCanReadFromFilesystem = true,
|
|
64
65
|
compileServerCanWriteOnFilesystem = true,
|
|
@@ -269,6 +270,8 @@ export const startCompileServer = async ({
|
|
|
269
270
|
babelPluginMap,
|
|
270
271
|
customCompilers,
|
|
271
272
|
jsenvToolbarInjection,
|
|
273
|
+
sourcemapMethod,
|
|
274
|
+
sourcemapExcludeSources,
|
|
272
275
|
})
|
|
273
276
|
if (compileServerCanWriteOnFilesystem) {
|
|
274
277
|
await setupOutDirectory({
|
|
@@ -280,31 +283,6 @@ export const startCompileServer = async ({
|
|
|
280
283
|
})
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
const sourceFileService = composeServicesWithTiming({
|
|
284
|
-
...(transformHtmlSourceFiles
|
|
285
|
-
? {
|
|
286
|
-
"service:transform html source file":
|
|
287
|
-
createTransformHtmlSourceFileService({
|
|
288
|
-
logger,
|
|
289
|
-
projectDirectoryUrl,
|
|
290
|
-
inlineImportMapIntoHTML,
|
|
291
|
-
jsenvScriptInjection,
|
|
292
|
-
jsenvToolbarInjection,
|
|
293
|
-
}),
|
|
294
|
-
}
|
|
295
|
-
: {}),
|
|
296
|
-
"service:source file": createSourceFileService({
|
|
297
|
-
logger,
|
|
298
|
-
projectDirectoryUrl,
|
|
299
|
-
projectFileRequestedCallback,
|
|
300
|
-
projectFileEtagEnabled,
|
|
301
|
-
transformHtmlSourceFiles,
|
|
302
|
-
inlineImportMapIntoHTML,
|
|
303
|
-
jsenvScriptInjection,
|
|
304
|
-
jsenvToolbarInjection,
|
|
305
|
-
}),
|
|
306
|
-
})
|
|
307
|
-
|
|
308
286
|
const jsenvServices = {
|
|
309
287
|
"service:compilation asset": createCompilationAssetFileService({
|
|
310
288
|
projectDirectoryUrl,
|
|
@@ -324,7 +302,6 @@ export const startCompileServer = async ({
|
|
|
324
302
|
projectDirectoryUrl,
|
|
325
303
|
}),
|
|
326
304
|
"service:compiled file": createCompiledFileService({
|
|
327
|
-
sourceFileService,
|
|
328
305
|
cancellationToken,
|
|
329
306
|
logger,
|
|
330
307
|
|
|
@@ -345,9 +322,32 @@ export const startCompileServer = async ({
|
|
|
345
322
|
projectFileRequestedCallback,
|
|
346
323
|
useFilesystemAsCache: compileServerCanReadFromFilesystem,
|
|
347
324
|
writeOnFilesystem: compileServerCanWriteOnFilesystem,
|
|
325
|
+
sourcemapMethod,
|
|
348
326
|
sourcemapExcludeSources,
|
|
349
327
|
compileCacheStrategy,
|
|
350
328
|
}),
|
|
329
|
+
...(transformHtmlSourceFiles
|
|
330
|
+
? {
|
|
331
|
+
"service:transform html source file":
|
|
332
|
+
createTransformHtmlSourceFileService({
|
|
333
|
+
logger,
|
|
334
|
+
projectDirectoryUrl,
|
|
335
|
+
inlineImportMapIntoHTML,
|
|
336
|
+
jsenvScriptInjection,
|
|
337
|
+
jsenvToolbarInjection,
|
|
338
|
+
}),
|
|
339
|
+
}
|
|
340
|
+
: {}),
|
|
341
|
+
"service:source file": createSourceFileService({
|
|
342
|
+
logger,
|
|
343
|
+
projectDirectoryUrl,
|
|
344
|
+
projectFileRequestedCallback,
|
|
345
|
+
projectFileEtagEnabled,
|
|
346
|
+
transformHtmlSourceFiles,
|
|
347
|
+
inlineImportMapIntoHTML,
|
|
348
|
+
jsenvScriptInjection,
|
|
349
|
+
jsenvToolbarInjection,
|
|
350
|
+
}),
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
const compileServer = await startServer({
|
|
@@ -361,7 +361,8 @@ export const startCompileServer = async ({
|
|
|
361
361
|
ip: compileServerIp,
|
|
362
362
|
port: compileServerPort,
|
|
363
363
|
sendServerTiming: true,
|
|
364
|
-
nagle: false,
|
|
364
|
+
// nagle: false,
|
|
365
|
+
requestWaitingMs: 60 * 1000,
|
|
365
366
|
sendServerInternalErrorDetails: true,
|
|
366
367
|
requestToResponse: composeServicesWithTiming({
|
|
367
368
|
...customServices,
|
|
@@ -945,6 +946,8 @@ const createOutJSONFiles = ({
|
|
|
945
946
|
inlineImportMapIntoHTML,
|
|
946
947
|
customCompilers,
|
|
947
948
|
jsenvToolbarInjection,
|
|
949
|
+
sourcemapMethod,
|
|
950
|
+
sourcemapExcludeSources,
|
|
948
951
|
}) => {
|
|
949
952
|
const outJSONFiles = {}
|
|
950
953
|
const outDirectoryUrl = resolveUrl(
|
|
@@ -968,6 +971,8 @@ const createOutJSONFiles = ({
|
|
|
968
971
|
processEnvNodeEnv,
|
|
969
972
|
customCompilerPatterns,
|
|
970
973
|
jsenvToolbarInjection,
|
|
974
|
+
sourcemapMethod,
|
|
975
|
+
sourcemapExcludeSources,
|
|
971
976
|
}
|
|
972
977
|
outJSONFiles.meta = {
|
|
973
978
|
url: metaOutFileUrl,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
readFile,
|
|
5
5
|
resolveUrl,
|
|
6
6
|
} from "@jsenv/filesystem"
|
|
7
|
+
import { createDetailedMessage } from "@jsenv/logger"
|
|
7
8
|
|
|
8
9
|
import { v8CoverageFromAllV8Coverages } from "./v8CoverageFromAllV8Coverages.js"
|
|
9
10
|
|
|
@@ -12,7 +13,9 @@ export const v8CoverageFromNodeV8Directory = async ({
|
|
|
12
13
|
NODE_V8_COVERAGE,
|
|
13
14
|
coverageConfig,
|
|
14
15
|
}) => {
|
|
15
|
-
const allV8Coverages = await readV8CoverageReportsFromDirectory(
|
|
16
|
+
const allV8Coverages = await readV8CoverageReportsFromDirectory(
|
|
17
|
+
NODE_V8_COVERAGE,
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
const v8Coverage = v8CoverageFromAllV8Coverages(allV8Coverages, {
|
|
18
21
|
coverageRootUrl: projectDirectoryUrl,
|
|
@@ -28,33 +31,47 @@ const readV8CoverageReportsFromDirectory = async (coverageDirectory) => {
|
|
|
28
31
|
if (dirContent.length > 0) {
|
|
29
32
|
return dirContent
|
|
30
33
|
}
|
|
31
|
-
if (timeSpentTrying
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
+
if (timeSpentTrying < 800) {
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
36
|
+
return tryReadDirectory(timeSpentTrying + 100)
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
38
|
+
console.warn(`v8 coverage directory is empty at ${coverageDirectory}`)
|
|
39
|
+
return dirContent
|
|
37
40
|
}
|
|
38
41
|
const dirContent = await tryReadDirectory()
|
|
39
42
|
|
|
40
43
|
const coverageReports = []
|
|
41
|
-
|
|
42
44
|
const coverageDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageDirectory)
|
|
43
45
|
await Promise.all(
|
|
44
46
|
dirContent.map(async (dirEntry) => {
|
|
45
47
|
const dirEntryUrl = resolveUrl(dirEntry, coverageDirectoryUrl)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const tryReadJsonFile = async (timeSpentTrying = 0) => {
|
|
49
|
+
try {
|
|
50
|
+
const fileContent = await readFile(dirEntryUrl, { as: "json" })
|
|
51
|
+
return fileContent
|
|
52
|
+
} catch (e) {
|
|
53
|
+
if (e.name === "SyntaxError") {
|
|
54
|
+
// If there is a syntax error it's likely because Node.js
|
|
55
|
+
// is not done writing the json file content
|
|
56
|
+
// -> let's retry to read file after 100ms
|
|
57
|
+
if (timeSpentTrying < 100) {
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
59
|
+
return tryReadJsonFile(timeSpentTrying + 100)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.warn(
|
|
63
|
+
createDetailedMessage(`Error while reading coverage file`, {
|
|
64
|
+
"error stack": e.stack,
|
|
65
|
+
"file": dirEntryUrl,
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
return null
|
|
50
69
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
${dirEntryUrl}`)
|
|
57
|
-
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const fileContent = await tryReadJsonFile()
|
|
73
|
+
if (fileContent) {
|
|
74
|
+
coverageReports.push(fileContent)
|
|
58
75
|
}
|
|
59
76
|
}),
|
|
60
77
|
)
|
|
@@ -54,7 +54,7 @@ file: ${fileRelativeUrl}
|
|
|
54
54
|
runtime: ${runtime}${appendDuration({
|
|
55
55
|
startMs,
|
|
56
56
|
endMs,
|
|
57
|
-
})}${appendConsole(consoleCalls)}${appendError(error)}`
|
|
57
|
+
})}${appendConsole(consoleCalls)}${appendError(error, runtimeName)}`
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const descriptionFormatters = {
|
|
@@ -107,8 +107,17 @@ ${consoleOutputTrimmed}
|
|
|
107
107
|
${setANSIColor(`-------------------------`, ANSI_GREY)}`
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
const appendError = (error) => {
|
|
111
|
-
if (!error)
|
|
110
|
+
const appendError = (error, runtimeName) => {
|
|
111
|
+
if (!error) {
|
|
112
|
+
return ``
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (runtimeName === "webkit") {
|
|
116
|
+
return `
|
|
117
|
+
error: ${error.message}
|
|
118
|
+
at ${error.stack}`
|
|
119
|
+
}
|
|
120
|
+
|
|
112
121
|
return `
|
|
113
122
|
error: ${error.stack}`
|
|
114
123
|
}
|
|
@@ -47,7 +47,10 @@ export const launchAndExecute = async ({
|
|
|
47
47
|
runtimeConsoleCallback = () => {},
|
|
48
48
|
runtimeStartedCallback = () => {},
|
|
49
49
|
runtimeStoppedCallback = () => {},
|
|
50
|
-
|
|
50
|
+
runtimeErrorAfterExecutionCallback = (error) => {
|
|
51
|
+
// by default throw on error after execution
|
|
52
|
+
throw error
|
|
53
|
+
},
|
|
51
54
|
runtimeDisconnectCallback = () => {},
|
|
52
55
|
|
|
53
56
|
coverageV8MergeConflictIsExpected,
|
|
@@ -205,7 +208,7 @@ export const launchAndExecute = async ({
|
|
|
205
208
|
stopAfterExecuteReason,
|
|
206
209
|
gracefulStopAllocatedMs,
|
|
207
210
|
runtimeConsoleCallback,
|
|
208
|
-
|
|
211
|
+
runtimeErrorAfterExecutionCallback,
|
|
209
212
|
runtimeDisconnectCallback,
|
|
210
213
|
runtimeStartedCallback,
|
|
211
214
|
runtimeStoppedCallback,
|
|
@@ -280,7 +283,7 @@ const computeExecutionResult = async ({
|
|
|
280
283
|
runtimeStartedCallback,
|
|
281
284
|
runtimeStoppedCallback,
|
|
282
285
|
runtimeConsoleCallback,
|
|
283
|
-
|
|
286
|
+
runtimeErrorAfterExecutionCallback,
|
|
284
287
|
runtimeDisconnectCallback,
|
|
285
288
|
}) => {
|
|
286
289
|
const runtimeLabel = `${runtime.name}/${runtime.version}`
|
|
@@ -387,17 +390,6 @@ const computeExecutionResult = async ({
|
|
|
387
390
|
|
|
388
391
|
timing = TIMING_DURING_EXECUTION
|
|
389
392
|
|
|
390
|
-
registerErrorCallback((error) => {
|
|
391
|
-
logger.error(
|
|
392
|
-
createDetailedMessage(`error ${timing}.`, {
|
|
393
|
-
["error stack"]: error.stack,
|
|
394
|
-
["execute params"]: JSON.stringify(executeParams, null, " "),
|
|
395
|
-
["runtime"]: runtimeLabel,
|
|
396
|
-
}),
|
|
397
|
-
)
|
|
398
|
-
runtimeErrorCallback({ error, timing })
|
|
399
|
-
})
|
|
400
|
-
|
|
401
393
|
const raceResult = await promiseTrackRace([disconnected, executed])
|
|
402
394
|
timing = TIMING_AFTER_EXECUTION
|
|
403
395
|
|
|
@@ -406,7 +398,24 @@ const computeExecutionResult = async ({
|
|
|
406
398
|
}
|
|
407
399
|
|
|
408
400
|
if (stopAfterExecute) {
|
|
409
|
-
|
|
401
|
+
// if there is an error while stopping the runtine
|
|
402
|
+
// the execution is considered as failed
|
|
403
|
+
try {
|
|
404
|
+
await launchOperation.stop(stopAfterExecuteReason)
|
|
405
|
+
} catch (e) {
|
|
406
|
+
return finalizeExecutionResult(
|
|
407
|
+
createErroredExecutionResult({
|
|
408
|
+
error: e,
|
|
409
|
+
}),
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
// when the process is still alive
|
|
414
|
+
// we want to catch error to notify runtimeErrorAfterExecutionCallback
|
|
415
|
+
// and throw that error by default
|
|
416
|
+
registerErrorCallback((error) => {
|
|
417
|
+
runtimeErrorAfterExecutionCallback(error)
|
|
418
|
+
})
|
|
410
419
|
}
|
|
411
420
|
|
|
412
421
|
const executionResult = raceResult.value
|
|
@@ -3,6 +3,18 @@ import { createSupportsColor } from "supports-color"
|
|
|
3
3
|
|
|
4
4
|
const canUseUnicode = isUnicodeSupported()
|
|
5
5
|
const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic
|
|
6
|
+
let canUseColors = processSupportsBasicColor
|
|
7
|
+
|
|
8
|
+
// GitHub workflow does support ANSI but "supports-color" returns false
|
|
9
|
+
// because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
|
|
10
|
+
if (process.env.GITHUB_WORKFLOW) {
|
|
11
|
+
// Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
|
|
12
|
+
if (process.env.FORCE_COLOR !== "false") {
|
|
13
|
+
// in unit test we use process.env.FORCE_COLOR = 'false' to fake
|
|
14
|
+
// that colors are not supported. Let it have priority
|
|
15
|
+
canUseColors = true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
6
18
|
|
|
7
19
|
const ansiStyles = {
|
|
8
20
|
red: { open: 31, close: 39 },
|
|
@@ -22,7 +34,7 @@ export const ANSI_GREY = ansiStyles.grey
|
|
|
22
34
|
|
|
23
35
|
export const ANSI_RESET = "\x1b[0m"
|
|
24
36
|
|
|
25
|
-
export const setANSIColor =
|
|
37
|
+
export const setANSIColor = canUseColors
|
|
26
38
|
? (text, ansiColor) =>
|
|
27
39
|
`\x1b[${ansiColor.open}m${text}\x1b[${ansiColor.close}m`
|
|
28
40
|
: (text) => text
|
|
@@ -77,16 +77,17 @@ export const createControllableNodeProcess = async ({
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
await assertFilePresence(nodeControllableFileUrl)
|
|
80
|
+
const envForChildProcess = {
|
|
81
|
+
...(inheritProcessEnv ? process.env : {}),
|
|
82
|
+
...env,
|
|
83
|
+
}
|
|
80
84
|
const childProcess = forkChildProcess(
|
|
81
85
|
urlToFileSystemPath(nodeControllableFileUrl),
|
|
82
86
|
{
|
|
83
87
|
execArgv,
|
|
84
88
|
// silent: true
|
|
85
89
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
86
|
-
env:
|
|
87
|
-
...(inheritProcessEnv ? process.env : {}),
|
|
88
|
-
...env,
|
|
89
|
-
},
|
|
90
|
+
env: envForChildProcess,
|
|
90
91
|
},
|
|
91
92
|
)
|
|
92
93
|
|
|
@@ -123,14 +124,17 @@ ${JSON.stringify(env, null, " ")}`)
|
|
|
123
124
|
const registerConsoleCallback = (callback) => {
|
|
124
125
|
consoleCallbackArray.push(callback)
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
const removeOutputListener = installProcessOutputListener(
|
|
128
|
+
childProcess,
|
|
129
|
+
({ type, text }) => {
|
|
130
|
+
consoleCallbackArray.forEach((callback) => {
|
|
131
|
+
callback({
|
|
132
|
+
type,
|
|
133
|
+
text,
|
|
134
|
+
})
|
|
131
135
|
})
|
|
132
|
-
}
|
|
133
|
-
|
|
136
|
+
},
|
|
137
|
+
)
|
|
134
138
|
// keep listening process outputs while child process is killed to catch
|
|
135
139
|
// outputs until it's actually disconnected
|
|
136
140
|
// registerCleanupCallback(removeProcessOutputListener)
|
|
@@ -138,38 +142,68 @@ ${JSON.stringify(env, null, " ")}`)
|
|
|
138
142
|
const errorCallbackArray = []
|
|
139
143
|
const registerErrorCallback = (callback) => {
|
|
140
144
|
errorCallbackArray.push(callback)
|
|
145
|
+
return () => {
|
|
146
|
+
const index = errorCallbackArray.indexOf(callback)
|
|
147
|
+
if (index > -1) {
|
|
148
|
+
errorCallbackArray.splice(index, 1)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
141
151
|
}
|
|
142
152
|
let killing = false
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
const removeErrorListener = installProcessErrorListener(
|
|
154
|
+
childProcess,
|
|
155
|
+
(error) => {
|
|
156
|
+
removeOutputListener()
|
|
157
|
+
if (!childProcess.connected && error.code === "ERR_IPC_DISCONNECTED") {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
// on windows killProcessTree uses taskkill which seems to kill the process
|
|
161
|
+
// with an exitCode of 1
|
|
162
|
+
if (process.platform === "win32" && killing && error.exitCode === 1) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
errorCallbackArray.forEach((callback) => {
|
|
166
|
+
callback(error)
|
|
167
|
+
})
|
|
168
|
+
},
|
|
169
|
+
)
|
|
156
170
|
// keep listening process errors while child process is killed to catch
|
|
157
171
|
// errors until it's actually disconnected
|
|
158
172
|
// registerCleanupCallback(removeProcessErrorListener)
|
|
159
173
|
|
|
160
174
|
// https://nodejs.org/api/child_process.html#child_process_event_disconnect
|
|
161
175
|
let resolveDisconnect
|
|
176
|
+
let hasExitedOrDisconnected = false
|
|
162
177
|
const disconnected = new Promise((resolve) => {
|
|
163
|
-
resolveDisconnect =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
resolveDisconnect = () => {
|
|
179
|
+
hasExitedOrDisconnected = true
|
|
180
|
+
removeExitListener()
|
|
181
|
+
removeDisconnectListener()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const removeDisconnectListener = onceProcessEvent(
|
|
185
|
+
childProcess,
|
|
186
|
+
"disconnect",
|
|
187
|
+
() => {
|
|
188
|
+
hasExitedOrDisconnected = true
|
|
189
|
+
removeErrorListener()
|
|
190
|
+
removeExitListener()
|
|
191
|
+
resolve()
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
const removeExitListener = onceProcessEvent(childProcess, "exit", () => {
|
|
196
|
+
hasExitedOrDisconnected = true
|
|
197
|
+
removeErrorListener()
|
|
198
|
+
removeDisconnectListener()
|
|
168
199
|
resolve()
|
|
169
200
|
})
|
|
170
201
|
})
|
|
171
202
|
|
|
172
|
-
const disconnectChildProcess = () => {
|
|
203
|
+
const disconnectChildProcess = async () => {
|
|
204
|
+
if (hasExitedOrDisconnected) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
173
207
|
try {
|
|
174
208
|
childProcess.disconnect()
|
|
175
209
|
} catch (e) {
|
|
@@ -179,14 +213,20 @@ ${JSON.stringify(env, null, " ")}`)
|
|
|
179
213
|
throw e
|
|
180
214
|
}
|
|
181
215
|
}
|
|
182
|
-
|
|
216
|
+
await disconnected
|
|
183
217
|
}
|
|
184
218
|
|
|
185
219
|
const killChildProcess = async ({ signal }) => {
|
|
220
|
+
if (hasExitedOrDisconnected) {
|
|
221
|
+
await disconnectChildProcess()
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
186
225
|
killing = true
|
|
187
226
|
logger.debug(`send ${signal} to child process with pid ${childProcess.pid}`)
|
|
188
227
|
|
|
189
228
|
await new Promise((resolve) => {
|
|
229
|
+
// see also https://github.com/sindresorhus/execa/issues/96
|
|
190
230
|
const killProcessTree = require("tree-kill")
|
|
191
231
|
killProcessTree(childProcess.pid, signal, (error) => {
|
|
192
232
|
if (error) {
|
|
@@ -214,7 +254,7 @@ ${JSON.stringify(env, null, " ")}`)
|
|
|
214
254
|
return
|
|
215
255
|
}
|
|
216
256
|
|
|
217
|
-
logger.
|
|
257
|
+
logger.warn(
|
|
218
258
|
createDetailedMessage(
|
|
219
259
|
`error while killing process tree with ${signal}`,
|
|
220
260
|
{
|
|
@@ -237,17 +277,31 @@ ${JSON.stringify(env, null, " ")}`)
|
|
|
237
277
|
// in case the child process did not disconnect by itself at this point
|
|
238
278
|
// something is keeping it alive and it cannot be propely killed.
|
|
239
279
|
// wait for the child process to disconnect by itself
|
|
240
|
-
|
|
280
|
+
await disconnectChildProcess()
|
|
241
281
|
}
|
|
242
282
|
|
|
243
|
-
const stop = ({ gracefulFailed } = {}) => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
283
|
+
const stop = async ({ gracefulFailed } = {}) => {
|
|
284
|
+
let unregisterErrorCallback
|
|
285
|
+
await Promise.race([
|
|
286
|
+
new Promise((resolve, reject) => {
|
|
287
|
+
unregisterErrorCallback = registerErrorCallback(reject)
|
|
288
|
+
}),
|
|
289
|
+
killChildProcess({
|
|
290
|
+
signal: gracefulFailed ? GRACEFUL_STOP_FAILED_SIGNAL : STOP_SIGNAL,
|
|
291
|
+
}),
|
|
292
|
+
])
|
|
293
|
+
unregisterErrorCallback()
|
|
247
294
|
}
|
|
248
295
|
|
|
249
|
-
const gracefulStop = () => {
|
|
250
|
-
|
|
296
|
+
const gracefulStop = async () => {
|
|
297
|
+
let unregisterErrorCallback
|
|
298
|
+
await Promise.race([
|
|
299
|
+
new Promise((resolve, reject) => {
|
|
300
|
+
unregisterErrorCallback = registerErrorCallback(reject)
|
|
301
|
+
}),
|
|
302
|
+
killChildProcess({ signal: GRACEFUL_STOP_SIGNAL }),
|
|
303
|
+
])
|
|
304
|
+
unregisterErrorCallback()
|
|
251
305
|
}
|
|
252
306
|
|
|
253
307
|
const requestActionOnChildProcess = ({ actionType, actionParams }) => {
|
|
@@ -338,15 +392,13 @@ const installProcessOutputListener = (childProcess, callback) => {
|
|
|
338
392
|
|
|
339
393
|
const installProcessErrorListener = (childProcess, callback) => {
|
|
340
394
|
// https://nodejs.org/api/child_process.html#child_process_event_error
|
|
341
|
-
const errorListener = (error) => {
|
|
342
|
-
removeExitListener() // if an error occured we ignore the child process exitCode
|
|
343
|
-
callback(error)
|
|
344
|
-
onceProcessMessage(childProcess, "error", errorListener)
|
|
345
|
-
}
|
|
346
395
|
const removeErrorListener = onceProcessMessage(
|
|
347
396
|
childProcess,
|
|
348
397
|
"error",
|
|
349
|
-
|
|
398
|
+
(error) => {
|
|
399
|
+
removeExitListener() // if an error occured we ignore the child process exitCode
|
|
400
|
+
callback(error)
|
|
401
|
+
},
|
|
350
402
|
)
|
|
351
403
|
// process.exit(1) in child process or process.exitCode = 1 + process.exit()
|
|
352
404
|
// means there was an error even if we don't know exactly what.
|
|
@@ -150,7 +150,7 @@ const removeActionRequestListener = onceProcessMessage(
|
|
|
150
150
|
// removeActionRequestListener()
|
|
151
151
|
if (actionParams.exitAfterAction) {
|
|
152
152
|
// for some reason this fixes v8 coverage directory somtimes empty
|
|
153
|
-
|
|
153
|
+
process.exit()
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
)
|