Skip to content

Commit 809d015

Browse files
authored
Add support for removing React properties. (#31606)
1 parent ed1d024 commit 809d015

File tree

17 files changed

+249
-1
lines changed

17 files changed

+249
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env.local
29+
.env.development.local
30+
.env.test.local
31+
.env.production.local
32+
33+
# vercel
34+
.vercel
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# React Remove Properties Example
2+
3+
This example shows how to use the `reactRemoveProperties` config option to remove React properties.
4+
5+
## Preview
6+
7+
Preview the example live on [StackBlitz](http://stackblitz.com/):
8+
9+
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/react-remove-properties)
10+
11+
## Deploy your own
12+
13+
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
14+
15+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/react-remove-properties&project-name=react-remove-properties&repository-name=react-remove-properties)
16+
17+
## How to use
18+
19+
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
20+
21+
```bash
22+
npx create-next-app --example react-remove-properties react-remove-properties-app
23+
# or
24+
yarn create next-app --example react-remove-properties react-remove-properties-app
25+
```
26+
27+
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
experimental: {
3+
reactRemoveProperties: true,
4+
// Or, specify a custom list of regular expressions to match properties to remove.
5+
// The regexes defined here are processed in Rust so the syntax is different from
6+
// JavaScript `RegExp`s. See https://docs.rs/regex.
7+
// reactRemoveProperties: { properties: ['^data-custom$'] },
8+
},
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"dev": "next dev",
5+
"build": "next build",
6+
"start": "next start"
7+
},
8+
"dependencies": {
9+
"next": "latest",
10+
"react": "^17.0.2",
11+
"react-dom": "^17.0.2"
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const Index = () => (
2+
<div data-test-id="1" data-custom="1a">
3+
<div data-custom="2">
4+
<h1 data-testid="3">Hello World!</h1>
5+
</div>
6+
</div>
7+
)
8+
9+
export default Index

packages/next-swc/Cargo.lock

+1
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

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ swc_ecmascript = { version = "0.88.1", features = ["codegen", "minifier", "optim
2525
swc_node_base = "0.5.1"
2626
swc_stylis = "0.28.0"
2727
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
28+
regex = "1.5"
2829

2930
[dev-dependencies]
3031
swc_ecma_transforms_testing = "0.45.1"

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

+9
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub mod hook_optimizer;
5252
pub mod next_dynamic;
5353
pub mod next_ssg;
5454
pub mod page_config;
55+
pub mod react_remove_properties;
5556
pub mod remove_console;
5657
pub mod styled_jsx;
5758
mod top_level_binding_collector;
@@ -82,6 +83,9 @@ pub struct TransformOptions {
8283

8384
#[serde(default)]
8485
pub remove_console: Option<remove_console::Config>,
86+
87+
#[serde(default)]
88+
pub react_remove_properties: Option<react_remove_properties::Config>,
8589
}
8690

8791
pub fn custom_before_pass(file: Arc<SourceFile>, opts: &TransformOptions) -> impl Fold {
@@ -115,6 +119,11 @@ pub fn custom_before_pass(file: Arc<SourceFile>, opts: &TransformOptions) -> imp
115119
Either::Left(remove_console::remove_console(config.clone())),
116120
_ => Either::Right(noop()),
117121
},
122+
match &opts.react_remove_properties {
123+
Some(config) if config.truthy() =>
124+
Either::Left(react_remove_properties::remove_properties(config.clone())),
125+
_ => Either::Right(noop()),
126+
},
118127
)
119128
}
120129

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use regex::Regex;
2+
use serde::Deserialize;
3+
use swc_ecmascript::ast::*;
4+
use swc_ecmascript::visit::{noop_fold_type, Fold, FoldWith};
5+
6+
#[derive(Clone, Debug, Deserialize)]
7+
#[serde(untagged)]
8+
pub enum Config {
9+
All(bool),
10+
WithOptions(Options),
11+
}
12+
13+
impl Config {
14+
pub fn truthy(&self) -> bool {
15+
match self {
16+
Config::All(b) => *b,
17+
Config::WithOptions(_) => true,
18+
}
19+
}
20+
}
21+
22+
#[derive(Clone, Debug, Deserialize)]
23+
pub struct Options {
24+
#[serde(default)]
25+
pub properties: Vec<String>,
26+
}
27+
28+
struct RemoveProperties {
29+
properties: Vec<Regex>,
30+
}
31+
32+
impl RemoveProperties {
33+
fn should_remove_property(&self, name: &str) -> bool {
34+
self.properties.iter().any(|p| p.is_match(name))
35+
}
36+
}
37+
38+
impl Fold for RemoveProperties {
39+
noop_fold_type!();
40+
41+
fn fold_jsx_opening_element(&mut self, mut el: JSXOpeningElement) -> JSXOpeningElement {
42+
el.attrs.retain(|attr| match attr {
43+
JSXAttrOrSpread::JSXAttr(JSXAttr {
44+
name: JSXAttrName::Ident(ident),
45+
..
46+
}) if self.should_remove_property(ident.sym.as_ref()) => false,
47+
_ => true,
48+
});
49+
el.fold_children_with(self)
50+
}
51+
}
52+
53+
pub fn remove_properties(config: Config) -> impl Fold {
54+
let mut properties: Vec<Regex> = match config {
55+
Config::WithOptions(x) => x
56+
.properties
57+
.iter()
58+
.map(|pattern| {
59+
Regex::new(pattern).unwrap_or_else(|e| {
60+
panic!("error compiling property regex `{}`: {}", pattern, e);
61+
})
62+
})
63+
.collect(),
64+
_ => vec![],
65+
};
66+
if properties.is_empty() {
67+
// Keep the default regex identical to `babel-plugin-react-remove-properties`.
68+
properties.push(Regex::new(r"^data-test").unwrap());
69+
}
70+
let remover = RemoveProperties { properties };
71+
remover
72+
}

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

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use next_swc::{
22
amp_attributes::amp_attributes, next_dynamic::next_dynamic, next_ssg::next_ssg,
3-
page_config::page_config_test, remove_console::remove_console, styled_jsx::styled_jsx,
3+
page_config::page_config_test, react_remove_properties::remove_properties,
4+
remove_console::remove_console, styled_jsx::styled_jsx,
45
};
56
use std::path::PathBuf;
67
use swc_common::{chain, comments::SingleThreadedComments, FileName, Mark, Span, DUMMY_SP};
@@ -130,3 +131,31 @@ fn remove_console_fixture(input: PathBuf) {
130131
&output,
131132
);
132133
}
134+
135+
#[fixture("tests/fixture/react-remove-properties/default/**/input.js")]
136+
fn react_remove_properties_default_fixture(input: PathBuf) {
137+
let output = input.parent().unwrap().join("output.js");
138+
test_fixture(
139+
syntax(),
140+
&|_tr| remove_properties(next_swc::react_remove_properties::Config::All(true)),
141+
&input,
142+
&output,
143+
);
144+
}
145+
146+
#[fixture("tests/fixture/react-remove-properties/custom/**/input.js")]
147+
fn react_remove_properties_custom_fixture(input: PathBuf) {
148+
let output = input.parent().unwrap().join("output.js");
149+
test_fixture(
150+
syntax(),
151+
&|_tr| {
152+
remove_properties(next_swc::react_remove_properties::Config::WithOptions(
153+
next_swc::react_remove_properties::Options {
154+
properties: vec!["^data-custom$".into()],
155+
},
156+
))
157+
},
158+
&input,
159+
&output,
160+
);
161+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Home() {
2+
return <div data-test-id="1" data-custom="1a">
3+
<div data-custom="2">
4+
<h1 data-testid="3">Hello World!</h1>
5+
</div>
6+
</div>
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default function Home() {
2+
return <div data-test-id="1">
3+
4+
<div >
5+
6+
<h1 data-testid="3">Hello World!</h1>
7+
8+
</div>
9+
10+
</div>;
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Home() {
2+
return <div data-test-id="1" data-custom="1a">
3+
<div data-custom="2">
4+
<h1 data-testid="3" nested={() => (<div data-testid="4">nested</div>)}>Hello World!</h1>
5+
</div>
6+
</div>
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default function Home() {
2+
return <div data-custom="1a">
3+
4+
<div data-custom="2">
5+
6+
<h1 nested={()=><div >nested</div>
7+
}>Hello World!</h1>
8+
9+
</div>
10+
11+
</div>;
12+
};

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

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fn test(input: &Path, minify: bool) {
5858
is_development: true,
5959
styled_components: Some(assert_json("{}")),
6060
remove_console: None,
61+
react_remove_properties: None,
6162
};
6263

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

packages/next/build/swc/options.js

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function getBaseSWCOptions({
6767
}
6868
: null,
6969
removeConsole: nextConfig?.experimental?.removeConsole,
70+
reactRemoveProperties: nextConfig?.experimental?.reactRemoveProperties,
7071
}
7172
}
7273

packages/next/server/config-shared.ts

+5
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ export type NextConfig = { [key: string]: any } & {
132132
| {
133133
exclude?: string[]
134134
}
135+
reactRemoveProperties?:
136+
| boolean
137+
| {
138+
properties?: string[]
139+
}
135140
styledComponents?: boolean
136141
swcMinify?: boolean
137142
cpus?: number

0 commit comments

Comments
 (0)