@jsenv/core 23.3.0 → 23.4.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 (31) hide show
  1. package/package.json +1 -1
  2. package/readme.md +1 -1
  3. package/src/executeTestPlan.js +9 -8
  4. package/src/internal/browser-launcher/executeHtmlFile.js +20 -16
  5. package/src/internal/executing/coverage/babel_plugin_instrument.js +1 -0
  6. package/src/internal/executing/coverage/reportToCoverage.js +147 -120
  7. package/src/internal/executing/{coverage → coverage_empty}/createEmptyCoverage.js +0 -0
  8. package/src/internal/executing/coverage_empty/list_files_not_covered.js +20 -0
  9. package/src/internal/executing/{coverage → coverage_empty}/relativeUrlToEmptyCoverage.js +1 -1
  10. package/src/internal/executing/{coverage/generateCoverageHtmlDirectory.js → coverage_reporter/coverage_reporter_html_directory.js} +11 -4
  11. package/src/internal/executing/{coverage/generateCoverageJsonFile.js → coverage_reporter/coverage_reporter_json_file.js} +0 -0
  12. package/src/internal/executing/{coverage/generateCoverageTextLog.js → coverage_reporter/coverage_reporter_text_log.js} +4 -2
  13. package/src/internal/executing/{coverage → coverage_reporter}/istanbulCoverageMapFromCoverage.js +0 -0
  14. package/src/internal/executing/{coverage/normalizeIstanbulCoverage.js → coverage_utils/file_by_file_coverage.js} +9 -7
  15. package/src/internal/executing/coverage_utils/istanbul_coverage_composition.js +28 -0
  16. package/src/internal/executing/coverage_utils/v8_and_istanbul.js +38 -0
  17. package/src/internal/executing/coverage_utils/v8_coverage_composition.js +23 -0
  18. package/src/internal/executing/coverage_utils/v8_coverage_from_directory.js +59 -0
  19. package/src/internal/executing/coverage_utils/v8_coverage_to_istanbul.js +90 -0
  20. package/src/internal/executing/createSummaryLog.js +3 -1
  21. package/src/internal/executing/executeConcurrently.js +85 -27
  22. package/src/internal/executing/executePlan.js +5 -2
  23. package/src/internal/executing/launchAndExecute.js +47 -24
  24. package/src/launchBrowser.js +8 -8
  25. package/src/launchNode.js +5 -93
  26. package/src/internal/executing/coverage/composeIstanbulCoverages.js +0 -108
  27. package/src/internal/executing/coverage/composeV8Coverages.js +0 -20
  28. package/src/internal/executing/coverage/istanbulCoverageFromCoverages.js +0 -43
  29. package/src/internal/executing/coverage/istanbulCoverageFromV8Coverage.js +0 -79
  30. package/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js +0 -40
  31. package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +0 -67
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "23.3.0",
3
+ "version": "23.4.0",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
package/readme.md CHANGED
@@ -107,7 +107,7 @@ runtime: firefox/73.0b13
107
107
  duration: 2.42 seconds
108
108
 
109
109
  -------------- summary -----------------
110
- 2 execution: all completed
110
+ 2 executions: all completed
111
111
  total duration: 3.72 seconds
112
112
  ----------------------------------------
