| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 91c9ce5 commit 0948693
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -170,6 +170,11 @@ function getCachedCodeType(mode, sourceMap) { | |||
| 170 | 170 | return kStrippedTypeScript; | |
| 171 | 171 | } | |
| 172 | 172 | ||
| 173 | + function stripTypeScriptTypesForCoverage(code) { | ||
| 174 | + validateString(code, 'code'); | ||
| 175 | + return processTypeScriptCode(code, { mode: 'strip-only' }); | ||
| 176 | + } | ||
| 177 | + | ||
| 173 | 178 | /** | |
| 174 | 179 | * Performs type-stripping to TypeScript source code internally. | |
| 175 | 180 | * It is used by internal loaders. | |
@@ -236,4 +241,5 @@ function addSourceMap(code, sourceMap) { | |||
| 236 | 241 | module.exports = { | |
| 237 | 242 | stripTypeScriptModuleTypes, | |
| 238 | 243 | stripTypeScriptTypes, | |
| 244 | + stripTypeScriptTypesForCoverage, | ||
| 239 | 245 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,7 @@ const { | |||
| 16 | 16 | StringPrototypeIncludes, | |
| 17 | 17 | StringPrototypeLocaleCompare, | |
| 18 | 18 | StringPrototypeStartsWith, | |
| 19 | + StringPrototypeTrim, | ||
| 19 | 20 | } = primordials; | |
| 20 | 21 | const { | |
| 21 | 22 | copyFileSync, | |
@@ -45,6 +46,20 @@ const kIgnoreRegex = /\/\* node:coverage ignore next (?<count>\d+ )?\*\//; | |||
| 45 | 46 | const kLineEndingRegex = /\r?\n$/u; | |
| 46 | 47 | const kLineSplitRegex = /(?<=\r?\n)/u; | |
| 47 | 48 | const kStatusRegex = /\/\* node:coverage (?<status>enable|disable) \*\//; | |
| 49 | + const kTypeOnlyImportRegex = /^\s*import\s+type\b/u; | ||
| 50 | + const kTypeScriptSourceRegex = /\.(?:cts|mts|ts)$/u; | ||
| 51 | + | ||
| 52 | + let stripTypeScriptTypesForCoverage; | ||
| 53 | + | ||
| 54 | + function getStripTypeScriptTypesForCoverage() { | ||
| 55 | + if (!process.config.variables.node_use_amaro) { | ||
| 56 | + return; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + stripTypeScriptTypesForCoverage ??= | ||
| 60 | + require('internal/modules/typescript').stripTypeScriptTypesForCoverage; | ||
| 61 | + return stripTypeScriptTypesForCoverage; | ||
| 62 | + } | ||
| 48 | 63 | ||
| 49 | 64 | class CoverageLine { | |
| 50 | 65 | constructor(line, startOffset, src, length = src?.length) { | |
@@ -70,6 +85,7 @@ class TestCoverage { | |||
| 70 | 85 | } | |
| 71 | 86 | ||
| 72 | 87 | #sourceLines = new SafeMap(); | |
| 88 | + #typeScriptLines = new SafeSet(); | ||
| 73 | 89 | ||
| 74 | 90 | getLines(fileUrl, source) { | |
| 75 | 91 | // Split the file source into lines. Make sure the lines maintain their | |
@@ -134,6 +150,57 @@ class TestCoverage { | |||
| 134 | 150 | return lines; | |
| 135 | 151 | } | |
| 136 | 152 | ||
| 153 | + markTypeScriptOnlyLines(fileUrl, source) { | ||
| 154 | + if (this.#typeScriptLines.has(fileUrl)) { | ||
| 155 | + return; | ||
| 156 | + } | ||
| 157 | + this.#typeScriptLines.add(fileUrl); | ||
| 158 | + | ||
| 159 | + if (RegExpPrototypeExec(kTypeScriptSourceRegex, fileUrl) === null) { | ||
| 160 | + return; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + const lines = this.getLines(fileUrl, source); | ||
| 164 | + if (!lines) { | ||
| 165 | + return; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + let strippedLines; | ||
| 169 | + const stripSource = getStripTypeScriptTypesForCoverage(); | ||
| 170 | + | ||
| 171 | + if (stripSource) { | ||
| 172 | + source ??= readFileSync(fileURLToPath(fileUrl), 'utf8'); | ||
| 173 | + | ||
| 174 | + try { | ||
| 175 | + strippedLines = RegExpPrototypeSymbolSplit( | ||
| 176 | + kLineSplitRegex, | ||
| 177 | + stripSource(source), | ||
| 178 | + ); | ||
| 179 | + } catch { | ||
| 180 | + strippedLines = undefined; | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + for (let i = 0; i < lines.length; ++i) { | ||
| 185 | + const originalLine = lines[i].src; | ||
| 186 | + | ||
| 187 | + if (StringPrototypeTrim(originalLine).length === 0) { | ||
| 188 | + continue; | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + if (strippedLines?.[i] !== undefined) { | ||
| 192 | + if (StringPrototypeTrim(strippedLines[i]).length === 0) { | ||
| 193 | + lines[i].ignore = true; | ||
| 194 | + } | ||
| 195 | + continue; | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + if (RegExpPrototypeExec(kTypeOnlyImportRegex, originalLine) !== null) { | ||
| 199 | + lines[i].ignore = true; | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + | ||
| 137 | 204 | summary() { | |
| 138 | 205 | internalBinding('profiler').takeCoverage(); | |
| 139 | 206 | const coverage = this.getCoverageFromDirectory(); | |
@@ -369,10 +436,12 @@ class TestCoverage { | |||
| 369 | 436 | offset += length + 1; | |
| 370 | 437 | return coverageLine; | |
| 371 | 438 | }); | |
| 372 | - if (data.sourcesContent != null) { | ||
| 373 | - for (let j = 0; j < data.sources.length; ++j) { | ||
| 374 | - this.getLines(data.sources[j], data.sourcesContent[j]); | ||
| 439 | + for (let j = 0; j < data.sources.length; ++j) { | ||
| 440 | + const source = data.sourcesContent?.[j]; | ||
| 441 | + if (source != null) { | ||
| 442 | + this.getLines(data.sources[j], source); | ||
| 375 | 443 | } | |
| 444 | + this.markTypeScriptOnlyLines(data.sources[j], source); | ||
| 376 | 445 | } | |
| 377 | 446 | const sourceMap = new SourceMap(data, { __proto__: null, lineLengths }); | |
| 378 | 447 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + console.log('Hi'); | ||
| 2 | + export {}; | ||
| 3 | + //# sourceMappingURL=a.mjs.map | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + import type {} from 'node:assert'; | ||
| 2 | + | ||
| 3 | + console.log('Hi'); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + import './dist/a.mjs'; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,6 +73,31 @@ describe('Coverage with source maps', async () => { | |||
| 73 | 73 | t.assert.strictEqual(spawned.code, 1); | |
| 74 | 74 | }); | |
| 75 | 75 | ||
| 76 | + await it('should ignore erased TypeScript import type lines', async (t) => { | ||
| 77 | + const report = generateReport([ | ||
| 78 | + '# ----------------------------------------------------------', | ||
| 79 | + '# file | line % | branch % | funcs % | uncovered lines', | ||
| 80 | + '# ----------------------------------------------------------', | ||
| 81 | + '# src | | | | ', | ||
| 82 | + '# a.mts | 100.00 | 100.00 | 100.00 | ', | ||
| 83 | + '# test.mjs | 100.00 | 100.00 | 100.00 | ', | ||
| 84 | + '# ----------------------------------------------------------', | ||
| 85 | + '# all files | 100.00 | 100.00 | 100.00 | ', | ||
| 86 | + '# ----------------------------------------------------------', | ||
| 87 | + ]); | ||
| 88 | + | ||
| 89 | + const spawned = await common.spawnPromisified(process.execPath, [ | ||
| 90 | + ...flags, | ||
| 91 | + 'test.mjs', | ||
| 92 | + ], { | ||
| 93 | + cwd: fixtures.path('test-runner', 'source-maps', 'type-only-import'), | ||
| 94 | + }); | ||
| 95 | + | ||
| 96 | + t.assert.strictEqual(spawned.stderr, ''); | ||
| 97 | + t.assert.ok(spawned.stdout.includes(report)); | ||
| 98 | + t.assert.strictEqual(spawned.code, 0); | ||
| 99 | + }); | ||
| 100 | + | ||
| 76 | 101 | await it('properly accounts for line endings in source maps', async (t) => { | |
| 77 | 102 | const report = generateReport([ | |
| 78 | 103 | '# ------------------------------------------------------------------', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments