Skip to content

Commit f01a4ff

Browse files
committed
feat(next/swc): support experimental coverage instrument
1 parent d1db714 commit f01a4ff

File tree

9 files changed

+256
-7
lines changed

9 files changed

+256
-7
lines changed

packages/next-swc/Cargo.lock

Lines changed: 62 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/next-swc/crates/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ swc_ecma_loader = { version = "0.29.1", features = ["node", "lru"] }
2828
swc_ecmascript = { version = "0.150.0", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] }
2929
swc_cached = "0.1.1"
3030
tracing = { version = "0.1.32", features = ["release_max_level_info"] }
31+
swc-coverage-instrument = "0.0.2"
3132

3233
[dev-dependencies]
3334
swc_ecma_transforms_testing = "0.82.0"

packages/next-swc/crates/core/src/lib.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use swc_common::{FileName, SourceFile, SourceMap};
4343
use swc_ecmascript::ast::EsVersion;
4444
use swc_ecmascript::parser::parse_file_as_module;
4545
use swc_ecmascript::transforms::pass::noop;
46-
use swc_ecmascript::visit::Fold;
46+
use swc_ecmascript::visit::{as_folder, Fold};
4747

4848
pub mod amp_attributes;
4949
mod auto_cjs;
@@ -104,9 +104,12 @@ pub struct TransformOptions {
104104

105105
#[serde(default)]
106106
pub modularize_imports: Option<modularize_imports::Config>,
107+
108+
#[serde(default)]
109+
pub coverage_instrument: Option<swc_coverage_instrument::InstrumentOptions>,
107110
}
108111

109-
pub fn custom_before_pass<'a, C: Comments + 'a>(
112+
pub fn custom_before_pass<'a, C: Comments + 'a + std::clone::Clone>(
110113
cm: Arc<SourceMap>,
111114
file: Arc<SourceFile>,
112115
opts: &'a TransformOptions,
@@ -193,8 +196,8 @@ pub fn custom_before_pass<'a, C: Comments + 'a>(
193196
Either::Left(swc_emotion::EmotionTransformer::new(
194197
config.clone(),
195198
path,
196-
cm,
197-
comments,
199+
cm.clone(),
200+
comments.clone(),
198201
))
199202
})
200203
} else {
@@ -205,7 +208,18 @@ pub fn custom_before_pass<'a, C: Comments + 'a>(
205208
match &opts.modularize_imports {
206209
Some(config) => Either::Left(modularize_imports::modularize_imports(config.clone())),
207210
None => Either::Right(noop()),
208-
}
211+
},
212+
match &opts.coverage_instrument {
213+
Some(config) => Either::Left(as_folder(
214+
swc_coverage_instrument::create_coverage_instrumentation_visitor(
215+
cm,
216+
comments,
217+
config.clone(),
218+
file.name.to_string()
219+
)
220+
)),
221+
None => Either::Right(noop()),
222+
},
209223
)
210224
}
211225

