-
Notifications
You must be signed in to change notification settings - Fork 27.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a swc transform for removal of
console.*
calls. (#31449)
- Loading branch information
Showing
16 changed files
with
492 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Remove Console Example | ||
|
||
This example shows how to use the `removeConsole` config option to remove all `console.*` calls. | ||
|
||
## 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/remove-console) | ||
|
||
## 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/remove-console&project-name=remove-console&repository-name=remove-console) | ||
|
||
## 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 remove-console remove-console-app | ||
# or | ||
yarn create next-app --example remove-console remove-console-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)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
experimental: { | ||
removeConsole: { | ||
exclude: ['error'], | ||
}, | ||
// Uncomment this to suppress all logs. | ||
// removeConsole: true, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const Index = () => ( | ||
<div> | ||
<p>The console should be empty.</p> | ||
</div> | ||
) | ||
|
||
console.log('log from index.js') | ||
console.error('error log from index.js') | ||
|
||
export default Index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use serde::Deserialize; | ||
use swc_atoms::JsWord; | ||
use swc_common::collections::AHashSet; | ||
use swc_common::DUMMY_SP; | ||
use swc_ecmascript::ast::*; | ||
use swc_ecmascript::utils::ident::IdentLike; | ||
use swc_ecmascript::utils::Id; | ||
use swc_ecmascript::visit::{noop_fold_type, Fold, FoldWith}; | ||
|
||
use crate::top_level_binding_collector::collect_top_level_decls; | ||
|
||
#[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 exclude: Vec<JsWord>, | ||
} | ||
|
||
struct RemoveConsole { | ||
exclude: Vec<JsWord>, | ||
bindings: Vec<AHashSet<Id>>, | ||
in_function_params: bool, | ||
} | ||
|
||
impl RemoveConsole { | ||
fn is_global_console(&self, ident: &Ident) -> bool { | ||
&ident.sym == "console" | ||
&& self | ||
.bindings | ||
.iter() | ||
.find(|x| x.contains(&ident.to_id())) | ||
.is_none() | ||
} | ||
|
||
fn should_remove_call(&mut self, n: &CallExpr) -> bool { | ||
let callee = &n.callee; | ||
let member_expr = match callee { | ||
ExprOrSuper::Expr(e) => match &**e { | ||
Expr::Member(m) => m, | ||
_ => return false, | ||
}, | ||
_ => return false, | ||
}; | ||
|
||
// Don't attempt to evaluate computed properties. | ||
if member_expr.computed { | ||
return false; | ||
} | ||
|
||
// Only proceed if the object is the global `console` object. | ||
match &member_expr.obj { | ||
ExprOrSuper::Expr(e) => match &**e { | ||
Expr::Ident(i) if self.is_global_console(i) => {} | ||
_ => return false, | ||
}, | ||
_ => return false, | ||
} | ||
|
||
// Check if the property is requested to be excluded. | ||
// Here we do an O(n) search on the list of excluded properties because the size | ||
// should be small. | ||
match &*member_expr.prop { | ||
Expr::Ident(i) if self.exclude.iter().find(|x| **x == i.sym).is_none() => {} | ||
_ => return false, | ||
} | ||
|
||
true | ||
} | ||
} | ||
|
||
impl Fold for RemoveConsole { | ||
noop_fold_type!(); | ||
|
||
fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { | ||
match &stmt { | ||
Stmt::Expr(e) => match &*e.expr { | ||
Expr::Call(c) => { | ||
if self.should_remove_call(c) { | ||
return Stmt::Empty(EmptyStmt { span: DUMMY_SP }); | ||
} | ||
} | ||
_ => {} | ||
}, | ||
_ => {} | ||
} | ||
stmt.fold_children_with(self) | ||
} | ||
|
||
fn fold_function(&mut self, mut func: Function) -> Function { | ||
self.in_function_params = true; | ||
let mut new_params: AHashSet<Id> = AHashSet::default(); | ||
for param in &func.params { | ||
new_params.extend(collect_top_level_decls(param)); | ||
} | ||
self.in_function_params = false; | ||
|
||
self.bindings.push(new_params); | ||
self.bindings.push(collect_top_level_decls(&func)); | ||
func.body = func.body.fold_with(self); | ||
self.bindings.pop().unwrap(); | ||
self.bindings.pop().unwrap(); | ||
func | ||
} | ||
|
||
fn fold_module(&mut self, module: Module) -> Module { | ||
self.bindings.push(collect_top_level_decls(&module)); | ||
let m = module.fold_children_with(self); | ||
self.bindings.pop().unwrap(); | ||
m | ||
} | ||
|
||
fn fold_script(&mut self, script: Script) -> Script { | ||
self.bindings.push(collect_top_level_decls(&script)); | ||
let s = script.fold_with(self); | ||
self.bindings.pop().unwrap(); | ||
s | ||
} | ||
} | ||
|
||
pub fn remove_console(config: Config) -> impl Fold { | ||
let exclude = match config { | ||
Config::WithOptions(x) => x.exclude, | ||
_ => vec![], | ||
}; | ||
let remover = RemoveConsole { | ||
exclude, | ||
bindings: Default::default(), | ||
in_function_params: false, | ||
}; | ||
remover | ||
} |
Oops, something went wrong.