From 4c359cb832e89091597dd2b20a26a20bac3ab31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 07:43:50 +0900 Subject: [PATCH 01/43] Add a module --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 1 + packages/next-swc/crates/core/src/lib.rs | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/next-swc/crates/core/src/cjs_optimizer.rs diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -0,0 +1 @@ + diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index 790492a29ab50..19b2809e2dd03 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -46,6 +46,7 @@ use turbopack_binding::swc::core::{ pub mod amp_attributes; mod auto_cjs; +pub mod cjs_optimizer; pub mod disallow_re_export_all_in_page; pub mod next_dynamic; pub mod next_ssg; From 2ebe0c2594b358885fda69fa0abb0d7ccba52929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 09:59:44 +0900 Subject: [PATCH 02/43] Data --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 8b137891791fe..a81b8110798da 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1 +1,12 @@ +use turbopack_binding::swc::core::ecma::visit::Visit; +pub struct Optimizer { + data: Data, +} + +struct Analyzer<'a> { + data: &'a mut Data, +} + +#[derive(Debug, Default)] +struct Data {} From ab5a0d6374a3da83b876606e074c81a6a5bd4806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 10:02:01 +0900 Subject: [PATCH 03/43] Use visitor --- .../next-swc/crates/core/src/cjs_optimizer.rs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index a81b8110798da..e3180f87db8d6 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,6 +1,18 @@ -use turbopack_binding::swc::core::ecma::visit::Visit; +use turbopack_binding::swc::core::ecma::{ + ast::{Module, Script}, + visit::{ + as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, + VisitWith, + }, +}; -pub struct Optimizer { +pub fn cjs_optimizer() -> impl Fold + VisitMut { + as_folder(Optimizer { + data: Data::default(), + }) +} + +struct Optimizer { data: Data, } @@ -10,3 +22,27 @@ struct Analyzer<'a> { #[derive(Debug, Default)] struct Data {} + +impl VisitMut for Optimizer { + fn visit_mut_module(&mut self, n: &mut Module) { + n.visit_with(&mut Analyzer { + data: &mut self.data, + }); + + n.visit_mut_children_with(self); + } + + fn visit_mut_script(&mut self, n: &mut Script) { + n.visit_with(&mut Analyzer { + data: &mut self.data, + }); + + n.visit_mut_children_with(self); + } + + noop_visit_mut_type!(); +} + +impl Visit for Analyzer<'_> { + noop_visit_type!(); +} From 20ac12d576938942440c33f05db7369fd89314db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 10:08:55 +0900 Subject: [PATCH 04/43] config --- .../next-swc/crates/core/src/cjs_optimizer.rs | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index e3180f87db8d6..27c2df4e9ac92 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,32 +1,48 @@ -use turbopack_binding::swc::core::ecma::{ - ast::{Module, Script}, - visit::{ - as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, - VisitWith, +use turbopack_binding::swc::core::{ + common::SyntaxContext, + ecma::{ + ast::{CallExpr, Callee, Expr, Lit, Module, Script, VarDeclarator}, + visit::{ + as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, + VisitWith, + }, }, }; -pub fn cjs_optimizer() -> impl Fold + VisitMut { +pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fold + VisitMut { as_folder(Optimizer { data: Data::default(), + config, + unresolved_ctxt, }) } +#[derive(Debug)] +pub struct Config {} + struct Optimizer { data: Data, + config: Config, + unresolved_ctxt: SyntaxContext, } struct Analyzer<'a> { data: &'a mut Data, + config: &'a Config, + unresolved_ctxt: SyntaxContext, } #[derive(Debug, Default)] struct Data {} impl VisitMut for Optimizer { + noop_visit_mut_type!(); + fn visit_mut_module(&mut self, n: &mut Module) { n.visit_with(&mut Analyzer { data: &mut self.data, + config: &self.config, + unresolved_ctxt: self.unresolved_ctxt, }); n.visit_mut_children_with(self); @@ -35,14 +51,36 @@ impl VisitMut for Optimizer { fn visit_mut_script(&mut self, n: &mut Script) { n.visit_with(&mut Analyzer { data: &mut self.data, + config: &self.config, + unresolved_ctxt: self.unresolved_ctxt, }); n.visit_mut_children_with(self); } - - noop_visit_mut_type!(); } impl Visit for Analyzer<'_> { noop_visit_type!(); + + fn visit_var_declarator(&mut self, n: &VarDeclarator) { + n.visit_children_with(self); + + // Find `require('foo')` + if let Some(Expr::Call(CallExpr { + callee: Callee::Expr(callee), + args, + .. + })) = n.init.as_deref() + { + if let Expr::Ident(ident) = &**callee { + if ident.sym == *"require" { + if let Some(arg) = args.get(0) { + if let Expr::Lit(Lit::Str(v)) = &*arg.expr { + // + } + } + } + } + } + } } From 03e01cf297ec385c6d084eea1df637344d17e272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 10:11:57 +0900 Subject: [PATCH 05/43] field --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 27c2df4e9ac92..2c78641dd533d 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,7 +1,9 @@ +use fxhash::FxHashMap; use turbopack_binding::swc::core::{ common::SyntaxContext, ecma::{ - ast::{CallExpr, Callee, Expr, Lit, Module, Script, VarDeclarator}, + ast::{CallExpr, Callee, Expr, Id, Lit, Module, Script, VarDeclarator}, + atoms::JsWord, visit::{ as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith, @@ -33,7 +35,10 @@ struct Analyzer<'a> { } #[derive(Debug, Default)] -struct Data {} +struct Data { + /// `(identifier) :(module_specifier, exported symbol)` + imports: FxHashMap, +} impl VisitMut for Optimizer { noop_visit_mut_type!(); From f228dc7108d76e70ecc160ef125e4878dc20445b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 13:35:23 +0900 Subject: [PATCH 06/43] Collect require calls --- .../next-swc/crates/core/src/cjs_optimizer.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 2c78641dd533d..a405ea54611bf 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -2,8 +2,8 @@ use fxhash::FxHashMap; use turbopack_binding::swc::core::{ common::SyntaxContext, ecma::{ - ast::{CallExpr, Callee, Expr, Id, Lit, Module, Script, VarDeclarator}, - atoms::JsWord, + ast::{CallExpr, Callee, Expr, Id, Lit, Module, Pat, Script, VarDeclarator}, + atoms::{Atom, JsWord}, visit::{ as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith, @@ -36,8 +36,10 @@ struct Analyzer<'a> { #[derive(Debug, Default)] struct Data { - /// `(identifier) :(module_specifier, exported symbol)` - imports: FxHashMap, + /// List of `require` calls + /// + /// `(identifier): (module_specifier)` + imports: FxHashMap, } impl VisitMut for Optimizer { @@ -81,7 +83,13 @@ impl Visit for Analyzer<'_> { if ident.sym == *"require" { if let Some(arg) = args.get(0) { if let Expr::Lit(Lit::Str(v)) = &*arg.expr { - // + // TODO: Config + + if let Pat::Ident(name) = &n.name { + self.data + .imports + .insert(name.to_id(), v.value.clone().into()); + } } } } From 6424f4cd2be8135ef616dbb17852078e679f7c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 13:40:31 +0900 Subject: [PATCH 07/43] Dep on rustc-hash --- packages/next-swc/crates/core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index d7fc2a94ccb32..19ece728c280f 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -17,6 +17,7 @@ once_cell = { workspace = true } next-transform-font = {workspace = true} pathdiff = "0.2.0" regex = "1.5" +rustc-hash = "1" serde = "1" serde_json = "1" sha1 = "0.10.1" From 19c3d1744fdf881d6faddd3dafb9e277ee991829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 13:40:36 +0900 Subject: [PATCH 08/43] cargo lockfile --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 5b1ace8fae98d..5cd98dea1df65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3333,6 +3333,7 @@ dependencies = [ "once_cell", "pathdiff", "regex", + "rustc-hash", "serde", "serde_json", "sha1 0.10.5", From b67710ef969d41c58c2f252dda8eef2e9e839b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 13:42:58 +0900 Subject: [PATCH 09/43] unresolved --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index a405ea54611bf..20fbadb96b849 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,9 +1,10 @@ -use fxhash::FxHashMap; +use rustc_hash::FxHashMap; use turbopack_binding::swc::core::{ common::SyntaxContext, ecma::{ ast::{CallExpr, Callee, Expr, Id, Lit, Module, Pat, Script, VarDeclarator}, - atoms::{Atom, JsWord}, + atoms::Atom, + utils::IdentRenamer, visit::{ as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith, @@ -40,6 +41,8 @@ struct Data { /// /// `(identifier): (module_specifier)` imports: FxHashMap, + + rename_map: FxHashMap, } impl VisitMut for Optimizer { @@ -52,7 +55,7 @@ impl VisitMut for Optimizer { unresolved_ctxt: self.unresolved_ctxt, }); - n.visit_mut_children_with(self); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } fn visit_mut_script(&mut self, n: &mut Script) { @@ -62,7 +65,7 @@ impl VisitMut for Optimizer { unresolved_ctxt: self.unresolved_ctxt, }); - n.visit_mut_children_with(self); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } } @@ -80,7 +83,7 @@ impl Visit for Analyzer<'_> { })) = n.init.as_deref() { if let Expr::Ident(ident) = &**callee { - if ident.sym == *"require" { + if ident.span.ctxt == self.unresolved_ctxt && ident.sym == *"require" { if let Some(arg) = args.get(0) { if let Expr::Lit(Lit::Str(v)) = &*arg.expr { // TODO: Config From 13efa00a1eaed1cb33eb9c76c41a2994c14a6177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 13:50:56 +0900 Subject: [PATCH 10/43] VisitMut --- .../next-swc/crates/core/src/cjs_optimizer.rs | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 20fbadb96b849..efb78d3114385 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -2,7 +2,10 @@ use rustc_hash::FxHashMap; use turbopack_binding::swc::core::{ common::SyntaxContext, ecma::{ - ast::{CallExpr, Callee, Expr, Id, Lit, Module, Pat, Script, VarDeclarator}, + ast::{ + CallExpr, Callee, Expr, Id, Lit, MemberExpr, MemberProp, Module, Pat, Script, + VarDeclarator, + }, atoms::Atom, utils::IdentRenamer, visit::{ @@ -29,12 +32,6 @@ struct Optimizer { unresolved_ctxt: SyntaxContext, } -struct Analyzer<'a> { - data: &'a mut Data, - config: &'a Config, - unresolved_ctxt: SyntaxContext, -} - #[derive(Debug, Default)] struct Data { /// List of `require` calls @@ -48,32 +45,18 @@ struct Data { impl VisitMut for Optimizer { noop_visit_mut_type!(); - fn visit_mut_module(&mut self, n: &mut Module) { - n.visit_with(&mut Analyzer { - data: &mut self.data, - config: &self.config, - unresolved_ctxt: self.unresolved_ctxt, - }); - - n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); - } - - fn visit_mut_script(&mut self, n: &mut Script) { - n.visit_with(&mut Analyzer { - data: &mut self.data, - config: &self.config, - unresolved_ctxt: self.unresolved_ctxt, - }); + fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) { + n.visit_mut_children_with(self); - n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + if let MemberProp::Ident(prop) = &n.prop { + if let Expr::Ident(obj) = &*n.obj { + if let Some(module_specifier) = self.data.imports.get(&obj.to_id()) {} + } + } } -} - -impl Visit for Analyzer<'_> { - noop_visit_type!(); - fn visit_var_declarator(&mut self, n: &VarDeclarator) { - n.visit_children_with(self); + fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) { + n.visit_mut_children_with(self); // Find `require('foo')` if let Some(Expr::Call(CallExpr { @@ -99,4 +82,14 @@ impl Visit for Analyzer<'_> { } } } + + fn visit_mut_module(&mut self, n: &mut Module) { + n.visit_mut_children_with(self); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + } + + fn visit_mut_script(&mut self, n: &mut Script) { + n.visit_mut_children_with(self); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + } } From a5541c0a891799ef6627308e4c2793aed0be520f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:01:43 +0900 Subject: [PATCH 11/43] Replace --- .../next-swc/crates/core/src/cjs_optimizer.rs | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index efb78d3114385..e2ba5f508f24b 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,13 +1,13 @@ use rustc_hash::FxHashMap; use turbopack_binding::swc::core::{ - common::SyntaxContext, + common::{SyntaxContext, DUMMY_SP}, ecma::{ ast::{ - CallExpr, Callee, Expr, Id, Lit, MemberExpr, MemberProp, Module, Pat, Script, - VarDeclarator, + CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, Pat, + Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, }, - atoms::Atom, - utils::IdentRenamer, + atoms::{Atom, JsWord}, + utils::{private_ident, ExprFactory, IdentRenamer}, visit::{ as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith, @@ -39,18 +39,61 @@ struct Data { /// `(identifier): (module_specifier)` imports: FxHashMap, + /// `(module_specifier, property): (identifier)` + replaced: FxHashMap<(Atom, JsWord), Id>, + + extra_stmts: Vec, + rename_map: FxHashMap, } impl VisitMut for Optimizer { noop_visit_mut_type!(); - fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) { - n.visit_mut_children_with(self); + fn visit_mut_expr(&mut self, e: &mut Expr) { + e.visit_mut_children_with(self); + + if let Expr::Member(n) = e { + if let MemberProp::Ident(prop) = &n.prop { + if let Expr::Ident(obj) = &*n.obj { + if let Some(module_specifier) = self.data.imports.get(&obj.to_id()) { + let new_id = self + .data + .replaced + .entry((module_specifier.clone(), prop.sym.clone())) + .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) + .clone(); - if let MemberProp::Ident(prop) = &n.prop { - if let Expr::Ident(obj) = &*n.obj { - if let Some(module_specifier) = self.data.imports.get(&obj.to_id()) {} + let var = VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(new_id.clone().into()), + init: Some(Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Ident::new( + "require".into(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + ) + .as_callee(), + args: vec![ + Expr::Lit(Lit::Str(module_specifier.clone().into())).as_arg() + ], + type_args: None, + }))), + definite: false, + }; + + self.data + .extra_stmts + .push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + declare: false, + decls: vec![var], + })))); + + *e = Expr::Ident(new_id.into()); + } + } } } } From 40fb7a94592c5a40806e74802d2971a7d13921b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:05:23 +0900 Subject: [PATCH 12/43] Add a test --- .../next-swc/crates/core/tests/fixture.rs | 25 ++++++++++++++++++- .../tests/fixture/cjs-optimize/1/input.js | 6 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index ac7a28d0ec162..bbb7ede6899b6 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -2,6 +2,7 @@ use std::{env::current_dir, path::PathBuf}; use next_swc::{ amp_attributes::amp_attributes, + cjs_optimizer::cjs_optimizer, next_dynamic::next_dynamic, next_ssg::next_ssg, page_config::page_config_test, @@ -14,7 +15,7 @@ use next_swc::{ use next_transform_font::{next_font_loaders, Config as FontLoaderConfig}; use turbopack_binding::swc::{ core::{ - common::{chain, comments::SingleThreadedComments, FileName, Mark}, + common::{chain, comments::SingleThreadedComments, FileName, Mark, SyntaxContext}, ecma::{ parser::{EsConfig, Syntax}, transforms::{ @@ -354,3 +355,25 @@ fn server_actions_client_fixture(input: PathBuf) { Default::default(), ); } + +#[fixture("tests/fixture/cjs-optimize/**/input.js")] +fn cjs_optimize_fixture(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); + test_fixture( + syntax(), + &|_tr| { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + let unresolved_ctxt = SyntaxContext::empty().apply_mark(unresolved_mark); + + chain!( + resolver(unresolved_mark, top_level_mark, false), + cjs_optimizer(next_swc::cjs_optimizer::Config {}, unresolved_ctxt) + ) + }, + &input, + &output, + Default::default(), + ); +} diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js new file mode 100644 index 0000000000000..3b1090559fa80 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js @@ -0,0 +1,6 @@ + + +const foo = require('next/server'); + + +console.log(foo.Response) \ No newline at end of file From 6ea0b8a8780d0d74dc8d272c43af79fc205eb4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:09:41 +0900 Subject: [PATCH 13/43] extra_stmts --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index e2ba5f508f24b..6f3b2282f13ed 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -3,8 +3,8 @@ use turbopack_binding::swc::core::{ common::{SyntaxContext, DUMMY_SP}, ecma::{ ast::{ - CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, Pat, - Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, + CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, + ModuleItem, Pat, Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, }, atoms::{Atom, JsWord}, utils::{private_ident, ExprFactory, IdentRenamer}, @@ -128,11 +128,18 @@ impl VisitMut for Optimizer { fn visit_mut_module(&mut self, n: &mut Module) { n.visit_mut_children_with(self); + + n.body + .extend(self.data.extra_stmts.drain(..).map(ModuleItem::Stmt)); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } fn visit_mut_script(&mut self, n: &mut Script) { n.visit_mut_children_with(self); + + n.body.append(&mut self.data.extra_stmts); + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } } From cd4293908b8d72e62db2408c56f73cb5f2440fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:10:45 +0900 Subject: [PATCH 14/43] prepend --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 6f3b2282f13ed..272083fa33662 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -7,7 +7,7 @@ use turbopack_binding::swc::core::{ ModuleItem, Pat, Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, }, atoms::{Atom, JsWord}, - utils::{private_ident, ExprFactory, IdentRenamer}, + utils::{prepend_stmts, private_ident, ExprFactory, IdentRenamer}, visit::{ as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith, @@ -129,8 +129,10 @@ impl VisitMut for Optimizer { fn visit_mut_module(&mut self, n: &mut Module) { n.visit_mut_children_with(self); - n.body - .extend(self.data.extra_stmts.drain(..).map(ModuleItem::Stmt)); + prepend_stmts( + &mut n.body, + self.data.extra_stmts.drain(..).map(ModuleItem::Stmt), + ); n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } @@ -138,7 +140,7 @@ impl VisitMut for Optimizer { fn visit_mut_script(&mut self, n: &mut Script) { n.visit_mut_children_with(self); - n.body.append(&mut self.data.extra_stmts); + prepend_stmts(&mut n.body, self.data.extra_stmts.drain(..)); n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } From 2cd9d8470ef64c79d43e299cf668ac922ecd6434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:11:17 +0900 Subject: [PATCH 15/43] Add a todo --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 272083fa33662..bba830fcc6ad4 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -64,6 +64,7 @@ impl VisitMut for Optimizer { .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) .clone(); + // TODO: Adjust module specifier let var = VarDeclarator { span: DUMMY_SP, name: Pat::Ident(new_id.clone().into()), From 3d1a49eb4e5b0b4db8fc5d6186bb39f075334046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:11:21 +0900 Subject: [PATCH 16/43] Update test refs --- .../crates/core/tests/fixture/cjs-optimize/1/output.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js new file mode 100644 index 0000000000000..f6fbc91adb72d --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js @@ -0,0 +1,3 @@ +const Response = require("next/server"); +const foo = require('next/server'); +console.log(Response); From 8ed27f511a1519ebb9da915a754de1435776ed18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 18 May 2023 14:11:35 +0900 Subject: [PATCH 17/43] clippy --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index bba830fcc6ad4..f8dffe997f1c1 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -3,15 +3,12 @@ use turbopack_binding::swc::core::{ common::{SyntaxContext, DUMMY_SP}, ecma::{ ast::{ - CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, - ModuleItem, Pat, Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, + CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberProp, Module, ModuleItem, Pat, + Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, }, atoms::{Atom, JsWord}, utils::{prepend_stmts, private_ident, ExprFactory, IdentRenamer}, - visit::{ - as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, - VisitWith, - }, + visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}, }, }; From 144b65ec9d553618e74214462a7adf23da49965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:34:51 +0900 Subject: [PATCH 18/43] Dep --- packages/next-swc/crates/core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index 19ece728c280f..1e743f8261b65 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -12,6 +12,7 @@ chrono = "0.4" easy-error = "1.0.0" either = "1" fxhash = "0.2.1" +handlebars = "4.2.1" hex = "0.4.3" once_cell = { workspace = true } next-transform-font = {workspace = true} From b0fe6acff7c5a29eb66b60e00fd1746179507f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:34:56 +0900 Subject: [PATCH 19/43] cargo lockfile --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 5cd98dea1df65..7130583e3898a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3328,6 +3328,7 @@ dependencies = [ "easy-error", "either", "fxhash", + "handlebars", "hex", "next-transform-font", "once_cell", From 30dbafe5d305120fe063732106901d9d6c65ec0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:41:45 +0900 Subject: [PATCH 20/43] test --- .../next-swc/crates/core/tests/fixture.rs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index bbb7ede6899b6..9a0a9eac049b3 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -13,6 +13,7 @@ use next_swc::{ shake_exports::{shake_exports, Config as ShakeExportsConfig}, }; use next_transform_font::{next_font_loaders, Config as FontLoaderConfig}; +use serde::de::DeserializeOwned; use turbopack_binding::swc::{ core::{ common::{chain, comments::SingleThreadedComments, FileName, Mark, SyntaxContext}, @@ -369,7 +370,18 @@ fn cjs_optimize_fixture(input: PathBuf) { chain!( resolver(unresolved_mark, top_level_mark, false), - cjs_optimizer(next_swc::cjs_optimizer::Config {}, unresolved_ctxt) + cjs_optimizer( + json( + r###" + { + "next/server": { + "transform": "next/server/{{ kebabCase member }}", + } + } + "### + ), + unresolved_ctxt + ) ) }, &input, @@ -377,3 +389,10 @@ fn cjs_optimize_fixture(input: PathBuf) { Default::default(), ); } + +fn json(s: &str) -> T +where + T: DeserializeOwned, +{ + serde_json::from_str(s).expect("failed to deserialize") +} From f2cdb95d82ec6f655bbdc82a0a89e3f634dfb744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:45:47 +0900 Subject: [PATCH 21/43] Dep on `convert_case` --- packages/next-swc/crates/core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index 1e743f8261b65..f7471479891f4 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -9,6 +9,7 @@ plugin = ["turbopack-binding/__swc_core_binding_napi_plugin"] [dependencies] chrono = "0.4" +convert_case = "0.5.0" easy-error = "1.0.0" either = "1" fxhash = "0.2.1" From 025727fc77aa0112e4c68ecd7957a4a86a6dab03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:45:53 +0900 Subject: [PATCH 22/43] cargo lockfile --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 7130583e3898a..a1f094de1e719 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3325,6 +3325,7 @@ name = "next-swc" version = "0.0.0" dependencies = [ "chrono", + "convert_case 0.5.0", "easy-error", "either", "fxhash", From 2e7061534cdf07cad09bcbdc6b5262ec45e46d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:49:06 +0900 Subject: [PATCH 23/43] `ImportRecord` --- .../next-swc/crates/core/src/cjs_optimizer.rs | 172 ++++++++++++++++-- 1 file changed, 153 insertions(+), 19 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index f8dffe997f1c1..4b18c8826d40c 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,5 +1,10 @@ +use convert_case::{Case, Casing}; +use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext}; use rustc_hash::FxHashMap; +use serde::Deserialize; use turbopack_binding::swc::core::{ + bundler::ModuleRecord, + cached::regex::CachedRegex, common::{SyntaxContext, DUMMY_SP}, ecma::{ ast::{ @@ -13,28 +18,73 @@ use turbopack_binding::swc::core::{ }; pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fold + VisitMut { - as_folder(Optimizer { + let mut folder = CjsOptimizer { data: Data::default(), - config, + renderer: handlebars::Handlebars::new(), + packages: vec![], unresolved_ctxt, - }) + }; + folder + .renderer + .register_helper("lowerCase", Box::new(helper_lower_case)); + folder + .renderer + .register_helper("upperCase", Box::new(helper_upper_case)); + folder + .renderer + .register_helper("camelCase", Box::new(helper_camel_case)); + folder + .renderer + .register_helper("kebabCase", Box::new(helper_kebab_case)); + for (mut k, v) in config.packages { + // XXX: Should we keep this hack? + if !k.starts_with('^') && !k.ends_with('$') { + k = format!("^{}$", k); + } + folder.packages.push(( + CachedRegex::new(&k).expect("transform-imports: invalid regex"), + v, + )); + } + + as_folder(folder) } -#[derive(Debug)] -pub struct Config {} +#[derive(Clone, Debug, Deserialize)] +pub struct Config { + pub packages: FxHashMap, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PackageConfig { + pub transform: String, + #[serde(default)] + pub prevent_full_import: bool, + #[serde(default)] + pub skip_default_conversion: bool, +} -struct Optimizer { +struct Rewriter<'a> { + renderer: &'a handlebars::Handlebars<'static>, + key: &'a str, + config: &'a PackageConfig, + group: Vec<&'a str>, +} + +struct CjsOptimizer { data: Data, - config: Config, + renderer: handlebars::Handlebars<'static>, + packages: Vec<(CachedRegex, PackageConfig)>, unresolved_ctxt: SyntaxContext, } #[derive(Debug, Default)] struct Data { - /// List of `require` calls + /// List of `require` calls **which should be replaced**. /// - /// `(identifier): (module_specifier)` - imports: FxHashMap, + /// `(identifier): (module_record)` + imports: FxHashMap, /// `(module_specifier, property): (identifier)` replaced: FxHashMap<(Atom, JsWord), Id>, @@ -44,7 +94,33 @@ struct Data { rename_map: FxHashMap, } -impl VisitMut for Optimizer { +#[derive(Debug)] +struct ImportRecord { + module_specifier: Atom, +} + +impl CjsOptimizer { + fn should_rewrite<'a>(&'a self, name: &'a str) -> Option> { + for (regex, config) in &self.packages { + let group = regex.captures(name); + if let Some(group) = group { + let group = group + .iter() + .map(|x| x.map(|x| x.as_str()).unwrap_or_default()) + .collect::>(); + return Some(Rewriter { + renderer: &self.renderer, + key: name, + config, + group, + }); + } + } + None + } +} + +impl VisitMut for CjsOptimizer { noop_visit_mut_type!(); fn visit_mut_expr(&mut self, e: &mut Expr) { @@ -53,11 +129,11 @@ impl VisitMut for Optimizer { if let Expr::Member(n) = e { if let MemberProp::Ident(prop) = &n.prop { if let Expr::Ident(obj) = &*n.obj { - if let Some(module_specifier) = self.data.imports.get(&obj.to_id()) { + if let Some(record) = self.data.imports.get(&obj.to_id()) { let new_id = self .data .replaced - .entry((module_specifier.clone(), prop.sym.clone())) + .entry((record.module_specifier.clone(), prop.sym.clone())) .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) .clone(); @@ -72,9 +148,10 @@ impl VisitMut for Optimizer { DUMMY_SP.with_ctxt(self.unresolved_ctxt), ) .as_callee(), - args: vec![ - Expr::Lit(Lit::Str(module_specifier.clone().into())).as_arg() - ], + args: vec![Expr::Lit(Lit::Str( + record.module_specifier.clone().into(), + )) + .as_arg()], type_args: None, }))), definite: false, @@ -113,9 +190,12 @@ impl VisitMut for Optimizer { // TODO: Config if let Pat::Ident(name) = &n.name { - self.data - .imports - .insert(name.to_id(), v.value.clone().into()); + self.data.imports.insert( + name.to_id(), + ImportRecord { + module_specifier: v.value.clone().into(), + }, + ); } } } @@ -143,3 +223,57 @@ impl VisitMut for Optimizer { n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); } } + +fn helper_lower_case( + h: &Helper<'_, '_>, + _: &Handlebars<'_>, + _: &Context, + _: &mut RenderContext<'_, '_>, + out: &mut dyn Output, +) -> HelperResult { + // get parameter from helper or throw an error + let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); + out.write(param.to_lowercase().as_ref())?; + Ok(()) +} + +fn helper_upper_case( + h: &Helper<'_, '_>, + _: &Handlebars<'_>, + _: &Context, + _: &mut RenderContext<'_, '_>, + out: &mut dyn Output, +) -> HelperResult { + // get parameter from helper or throw an error + let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); + out.write(param.to_uppercase().as_ref())?; + Ok(()) +} + +fn helper_camel_case( + h: &Helper<'_, '_>, + _: &Handlebars<'_>, + _: &Context, + _: &mut RenderContext<'_, '_>, + out: &mut dyn Output, +) -> HelperResult { + // get parameter from helper or throw an error + let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); + + out.write(param.to_case(Case::Camel).as_ref())?; + Ok(()) +} + +fn helper_kebab_case( + h: &Helper<'_, '_>, + _: &Handlebars<'_>, + _: &Context, + _: &mut RenderContext<'_, '_>, + out: &mut dyn Output, +) -> HelperResult { + // get parameter from helper or throw an error + let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); + + out.write(param.to_case(Case::Kebab).as_ref())?; + Ok(()) +} From 658e66f896f1c335a4e8ef6c5f1cb2b5412a0b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:51:15 +0900 Subject: [PATCH 24/43] Store only if it should be rewrite --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 4b18c8826d40c..55df2d60a7906 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -190,12 +190,14 @@ impl VisitMut for CjsOptimizer { // TODO: Config if let Pat::Ident(name) = &n.name { - self.data.imports.insert( - name.to_id(), - ImportRecord { - module_specifier: v.value.clone().into(), - }, - ); + if let Some(..) = self.should_rewrite(&v.value) { + self.data.imports.insert( + name.to_id(), + ImportRecord { + module_specifier: v.value.clone().into(), + }, + ); + } } } } From d77a74ebce4febeaec0af02687dd7ef9b5714822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:56:01 +0900 Subject: [PATCH 25/43] WIP: Rewrite --- .../next-swc/crates/core/src/cjs_optimizer.rs | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 55df2d60a7906..3956151a875b4 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -130,43 +130,44 @@ impl VisitMut for CjsOptimizer { if let MemberProp::Ident(prop) = &n.prop { if let Expr::Ident(obj) = &*n.obj { if let Some(record) = self.data.imports.get(&obj.to_id()) { - let new_id = self - .data - .replaced - .entry((record.module_specifier.clone(), prop.sym.clone())) - .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) - .clone(); - - // TODO: Adjust module specifier - let var = VarDeclarator { - span: DUMMY_SP, - name: Pat::Ident(new_id.clone().into()), - init: Some(Box::new(Expr::Call(CallExpr { + if let Some(rewriter) = self.should_rewrite(&record.module_specifier) { + let new_id = self + .data + .replaced + .entry((record.module_specifier.clone(), prop.sym.clone())) + .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) + .clone(); + + let var = VarDeclarator { span: DUMMY_SP, - callee: Ident::new( - "require".into(), - DUMMY_SP.with_ctxt(self.unresolved_ctxt), - ) - .as_callee(), - args: vec![Expr::Lit(Lit::Str( - record.module_specifier.clone().into(), - )) - .as_arg()], - type_args: None, - }))), - definite: false, - }; - - self.data - .extra_stmts - .push(Stmt::Decl(Decl::Var(Box::new(VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Const, - declare: false, - decls: vec![var], - })))); - - *e = Expr::Ident(new_id.into()); + name: Pat::Ident(new_id.clone().into()), + init: Some(Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Ident::new( + "require".into(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + ) + .as_callee(), + args: vec![Expr::Lit(Lit::Str( + rewriter.rewrite(record.module_specifier.clone().into()), + )) + .as_arg()], + type_args: None, + }))), + definite: false, + }; + + self.data + .extra_stmts + .push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + declare: false, + decls: vec![var], + })))); + + *e = Expr::Ident(new_id.into()); + } } } } From 1e2a5389d68505c22617b0e5ccadfff74be6d7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 13:56:13 +0900 Subject: [PATCH 26/43] Rename --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 3956151a875b4..4699f6b1d9949 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -19,7 +19,7 @@ use turbopack_binding::swc::core::{ pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fold + VisitMut { let mut folder = CjsOptimizer { - data: Data::default(), + data: State::default(), renderer: handlebars::Handlebars::new(), packages: vec![], unresolved_ctxt, @@ -73,14 +73,14 @@ struct Rewriter<'a> { } struct CjsOptimizer { - data: Data, + data: State, renderer: handlebars::Handlebars<'static>, packages: Vec<(CachedRegex, PackageConfig)>, unresolved_ctxt: SyntaxContext, } #[derive(Debug, Default)] -struct Data { +struct State { /// List of `require` calls **which should be replaced**. /// /// `(identifier): (module_record)` From 249bb0be3ad8a0b63fd8a89bf2cce7ea1113e7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:01:05 +0900 Subject: [PATCH 27/43] Implement --- .../next-swc/crates/core/src/cjs_optimizer.rs | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 4699f6b1d9949..c9cc930a1644f 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,9 +1,12 @@ +use std::collections::HashMap; + use convert_case::{Case, Casing}; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext}; +use once_cell::sync::Lazy; +use regex::{Captures, Regex}; use rustc_hash::FxHashMap; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use turbopack_binding::swc::core::{ - bundler::ModuleRecord, cached::regex::CachedRegex, common::{SyntaxContext, DUMMY_SP}, ecma::{ @@ -17,6 +20,8 @@ use turbopack_binding::swc::core::{ }, }; +static DUP_SLASH_REGEX: Lazy = Lazy::new(|| Regex::new(r"//").unwrap()); + pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fold + VisitMut { let mut folder = CjsOptimizer { data: State::default(), @@ -99,6 +104,29 @@ struct ImportRecord { module_specifier: Atom, } +impl<'a> Rewriter<'a> { + fn rewrite(&self, module_specifier: &str, member: &str) -> Atom { + #[derive(Serialize)] + #[serde(untagged)] + enum Data<'a> { + Plain(&'a str), + Array(&'a [&'a str]), + } + let mut ctx: FxHashMap<&str, Data> = HashMap::default(); + ctx.insert("matches", Data::Array(&self.group[..])); + ctx.insert("member", Data::Plain(member)); + let new_path = self + .renderer + .render_template(&self.config.transform, &ctx) + .unwrap_or_else(|e| { + panic!("error rendering template for '{}': {}", self.key, e); + }); + let new_path = DUP_SLASH_REGEX.replace_all(&new_path, |_: &Captures| "/"); + + new_path.into() + } +} + impl CjsOptimizer { fn should_rewrite<'a>(&'a self, name: &'a str) -> Option> { for (regex, config) in &self.packages { @@ -130,14 +158,14 @@ impl VisitMut for CjsOptimizer { if let MemberProp::Ident(prop) = &n.prop { if let Expr::Ident(obj) = &*n.obj { if let Some(record) = self.data.imports.get(&obj.to_id()) { - if let Some(rewriter) = self.should_rewrite(&record.module_specifier) { - let new_id = self - .data - .replaced - .entry((record.module_specifier.clone(), prop.sym.clone())) - .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) - .clone(); + let new_id = self + .data + .replaced + .entry((record.module_specifier.clone(), prop.sym.clone())) + .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) + .clone(); + if let Some(rewriter) = self.should_rewrite(&record.module_specifier) { let var = VarDeclarator { span: DUMMY_SP, name: Pat::Ident(new_id.clone().into()), @@ -149,7 +177,9 @@ impl VisitMut for CjsOptimizer { ) .as_callee(), args: vec![Expr::Lit(Lit::Str( - rewriter.rewrite(record.module_specifier.clone().into()), + rewriter + .rewrite(&record.module_specifier, &prop.sym) + .into(), )) .as_arg()], type_args: None, From 9b5ee8ea6b4d468fc8c62d54f2f10643113998a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:02:22 +0900 Subject: [PATCH 28/43] lint --- packages/next-swc/crates/core/src/cjs_optimizer.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index c9cc930a1644f..c820247a628e4 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -105,7 +105,7 @@ struct ImportRecord { } impl<'a> Rewriter<'a> { - fn rewrite(&self, module_specifier: &str, member: &str) -> Atom { + fn rewrite(&self, member: &str) -> Atom { #[derive(Serialize)] #[serde(untagged)] enum Data<'a> { @@ -177,9 +177,7 @@ impl VisitMut for CjsOptimizer { ) .as_callee(), args: vec![Expr::Lit(Lit::Str( - rewriter - .rewrite(&record.module_specifier, &prop.sym) - .into(), + rewriter.rewrite(&prop.sym).into(), )) .as_arg()], type_args: None, From bcaf41244cbdb0941c0f2ac4e7697d92792525ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:03:10 +0900 Subject: [PATCH 29/43] test --- packages/next-swc/crates/core/tests/fixture.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index 9a0a9eac049b3..39e400f918815 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -374,8 +374,10 @@ fn cjs_optimize_fixture(input: PathBuf) { json( r###" { - "next/server": { - "transform": "next/server/{{ kebabCase member }}", + "packages": { + "next/server": { + "transform": "next/server/{{ kebabCase member }}" + } } } "### From 9308abba47c4d713ec4dbd3f792756b26e5f3ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:04:07 +0900 Subject: [PATCH 30/43] Harden --- .../crates/core/tests/fixture/cjs-optimize/1/input.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js index 3b1090559fa80..14c75b0809a2b 100644 --- a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js @@ -1,6 +1,6 @@ +const foo = require('next/server') +const preserved = require('next/unmatched') - -const foo = require('next/server'); - - -console.log(foo.Response) \ No newline at end of file +console.log(foo.Response) +console.log(foo['Re' + 'spawn']) +console.log(preserved.Preserved) From 35ed85fc0ec6ccafc9b192217f0e97b9eb58a674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:06:01 +0900 Subject: [PATCH 31/43] Add a test --- .../crates/core/tests/fixture/cjs-optimize/1/input.js | 1 - .../core/tests/fixture/cjs-optimize/not-processed/input.js | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/input.js diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js index 14c75b0809a2b..2ae2c355a7cd1 100644 --- a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/input.js @@ -2,5 +2,4 @@ const foo = require('next/server') const preserved = require('next/unmatched') console.log(foo.Response) -console.log(foo['Re' + 'spawn']) console.log(preserved.Preserved) diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/input.js new file mode 100644 index 0000000000000..14c75b0809a2b --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/input.js @@ -0,0 +1,6 @@ +const foo = require('next/server') +const preserved = require('next/unmatched') + +console.log(foo.Response) +console.log(foo['Re' + 'spawn']) +console.log(preserved.Preserved) From 4b9a9f8995e1061a7cd417042297451733768bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:13:09 +0900 Subject: [PATCH 32/43] ignore --- .../next-swc/crates/core/src/cjs_optimizer.rs | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index c820247a628e4..d170c3b28abb9 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -4,19 +4,22 @@ use convert_case::{Case, Casing}; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext}; use once_cell::sync::Lazy; use regex::{Captures, Regex}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use turbopack_binding::swc::core::{ cached::regex::CachedRegex, common::{SyntaxContext, DUMMY_SP}, ecma::{ ast::{ - CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberProp, Module, ModuleItem, Pat, - Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, + CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, + ModuleItem, Pat, Script, Stmt, VarDecl, VarDeclKind, VarDeclarator, }, atoms::{Atom, JsWord}, utils::{prepend_stmts, private_ident, ExprFactory, IdentRenamer}, - visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}, + visit::{ + as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, + VisitWith, + }, }, }; @@ -97,6 +100,9 @@ struct State { extra_stmts: Vec, rename_map: FxHashMap, + + /// Ignored identifiers for `obj` of [MemberExpr]. + ignored: FxHashSet, } #[derive(Debug)] @@ -157,6 +163,10 @@ impl VisitMut for CjsOptimizer { if let Expr::Member(n) = e { if let MemberProp::Ident(prop) = &n.prop { if let Expr::Ident(obj) = &*n.obj { + if self.data.ignored.contains(&obj.to_id()) { + return; + } + if let Some(record) = self.data.imports.get(&obj.to_id()) { let new_id = self .data @@ -236,6 +246,10 @@ impl VisitMut for CjsOptimizer { } fn visit_mut_module(&mut self, n: &mut Module) { + n.visit_children_with(&mut Analyzer { + data: &mut self.data, + }); + n.visit_mut_children_with(self); prepend_stmts( @@ -247,6 +261,10 @@ impl VisitMut for CjsOptimizer { } fn visit_mut_script(&mut self, n: &mut Script) { + n.visit_children_with(&mut Analyzer { + data: &mut self.data, + }); + n.visit_mut_children_with(self); prepend_stmts(&mut n.body, self.data.extra_stmts.drain(..)); @@ -255,6 +273,22 @@ impl VisitMut for CjsOptimizer { } } +struct Analyzer<'a> { + data: &'a mut State, +} + +impl Visit for Analyzer<'_> { + noop_visit_type!(); + + fn visit_member_expr(&mut self, e: &MemberExpr) { + e.visit_children_with(self); + + if let (Expr::Ident(obj), MemberProp::Computed(..)) = (&*e.obj, &e.prop) { + self.data.ignored.insert(obj.to_id()); + } + } +} + fn helper_lower_case( h: &Helper<'_, '_>, _: &Handlebars<'_>, From dbe7fffc26156f65df6d8db67875b4789b3e5104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:18:03 +0900 Subject: [PATCH 33/43] Drop old requires --- .../next-swc/crates/core/src/cjs_optimizer.rs | 72 ++++++++++++------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index d170c3b28abb9..438b5f586027c 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -8,7 +8,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use turbopack_binding::swc::core::{ cached::regex::CachedRegex, - common::{SyntaxContext, DUMMY_SP}, + common::{util::take::Take, SyntaxContext, DUMMY_SP}, ecma::{ ast::{ CallExpr, Callee, Decl, Expr, Id, Ident, Lit, MemberExpr, MemberProp, Module, @@ -212,6 +212,43 @@ impl VisitMut for CjsOptimizer { } } + fn visit_mut_module(&mut self, n: &mut Module) { + n.visit_children_with(&mut Analyzer { + data: &mut self.data, + }); + + n.visit_mut_children_with(self); + + prepend_stmts( + &mut n.body, + self.data.extra_stmts.drain(..).map(ModuleItem::Stmt), + ); + + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + } + + fn visit_mut_script(&mut self, n: &mut Script) { + n.visit_children_with(&mut Analyzer { + data: &mut self.data, + }); + + n.visit_mut_children_with(self); + + prepend_stmts(&mut n.body, self.data.extra_stmts.drain(..)); + + n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + } + + fn visit_mut_stmt(&mut self, n: &mut Stmt) { + n.visit_mut_children_with(self); + + if let Stmt::Decl(Decl::Var(v)) = n { + if v.decls.is_empty() { + n.take(); + } + } + } + fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) { n.visit_mut_children_with(self); @@ -230,8 +267,13 @@ impl VisitMut for CjsOptimizer { if let Pat::Ident(name) = &n.name { if let Some(..) = self.should_rewrite(&v.value) { + let key = name.to_id(); + + // Drop variable declarator. + n.name.take(); + self.data.imports.insert( - name.to_id(), + key, ImportRecord { module_specifier: v.value.clone().into(), }, @@ -245,31 +287,11 @@ impl VisitMut for CjsOptimizer { } } - fn visit_mut_module(&mut self, n: &mut Module) { - n.visit_children_with(&mut Analyzer { - data: &mut self.data, - }); - + fn visit_mut_var_declarators(&mut self, n: &mut Vec) { n.visit_mut_children_with(self); - prepend_stmts( - &mut n.body, - self.data.extra_stmts.drain(..).map(ModuleItem::Stmt), - ); - - n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); - } - - fn visit_mut_script(&mut self, n: &mut Script) { - n.visit_children_with(&mut Analyzer { - data: &mut self.data, - }); - - n.visit_mut_children_with(self); - - prepend_stmts(&mut n.body, self.data.extra_stmts.drain(..)); - - n.visit_mut_children_with(&mut IdentRenamer::new(&self.data.rename_map)); + // We make `name` invalid if we should drop it. + n.retain(|v| !v.name.is_invalid()); } } From b5fddaec9ddd346bd1510474b696e7f6e2333d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 May 2023 14:18:16 +0900 Subject: [PATCH 34/43] Update test refs --- .../crates/core/tests/fixture/cjs-optimize/1/output.js | 6 ++++-- .../core/tests/fixture/cjs-optimize/not-processed/output.js | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js index f6fbc91adb72d..f2e09182f7265 100644 --- a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js @@ -1,3 +1,5 @@ -const Response = require("next/server"); -const foo = require('next/server'); +const Response = require("next/server/response"); +; +const preserved = require('next/unmatched'); console.log(Response); +console.log(preserved.Preserved); diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js new file mode 100644 index 0000000000000..6231a086502f8 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js @@ -0,0 +1,5 @@ +; +const preserved = require('next/unmatched'); +console.log(foo.Response); +console.log(foo['Re' + 'spawn']); +console.log(preserved.Preserved); From 333944bc882f463eb1c9d25edbdc3d749b4cedb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 22 May 2023 12:08:38 +0900 Subject: [PATCH 35/43] config --- .../next-swc/crates/core/src/cjs_optimizer.rs | 128 +++++------------- 1 file changed, 35 insertions(+), 93 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 438b5f586027c..55faf16b9f440 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -29,7 +29,7 @@ pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fol let mut folder = CjsOptimizer { data: State::default(), renderer: handlebars::Handlebars::new(), - packages: vec![], + packages: config.packages, unresolved_ctxt, }; folder @@ -44,16 +44,6 @@ pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fol folder .renderer .register_helper("kebabCase", Box::new(helper_kebab_case)); - for (mut k, v) in config.packages { - // XXX: Should we keep this hack? - if !k.starts_with('^') && !k.ends_with('$') { - k = format!("^{}$", k); - } - folder.packages.push(( - CachedRegex::new(&k).expect("transform-imports: invalid regex"), - v, - )); - } as_folder(folder) } @@ -66,24 +56,13 @@ pub struct Config { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PackageConfig { - pub transform: String, - #[serde(default)] - pub prevent_full_import: bool, - #[serde(default)] - pub skip_default_conversion: bool, -} - -struct Rewriter<'a> { - renderer: &'a handlebars::Handlebars<'static>, - key: &'a str, - config: &'a PackageConfig, - group: Vec<&'a str>, + pub transforms: FxHashMap, } struct CjsOptimizer { data: State, renderer: handlebars::Handlebars<'static>, - packages: Vec<(CachedRegex, PackageConfig)>, + packages: FxHashMap, unresolved_ctxt: SyntaxContext, } @@ -110,47 +89,9 @@ struct ImportRecord { module_specifier: Atom, } -impl<'a> Rewriter<'a> { - fn rewrite(&self, member: &str) -> Atom { - #[derive(Serialize)] - #[serde(untagged)] - enum Data<'a> { - Plain(&'a str), - Array(&'a [&'a str]), - } - let mut ctx: FxHashMap<&str, Data> = HashMap::default(); - ctx.insert("matches", Data::Array(&self.group[..])); - ctx.insert("member", Data::Plain(member)); - let new_path = self - .renderer - .render_template(&self.config.transform, &ctx) - .unwrap_or_else(|e| { - panic!("error rendering template for '{}': {}", self.key, e); - }); - let new_path = DUP_SLASH_REGEX.replace_all(&new_path, |_: &Captures| "/"); - - new_path.into() - } -} - impl CjsOptimizer { - fn should_rewrite<'a>(&'a self, name: &'a str) -> Option> { - for (regex, config) in &self.packages { - let group = regex.captures(name); - if let Some(group) = group { - let group = group - .iter() - .map(|x| x.map(|x| x.as_str()).unwrap_or_default()) - .collect::>(); - return Some(Rewriter { - renderer: &self.renderer, - key: name, - config, - group, - }); - } - } - None + fn should_rewrite(&self, module_specifier: &str) -> Option<&FxHashMap> { + self.packages.get(module_specifier).map(|v| &v.transforms) } } @@ -175,36 +116,37 @@ impl VisitMut for CjsOptimizer { .or_insert_with(|| private_ident!(prop.sym.clone()).to_id()) .clone(); - if let Some(rewriter) = self.should_rewrite(&record.module_specifier) { - let var = VarDeclarator { - span: DUMMY_SP, - name: Pat::Ident(new_id.clone().into()), - init: Some(Box::new(Expr::Call(CallExpr { + if let Some(map) = self.should_rewrite(&record.module_specifier) { + if let Some(renamed) = map.get(&prop.sym) { + let var = VarDeclarator { span: DUMMY_SP, - callee: Ident::new( - "require".into(), - DUMMY_SP.with_ctxt(self.unresolved_ctxt), - ) - .as_callee(), - args: vec![Expr::Lit(Lit::Str( - rewriter.rewrite(&prop.sym).into(), - )) - .as_arg()], - type_args: None, - }))), - definite: false, - }; - - self.data - .extra_stmts - .push(Stmt::Decl(Decl::Var(Box::new(VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Const, - declare: false, - decls: vec![var], - })))); - - *e = Expr::Ident(new_id.into()); + name: Pat::Ident(new_id.clone().into()), + init: Some(Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Ident::new( + "require".into(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + ) + .as_callee(), + args: vec![ + Expr::Lit(Lit::Str(renamed.clone().into())).as_arg() + ], + type_args: None, + }))), + definite: false, + }; + + self.data.extra_stmts.push(Stmt::Decl(Decl::Var(Box::new( + VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + declare: false, + decls: vec![var], + }, + )))); + + *e = Expr::Ident(new_id.into()); + } } } } From fb7a29b62878086c0c2cdbc8572c46e392ed9d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 22 May 2023 12:09:34 +0900 Subject: [PATCH 36/43] Drop dep --- packages/next-swc/crates/core/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index f7471479891f4..9ee942057cb29 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -13,7 +13,6 @@ convert_case = "0.5.0" easy-error = "1.0.0" either = "1" fxhash = "0.2.1" -handlebars = "4.2.1" hex = "0.4.3" once_cell = { workspace = true } next-transform-font = {workspace = true} From d657c6eb43c62ab4d2f7bec40a995d3c63726ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 22 May 2023 12:09:53 +0900 Subject: [PATCH 37/43] cargo lockfile --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a1f094de1e719..eb56459e830e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3329,7 +3329,6 @@ dependencies = [ "easy-error", "either", "fxhash", - "handlebars", "hex", "next-transform-font", "once_cell", From c9938d7356a5d88b44a9f2c58b567b753c4ab35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 22 May 2023 12:12:19 +0900 Subject: [PATCH 38/43] Clippy --- .../next-swc/crates/core/src/cjs_optimizer.rs | 85 +------------------ 1 file changed, 3 insertions(+), 82 deletions(-) diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index 55faf16b9f440..bc6cff39c87fd 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -1,13 +1,6 @@ -use std::collections::HashMap; - -use convert_case::{Case, Casing}; -use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext}; -use once_cell::sync::Lazy; -use regex::{Captures, Regex}; use rustc_hash::{FxHashMap, FxHashSet}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use turbopack_binding::swc::core::{ - cached::regex::CachedRegex, common::{util::take::Take, SyntaxContext, DUMMY_SP}, ecma::{ ast::{ @@ -23,29 +16,12 @@ use turbopack_binding::swc::core::{ }, }; -static DUP_SLASH_REGEX: Lazy = Lazy::new(|| Regex::new(r"//").unwrap()); - pub fn cjs_optimizer(config: Config, unresolved_ctxt: SyntaxContext) -> impl Fold + VisitMut { - let mut folder = CjsOptimizer { + as_folder(CjsOptimizer { data: State::default(), - renderer: handlebars::Handlebars::new(), packages: config.packages, unresolved_ctxt, - }; - folder - .renderer - .register_helper("lowerCase", Box::new(helper_lower_case)); - folder - .renderer - .register_helper("upperCase", Box::new(helper_upper_case)); - folder - .renderer - .register_helper("camelCase", Box::new(helper_camel_case)); - folder - .renderer - .register_helper("kebabCase", Box::new(helper_kebab_case)); - - as_folder(folder) + }) } #[derive(Clone, Debug, Deserialize)] @@ -61,7 +37,6 @@ pub struct PackageConfig { struct CjsOptimizer { data: State, - renderer: handlebars::Handlebars<'static>, packages: FxHashMap, unresolved_ctxt: SyntaxContext, } @@ -252,57 +227,3 @@ impl Visit for Analyzer<'_> { } } } - -fn helper_lower_case( - h: &Helper<'_, '_>, - _: &Handlebars<'_>, - _: &Context, - _: &mut RenderContext<'_, '_>, - out: &mut dyn Output, -) -> HelperResult { - // get parameter from helper or throw an error - let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); - out.write(param.to_lowercase().as_ref())?; - Ok(()) -} - -fn helper_upper_case( - h: &Helper<'_, '_>, - _: &Handlebars<'_>, - _: &Context, - _: &mut RenderContext<'_, '_>, - out: &mut dyn Output, -) -> HelperResult { - // get parameter from helper or throw an error - let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); - out.write(param.to_uppercase().as_ref())?; - Ok(()) -} - -fn helper_camel_case( - h: &Helper<'_, '_>, - _: &Handlebars<'_>, - _: &Context, - _: &mut RenderContext<'_, '_>, - out: &mut dyn Output, -) -> HelperResult { - // get parameter from helper or throw an error - let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); - - out.write(param.to_case(Case::Camel).as_ref())?; - Ok(()) -} - -fn helper_kebab_case( - h: &Helper<'_, '_>, - _: &Handlebars<'_>, - _: &Context, - _: &mut RenderContext<'_, '_>, - out: &mut dyn Output, -) -> HelperResult { - // get parameter from helper or throw an error - let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or(""); - - out.write(param.to_case(Case::Kebab).as_ref())?; - Ok(()) -} From b42840e1c3d7dce0f1a7b9272b9f4d86db31cb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 22 May 2023 12:12:22 +0900 Subject: [PATCH 39/43] json --- packages/next-swc/crates/core/tests/fixture.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index 39e400f918815..43027af140233 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -376,7 +376,9 @@ fn cjs_optimize_fixture(input: PathBuf) { { "packages": { "next/server": { - "transform": "next/server/{{ kebabCase member }}" + "transforms": { + "Response": "next/server/response" + } } } } From 5945ecc268306842c63b26b317ffb674a3d0d635 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Mon, 22 May 2023 18:40:53 +0200 Subject: [PATCH 40/43] add config andtest in next.js --- .../next-swc/crates/core/src/cjs_optimizer.rs | 25 ++++++++++++------- packages/next-swc/crates/core/src/lib.rs | 14 ++++++++++- .../tests/fixture/cjs-optimize/1/output.js | 2 +- .../tests/fixture/cjs-optimize/2/input.js | 8 ++++++ .../tests/fixture/cjs-optimize/2/output.js | 9 +++++++ packages/next-swc/crates/core/tests/full.rs | 1 + packages/next/src/build/swc/options.ts | 9 +++++++ packages/next/src/build/webpack-config.ts | 2 +- test/e2e/app-dir/app-external/middleware.js | 7 ++++++ .../node_modules_bak/cjs-lib/index.js | 9 +++++++ .../node_modules_bak/cjs-lib/package.json | 4 +++ 11 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/input.js create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/output.js create mode 100644 test/e2e/app-dir/app-external/middleware.js create mode 100644 test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js create mode 100644 test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/package.json diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index bc6cff39c87fd..f9fe7e38f9d6d 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -93,20 +93,27 @@ impl VisitMut for CjsOptimizer { if let Some(map) = self.should_rewrite(&record.module_specifier) { if let Some(renamed) = map.get(&prop.sym) { + // Transform as `require('foo').bar` let var = VarDeclarator { span: DUMMY_SP, name: Pat::Ident(new_id.clone().into()), - init: Some(Box::new(Expr::Call(CallExpr { + init: Some(Box::new(Expr::Member(MemberExpr { span: DUMMY_SP, - callee: Ident::new( - "require".into(), + obj: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Ident::new( + "require".into(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + ) + .as_callee(), + args: vec![Expr::Lit(Lit::Str(renamed.clone().into())) + .as_arg()], + type_args: None, + })), + prop: MemberProp::Ident(Ident::new( + prop.sym.clone(), DUMMY_SP.with_ctxt(self.unresolved_ctxt), - ) - .as_callee(), - args: vec![ - Expr::Lit(Lit::Str(renamed.clone().into())).as_arg() - ], - type_args: None, + )), }))), definite: false, }; diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index 19b2809e2dd03..7e12301a750d1 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -38,7 +38,9 @@ use fxhash::FxHashSet; use next_transform_font::next_font_loaders; use serde::Deserialize; use turbopack_binding::swc::core::{ - common::{chain, comments::Comments, pass::Optional, FileName, SourceFile, SourceMap}, + common::{ + chain, comments::Comments, pass::Optional, FileName, SourceFile, SourceMap, SyntaxContext, + }, ecma::{ ast::EsVersion, parser::parse_file_as_module, transforms::base::pass::noop, visit::Fold, }, @@ -126,6 +128,9 @@ pub struct TransformOptions { #[serde(default)] pub server_actions: Option, + + #[serde(default)] + pub cjs_require_optimizer: Option, } pub fn custom_before_pass<'a, C: Comments + 'a>( @@ -278,6 +283,13 @@ where )), None => Either::Right(noop()), }, + // TODO: Pass in the syntax context + // match &opts.cjs_require_optimizer { + // Some(config) => { + // Either::Left(cjs_optimizer::cjs_optimizer(config.clone(), SyntaxContext::empty())) + // }, + // None => Either::Right(noop()), + // }, ) } diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js index f2e09182f7265..b0bd4b27550fe 100644 --- a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/1/output.js @@ -1,4 +1,4 @@ -const Response = require("next/server/response"); +const Response = require("next/server/response").Response; ; const preserved = require('next/unmatched'); console.log(Response); diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/input.js new file mode 100644 index 0000000000000..9984cbbe4b93c --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/input.js @@ -0,0 +1,8 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { + value: true, +}) +const server_1 = require('next/server') +const createResponse = (...args) => { + return new server_1.Response(...args) +} diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/output.js new file mode 100644 index 0000000000000..5c75490fb5990 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/2/output.js @@ -0,0 +1,9 @@ +'use strict'; +const Response = require("next/server/response").Response; +Object.defineProperty(exports, '__esModule', { + value: true +}); +; +const createResponse = (...args)=>{ + return new Response(...args); +}; diff --git a/packages/next-swc/crates/core/tests/full.rs b/packages/next-swc/crates/core/tests/full.rs index 98920786b657f..cfb54c6de1c5e 100644 --- a/packages/next-swc/crates/core/tests/full.rs +++ b/packages/next-swc/crates/core/tests/full.rs @@ -77,6 +77,7 @@ fn test(input: &Path, minify: bool) { font_loaders: None, app_dir: None, server_actions: None, + cjs_require_optimizer: None, }; let options = options.patch(&fm); diff --git a/packages/next/src/build/swc/options.ts b/packages/next/src/build/swc/options.ts index 722692f4f8055..704e0276c1a97 100644 --- a/packages/next/src/build/swc/options.ts +++ b/packages/next/src/build/swc/options.ts @@ -330,6 +330,15 @@ export function getLoaderSWCOptions({ ], relativeFilePathFromRoot, } + baseOptions.cjsRequireOptimizer = { + packages: { + 'next/server': { + transforms: { + NextResponse: 'next/dist/server/web/spec-extension/response', + }, + }, + }, + } const isNextDist = nextDistPath.test(filename) diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index d1291eaea505a..166ec0ec8bd30 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -1965,7 +1965,7 @@ export default async function getBaseWebpackConfig( use: loaderForAPIRoutes, }, { - ...codeCondition, + test: codeCondition.test, issuerLayer: WEBPACK_LAYERS.middleware, use: defaultLoaders.babel, }, diff --git a/test/e2e/app-dir/app-external/middleware.js b/test/e2e/app-dir/app-external/middleware.js new file mode 100644 index 0000000000000..bc818e60c7318 --- /dev/null +++ b/test/e2e/app-dir/app-external/middleware.js @@ -0,0 +1,7 @@ +import { createResponse } from 'cjs-lib' + +export function middleware(request) { + if (request.nextUrl.pathname === '/test-middleware') { + return createResponse('it works') + } +} diff --git a/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js new file mode 100644 index 0000000000000..36b096bf6f7a0 --- /dev/null +++ b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js @@ -0,0 +1,9 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const server_1 = require('next/server') +const createResponse = (...args) => { + return new server_1.NextResponse(...args) +} +exports.createResponse = createResponse + +// Note: this is a CJS library that used the `NextResponse` export from `next/server`. diff --git a/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/package.json b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/package.json new file mode 100644 index 0000000000000..3b26284716ace --- /dev/null +++ b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/package.json @@ -0,0 +1,4 @@ +{ + "name": "cjs-lib", + "exports": "./index.js" +} From 9c475976454c6f7a189ff007c8b6e063091f70a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Tue, 23 May 2023 13:10:23 +0900 Subject: [PATCH 41/43] unresolved_mark --- packages/next-swc/crates/core/src/lib.rs | 17 +++++++++-------- packages/next-swc/crates/core/tests/full.rs | 7 +++++-- packages/next-swc/crates/napi/src/transform.rs | 4 +++- packages/next-swc/crates/wasm/src/lib.rs | 6 +++++- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index 7e12301a750d1..becf6f71687f5 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -39,7 +39,8 @@ use next_transform_font::next_font_loaders; use serde::Deserialize; use turbopack_binding::swc::core::{ common::{ - chain, comments::Comments, pass::Optional, FileName, SourceFile, SourceMap, SyntaxContext, + chain, comments::Comments, pass::Optional, FileName, Mark, SourceFile, SourceMap, + SyntaxContext, }, ecma::{ ast::EsVersion, parser::parse_file_as_module, transforms::base::pass::noop, visit::Fold, @@ -139,6 +140,7 @@ pub fn custom_before_pass<'a, C: Comments + 'a>( opts: &'a TransformOptions, comments: C, eliminated_packages: Rc>>, + unresolved_mark: Mark, ) -> impl Fold + 'a where C: Clone, @@ -283,13 +285,12 @@ where )), None => Either::Right(noop()), }, - // TODO: Pass in the syntax context - // match &opts.cjs_require_optimizer { - // Some(config) => { - // Either::Left(cjs_optimizer::cjs_optimizer(config.clone(), SyntaxContext::empty())) - // }, - // None => Either::Right(noop()), - // }, + match &opts.cjs_require_optimizer { + Some(config) => { + Either::Left(cjs_optimizer::cjs_optimizer(config.clone(), SyntaxContext::empty().apply_mark(unresolved_mark))) + }, + None => Either::Right(noop()), + }, ) } diff --git a/packages/next-swc/crates/core/tests/full.rs b/packages/next-swc/crates/core/tests/full.rs index cfb54c6de1c5e..0c1d5f83731fb 100644 --- a/packages/next-swc/crates/core/tests/full.rs +++ b/packages/next-swc/crates/core/tests/full.rs @@ -5,7 +5,7 @@ use serde::de::DeserializeOwned; use turbopack_binding::swc::{ core::{ base::Compiler, - common::comments::SingleThreadedComments, + common::{comments::SingleThreadedComments, Mark}, ecma::{ parser::{Syntax, TsConfig}, transforms::base::pass::noop, @@ -80,7 +80,9 @@ fn test(input: &Path, minify: bool) { cjs_require_optimizer: None, }; - let options = options.patch(&fm); + let unresolved_mark = Mark::new(); + let mut options = options.patch(&fm); + options.swc.unresolved_mark = Some(unresolved_mark); let comments = SingleThreadedComments::default(); match c.process_js_with_custom_pass( @@ -96,6 +98,7 @@ fn test(input: &Path, minify: bool) { &options, comments.clone(), Default::default(), + unresolved_mark, ) }, |_| noop(), diff --git a/packages/next-swc/crates/napi/src/transform.rs b/packages/next-swc/crates/napi/src/transform.rs index 1c52fbd61031d..b84f786470619 100644 --- a/packages/next-swc/crates/napi/src/transform.rs +++ b/packages/next-swc/crates/napi/src/transform.rs @@ -107,7 +107,9 @@ impl Task for TransformTask { ) } }; - let options = options.patch(&fm); + let unresolved_mark = Mark::new(); + let mut options = options.patch(&m); + options.swc.unresolved_mark = Some(unresolved_mark); let cm = self.c.cm.clone(); let file = fm.clone(); diff --git a/packages/next-swc/crates/wasm/src/lib.rs b/packages/next-swc/crates/wasm/src/lib.rs index 32b9d35118f2e..bfd8d42f8694c 100644 --- a/packages/next-swc/crates/wasm/src/lib.rs +++ b/packages/next-swc/crates/wasm/src/lib.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use anyhow::{Context, Error}; use js_sys::JsString; use next_swc::{custom_before_pass, TransformOptions}; +use swc_core::common::Mark; use turbopack_binding::swc::core::{ base::{ config::{JsMinifyOptions, ParseOptions}, @@ -66,7 +67,9 @@ pub fn transform_sync(s: JsValue, opts: JsValue) -> Result { console_error_panic_hook::set_once(); let c = compiler(); - let opts: TransformOptions = serde_wasm_bindgen::from_value(opts)?; + let unresolved_mark = Mark::new(); + let mut opts: TransformOptions = serde_wasm_bindgen::from_value(opts)?; + opts.swc.unresolved_mark = Some(unresolved_mark); let s = s.dyn_into::(); let out = try_with_handler( @@ -103,6 +106,7 @@ pub fn transform_sync(s: JsValue, opts: JsValue) -> Result { &opts, comments.clone(), Default::default(), + unresolved_mark, ) }, |_| noop(), From 7f900fda49f195e82b5cc36e4b3d8858f59c16c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Tue, 23 May 2023 13:11:17 +0900 Subject: [PATCH 42/43] fixup --- packages/next-swc/crates/napi/src/transform.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/next-swc/crates/napi/src/transform.rs b/packages/next-swc/crates/napi/src/transform.rs index b84f786470619..a3f1bdff562ed 100644 --- a/packages/next-swc/crates/napi/src/transform.rs +++ b/packages/next-swc/crates/napi/src/transform.rs @@ -40,7 +40,7 @@ use napi::bindgen_prelude::*; use next_swc::{custom_before_pass, TransformOptions}; use turbopack_binding::swc::core::{ base::{try_with_handler, Compiler, TransformOutput}, - common::{comments::SingleThreadedComments, errors::ColorConfig, FileName, GLOBALS}, + common::{comments::SingleThreadedComments, errors::ColorConfig, FileName, Mark, GLOBALS}, ecma::transforms::base::pass::noop, }; @@ -108,7 +108,7 @@ impl Task for TransformTask { } }; let unresolved_mark = Mark::new(); - let mut options = options.patch(&m); + let mut options = options.patch(&fm); options.swc.unresolved_mark = Some(unresolved_mark); let cm = self.c.cm.clone(); @@ -128,6 +128,7 @@ impl Task for TransformTask { &options, comments.clone(), eliminated_packages.clone(), + unresolved_mark, ) }, |_| noop(), From 5dbc1053fa95df9d9f9dc26a5b4f096263e43cea Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Tue, 23 May 2023 18:40:31 +0200 Subject: [PATCH 43/43] fix cases and add test --- .../next-swc/crates/core/src/cjs_optimizer.rs | 127 ++++++++++++------ .../cjs-optimize/not-processed-2/input.js | 3 + .../cjs-optimize/not-processed-2/output.js | 3 + .../cjs-optimize/not-processed-3/input.js | 3 + .../cjs-optimize/not-processed-3/output.js | 3 + .../cjs-optimize/not-processed/output.js | 2 +- packages/next/src/build/swc/options.ts | 4 + .../app-dir/app-external/app-external.test.ts | 8 ++ .../node_modules_bak/cjs-lib/index.js | 1 - 9 files changed, 110 insertions(+), 44 deletions(-) create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/input.js create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/output.js create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/input.js create mode 100644 packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/output.js diff --git a/packages/next-swc/crates/core/src/cjs_optimizer.rs b/packages/next-swc/crates/core/src/cjs_optimizer.rs index f9fe7e38f9d6d..62d06a7df552c 100644 --- a/packages/next-swc/crates/core/src/cjs_optimizer.rs +++ b/packages/next-swc/crates/core/src/cjs_optimizer.rs @@ -57,6 +57,8 @@ struct State { /// Ignored identifiers for `obj` of [MemberExpr]. ignored: FxHashSet, + + is_prepass: bool, } #[derive(Debug)] @@ -73,17 +75,27 @@ impl CjsOptimizer { impl VisitMut for CjsOptimizer { noop_visit_mut_type!(); + fn visit_mut_module_items(&mut self, stmts: &mut Vec) { + self.data.is_prepass = true; + stmts.visit_mut_children_with(self); + self.data.is_prepass = false; + stmts.visit_mut_children_with(self); + } + fn visit_mut_expr(&mut self, e: &mut Expr) { e.visit_mut_children_with(self); if let Expr::Member(n) = e { if let MemberProp::Ident(prop) = &n.prop { if let Expr::Ident(obj) = &*n.obj { - if self.data.ignored.contains(&obj.to_id()) { + let key = obj.to_id(); + if self.data.ignored.contains(&key) { return; } - if let Some(record) = self.data.imports.get(&obj.to_id()) { + if let Some(record) = self.data.imports.get(&key) { + let mut replaced = false; + let new_id = self .data .replaced @@ -93,43 +105,52 @@ impl VisitMut for CjsOptimizer { if let Some(map) = self.should_rewrite(&record.module_specifier) { if let Some(renamed) = map.get(&prop.sym) { - // Transform as `require('foo').bar` - let var = VarDeclarator { - span: DUMMY_SP, - name: Pat::Ident(new_id.clone().into()), - init: Some(Box::new(Expr::Member(MemberExpr { + replaced = true; + if !self.data.is_prepass { + // Transform as `require('foo').bar` + let var = VarDeclarator { span: DUMMY_SP, - obj: Box::new(Expr::Call(CallExpr { + name: Pat::Ident(new_id.clone().into()), + init: Some(Box::new(Expr::Member(MemberExpr { span: DUMMY_SP, - callee: Ident::new( - "require".into(), - DUMMY_SP.with_ctxt(self.unresolved_ctxt), - ) - .as_callee(), - args: vec![Expr::Lit(Lit::Str(renamed.clone().into())) + obj: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Ident::new( + "require".into(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + ) + .as_callee(), + args: vec![Expr::Lit(Lit::Str( + renamed.clone().into(), + )) .as_arg()], - type_args: None, - })), - prop: MemberProp::Ident(Ident::new( - prop.sym.clone(), - DUMMY_SP.with_ctxt(self.unresolved_ctxt), - )), - }))), - definite: false, - }; - - self.data.extra_stmts.push(Stmt::Decl(Decl::Var(Box::new( - VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Const, - declare: false, - decls: vec![var], - }, - )))); + type_args: None, + })), + prop: MemberProp::Ident(Ident::new( + prop.sym.clone(), + DUMMY_SP.with_ctxt(self.unresolved_ctxt), + )), + }))), + definite: false, + }; + + self.data.extra_stmts.push(Stmt::Decl(Decl::Var(Box::new( + VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + declare: false, + decls: vec![var], + }, + )))); - *e = Expr::Ident(new_id.into()); + *e = Expr::Ident(new_id.into()); + } } } + + if !replaced { + self.data.ignored.insert(key); + } } } } @@ -139,6 +160,7 @@ impl VisitMut for CjsOptimizer { fn visit_mut_module(&mut self, n: &mut Module) { n.visit_children_with(&mut Analyzer { data: &mut self.data, + in_member_or_var: false, }); n.visit_mut_children_with(self); @@ -154,6 +176,7 @@ impl VisitMut for CjsOptimizer { fn visit_mut_script(&mut self, n: &mut Script) { n.visit_children_with(&mut Analyzer { data: &mut self.data, + in_member_or_var: false, }); n.visit_mut_children_with(self); @@ -193,15 +216,19 @@ impl VisitMut for CjsOptimizer { if let Some(..) = self.should_rewrite(&v.value) { let key = name.to_id(); - // Drop variable declarator. - n.name.take(); - - self.data.imports.insert( - key, - ImportRecord { - module_specifier: v.value.clone().into(), - }, - ); + if !self.data.is_prepass { + if !self.data.ignored.contains(&key) { + // Drop variable declarator. + n.name.take(); + } + } else { + self.data.imports.insert( + key, + ImportRecord { + module_specifier: v.value.clone().into(), + }, + ); + } } } } @@ -220,17 +247,33 @@ impl VisitMut for CjsOptimizer { } struct Analyzer<'a> { + in_member_or_var: bool, data: &'a mut State, } impl Visit for Analyzer<'_> { noop_visit_type!(); + fn visit_var_declarator(&mut self, n: &VarDeclarator) { + self.in_member_or_var = true; + n.visit_children_with(self); + self.in_member_or_var = false; + } + fn visit_member_expr(&mut self, e: &MemberExpr) { + self.in_member_or_var = true; e.visit_children_with(self); + self.in_member_or_var = false; if let (Expr::Ident(obj), MemberProp::Computed(..)) = (&*e.obj, &e.prop) { self.data.ignored.insert(obj.to_id()); } } + + fn visit_ident(&mut self, i: &Ident) { + i.visit_children_with(self); + if !self.in_member_or_var { + self.data.ignored.insert(i.to_id()); + } + } } diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/input.js new file mode 100644 index 0000000000000..c2f964a50dd9c --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/input.js @@ -0,0 +1,3 @@ +const foo = require('next/server') + +console.log(foo.bar) diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/output.js new file mode 100644 index 0000000000000..1196b63767b97 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-2/output.js @@ -0,0 +1,3 @@ +const foo = require('next/server'); + +console.log(foo.bar); diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/input.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/input.js new file mode 100644 index 0000000000000..034aa33d132ea --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/input.js @@ -0,0 +1,3 @@ +const foo = require('next/server') + +console.log(foo) diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/output.js new file mode 100644 index 0000000000000..7699c791c3f16 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed-3/output.js @@ -0,0 +1,3 @@ +const foo = require('next/server'); + +console.log(foo); diff --git a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js index 6231a086502f8..590e9ef5a14d1 100644 --- a/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js +++ b/packages/next-swc/crates/core/tests/fixture/cjs-optimize/not-processed/output.js @@ -1,4 +1,4 @@ -; +const foo = require('next/server'); const preserved = require('next/unmatched'); console.log(foo.Response); console.log(foo['Re' + 'spawn']); diff --git a/packages/next/src/build/swc/options.ts b/packages/next/src/build/swc/options.ts index 704e0276c1a97..0d8ecf6bf773c 100644 --- a/packages/next/src/build/swc/options.ts +++ b/packages/next/src/build/swc/options.ts @@ -334,7 +334,11 @@ export function getLoaderSWCOptions({ packages: { 'next/server': { transforms: { + NextRequest: 'next/dist/server/web/spec-extension/request', NextResponse: 'next/dist/server/web/spec-extension/response', + ImageResponse: 'next/dist/server/web/spec-extension/image-response', + userAgentFromString: 'next/dist/server/web/spec-extension/user-agent', + userAgent: 'next/dist/server/web/spec-extension/user-agent', }, }, }, diff --git a/test/e2e/app-dir/app-external/app-external.test.ts b/test/e2e/app-dir/app-external/app-external.test.ts index 5f791b1897a2f..e69b5b80b50ad 100644 --- a/test/e2e/app-dir/app-external/app-external.test.ts +++ b/test/e2e/app-dir/app-external/app-external.test.ts @@ -211,5 +211,13 @@ createNextDescribe( ) }) } + + it('should have proper tree-shaking for known modules in CJS', async () => { + const html = await next.render('/test-middleware') + expect(html).toContain('it works') + + const middlewareBundle = await next.readFile('.next/server/middleware.js') + expect(middlewareBundle).not.toContain('image-response') + }) } ) diff --git a/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js index 36b096bf6f7a0..a6865d191cd98 100644 --- a/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js +++ b/test/e2e/app-dir/app-external/node_modules_bak/cjs-lib/index.js @@ -1,4 +1,3 @@ -'use strict' Object.defineProperty(exports, '__esModule', { value: true }) const server_1 = require('next/server') const createResponse = (...args) => {