113
113
  ```
@@ -14,9 +14,9 @@ import {
14
14
  } from "./internal/argUtils.js"
15
15
  import { executePlan } from "./internal/executing/executePlan.js"
16
16
  import { executionIsPassed } from "./internal/executing/executionIsPassed.js"
17
- import { generateCoverageJsonFile } from "./internal/executing/coverage/generateCoverageJsonFile.js"
18
- import { generateCoverageHtmlDirectory } from "./internal/executing/coverage/generateCoverageHtmlDirectory.js"
19
- import { generateCoverageTextLog } from "./internal/executing/coverage/generateCoverageTextLog.js"
17
+ import { generateCoverageJsonFile } from "./internal/executing/coverage_reporter/coverage_reporter_json_file.js"
18
+ import { generateCoverageHtmlDirectory } from "./internal/executing/coverage_reporter/coverage_reporter_html_directory.js"
19
+ import { generateCoverageTextLog } from "./internal/executing/coverage_reporter/coverage_reporter_text_log.js"
20
20
  import { jsenvCoverageConfig } from "./jsenvCoverageConfig.js"
21
21
 
22
22
  export const executeTestPlan = async ({
@@ -44,18 +44,18 @@ export const executeTestPlan = async ({
44
44
 
45
45
  coverage = process.argv.includes("--cover") ||
46
46
  process.argv.includes("--coverage"),
47
+ coverageTempDirectoryRelativeUrl = "./coverage/tmp/",
47
48
  coverageConfig = jsenvCoverageConfig,
48
49
  coverageIncludeMissing = true,
49
50
  coverageAndExecutionAllowed = false,
50
51
  coverageForceIstanbul = false,
51
- coverageV8MergeConflictIsExpected = false,
52
-
52
+ coverageV8ConflictWarning = true,
53
53
  coverageTextLog = true,
54
54
  coverageJsonFile = Boolean(process.env.CI),
55
55
  coverageJsonFileLog = true,
56
56
  coverageJsonFileRelativeUrl = "./coverage/coverage.json",
57
57
  coverageHtmlDirectory = !process.env.CI,
58
- coverageHtmlDirectoryRelativeUrl = "./coverage",
58
+ coverageHtmlDirectoryRelativeUrl = "./coverage/",
59
59
  coverageHtmlDirectoryIndexLog = true,
60
60
  // skip empty means empty files won't appear in the coverage reports (log and html)
61
61
  coverageSkipEmpty = false,
@@ -153,7 +153,8 @@ export const executeTestPlan = async ({
153
153
  coverageConfig,
154
154
  coverageIncludeMissing,
155
155
  coverageForceIstanbul,
156
- coverageV8MergeConflictIsExpected,
156
+ coverageV8ConflictWarning,
157
+ coverageTempDirectoryRelativeUrl,
157
158
 
158
159
  jsenvDirectoryClean,
159
160
  compileServerProtocol,
@@ -173,7 +174,7 @@ export const executeTestPlan = async ({
173
174
  }
174
175
 
175
176
  const planCoverage = result.planCoverage
176
- // planCoverage can be null when execution is abortes
177
+ // planCoverage can be null when execution is aborted
177
178
  if (planCoverage) {
178
179
  const promises = []
179
180
  // keep this one first because it does ensureEmptyDirectory
@@ -6,8 +6,8 @@ import {
6
6
  } from "@jsenv/filesystem"
7
7
 
8
8
  import { jsenvCompileProxyHtmlFileInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
9
- import { v8CoverageFromAllV8Coverages } from "@jsenv/core/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js"
10
- import { composeIstanbulCoverages } from "@jsenv/core/src/internal/executing/coverage/composeIstanbulCoverages.js"
9
+ import { filterV8Coverage } from "@jsenv/core/src/internal/executing/coverage_utils/v8_coverage_from_directory.js"
10
+ import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/internal/executing/coverage_utils/istanbul_coverage_composition.js"
11
11
  import { evalSource } from "../runtime/createNodeRuntime/evalSource.js"
12
12
  import { escapeRegexpSpecialCharacters } from "../escapeRegexpSpecialCharacters.js"
13
13
 
@@ -23,7 +23,7 @@ export const executeHtmlFile = async (
23
23
  // measurePerformance,
24
24
  collectPerformance,
25
25
  collectCoverage,
26
- coverageConfig,
26
+ coverageIgnorePredicate,
27
27
  coverageForceIstanbul,
28
28
  coveragePlaywrightAPIAvailable,
29
29
  transformErrorHook,
@@ -76,7 +76,7 @@ export const executeHtmlFile = async (
76
76
  fileRelativeUrl,
77
77
  page,
78
78
  collectCoverage,
79
- coverageConfig,
79
+ coverageIgnorePredicate,
80
80
  transformErrorHook,
81
81
  })
82
82
  } else {
@@ -149,7 +149,7 @@ const executeSource = async ({
149
149
  fileRelativeUrl,
150
150
  page,
151
151
  collectCoverage,
152
- coverageConfig,
152
+ coverageIgnorePredicate,
153
153
  transformErrorHook,
154
154
  }) => {
155
155
  let transformResult = (result) => result
@@ -175,11 +175,12 @@ const executeSource = async ({
175
175
  }
176
176
  },
177
177
  )
178
- const allV8Coverages = [{ result: v8CoveragesWithFsUrls }]
179
- const coverage = v8CoverageFromAllV8Coverages(allV8Coverages, {
180
- coverageRootUrl: projectDirectoryUrl,
181
- coverageConfig,
182
- })
178
+ const coverage = filterV8Coverage(
179
+ { result: v8CoveragesWithFsUrls },
180
+ {
181
+ coverageIgnorePredicate,
182
+ },
183
+ )
183
184
  return {
184
185
  ...result,
185
186
  coverage,
@@ -297,15 +298,18 @@ const executeCompiledVersion = async ({
297
298
  }
298
299
 
299
300
  const generateCoverageForPage = (fileExecutionResultMap) => {
300
- const istanbulCoverages = []
301
+ let istanbulCoverageComposed = null
301
302
  Object.keys(fileExecutionResultMap).forEach((fileRelativeUrl) => {
302
303
  const istanbulCoverage = fileExecutionResultMap[fileRelativeUrl].coverage
303
- if (istanbulCoverage) {
304
- istanbulCoverages.push(istanbulCoverage)
305
- }
304
+ istanbulCoverageComposed = istanbulCoverageComposed
305
+ ? composeTwoFileByFileIstanbulCoverages(
306
+ istanbulCoverageComposed,
307
+ istanbulCoverage,
308
+ )
309
+ : istanbulCoverage
306
310
  })
307
- const istanbulCoverage = composeIstanbulCoverages(istanbulCoverages)
308
- return istanbulCoverage
311
+
312
+ return istanbulCoverageComposed
309
313
  }
310
314
 
311
315
  const evalException = (
@@ -3,6 +3,7 @@ import {
3
3
  normalizeStructuredMetaMap,
4
4
  urlToMeta,
5
5
  } from "@jsenv/filesystem"
6
+
6
7
  import { require } from "../../require.js"
7
8
 
8
9
  // https://github.com/istanbuljs/babel-plugin-istanbul/blob/321740f7b25d803f881466ea819d870f7ed6a254/src/index.js
@@ -1,8 +1,16 @@
1
- import { collectFiles } from "@jsenv/filesystem"
2
-
3
- import { relativeUrlToEmptyCoverage } from "./relativeUrlToEmptyCoverage.js"
4
- import { istanbulCoverageFromCoverages } from "./istanbulCoverageFromCoverages.js"
5
- import { normalizeIstanbulCoverage } from "./normalizeIstanbulCoverage.js"
1
+ import { readFile } from "@jsenv/filesystem"
2
+
3
+ import {
4
+ visitNodeV8Directory,
5
+ filterV8Coverage,
6
+ } from "../coverage_utils/v8_coverage_from_directory.js"
7
+ import { composeTwoV8Coverages } from "../coverage_utils/v8_coverage_composition.js"
8
+ import { composeTwoFileByFileIstanbulCoverages } from "../coverage_utils/istanbul_coverage_composition.js"
9
+ import { v8CoverageToIstanbul } from "../coverage_utils/v8_coverage_to_istanbul.js"
10
+ import { composeV8AndIstanbul } from "../coverage_utils/v8_and_istanbul.js"
11
+ import { normalizeFileByFileCoveragePaths } from "../coverage_utils/file_by_file_coverage.js"
12
+ import { listRelativeFileUrlToCover } from "../coverage_empty/list_files_not_covered.js"
13
+ import { relativeUrlToEmptyCoverage } from "../coverage_empty/relativeUrlToEmptyCoverage.js"
6
14
 
7
15
  export const reportToCoverage = async (
8
16
  report,
@@ -13,134 +21,153 @@ export const reportToCoverage = async (
13
21
  babelPluginMap,
14
22
  coverageConfig,
15
23
  coverageIncludeMissing,
16
- coverageV8MergeConflictIsExpected,
24
+ coverageIgnorePredicate,
25
+ coverageForceIstanbul,
26
+ coverageV8ConflictWarning,
17
27
  },
18
28
  ) => {
19
- // here we should forward multipleExecutionsOperation.signal
20
- // to allow aborting this too
21
- const istanbulCoverageFromExecution = await executionReportToCoverage(
22
- report,
23
- {
24
- logger,
25
- projectDirectoryUrl,
26
- coverageV8MergeConflictIsExpected,
27
- },
28
- )
29
+ let v8Coverage
30
+ let fileByFileIstanbulCoverage
29
31
 
30
- if (!coverageIncludeMissing) {
31
- return istanbulCoverageFromExecution
32
- }
32
+ // collect v8 and istanbul coverage from executions
33
+ await Object.keys(report).reduce(async (previous, file) => {
34
+ await previous
33
35
 
34
- const relativeFileUrlToCoverArray = await listRelativeFileUrlToCover({
35
- multipleExecutionsOperation,
36
- projectDirectoryUrl,
37
- coverageConfig,
38
- })
36
+ const executionResultForFile = report[file]
37
+ await Object.keys(executionResultForFile).reduce(
38
+ async (previous, executionName) => {
39
+ await previous
40
+
41
+ const executionResultForFileOnRuntime =
42
+ executionResultForFile[executionName]
43
+ const { status, coverageFileUrl } = executionResultForFileOnRuntime
44
+ if (!coverageFileUrl) {
45
+ // several reasons not to have coverage here:
46
+ // 1. the file we executed did not import an instrumented file.
47
+ // - a test file without import
48
+ // - a test file importing only file excluded from coverage
49
+ // - a coverDescription badly configured so that we don't realize
50
+ // a file should be covered
51
+
52
+ // 2. the file we wanted to executed timedout
53
+ // - infinite loop
54
+ // - too extensive operation
55
+ // - a badly configured or too low allocatedMs for that execution.
56
+
57
+ // 3. the file we wanted to execute contains syntax-error
58
+
59
+ // in any scenario we are fine because
60
+ // coverDescription will generate empty coverage for files
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
+ }
39
69
 
40
- const relativeFileUrlMissingCoverageArray =
41
- relativeFileUrlToCoverArray.filter((relativeFileUrlToCover) =>
42
- Object.keys(istanbulCoverageFromExecution).every((key) => {
43
- return key !== `./${relativeFileUrlToCover}`
44
- }),
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(),
45
87
  )
46
-
47
- const istanbulCoverageFromMissedFiles = {}
48
- // maybe we should prefer reduce over Promise.all here
49
- // because it creates a LOT of things to do
50
- await Promise.all(
51
- relativeFileUrlMissingCoverageArray.map(
52
- async (relativeFileUrlMissingCoverage) => {
53
- const emptyCoverage = await relativeUrlToEmptyCoverage(
54
- relativeFileUrlMissingCoverage,
55
- {
56
- multipleExecutionsOperation,
57
- projectDirectoryUrl,
58
- babelPluginMap,
59
- },
60
- )
61
- istanbulCoverageFromMissedFiles[relativeFileUrlMissingCoverage] =
62
- emptyCoverage
63
- return emptyCoverage
88
+ }, Promise.resolve())
89
+
90
+ if (!coverageForceIstanbul && process.env.NODE_V8_COVERAGE) {
91
+ await visitNodeV8Directory({
92
+ signal: multipleExecutionsOperation.signal,
93
+ NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,
94
+ onV8Coverage: (nodeV8Coverage) => {
95
+ const nodeV8CoverageLight = filterV8Coverage(nodeV8Coverage, {
96
+ coverageIgnorePredicate,
97
+ })
98
+ v8Coverage = v8Coverage
99
+ ? composeTwoV8Coverages(v8Coverage, nodeV8CoverageLight)
100
+ : nodeV8CoverageLight
64
101
  },
65
- ),
66
- )
67
-
68
- return {
69
- ...istanbulCoverageFromExecution, // already normalized
70
- ...normalizeIstanbulCoverage(
71
- istanbulCoverageFromMissedFiles,
72
- projectDirectoryUrl,
73
- ),
74
- }
75
- }
76
-
77
- const listRelativeFileUrlToCover = async ({
78
- multipleExecutionsOperation,
79
- projectDirectoryUrl,
80
- coverageConfig,
81
- }) => {
82
- const structuredMetaMapForCoverage = {
83
- cover: coverageConfig,
102
+ })
84
103
  }
85
104
 
86
- const matchingFileResultArray = await collectFiles({
87
- signal: multipleExecutionsOperation.signal,
88
- directoryUrl: projectDirectoryUrl,
89
- structuredMetaMap: structuredMetaMapForCoverage,
90
- predicate: ({ cover }) => cover,
91
- })
92
-
93
- return matchingFileResultArray.map(({ relativeUrl }) => relativeUrl)
94
- }
105
+ // try to merge v8 with istanbul, if any
106
+ let fileByFileCoverage
107
+ if (v8Coverage) {
108
+ let v8FileByFileCoverage = await v8CoverageToIstanbul(v8Coverage)
95
109
 
96
- const executionReportToCoverage = async (
97
- report,
98
- { logger, projectDirectoryUrl, coverageV8MergeConflictIsExpected },
99
- ) => {
100
- const coverages = []
110
+ v8FileByFileCoverage = normalizeFileByFileCoveragePaths(
111
+ v8FileByFileCoverage,
112
+ projectDirectoryUrl,
113
+ )
101
114
 
102
- Object.keys(report).forEach((file) => {
103
- const executionResultForFile = report[file]
104
- Object.keys(executionResultForFile).forEach((executionName) => {
105
- const executionResultForFileOnRuntime =
106
- executionResultForFile[executionName]
107
-
108
- const { status, coverage } = executionResultForFileOnRuntime
109
- if (!coverage) {
110
- // several reasons not to have coverage here:
111
- // 1. the file we executed did not import an instrumented file.
112
- // - a test file without import
113
- // - a test file importing only file excluded from coverage
114
- // - a coverDescription badly configured so that we don't realize
115
- // a file should be covered
116
-
117
- // 2. the file we wanted to executed timedout
118
- // - infinite loop
119
- // - too extensive operation
120
- // - a badly configured or too low allocatedMs for that execution.
121
-
122
- // 3. the file we wanted to execute contains syntax-error
123
-
124
- // in any scenario we are fine because
125
- // coverDescription will generate empty coverage for files
126
- // that were suppose to be coverage but were not.
127
-
128
- if (status === "completed") {
129
- logger.warn(
130
- `No execution.coverage from execution named "${executionName}" of ${file}`,
131
- )
132
- }
133
- return
134
- }
115
+ if (fileByFileIstanbulCoverage) {
116
+ fileByFileIstanbulCoverage = normalizeFileByFileCoveragePaths(
117
+ fileByFileIstanbulCoverage,
118
+ projectDirectoryUrl,
119
+ )
120
+ fileByFileCoverage = composeV8AndIstanbul(
121
+ v8FileByFileCoverage,
122
+ fileByFileIstanbulCoverage,
123
+ { coverageV8ConflictWarning },
124
+ )
125
+ } else {
126
+ fileByFileCoverage = v8FileByFileCoverage
127
+ }
128
+ }
129
+ // get istanbul only
130
+ else if (fileByFileIstanbulCoverage) {
131
+ fileByFileCoverage = normalizeFileByFileCoveragePaths(
132
+ fileByFileIstanbulCoverage,
133
+ projectDirectoryUrl,
134
+ )
135
+ }
136
+ // no coverage found in execution (or zero file where executed)
137
+ else {
138
+ fileByFileCoverage = {}
139
+ }
135
140
 
136
- coverages.push(coverage)
141
+ // now add coverage for file not covered
142
+ if (coverageIncludeMissing) {
143
+ const relativeUrlsToCover = await listRelativeFileUrlToCover({
144
+ multipleExecutionsOperation,
145
+ projectDirectoryUrl,
146
+ coverageConfig,
137
147
  })
138
- })
139
148
 
140
- const istanbulCoverage = await istanbulCoverageFromCoverages(coverages, {
141
- projectDirectoryUrl,
142
- coverageV8MergeConflictIsExpected,
143
- })
149
+ const relativeUrlsMissing = relativeUrlsToCover.filter(
150
+ (relativeUrlToCover) =>
151
+ Object.keys(fileByFileCoverage).every((key) => {
152
+ return key !== `./${relativeUrlToCover}`
153
+ }),
154
+ )
155
+
156
+ await relativeUrlsMissing.reduce(async (previous, relativeUrlMissing) => {
157
+ const emptyCoverage = await relativeUrlToEmptyCoverage(
158
+ relativeUrlMissing,
159
+ {
160
+ multipleExecutionsOperation,
161
+ projectDirectoryUrl,
162
+ babelPluginMap,
163
+ },
164
+ )
165
+ fileByFileCoverage[`./${relativeUrlMissing}`] = emptyCoverage
166
+ return emptyCoverage
167
+ }, Promise.resolve())
168
+ }
144
169
 
145
- return istanbulCoverage
170
+ return fileByFileCoverage
146
171
  }
172
+
173
+ const isV8Coverage = (coverage) => Boolean(coverage.result)
@@ -0,0 +1,20 @@
1
+ import { collectFiles } from "@jsenv/filesystem"
2
+
3
+ export const listRelativeFileUrlToCover = async ({
4
+ multipleExecutionsOperation,
5
+ projectDirectoryUrl,
6
+ coverageConfig,
7
+ }) => {
8
+ const structuredMetaMapForCoverage = {
9
+ cover: coverageConfig,
10
+ }
11
+
12
+ const matchingFileResultArray = await collectFiles({
13
+ signal: multipleExecutionsOperation.signal,
14
+ directoryUrl: projectDirectoryUrl,
15
+ structuredMetaMap: structuredMetaMapForCoverage,
16
+ predicate: ({ cover }) => cover,
17
+ })
18
+
19
+ return matchingFileResultArray.map(({ relativeUrl }) => relativeUrl)
20
+ }
@@ -4,7 +4,7 @@ import {
4
4
  babelPluginsFromBabelPluginMap,
5
5
  getMinimalBabelPluginMap,
6
6
  } from "@jsenv/core/src/internal/compiling/babel_plugins.js"
7
- import { babelPluginInstrument } from "./babel_plugin_instrument.js"
7
+ import { babelPluginInstrument } from "../coverage/babel_plugin_instrument.js"
8
8
  import { createEmptyCoverage } from "./createEmptyCoverage.js"
9
9
 
10
10
  export const relativeUrlToEmptyCoverage = async (
@@ -1,13 +1,17 @@
1
- import { readFileSync } from "fs"
1
+ import { readFileSync } from "node:fs"
2
2
  import { resolveUrl, urlToFileSystemPath } from "@jsenv/filesystem"
3
3
 
4
4
  import { require } from "../../require.js"
5
-
6
5
  import { istanbulCoverageMapFromCoverage } from "./istanbulCoverageMapFromCoverage.js"
7
6
 
8
7
  export const generateCoverageHtmlDirectory = async (
9
8
  coverage,
10
- { projectDirectoryUrl, coverageHtmlDirectoryRelativeUrl, coverageSkipEmpty, coverageSkipFull },
9
+ {
10
+ projectDirectoryUrl,
11
+ coverageHtmlDirectoryRelativeUrl,
12
+ coverageSkipEmpty,
13
+ coverageSkipFull,
14
+ },
11
15
  ) => {
12
16
  const libReport = require("istanbul-lib-report")
13
17
  const reports = require("istanbul-reports")
@@ -16,7 +20,10 @@ export const generateCoverageHtmlDirectory = async (
16
20
  dir: urlToFileSystemPath(projectDirectoryUrl),
17
21
  coverageMap: istanbulCoverageMapFromCoverage(coverage),
18
22
  sourceFinder: (path) => {
19
- return readFileSync(urlToFileSystemPath(resolveUrl(path, projectDirectoryUrl)), "utf8")
23
+ return readFileSync(
24
+ urlToFileSystemPath(resolveUrl(path, projectDirectoryUrl)),
25
+ "utf8",
26
+ )
20
27
  },
21
28
  })
22
29
 
@@ -1,8 +1,10 @@
1
1
  import { require } from "../../require.js"
2
-
3
2
  import { istanbulCoverageMapFromCoverage } from "./istanbulCoverageMapFromCoverage.js"
4
3
 
5
- export const generateCoverageTextLog = (coverage, { coverageSkipEmpty, coverageSkipFull }) => {
4
+ export const generateCoverageTextLog = (
5
+ coverage,
6
+ { coverageSkipEmpty, coverageSkipFull },
7
+ ) => {
6
8
  const libReport = require("istanbul-lib-report")
7
9
  const reports = require("istanbul-reports")
8
10
 
@@ -5,22 +5,24 @@ import {
5
5
  resolveUrl,
6
6
  } from "@jsenv/filesystem"
7
7
 
8
- export const normalizeIstanbulCoverage = (istanbulCoverage, projectDirectoryUrl) => {
9
- const istanbulCoverageNormalized = {}
8
+ export const normalizeFileByFileCoveragePaths = (
9
+ fileByFileCoverage,
10
+ projectDirectoryUrl,
11
+ ) => {
12
+ const fileByFileNormalized = {}
10
13
 
11
- Object.keys(istanbulCoverage).forEach((key) => {
12
- const fileCoverage = istanbulCoverage[key]
14
+ Object.keys(fileByFileCoverage).forEach((key) => {
15
+ const fileCoverage = fileByFileCoverage[key]
13
16
  const { path } = fileCoverage
14
17
  const url = isFileSystemPath(path)
15
18
  ? fileSystemPathToUrl(path)
16
19
  : resolveUrl(path, projectDirectoryUrl)
17
20
  const relativeUrl = urlToRelativeUrl(url, projectDirectoryUrl)
18
-
19
- istanbulCoverageNormalized[`./${relativeUrl}`] = {
21
+ fileByFileNormalized[`./${relativeUrl}`] = {
20
22
  ...fileCoverage,
21
23
  path: `./${relativeUrl}`,
22
24
  }
23
25
  })
24
26
 
25
- return istanbulCoverageNormalized
27
+ return fileByFileNormalized
26
28
  }
@@ -0,0 +1,28 @@
1
+ import { require } from "../../require.js"
2
+
3
+ export const composeTwoFileByFileIstanbulCoverages = (
4
+ firstFileByFileIstanbulCoverage,
5
+ secondFileByFileIstanbulCoverage,
6
+ ) => {
7
+ const fileByFileIstanbulCoverage = {}
8
+ Object.keys(firstFileByFileIstanbulCoverage).forEach((key) => {
9
+ fileByFileIstanbulCoverage[key] = firstFileByFileIstanbulCoverage[key]
10
+ })
11
+ Object.keys(secondFileByFileIstanbulCoverage).forEach((key) => {
12
+ const firstCoverage = firstFileByFileIstanbulCoverage[key]
13
+ const secondCoverage = secondFileByFileIstanbulCoverage[key]
14
+ fileByFileIstanbulCoverage[key] = firstCoverage
15
+ ? merge(firstCoverage, secondCoverage)
16
+ : secondCoverage
17
+ })
18
+
19
+ return fileByFileIstanbulCoverage
20
+ }
21
+
22
+ const merge = (firstIstanbulCoverage, secondIstanbulCoverage) => {
23
+ const { createFileCoverage } = require("istanbul-lib-coverage")
24
+ const istanbulFileCoverageObject = createFileCoverage(firstIstanbulCoverage)
25
+ istanbulFileCoverageObject.merge(secondIstanbulCoverage)
26
+ const istanbulCoverage = istanbulFileCoverageObject.toJSON()
27
+ return istanbulCoverage
28
+ }
@@ -0,0 +1,38 @@
1
+ import { createDetailedMessage } from "@jsenv/logger"
2
+
3
+ export const composeV8AndIstanbul = (
4
+ v8FileByFileCoverage,
5
+ istanbulFileByFileCoverage,
6
+ { coverageV8ConflictWarning },
7
+ ) => {
8
+ const fileByFileCoverage = {}
9
+ const v8Files = Object.keys(v8FileByFileCoverage)
10
+ const istanbulFiles = Object.keys(istanbulFileByFileCoverage)
11
+
12
+ v8Files.forEach((key) => {
13
+ fileByFileCoverage[key] = v8FileByFileCoverage[key]
14
+ })
15
+ istanbulFiles.forEach((key) => {
16
+ const v8Coverage = v8FileByFileCoverage[key]
17
+ if (v8Coverage) {
18
+ if (coverageV8ConflictWarning) {
19
+ console.warn(
20
+ createDetailedMessage(
21
+ `Coverage conflict on "${key}", found two coverage that cannot be merged together: v8 and istanbul. The istanbul coverage will be ignored.`,
22
+ {
23
+ "details": `This happens when a file is executed on a runtime using v8 coverage (node or chromium) and on runtime using istanbul coverage (firefox or webkit)`,
24
+ "suggestion":
25
+ "You can disable this warning with coverageV8ConflictWarning: false",
26
+ "suggestion 2": `You can force usage of istanbul to prevent this conflict with coverageForceIstanbul: true`,
27
+ },
28
+ ),
29
+ )
30
+ }
31
+ fileByFileCoverage[key] = v8Coverage
32
+ } else {
33
+ fileByFileCoverage[key] = istanbulFileByFileCoverage[key]
34
+ }
35
+ })
36
+
37
+ return fileByFileCoverage
38
+ }
@@ -0,0 +1,23 @@
1
+ import { require } from "@jsenv/core/src/internal/require.js"
2
+
3
+ export const composeTwoV8Coverages = (firstV8Coverage, secondV8Coverage) => {
4
+ if (secondV8Coverage.result.length === 0) {
5
+ return firstV8Coverage
6
+ }
7
+
8
+ const { mergeProcessCovs } = require("@c88/v8-coverage")
9
+ // "mergeProcessCovs" do not preserves source-map-cache during the merge
10
+ // so we store sourcemap cache now
11
+ const sourceMapCache = {}
12
+ const visit = (coverageReport) => {
13
+ if (coverageReport["source-map-cache"]) {
14
+ Object.assign(sourceMapCache, coverageReport["source-map-cache"])
15
+ }
16
+ }
17
+ visit(firstV8Coverage)
18
+ visit(secondV8Coverage)
19
+ const v8Coverage = mergeProcessCovs([firstV8Coverage, secondV8Coverage])
20
+ v8Coverage["source-map-cache"] = sourceMapCache
21
+
22
+ return v8Coverage
23
+ }