packages/next-swc/crates/core/tests/fixture.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use next_swc::{
1010
};
1111
use std::path::PathBuf;
1212
use swc_common::{chain, comments::SingleThreadedComments, FileName, Mark};
13+
use swc_coverage_instrument::create_coverage_instrumentation_visitor;
1314
use swc_ecma_transforms_testing::{test, test_fixture};
1415
use swc_ecmascript::{
1516
parser::{EsConfig, Syntax},
@@ -209,3 +210,21 @@ fn shake_exports_fixture_default(input: PathBuf) {
209210
&output,
210211
);
211212
}
213+
214+
#[fixture("tests/fixture/coverage-instrument/input.js")]
215+
fn coverage_instrument_simple_default(input: PathBuf) {
216+
let output = input.parent().unwrap().join("output.js");
217+
test_fixture(
218+
syntax(),
219+
&|tr| {
220+
swc_ecmascript::visit::as_folder(create_coverage_instrumentation_visitor(
221+
tr.cm.clone(),
222+
tr.comments.clone(),
223+
Default::default(),
224+
"anon".to_string(),
225+
))
226+
},
227+
&input,
228+
&output,
229+
);
230+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
function* x() { yield 1; yield 2; };
2+
var k;
3+
output = 0;
4+
for (k of x()) {
5+
output += k;
6+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
function cov_473891610594984201() {
2+
var path = "anon";
3+
var hash = "13293363232410732979";
4+
var global = new Function("return this")();
5+
var gcv = "__coverage__";
6+
var coverageData = {
7+
all: false,
8+
path: "anon",
9+
statementMap: {
10+
"0": {
11+
start: {
12+
line: 1,
13+
column: 16
14+
},
15+
end: {
16+
line: 1,
17+
column: 24
18+
}
19+
},
20+
"1": {
21+
start: {
22+
line: 1,
23+
column: 25
24+
},
25+
end: {
26+
line: 1,
27+
column: 33
28+
}
29+
},
30+
"2": {
31+
start: {
32+
line: 3,
33+
column: 0
34+
},
35+
end: {
36+
line: 3,
37+
column: 11
38+
}
39+
},
40+
"3": {
41+
start: {
42+
line: 4,
43+
column: 0
44+
},
45+
end: {
46+
line: 6,
47+
column: 1
48+
}
49+
},
50+
"4": {
51+
start: {
52+
line: 5,
53+
column: 2
54+
},
55+
end: {
56+
line: 5,
57+
column: 14
58+
}
59+
}
60+
},
61+
fnMap: {
62+
"0": {
63+
name: "x",
64+
decl: {
65+
start: {
66+
line: 1,
67+
column: 10
68+
},
69+
end: {
70+
line: 1,
71+
column: 11
72+
}
73+
},
74+
loc: {
75+
start: {
76+
line: 1,
77+
column: 14
78+
},
79+
end: {
80+
line: 1,
81+
column: 35
82+
}
83+
},
84+
line: 1
85+
}
86+
},
87+
branchMap: {},
88+
s: {
89+
"0": 0,
90+
"1": 0,
91+
"2": 0,
92+
"3": 0,
93+
"4": 0
94+
},
95+
f: {
96+
"0": 0
97+
},
98+
b: {},
99+
_coverageSchema: "11020577277169172593",
100+
hash: "13293363232410732979"
101+
};
102+
var coverage = global[gcv] || (global[gcv] = {});
103+
if (!coverage[path] || coverage[path].hash !== hash) {
104+
coverage[path] = coverageData;
105+
}
106+
var actualCoverage = coverage[path];
107+
{
108+
cov_473891610594984201 = function() {
109+
return actualCoverage;
110+
};
111+
}
112+
return actualCoverage;
113+
}
114+
cov_473891610594984201();
115+
function* x() {
116+
cov_473891610594984201().f[0]++;
117+
cov_473891610594984201().s[0]++;
118+
yield 1;
119+
cov_473891610594984201().s[1]++;
120+
yield 2;
121+
}
122+
;
123+
var k;
124+
cov_473891610594984201().s[2]++;
125+
output = 0;
126+
cov_473891610594984201().s[3]++;
127+
for (k of x()){
128+
cov_473891610594984201().s[4]++;
129+
output += k;
130+
}

packages/next-swc/crates/core/tests/full.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn test(input: &Path, minify: bool) {
6363
shake_exports: None,
6464
emotion: Some(assert_json("{}")),
6565
modularize_imports: None,
66+
coverage_instrument: None,
6667
};
6768

6869
let options = options.patch(&fm);

packages/next/build/swc/options.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ function getBaseSWCOptions({
109109
modularizeImports: nextConfig?.experimental?.modularizeImports,
110110
relay: nextConfig?.compiler?.relay,
111111
emotion: getEmotionOptions(nextConfig, development),
112+
coverageInstrument: getCoverageInstrumentOptions(nextConfig),
112113
}
113114
}
114115

@@ -139,6 +140,15 @@ function getEmotionOptions(nextConfig, development) {
139140
}
140141
}
141142

143+
function getCoverageInstrumentOptions(nextConfig) {
144+
const options = nextConfig?.experimental?.swcInstrumentCoverage
145+
if (!options) {
146+
return
147+
}
148+
149+
return options === true ? {} : options
150+
}
151+
142152
export function getJestSWCOptions({
143153
isServer,
144154
filename,

packages/next/server/config-shared.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ export interface ExperimentalConfig {
133133
}
134134
>
135135
swcTraceProfiling?: boolean
136+
swcInstrumentCoverage?:
137+
| boolean
138+
| {
139+
coverageVariable?: string
140+
compact?: boolean
141+
reportLogic?: boolean
142+
ignoreClassMethods?: Array<string>
143+
}
136144
}
137145

138146
/**

0 commit comments

Comments
 (0)