Add support for removing React properties. (#31606)
This commit is contained in:
parent
ed1d0241db
commit
809d0155ff
17 changed files with 249 additions and 1 deletions
34
examples/react-remove-properties/.gitignore
vendored
Normal file
34
examples/react-remove-properties/.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
27
examples/react-remove-properties/README.md
Normal file
27
examples/react-remove-properties/README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# React Remove Properties Example
|
||||
|
||||
This example shows how to use the `reactRemoveProperties` config option to remove React properties.
|
||||
|
||||
## Preview
|
||||
|
||||
Preview the example live on [StackBlitz](http://stackblitz.com/):
|
||||
|
||||
[![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)
|
||||
|
||||
## Deploy your own
|
||||
|
||||
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
|
||||
|
||||
[![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)
|
||||
|
||||
## How to use
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example react-remove-properties react-remove-properties-app
|
||||
# or
|
||||
yarn create next-app --example react-remove-properties react-remove-properties-app
|
||||
```
|
||||
|
||||
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)).
|
9
examples/react-remove-properties/next.config.js
Normal file
9
examples/react-remove-properties/next.config.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
experimental: {
|
||||
reactRemoveProperties: true,
|
||||
// Or, specify a custom list of regular expressions to match properties to remove.
|
||||
// The regexes defined here are processed in Rust so the syntax is different from
|
||||
// JavaScript `RegExp`s. See https://docs.rs/regex.
|
||||
// reactRemoveProperties: { properties: ['^data-custom$'] },
|
||||
},
|
||||
}
|
13
examples/react-remove-properties/package.json
Normal file
13
examples/react-remove-properties/package.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "latest",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
9
examples/react-remove-properties/pages/index.js
vendored
Normal file
9
examples/react-remove-properties/pages/index.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
const Index = () => (
|
||||
<div data-test-id="1" data-custom="1a">
|
||||
<div data-custom="2">
|
||||
<h1 data-testid="3">Hello World!</h1>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Index
|
1
packages/next-swc/Cargo.lock
generated
1
packages/next-swc/Cargo.lock
generated
|
@ -822,6 +822,7 @@ dependencies = [
|
|||
"either",
|
||||
"fxhash",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"styled_components",
|
||||
|
|
|
@ -25,6 +25,7 @@ swc_ecmascript = { version = "0.88.1", features = ["codegen", "minifier", "optim
|
|||
swc_node_base = "0.5.1"
|
||||
swc_stylis = "0.28.0"
|
||||
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
|
||||
regex = "1.5"
|
||||
|
||||
[dev-dependencies]
|
||||
swc_ecma_transforms_testing = "0.45.1"
|
||||
|
|
|
@ -52,6 +52,7 @@ pub mod hook_optimizer;
|
|||
pub mod next_dynamic;
|
||||
pub mod next_ssg;
|
||||
pub mod page_config;
|
||||
pub mod react_remove_properties;
|
||||
pub mod remove_console;
|
||||
pub mod styled_jsx;
|
||||
mod top_level_binding_collector;
|
||||
|
@ -82,6 +83,9 @@ pub struct TransformOptions {
|
|||
|
||||
#[serde(default)]
|
||||
pub remove_console: Option<remove_console::Config>,
|
||||
|
||||
#[serde(default)]
|
||||
pub react_remove_properties: Option<react_remove_properties::Config>,
|
||||
}
|
||||
|
||||
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
|
|||
Either::Left(remove_console::remove_console(config.clone())),
|
||||
_ => Either::Right(noop()),
|
||||
},
|
||||
match &opts.react_remove_properties {
|
||||
Some(config) if config.truthy() =>
|
||||
Either::Left(react_remove_properties::remove_properties(config.clone())),
|
||||
_ => Either::Right(noop()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
72
packages/next-swc/crates/core/src/react_remove_properties.rs
Normal file
72
packages/next-swc/crates/core/src/react_remove_properties.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use swc_ecmascript::ast::*;
|
||||
use swc_ecmascript::visit::{noop_fold_type, Fold, FoldWith};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Config {
|
||||
All(bool),
|
||||
WithOptions(Options),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn truthy(&self) -> bool {
|
||||
match self {
|
||||
Config::All(b) => *b,
|
||||
Config::WithOptions(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Options {
|
||||
#[serde(default)]
|
||||
pub properties: Vec<String>,
|
||||
}
|
||||
|
||||
struct RemoveProperties {
|
||||
properties: Vec<Regex>,
|
||||
}
|
||||
|
||||
impl RemoveProperties {
|
||||
fn should_remove_property(&self, name: &str) -> bool {
|
||||
self.properties.iter().any(|p| p.is_match(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for RemoveProperties {
|
||||
noop_fold_type!();
|
||||
|
||||
fn fold_jsx_opening_element(&mut self, mut el: JSXOpeningElement) -> JSXOpeningElement {
|
||||
el.attrs.retain(|attr| match attr {
|
||||
JSXAttrOrSpread::JSXAttr(JSXAttr {
|
||||
name: JSXAttrName::Ident(ident),
|
||||
..
|
||||
}) if self.should_remove_property(ident.sym.as_ref()) => false,
|
||||
_ => true,
|
||||
});
|
||||
el.fold_children_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_properties(config: Config) -> impl Fold {
|
||||
let mut properties: Vec<Regex> = match config {
|
||||
Config::WithOptions(x) => x
|
||||
.properties
|
||||
.iter()
|
||||
.map(|pattern| {
|
||||
Regex::new(pattern).unwrap_or_else(|e| {
|
||||
panic!("error compiling property regex `{}`: {}", pattern, e);
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
};
|
||||
if properties.is_empty() {
|
||||
// Keep the default regex identical to `babel-plugin-react-remove-properties`.
|
||||
properties.push(Regex::new(r"^data-test").unwrap());
|
||||
}
|
||||
let remover = RemoveProperties { properties };
|
||||
remover
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use next_swc::{
|
||||
amp_attributes::amp_attributes, next_dynamic::next_dynamic, next_ssg::next_ssg,
|
||||
page_config::page_config_test, remove_console::remove_console, styled_jsx::styled_jsx,
|
||||
page_config::page_config_test, react_remove_properties::remove_properties,
|
||||
remove_console::remove_console, styled_jsx::styled_jsx,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use swc_common::{chain, comments::SingleThreadedComments, FileName, Mark, Span, DUMMY_SP};
|
||||
|
@ -130,3 +131,31 @@ fn remove_console_fixture(input: PathBuf) {
|
|||
&output,
|
||||
);
|
||||
}
|
||||
|
||||
#[fixture("tests/fixture/react-remove-properties/default/**/input.js")]
|
||||
fn react_remove_properties_default_fixture(input: PathBuf) {
|
||||
let output = input.parent().unwrap().join("output.js");
|
||||
test_fixture(
|
||||
syntax(),
|
||||
&|_tr| remove_properties(next_swc::react_remove_properties::Config::All(true)),
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
}
|
||||
|
||||
#[fixture("tests/fixture/react-remove-properties/custom/**/input.js")]
|
||||
fn react_remove_properties_custom_fixture(input: PathBuf) {
|
||||
let output = input.parent().unwrap().join("output.js");
|
||||
test_fixture(
|
||||
syntax(),
|
||||
&|_tr| {
|
||||
remove_properties(next_swc::react_remove_properties::Config::WithOptions(
|
||||
next_swc::react_remove_properties::Options {
|
||||
properties: vec!["^data-custom$".into()],
|
||||
},
|
||||
))
|
||||
},
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
}
|
||||
|
|
7
packages/next-swc/crates/core/tests/fixture/react-remove-properties/custom/simple/input.js
vendored
Normal file
7
packages/next-swc/crates/core/tests/fixture/react-remove-properties/custom/simple/input.js
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function Home() {
|
||||
return <div data-test-id="1" data-custom="1a">
|
||||
<div data-custom="2">
|
||||
<h1 data-testid="3">Hello World!</h1>
|
||||
</div>
|
||||
</div>
|
||||
}
|
11
packages/next-swc/crates/core/tests/fixture/react-remove-properties/custom/simple/output.js
vendored
Normal file
11
packages/next-swc/crates/core/tests/fixture/react-remove-properties/custom/simple/output.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
export default function Home() {
|
||||
return <div data-test-id="1">
|
||||
|
||||
<div >
|
||||
|
||||
<h1 data-testid="3">Hello World!</h1>
|
||||
|
||||
</div>
|
||||
|
||||
</div>;
|
||||
};
|
7
packages/next-swc/crates/core/tests/fixture/react-remove-properties/default/simple/input.js
vendored
Normal file
7
packages/next-swc/crates/core/tests/fixture/react-remove-properties/default/simple/input.js
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function Home() {
|
||||
return <div data-test-id="1" data-custom="1a">
|
||||
<div data-custom="2">
|
||||
<h1 data-testid="3" nested={() => (<div data-testid="4">nested</div>)}>Hello World!</h1>
|
||||
</div>
|
||||
</div>
|
||||
}
|
12
packages/next-swc/crates/core/tests/fixture/react-remove-properties/default/simple/output.js
vendored
Normal file
12
packages/next-swc/crates/core/tests/fixture/react-remove-properties/default/simple/output.js
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
export default function Home() {
|
||||
return <div data-custom="1a">
|
||||
|
||||
<div data-custom="2">
|
||||
|
||||
<h1 nested={()=><div >nested</div>
|
||||
}>Hello World!</h1>
|
||||
|
||||
</div>
|
||||
|
||||
</div>;
|
||||
};
|
|
@ -58,6 +58,7 @@ fn test(input: &Path, minify: bool) {
|
|||
is_development: true,
|
||||
styled_components: Some(assert_json("{}")),
|
||||
remove_console: None,
|
||||
react_remove_properties: None,
|
||||
};
|
||||
|
||||
let options = options.patch(&fm);
|
||||
|
|
|
@ -67,6 +67,7 @@ function getBaseSWCOptions({
|
|||
}
|
||||
: null,
|
||||
removeConsole: nextConfig?.experimental?.removeConsole,
|
||||
reactRemoveProperties: nextConfig?.experimental?.reactRemoveProperties,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,11 @@ export type NextConfig = { [key: string]: any } & {
|
|||
| {
|
||||
exclude?: string[]
|
||||
}
|
||||
reactRemoveProperties?:
|
||||
| boolean
|
||||
| {
|
||||
properties?: string[]
|
||||
}
|
||||
styledComponents?: boolean
|
||||
swcMinify?: boolean
|
||||
cpus?: number
|
||||
|
|
Loading…
Reference in a new issue