@jsenv/core 23.4.1 → 23.5.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.
- package/{LICENSE → license} +0 -0
- package/package.json +6 -7
- package/src/execute.js +0 -6
- package/src/executeTestPlan.js +3 -2
- package/src/internal/compiling/createCompiledFileService.js +13 -1
- package/src/internal/executing/coverage/reportToCoverage.js +97 -83
- package/src/internal/executing/{coverage_empty → coverage_missing}/createEmptyCoverage.js +0 -0
- package/src/internal/executing/{coverage_empty → coverage_missing}/list_files_not_covered.js +2 -2
- package/src/internal/executing/coverage_missing/missing_coverage.js +46 -0
- package/src/internal/executing/{coverage_empty → coverage_missing}/relativeUrlToEmptyCoverage.js +11 -7
- package/src/internal/executing/coverage_utils/v8_coverage_from_directory.js +41 -28
- package/src/internal/executing/coverage_utils/v8_coverage_to_istanbul.js +48 -38
- package/src/internal/executing/createSummaryLog.js +2 -8
- package/src/internal/executing/executeConcurrently.js +119 -93
- package/src/internal/executing/executePlan.js +13 -3
- package/src/internal/executing/executionLogs.js +66 -37
- package/src/internal/executing/execution_colors.js +2 -1
- package/src/internal/executing/launchAndExecute.js +16 -34
- package/src/internal/logs/msAsDuration.js +4 -3
package/{LICENSE → license}
RENAMED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.5.2",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
"node": ">=14.9.0"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
"registry": "https://registry.npmjs.org"
|
|
14
|
+
"access": "public"
|
|
16
15
|
},
|
|
17
16
|
"type": "module",
|
|
18
17
|
"exports": {
|
|
@@ -63,12 +62,12 @@
|
|
|
63
62
|
"@babel/plugin-syntax-numeric-separator": "7.10.4",
|
|
64
63
|
"@babel/plugin-transform-modules-systemjs": "7.15.4",
|
|
65
64
|
"@c88/v8-coverage": "0.1.1",
|
|
66
|
-
"@jsenv/abort": "4.
|
|
67
|
-
"@jsenv/filesystem": "2.5.
|
|
65
|
+
"@jsenv/abort": "4.1.1",
|
|
66
|
+
"@jsenv/filesystem": "2.5.1",
|
|
68
67
|
"@jsenv/importmap": "1.1.0",
|
|
69
|
-
"@jsenv/log": "1.
|
|
68
|
+
"@jsenv/log": "1.4.0",
|
|
70
69
|
"@jsenv/logger": "4.0.1",
|
|
71
|
-
"@jsenv/server": "10.0.
|
|
70
|
+
"@jsenv/server": "10.0.4",
|
|
72
71
|
"@jsenv/uneval": "1.6.0",
|
|
73
72
|
"@jsenv/workers": "1.2.0",
|
|
74
73
|
"@rollup/plugin-commonjs": "21.0.0",
|
package/src/execute.js
CHANGED
|
@@ -27,11 +27,8 @@ export const execute = async ({
|
|
|
27
27
|
runtimeParams,
|
|
28
28
|
|
|
29
29
|
allocatedMs,
|
|
30
|
-
measureDuration,
|
|
31
30
|
mirrorConsole = true,
|
|
32
31
|
captureConsole,
|
|
33
|
-
collectRuntimeName,
|
|
34
|
-
collectRuntimeVersion,
|
|
35
32
|
inheritCoverage,
|
|
36
33
|
collectCoverage,
|
|
37
34
|
measurePerformance,
|
|
@@ -139,11 +136,8 @@ export const execute = async ({
|
|
|
139
136
|
},
|
|
140
137
|
|
|
141
138
|
allocatedMs,
|
|
142
|
-
measureDuration,
|
|
143
139
|
mirrorConsole,
|
|
144
140
|
captureConsole,
|
|
145
|
-
collectRuntimeName,
|
|
146
|
-
collectRuntimeVersion,
|
|
147
141
|
inheritCoverage,
|
|
148
142
|
collectCoverage,
|
|
149
143
|
measurePerformance,
|
package/src/executeTestPlan.js
CHANGED
|
@@ -33,13 +33,13 @@ export const executeTestPlan = async ({
|
|
|
33
33
|
|
|
34
34
|
testPlan,
|
|
35
35
|
defaultMsAllocatedPerExecution,
|
|
36
|
+
cooldownBetweenExecutions,
|
|
36
37
|
|
|
37
38
|
maxExecutionsInParallel,
|
|
38
39
|
|
|
39
40
|
completedExecutionLogAbbreviation = false,
|
|
40
41
|
completedExecutionLogMerging = false,
|
|
41
42
|
logSummary = true,
|
|
42
|
-
measureGlobalDuration = true,
|
|
43
43
|
updateProcessExitCode = true,
|
|
44
44
|
|
|
45
45
|
coverage = process.argv.includes("--cover") ||
|
|
@@ -144,10 +144,10 @@ export const executeTestPlan = async ({
|
|
|
144
144
|
|
|
145
145
|
defaultMsAllocatedPerExecution,
|
|
146
146
|
maxExecutionsInParallel,
|
|
147
|
+
cooldownBetweenExecutions,
|
|
147
148
|
completedExecutionLogMerging,
|
|
148
149
|
completedExecutionLogAbbreviation,
|
|
149
150
|
logSummary,
|
|
150
|
-
measureGlobalDuration,
|
|
151
151
|
|
|
152
152
|
coverage,
|
|
153
153
|
coverageConfig,
|
|
@@ -225,6 +225,7 @@ export const executeTestPlan = async ({
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
return {
|
|
228
|
+
testPlanAborted: result.aborted,
|
|
228
229
|
testPlanSummary: result.planSummary,
|
|
229
230
|
testPlanReport: result.planReport,
|
|
230
231
|
testPlanCoverage: planCoverage,
|
|
@@ -71,7 +71,10 @@ export const createCompiledFileService = ({
|
|
|
71
71
|
|
|
72
72
|
return (request) => {
|
|
73
73
|
const { origin, ressource } = request
|
|
74
|
-
|
|
74
|
+
// we use "ressourceToPathname" to remove eventual query param from the url
|
|
75
|
+
// Without this a pattern like "**/*.js" would not match "file.js?t=1"
|
|
76
|
+
// This would result in file not being compiled when they should
|
|
77
|
+
const requestUrl = `${origin}${ressourceToPathname(ressource)}`
|
|
75
78
|
|
|
76
79
|
const requestCompileInfo = serverUrlToCompileInfo(requestUrl, {
|
|
77
80
|
outDirectoryRelativeUrl,
|
|
@@ -276,3 +279,12 @@ const babelPluginMapFromCompileId = (
|
|
|
276
279
|
|
|
277
280
|
return babelPluginMapForGroup
|
|
278
281
|
}
|
|
282
|
+
|
|
283
|
+
const ressourceToPathname = (ressource) => {
|
|
284
|
+
const searchSeparatorIndex = ressource.indexOf("?")
|
|
285
|
+
const pathname =
|
|
286
|
+
searchSeparatorIndex === -1
|
|
287
|
+
? ressource
|
|
288
|
+
: ressource.slice(0, searchSeparatorIndex)
|
|
289
|
+
return pathname
|
|
290
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile } from "@jsenv/filesystem"
|
|
2
|
+
import { Abort } from "@jsenv/abort"
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
visitNodeV8Directory,
|
|
@@ -9,13 +10,12 @@ import { composeTwoFileByFileIstanbulCoverages } from "../coverage_utils/istanbu
|
|
|
9
10
|
import { v8CoverageToIstanbul } from "../coverage_utils/v8_coverage_to_istanbul.js"
|
|
10
11
|
import { composeV8AndIstanbul } from "../coverage_utils/v8_and_istanbul.js"
|
|
11
12
|
import { normalizeFileByFileCoveragePaths } from "../coverage_utils/file_by_file_coverage.js"
|
|
12
|
-
import {
|
|
13
|
-
import { relativeUrlToEmptyCoverage } from "../coverage_empty/relativeUrlToEmptyCoverage.js"
|
|
13
|
+
import { getMissingFileByFileCoverage } from "../coverage_missing/missing_coverage.js"
|
|
14
14
|
|
|
15
15
|
export const reportToCoverage = async (
|
|
16
16
|
report,
|
|
17
17
|
{
|
|
18
|
-
|
|
18
|
+
signal,
|
|
19
19
|
logger,
|
|
20
20
|
projectDirectoryUrl,
|
|
21
21
|
babelPluginMap,
|
|
@@ -26,70 +26,39 @@ export const reportToCoverage = async (
|
|
|
26
26
|
coverageV8ConflictWarning,
|
|
27
27
|
},
|
|
28
28
|
) => {
|
|
29
|
-
let v8Coverage
|
|
30
|
-
let fileByFileIstanbulCoverage
|
|
31
|
-
|
|
32
29
|
// collect v8 and istanbul coverage from executions
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// that were suppose to be coverage but were not.
|
|
62
|
-
if (status === "completed") {
|
|
63
|
-
logger.debug(
|
|
64
|
-
`No execution.coverageFileUrl from execution named "${executionName}" of ${file}`,
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const executionCoverage = await readFile(coverageFileUrl, {
|
|
71
|
-
as: "json",
|
|
72
|
-
})
|
|
73
|
-
if (isV8Coverage(executionCoverage)) {
|
|
74
|
-
v8Coverage = v8Coverage
|
|
75
|
-
? composeTwoV8Coverages(v8Coverage, executionCoverage)
|
|
76
|
-
: executionCoverage
|
|
77
|
-
} else {
|
|
78
|
-
fileByFileIstanbulCoverage = fileByFileIstanbulCoverage
|
|
79
|
-
? composeTwoFileByFileIstanbulCoverages(
|
|
80
|
-
fileByFileIstanbulCoverage,
|
|
81
|
-
executionCoverage,
|
|
82
|
-
)
|
|
83
|
-
: executionCoverage
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
Promise.resolve(),
|
|
87
|
-
)
|
|
88
|
-
}, Promise.resolve())
|
|
30
|
+
let { v8Coverage, fileByFileIstanbulCoverage } = await getCoverageFromReport({
|
|
31
|
+
signal,
|
|
32
|
+
report,
|
|
33
|
+
onMissing: ({ file, executionResult, executionName }) => {
|
|
34
|
+
// several reasons not to have coverage here:
|
|
35
|
+
// 1. the file we executed did not import an instrumented file.
|
|
36
|
+
// - a test file without import
|
|
37
|
+
// - a test file importing only file excluded from coverage
|
|
38
|
+
// - a coverDescription badly configured so that we don't realize
|
|
39
|
+
// a file should be covered
|
|
40
|
+
|
|
41
|
+
// 2. the file we wanted to executed timedout
|
|
42
|
+
// - infinite loop
|
|
43
|
+
// - too extensive operation
|
|
44
|
+
// - a badly configured or too low allocatedMs for that execution.
|
|
45
|
+
|
|
46
|
+
// 3. the file we wanted to execute contains syntax-error
|
|
47
|
+
|
|
48
|
+
// in any scenario we are fine because
|
|
49
|
+
// coverDescription will generate empty coverage for files
|
|
50
|
+
// that were suppose to be coverage but were not.
|
|
51
|
+
if (executionResult.status === "completed") {
|
|
52
|
+
logger.debug(
|
|
53
|
+
`No execution.coverageFileUrl from execution named "${executionName}" of ${file}`,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
})
|
|
89
58
|
|
|
90
59
|
if (!coverageForceIstanbul && process.env.NODE_V8_COVERAGE) {
|
|
91
60
|
await visitNodeV8Directory({
|
|
92
|
-
signal
|
|
61
|
+
signal,
|
|
93
62
|
NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,
|
|
94
63
|
onV8Coverage: (nodeV8Coverage) => {
|
|
95
64
|
const nodeV8CoverageLight = filterV8Coverage(nodeV8Coverage, {
|
|
@@ -105,7 +74,9 @@ export const reportToCoverage = async (
|
|
|
105
74
|
// try to merge v8 with istanbul, if any
|
|
106
75
|
let fileByFileCoverage
|
|
107
76
|
if (v8Coverage) {
|
|
108
|
-
let v8FileByFileCoverage = await v8CoverageToIstanbul(v8Coverage
|
|
77
|
+
let v8FileByFileCoverage = await v8CoverageToIstanbul(v8Coverage, {
|
|
78
|
+
signal,
|
|
79
|
+
})
|
|
109
80
|
|
|
110
81
|
v8FileByFileCoverage = normalizeFileByFileCoveragePaths(
|
|
111
82
|
v8FileByFileCoverage,
|
|
@@ -140,34 +111,77 @@ export const reportToCoverage = async (
|
|
|
140
111
|
|
|
141
112
|
// now add coverage for file not covered
|
|
142
113
|
if (coverageIncludeMissing) {
|
|
143
|
-
const
|
|
144
|
-
|
|
114
|
+
const missingFileByFileCoverage = await getMissingFileByFileCoverage({
|
|
115
|
+
signal,
|
|
145
116
|
projectDirectoryUrl,
|
|
146
117
|
coverageConfig,
|
|
118
|
+
fileByFileCoverage,
|
|
119
|
+
babelPluginMap,
|
|
147
120
|
})
|
|
121
|
+
Object.assign(fileByFileCoverage, missingFileByFileCoverage)
|
|
122
|
+
}
|
|
148
123
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
124
|
+
return fileByFileCoverage
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const getCoverageFromReport = async ({ signal, report, onMissing }) => {
|
|
128
|
+
const operation = Abort.startOperation()
|
|
129
|
+
operation.addAbortSignal(signal)
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
let v8Coverage
|
|
133
|
+
let fileByFileIstanbulCoverage
|
|
134
|
+
|
|
135
|
+
// collect v8 and istanbul coverage from executions
|
|
136
|
+
await Object.keys(report).reduce(async (previous, file) => {
|
|
137
|
+
operation.throwIfAborted()
|
|
138
|
+
await previous
|
|
139
|
+
|
|
140
|
+
const executionResultForFile = report[file]
|
|
141
|
+
await Object.keys(executionResultForFile).reduce(
|
|
142
|
+
async (previous, executionName) => {
|
|
143
|
+
operation.throwIfAborted()
|
|
144
|
+
await previous
|
|
145
|
+
|
|
146
|
+
const executionResultForFileOnRuntime =
|
|
147
|
+
executionResultForFile[executionName]
|
|
148
|
+
const { coverageFileUrl } = executionResultForFileOnRuntime
|
|
149
|
+
if (!coverageFileUrl) {
|
|
150
|
+
onMissing({
|
|
151
|
+
executionName,
|
|
152
|
+
file,
|
|
153
|
+
executionResult: executionResultForFileOnRuntime,
|
|
154
|
+
})
|
|
155
|
+
return
|
|
156
|
+
}
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
const executionCoverage = await readFile(coverageFileUrl, {
|
|
159
|
+
as: "json",
|
|
160
|
+
})
|
|
161
|
+
if (isV8Coverage(executionCoverage)) {
|
|
162
|
+
v8Coverage = v8Coverage
|
|
163
|
+
? composeTwoV8Coverages(v8Coverage, executionCoverage)
|
|
164
|
+
: executionCoverage
|
|
165
|
+
} else {
|
|
166
|
+
fileByFileIstanbulCoverage = fileByFileIstanbulCoverage
|
|
167
|
+
? composeTwoFileByFileIstanbulCoverages(
|
|
168
|
+
fileByFileIstanbulCoverage,
|
|
169
|
+
executionCoverage,
|
|
170
|
+
)
|
|
171
|
+
: executionCoverage
|
|
172
|
+
}
|
|
163
173
|
},
|
|
174
|
+
Promise.resolve(),
|
|
164
175
|
)
|
|
165
|
-
fileByFileCoverage[`./${relativeUrlMissing}`] = emptyCoverage
|
|
166
|
-
return emptyCoverage
|
|
167
176
|
}, Promise.resolve())
|
|
168
|
-
}
|
|
169
177
|
|
|
170
|
-
|
|
178
|
+
return {
|
|
179
|
+
v8Coverage,
|
|
180
|
+
fileByFileIstanbulCoverage,
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
await operation.end()
|
|
184
|
+
}
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
const isV8Coverage = (coverage) => Boolean(coverage.result)
|
|
File without changes
|
package/src/internal/executing/{coverage_empty → coverage_missing}/list_files_not_covered.js
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { collectFiles } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
3
|
export const listRelativeFileUrlToCover = async ({
|
|
4
|
-
|
|
4
|
+
signal,
|
|
5
5
|
projectDirectoryUrl,
|
|
6
6
|
coverageConfig,
|
|
7
7
|
}) => {
|
|
@@ -10,7 +10,7 @@ export const listRelativeFileUrlToCover = async ({
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const matchingFileResultArray = await collectFiles({
|
|
13
|
-
signal
|
|
13
|
+
signal,
|
|
14
14
|
directoryUrl: projectDirectoryUrl,
|
|
15
15
|
structuredMetaMap: structuredMetaMapForCoverage,
|
|
16
16
|
predicate: ({ cover }) => cover,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Abort } from "@jsenv/abort"
|
|
2
|
+
|
|
3
|
+
import { listRelativeFileUrlToCover } from "./list_files_not_covered.js"
|
|
4
|
+
import { relativeUrlToEmptyCoverage } from "./relativeUrlToEmptyCoverage.js"
|
|
5
|
+
|
|
6
|
+
export const getMissingFileByFileCoverage = async ({
|
|
7
|
+
signal,
|
|
8
|
+
projectDirectoryUrl,
|
|
9
|
+
coverageConfig,
|
|
10
|
+
fileByFileCoverage,
|
|
11
|
+
babelPluginMap,
|
|
12
|
+
}) => {
|
|
13
|
+
const relativeUrlsToCover = await listRelativeFileUrlToCover({
|
|
14
|
+
signal,
|
|
15
|
+
projectDirectoryUrl,
|
|
16
|
+
coverageConfig,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const relativeUrlsMissing = relativeUrlsToCover.filter((relativeUrlToCover) =>
|
|
20
|
+
Object.keys(fileByFileCoverage).every((key) => {
|
|
21
|
+
return key !== `./${relativeUrlToCover}`
|
|
22
|
+
}),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const operation = Abort.startOperation()
|
|
26
|
+
operation.addAbortSignal(signal)
|
|
27
|
+
|
|
28
|
+
const missingFileByFileCoverage = {}
|
|
29
|
+
await relativeUrlsMissing.reduce(async (previous, relativeUrlMissing) => {
|
|
30
|
+
operation.throwIfAborted()
|
|
31
|
+
await previous
|
|
32
|
+
await operation.withSignal(async (signal) => {
|
|
33
|
+
const emptyCoverage = await relativeUrlToEmptyCoverage(
|
|
34
|
+
relativeUrlMissing,
|
|
35
|
+
{
|
|
36
|
+
signal,
|
|
37
|
+
projectDirectoryUrl,
|
|
38
|
+
babelPluginMap,
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
missingFileByFileCoverage[`./${relativeUrlMissing}`] = emptyCoverage
|
|
42
|
+
})
|
|
43
|
+
}, Promise.resolve())
|
|
44
|
+
|
|
45
|
+
return missingFileByFileCoverage
|
|
46
|
+
}
|
package/src/internal/executing/{coverage_empty → coverage_missing}/relativeUrlToEmptyCoverage.js
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveUrl, urlToFileSystemPath, readFile } from "@jsenv/filesystem"
|
|
2
|
+
import { Abort } from "@jsenv/abort"
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
babelPluginsFromBabelPluginMap,
|
|
@@ -9,22 +10,23 @@ import { createEmptyCoverage } from "./createEmptyCoverage.js"
|
|
|
9
10
|
|
|
10
11
|
export const relativeUrlToEmptyCoverage = async (
|
|
11
12
|
relativeUrl,
|
|
12
|
-
{
|
|
13
|
+
{ signal, projectDirectoryUrl, babelPluginMap },
|
|
13
14
|
) => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const fileUrl = resolveUrl(relativeUrl, projectDirectoryUrl)
|
|
17
|
-
multipleExecutionsOperation.throwIfAborted()
|
|
18
|
-
const source = await readFile(fileUrl)
|
|
15
|
+
const operation = Abort.startOperation()
|
|
16
|
+
operation.addAbortSignal(signal)
|
|
19
17
|
|
|
20
18
|
try {
|
|
19
|
+
const { transformAsync } = await import("@babel/core")
|
|
20
|
+
const fileUrl = resolveUrl(relativeUrl, projectDirectoryUrl)
|
|
21
|
+
const source = await readFile(fileUrl)
|
|
22
|
+
|
|
21
23
|
babelPluginMap = {
|
|
22
24
|
...getMinimalBabelPluginMap(),
|
|
23
25
|
...babelPluginMap,
|
|
24
26
|
"transform-instrument": [babelPluginInstrument, { projectDirectoryUrl }],
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
operation.throwIfAborted()
|
|
28
30
|
const { metadata } = await transformAsync(source, {
|
|
29
31
|
filename: urlToFileSystemPath(fileUrl),
|
|
30
32
|
filenameRelative: relativeUrl,
|
|
@@ -55,5 +57,7 @@ export const relativeUrlToEmptyCoverage = async (
|
|
|
55
57
|
return createEmptyCoverage(relativeUrl)
|
|
56
58
|
}
|
|
57
59
|
throw e
|
|
60
|
+
} finally {
|
|
61
|
+
await operation.end()
|
|
58
62
|
}
|
|
59
63
|
}
|
|
@@ -5,12 +5,16 @@ import {
|
|
|
5
5
|
resolveUrl,
|
|
6
6
|
} from "@jsenv/filesystem"
|
|
7
7
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
8
|
+
import { Abort } from "@jsenv/abort"
|
|
8
9
|
|
|
9
10
|
export const visitNodeV8Directory = async ({
|
|
10
|
-
|
|
11
|
+
signal,
|
|
11
12
|
NODE_V8_COVERAGE,
|
|
12
13
|
onV8Coverage,
|
|
13
14
|
}) => {
|
|
15
|
+
const operation = Abort.startOperation()
|
|
16
|
+
operation.addAbortSignal(signal)
|
|
17
|
+
|
|
14
18
|
const tryReadDirectory = async () => {
|
|
15
19
|
const dirContent = await readDirectory(NODE_V8_COVERAGE)
|
|
16
20
|
if (dirContent.length > 0) {
|
|
@@ -19,38 +23,47 @@ export const visitNodeV8Directory = async ({
|
|
|
19
23
|
console.warn(`v8 coverage directory is empty at ${NODE_V8_COVERAGE}`)
|
|
20
24
|
return dirContent
|
|
21
25
|
}
|
|
22
|
-
const dirContent = await tryReadDirectory()
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await
|
|
27
|
+
try {
|
|
28
|
+
operation.throwIfAborted()
|
|
29
|
+
const dirContent = await tryReadDirectory()
|
|
27
30
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
31
|
+
const coverageDirectoryUrl =
|
|
32
|
+
assertAndNormalizeDirectoryUrl(NODE_V8_COVERAGE)
|
|
33
|
+
await dirContent.reduce(async (previous, dirEntry) => {
|
|
34
|
+
operation.throwIfAborted()
|
|
35
|
+
await previous
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
const dirEntryUrl = resolveUrl(dirEntry, coverageDirectoryUrl)
|
|
38
|
+
const tryReadJsonFile = async () => {
|
|
39
|
+
const fileContent = await readFile(dirEntryUrl, { as: "string" })
|
|
40
|
+
if (fileContent === "") {
|
|
41
|
+
console.warn(`Coverage JSON file is empty at ${dirEntryUrl}`)
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const fileAsJson = JSON.parse(fileContent)
|
|
47
|
+
return fileAsJson
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn(
|
|
50
|
+
createDetailedMessage(`Error while reading coverage file`, {
|
|
51
|
+
"error stack": e.stack,
|
|
52
|
+
"file": dirEntryUrl,
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
46
57
|
}
|
|
47
|
-
}
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
const fileContent = await tryReadJsonFile()
|
|
60
|
+
if (fileContent) {
|
|
61
|
+
onV8Coverage(fileContent)
|
|
62
|
+
}
|
|
63
|
+
}, Promise.resolve())
|
|
64
|
+
} finally {
|
|
65
|
+
await operation.end()
|
|
66
|
+
}
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
export const filterV8Coverage = (v8Coverage, { coverageIgnorePredicate }) => {
|
|
@@ -1,53 +1,63 @@
|
|
|
1
1
|
import { urlToFileSystemPath } from "@jsenv/filesystem"
|
|
2
|
+
import { Abort } from "@jsenv/abort"
|
|
2
3
|
|
|
3
4
|
import { require } from "@jsenv/core/src/internal/require.js"
|
|
4
5
|
|
|
5
6
|
import { composeTwoFileByFileIstanbulCoverages } from "./istanbul_coverage_composition.js"
|
|
6
7
|
|
|
7
|
-
export const v8CoverageToIstanbul = async (v8Coverage) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
let istanbulCoverageComposed = null
|
|
11
|
-
await v8Coverage.result.reduce(async (previous, fileV8Coverage) => {
|
|
12
|
-
await previous
|
|
8
|
+
export const v8CoverageToIstanbul = async (v8Coverage, { signal }) => {
|
|
9
|
+
const operation = Abort.startOperation()
|
|
10
|
+
operation.addAbortSignal(signal)
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
sources = { source }
|
|
19
|
-
}
|
|
20
|
-
// when v8 coverage comes from Node.js, the source can be read from sourcemapCache
|
|
21
|
-
else if (sourcemapCache) {
|
|
22
|
-
sources = sourcesFromSourceMapCache(fileV8Coverage.url, sourcemapCache)
|
|
23
|
-
}
|
|
24
|
-
const path = urlToFileSystemPath(fileV8Coverage.url)
|
|
12
|
+
try {
|
|
13
|
+
const v8ToIstanbul = require("v8-to-istanbul")
|
|
14
|
+
const sourcemapCache = v8Coverage["source-map-cache"]
|
|
15
|
+
let istanbulCoverageComposed = null
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// https://github.com/istanbuljs/v8-to-istanbul/blob/2b54bc97c5edf8a37b39a171ec29134ba9bfd532/lib/v8-to-istanbul.js#L27
|
|
30
|
-
undefined,
|
|
31
|
-
sources,
|
|
32
|
-
)
|
|
33
|
-
await converter.load()
|
|
17
|
+
await v8Coverage.result.reduce(async (previous, fileV8Coverage) => {
|
|
18
|
+
operation.throwIfAborted()
|
|
19
|
+
await previous
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
const { source } = fileV8Coverage
|
|
22
|
+
let sources
|
|
23
|
+
// when v8 coverage comes from playwright (chromium) v8Coverage.source is set
|
|
24
|
+
if (typeof source === "string") {
|
|
25
|
+
sources = { source }
|
|
26
|
+
}
|
|
27
|
+
// when v8 coverage comes from Node.js, the source can be read from sourcemapCache
|
|
28
|
+
else if (sourcemapCache) {
|
|
29
|
+
sources = sourcesFromSourceMapCache(fileV8Coverage.url, sourcemapCache)
|
|
30
|
+
}
|
|
31
|
+
const path = urlToFileSystemPath(fileV8Coverage.url)
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
const converter = v8ToIstanbul(
|
|
34
|
+
path,
|
|
35
|
+
// wrapperLength is undefined we don't need it
|
|
36
|
+
// https://github.com/istanbuljs/v8-to-istanbul/blob/2b54bc97c5edf8a37b39a171ec29134ba9bfd532/lib/v8-to-istanbul.js#L27
|
|
37
|
+
undefined,
|
|
38
|
+
sources,
|
|
39
|
+
)
|
|
40
|
+
await converter.load()
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
converter.applyCoverage(fileV8Coverage.functions)
|
|
43
|
+
const istanbulCoverage = converter.toIstanbul()
|
|
44
|
+
|
|
45
|
+
istanbulCoverageComposed = istanbulCoverageComposed
|
|
46
|
+
? composeTwoFileByFileIstanbulCoverages(
|
|
47
|
+
istanbulCoverageComposed,
|
|
48
|
+
istanbulCoverage,
|
|
49
|
+
)
|
|
50
|
+
: istanbulCoverage
|
|
51
|
+
}, Promise.resolve())
|
|
52
|
+
|
|
53
|
+
if (!istanbulCoverageComposed) {
|
|
54
|
+
return {}
|
|
55
|
+
}
|
|
56
|
+
istanbulCoverageComposed = markAsConvertedFromV8(istanbulCoverageComposed)
|
|
57
|
+
return istanbulCoverageComposed
|
|
58
|
+
} finally {
|
|
59
|
+
await operation.end()
|
|
48
60
|
}
|
|
49
|
-
istanbulCoverageComposed = markAsConvertedFromV8(istanbulCoverageComposed)
|
|
50
|
-
return istanbulCoverageComposed
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
const markAsConvertedFromV8 = (fileByFileCoverage) => {
|