Update wasm and next-custom-transforms crates to Rust edition 2024 (#90350)

The `wasm` and `next-custom-transforms` crates were still on Rust edition 2018 while the rest of the crates in this repo use edition 2024. This caused a pre-commit hook conflict:

- `cargo fmt` passes the crate's own edition (2018) to `rustfmt`, which uses **case-insensitive** import sorting
- The lint-staged pre-commit hook runs `rustfmt --edition 2024 --`, which uses **ASCII order** import sorting (uppercase before lowercase)

These two sort orders disagree on items like `{custom_before_pass, TransformOptions}` vs `{TransformOptions, custom_before_pass}`, causing an infinite flip-flop: every commit would reorder imports, and the next `cargo fmt` would reorder them back.

## Changes

- Bump `crates/wasm` and `crates/next-custom-transforms` from edition 2018 to 2024
- Add `+ use<C>` precise capturing to `server_actions()` return type to satisfy edition 2024's stricter `impl Trait` lifetime capture rules
- Add `style_edition = "2024"` to `.rustfmt.toml` as a safety net so `cargo fmt` uses 2024-style formatting regardless of individual crate editions
- Reformat all affected files with `cargo fmt`
- Apply new clippy rules
This commit is contained in:
Luke Sandberg
2026-02-23 01:48:12 -08:00
committed by GitHub
parent 757ab17e54
commit f93a867d77
35 changed files with 968 additions and 1027 deletions
+1
View File
@@ -1,4 +1,5 @@
edition = "2024"
style_edition = "2024"
max_width = 100
comment_width = 100
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
edition = "2018"
edition = "2024"
name = "next-custom-transforms"
version = "0.0.0"
publish = false
@@ -8,12 +8,12 @@ use serde::Deserialize;
use swc_core::{
atoms::Atom,
common::{
FileName, Mark, SourceFile, SourceMap, SyntaxContext,
comments::{Comments, NoopComments},
pass::Optional,
FileName, Mark, SourceFile, SourceMap, SyntaxContext,
},
ecma::{
ast::{fn_pass, noop_pass, EsVersion, Pass},
ast::{EsVersion, Pass, fn_pass, noop_pass},
parser::parse_file_as_module,
visit::visit_mut_pass,
},
@@ -23,7 +23,7 @@ use crate::{
linter::linter,
transforms::{
cjs_finder::contains_cjs,
dynamic::{next_dynamic, NextDynamicMode},
dynamic::{NextDynamicMode, next_dynamic},
fonts::next_font_loaders,
lint_codemod_comments::lint_codemod_comments,
react_server_components,
+1 -1
View File
@@ -1,6 +1,6 @@
use swc_core::ecma::{
ast::Pass,
visit::{visit_pass, Visit},
visit::{Visit, visit_pass},
};
pub fn linter<V>(visitor: V) -> impl Pass
@@ -23,15 +23,13 @@ struct Finder {
impl Visit for Finder {
fn visit_callee(&mut self, node: &Callee) {
if self.is_interested {
if let Callee::Expr(e) = node {
if let Expr::Ident(c) = &**e {
if c.sym.starts_with("use") {
self.found = true;
return;
}
}
}
if self.is_interested
&& let Callee::Expr(e) = node
&& let Expr::Ident(c) = &**e
&& c.sym.starts_with("use")
{
self.found = true;
return;
}
node.visit_children_with(self);
@@ -130,7 +128,7 @@ impl Visit for Finder {
mod tests {
use swc_core::{
common::FileName,
ecma::parser::{parse_file_as_program, EsSyntax},
ecma::parser::{EsSyntax, parse_file_as_program},
};
use testing::run_test2;
@@ -56,33 +56,22 @@ impl Visit for CjsFinder {
// Note that `Object.defineProperty(module.exports, ...)` will be handled by
// `visit_member_expr`.
fn visit_call_expr(&mut self, e: &CallExpr) {
if !self.ignore_exports {
if let Callee::Expr(expr) = &e.callee {
if let Expr::Member(member_expr) = &**expr {
if let (Expr::Ident(obj), MemberProp::Ident(prop)) =
(&*member_expr.obj, &member_expr.prop)
{
if &*obj.sym == "Object" && &*prop.sym == "defineProperty" {
if let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first() {
if let Expr::Ident(arg0) = &**expr0 {
if &*arg0.sym == "exports" {
if let Some(ExprOrSpread { expr: expr1, .. }) =
e.args.get(1)
{
if let Expr::Lit(Lit::Str(arg1)) = &**expr1 {
if &*arg1.value == "__esModule" {
self.found = true;
return;
}
}
}
}
}
}
}
}
}
}
if !self.ignore_exports
&& let Callee::Expr(expr) = &e.callee
&& let Expr::Member(member_expr) = &**expr
&& let (Expr::Ident(obj), MemberProp::Ident(prop)) =
(&*member_expr.obj, &member_expr.prop)
&& &*obj.sym == "Object"
&& &*prop.sym == "defineProperty"
&& let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first()
&& let Expr::Ident(arg0) = &**expr0
&& &*arg0.sym == "exports"
&& let Some(ExprOrSpread { expr: expr1, .. }) = e.args.get(1)
&& let Expr::Lit(Lit::Str(arg1)) = &**expr1
&& &*arg1.value == "__esModule"
{
self.found = true;
return;
}
e.callee.visit_with(self);
@@ -113,15 +102,15 @@ impl Visit for CjsFinder {
}
fn visit_member_expr(&mut self, e: &MemberExpr) {
if let Expr::Ident(obj) = &*e.obj {
if let MemberProp::Ident(prop) = &e.prop {
// Detect `module.exports` and `exports.__esModule`
if (!self.ignore_module && &*obj.sym == "module" && &*prop.sym == "exports")
|| (!self.ignore_exports && &*obj.sym == "exports")
{
self.found = true;
return;
}
if let Expr::Ident(obj) = &*e.obj
&& let MemberProp::Ident(prop) = &e.prop
{
// Detect `module.exports` and `exports.__esModule`
if (!self.ignore_module && &*obj.sym == "module" && &*prop.sym == "exports")
|| (!self.ignore_exports && &*obj.sym == "exports")
{
self.found = true;
return;
}
}
@@ -1,16 +1,16 @@
use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
use swc_core::{
atoms::{atom, Wtf8Atom},
common::{util::take::Take, SyntaxContext, DUMMY_SP},
atoms::{Wtf8Atom, atom},
common::{DUMMY_SP, SyntaxContext, util::take::Take},
ecma::{
ast::{
CallExpr, Callee, Decl, Expr, Id, Ident, IdentName, Lit, MemberExpr, MemberProp,
Module, ModuleItem, Pat, Script, Stmt, VarDecl, VarDeclKind, VarDeclarator,
},
atoms::Atom,
utils::{prepend_stmts, private_ident, ExprFactory, IdentRenamer},
visit::{noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith},
utils::{ExprFactory, IdentRenamer, prepend_stmts, private_ident},
visit::{Visit, VisitMut, VisitMutWith, VisitWith, noop_visit_mut_type, noop_visit_type},
},
};
@@ -83,83 +83,78 @@ impl VisitMut for CjsOptimizer {
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 {
let key = obj.to_id();
if self.data.ignored.contains(&key) {
return;
}
if let Expr::Member(n) = e
&& let MemberProp::Ident(prop) = &n.prop
&& let Expr::Ident(obj) = &*n.obj
{
let key = obj.to_id();
if self.data.ignored.contains(&key) {
return;
}
if let Some(record) = self.data.imports.get(&key) {
let mut replaced = false;
if let Some(record) = self.data.imports.get(&key) {
let mut replaced = false;
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(map) = self.should_rewrite(&record.module_specifier) {
if let Some(renamed) = map.get(&prop.sym) {
replaced = true;
if !self.data.is_prepass {
// 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 {
span: DUMMY_SP,
obj: Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: Ident::new(
atom!("require"),
DUMMY_SP,
self.unresolved_ctxt,
)
.as_callee(),
args: vec![Expr::Lit(Lit::Str(
renamed.clone().into(),
))
.as_arg()],
..Default::default()
})),
prop: MemberProp::Ident(IdentName::new(
prop.sym.clone(),
DUMMY_SP,
)),
}))),
definite: false,
};
if let Some(map) = self.should_rewrite(&record.module_specifier)
&& let Some(renamed) = map.get(&prop.sym)
{
replaced = true;
if !self.data.is_prepass {
// 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 {
span: DUMMY_SP,
obj: Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: Ident::new(
atom!("require"),
DUMMY_SP,
self.unresolved_ctxt,
)
.as_callee(),
args: vec![
Expr::Lit(Lit::Str(renamed.clone().into())).as_arg(),
],
..Default::default()
})),
prop: MemberProp::Ident(IdentName::new(prop.sym.clone(), DUMMY_SP)),
}))),
definite: false,
};
if !self.data.extra_stmts.iter().any(|s| {
if let Stmt::Decl(Decl::Var(v)) = &s {
v.decls.iter().any(|d| d.name == var.name)
} else {
false
}
}) {
self.data.extra_stmts.push(Stmt::Decl(Decl::Var(
Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Const,
decls: vec![var],
..Default::default()
}),
)));
}
*e = Expr::Ident(new_id.into());
}
if !self.data.extra_stmts.iter().any(|s| {
if let Stmt::Decl(Decl::Var(v)) = &s {
v.decls.iter().any(|d| d.name == var.name)
} else {
false
}
}) {
self.data
.extra_stmts
.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Const,
decls: vec![var],
..Default::default()
}))));
}
if !replaced {
self.data.ignored.insert(key);
}
*e = Expr::Ident(new_id.into());
}
}
if !replaced {
self.data.ignored.insert(key);
}
}
}
}
@@ -196,10 +191,10 @@ impl VisitMut for CjsOptimizer {
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();
}
if let Stmt::Decl(Decl::Var(v)) = n
&& v.decls.is_empty()
{
n.take();
}
}
@@ -212,34 +207,31 @@ impl VisitMut for CjsOptimizer {
args,
..
})) = n.init.as_deref()
&& let Expr::Ident(ident) = &**callee
&& ident.ctxt == self.unresolved_ctxt
&& ident.sym == *"require"
&& let Some(arg) = args.first()
&& let Expr::Lit(Lit::Str(v)) = &*arg.expr
{
if let Expr::Ident(ident) = &**callee {
if ident.ctxt == self.unresolved_ctxt && ident.sym == *"require" {
if let Some(arg) = args.first() {
if let Expr::Lit(Lit::Str(v)) = &*arg.expr {
// TODO: Config
// TODO: Config
if let Pat::Ident(name) = &n.name {
if self.should_rewrite(&v.value).is_some() {
let key = name.to_id();
if let Pat::Ident(name) = &n.name
&& self.should_rewrite(&v.value).is_some()
{
let key = name.to_id();
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(),
},
);
}
}
}
}
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(),
},
);
}
}
}
@@ -269,12 +261,10 @@ impl Visit for Analyzer<'_> {
callee: Callee::Expr(callee),
..
})) = n.init.as_deref()
&& let Expr::Ident(ident) = &**callee
&& ident.sym == *"require"
{
if let Expr::Ident(ident) = &**callee {
if ident.sym == *"require" {
safe_to_ignore = true;
}
}
safe_to_ignore = true;
}
if safe_to_ignore {
@@ -2,14 +2,14 @@ use std::fmt::Write;
use swc_core::{
atoms::Atom,
common::{util::take::Take, DUMMY_SP},
common::{DUMMY_SP, util::take::Take},
ecma::{
ast::{
CallExpr, Callee, ExportDefaultDecl, ExportDefaultExpr, Expr, FnDecl, FnExpr,
KeyValueProp, MemberProp, ObjectLit, Pass, PropOrSpread, VarDeclarator,
},
utils::ExprFactory,
visit::{visit_mut_pass, VisitMut, VisitMutWith},
visit::{VisitMut, VisitMutWith, visit_mut_pass},
},
};
@@ -2,7 +2,7 @@ use swc_core::{
common::{Span, Spanned},
ecma::{
ast::*,
visit::{fold_pass, Fold},
visit::{Fold, fold_pass},
},
quote,
};
@@ -52,12 +52,11 @@ fn find_var_init_span(items: &[ModuleItem], local_name: &str) -> Option<Span> {
_ => continue,
};
for d in &decl.decls {
if let Pat::Ident(ident) = &d.name {
if ident.id.sym == local_name {
if let Some(init) = &d.init {
return Some(init.span());
}
}
if let Pat::Ident(ident) = &d.name
&& ident.id.sym == local_name
&& let Some(init) = &d.init
{
return Some(init.span());
}
}
}
@@ -72,12 +71,11 @@ impl Fold for DebugInstantStack {
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => {
if let Decl::Var(var_decl) = &export_decl.decl {
for decl in &var_decl.decls {
if let Pat::Ident(ident) = &decl.name {
if ident.id.sym == "unstable_instant" {
if let Some(init) = &decl.init {
self.instant_export_span = Some(init.span());
}
}
if let Pat::Ident(ident) = &decl.name
&& ident.id.sym == "unstable_instant"
&& let Some(init) = &decl.init
{
self.instant_export_span = Some(init.span());
}
}
}
@@ -5,17 +5,17 @@ use std::{
use pathdiff::diff_paths;
use swc_core::{
atoms::{atom, Atom, Wtf8Atom},
common::{errors::HANDLER, FileName, Span, DUMMY_SP},
atoms::{Atom, Wtf8Atom, atom},
common::{DUMMY_SP, FileName, Span, errors::HANDLER},
ecma::{
ast::{
op, ArrayLit, ArrowExpr, BinExpr, BlockStmt, BlockStmtOrExpr, Bool, CallExpr, Callee,
Expr, ExprOrSpread, ExprStmt, Id, Ident, IdentName, ImportDecl, ImportNamedSpecifier,
ArrayLit, ArrowExpr, BinExpr, BlockStmt, BlockStmtOrExpr, Bool, CallExpr, Callee, Expr,
ExprOrSpread, ExprStmt, Id, Ident, IdentName, ImportDecl, ImportNamedSpecifier,
ImportSpecifier, KeyValueProp, Lit, ModuleDecl, ModuleItem, ObjectLit, Pass, Prop,
PropName, PropOrSpread, Stmt, Str, Tpl, UnaryExpr, UnaryOp,
PropName, PropOrSpread, Stmt, Str, Tpl, UnaryExpr, UnaryOp, op,
},
utils::{private_ident, quote_ident, ExprFactory},
visit::{visit_mut_pass, VisitMut, VisitMutWith},
utils::{ExprFactory, private_ident, quote_ident},
visit::{VisitMut, VisitMutWith, visit_mut_pass},
},
quote,
};
@@ -154,35 +154,33 @@ impl VisitMut for NextDynamicPatcher {
expr.visit_mut_children_with(self);
if let Callee::Expr(i) = &expr.callee {
if let Expr::Ident(identifier) = &**i {
if self.dynamic_bindings.contains(&identifier.to_id()) {
if expr.args.is_empty() {
if let Callee::Expr(i) = &expr.callee
&& let Expr::Ident(identifier) = &**i
&& self.dynamic_bindings.contains(&identifier.to_id())
{
if expr.args.is_empty() {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
"next/dynamic requires at least one argument",
)
.emit()
});
return;
} else if expr.args.len() > 2 {
HANDLER.with(|handler| {
handler
.struct_span_err(identifier.span, "next/dynamic only accepts 2 arguments")
.emit()
});
return;
}
if expr.args.len() == 2 {
match &*expr.args[1].expr {
Expr::Object(_) => {}
_ => {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
"next/dynamic requires at least one argument",
)
.emit()
});
return;
} else if expr.args.len() > 2 {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
"next/dynamic only accepts 2 arguments",
)
.emit()
});
return;
}
if expr.args.len() == 2 {
match &*expr.args[1].expr {
Expr::Object(_) => {}
_ => {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
@@ -190,228 +188,215 @@ impl VisitMut for NextDynamicPatcher {
)
.emit();
});
return;
}
}
}
self.is_next_dynamic_first_arg = true;
expr.args[0].expr.visit_mut_with(self);
self.is_next_dynamic_first_arg = false;
let Some((dynamically_imported_specifier, dynamically_imported_specifier_span)) =
self.dynamically_imported_specifier.take()
else {
return;
};
let project_dir = match self.pages_or_app_dir.as_deref() {
Some(pages_or_app) => pages_or_app.parent(),
_ => None,
};
let generated = Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: match &mut self.state {
NextDynamicPatcherState::Webpack => {
// dev client or server:
// loadableGenerated: {
// modules:
// ["/project/src/file-being-transformed.js -> " +
// '../components/hello'] }
//
// prod client
// loadableGenerated: {
// webpack: () => [require.resolveWeak('../components/hello')],
if self.is_development || self.is_server_compiler {
module_id_options(quote!(
"$left + $right" as Expr,
left: Expr = format!(
"{} -> ",
rel_filename(project_dir, &self.filename)
)
.into(),
right: Expr = dynamically_imported_specifier.clone().into(),
))
} else {
webpack_options(quote!(
"require.resolveWeak($id)" as Expr,
id: Expr = dynamically_imported_specifier.clone().into()
))
}
}
NextDynamicPatcherState::Turbopack { imports, .. } => {
// loadableGenerated: { modules: [
// ".../client.js [app-client] (ecmascript, next/dynamic entry)"
// ]}
let id_ident =
private_ident!(dynamically_imported_specifier_span, "id");
imports.push(TurbopackImport::Import {
id_ident: id_ident.clone(),
specifier: dynamically_imported_specifier.clone(),
});
module_id_options(Expr::Ident(id_ident))
}
},
}));
let mut props =
vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(IdentName::new(
atom!("loadableGenerated"),
DUMMY_SP,
)),
value: generated,
})))];
let mut has_ssr_false = false;
if expr.args.len() == 2 {
if let Expr::Object(ObjectLit {
props: options_props,
..
}) = &*expr.args[1].expr
{
for prop in options_props.iter() {
if let Some(KeyValueProp { key, value }) = match prop {
PropOrSpread::Prop(prop) => match &**prop {
Prop::KeyValue(key_value_prop) => Some(key_value_prop),
_ => None,
},
_ => None,
} {
if let Some(IdentName { sym, span: _ }) = match key {
PropName::Ident(ident) => Some(ident),
_ => None,
} {
if sym == "ssr" {
if let Some(Lit::Bool(Bool {
value: false,
span: _,
})) = value.as_lit()
{
has_ssr_false = true
}
}
}
}
}
props.extend(options_props.iter().cloned());
}
}
let should_skip_ssr_compile = has_ssr_false
&& self.is_server_compiler
&& !self.is_react_server_layer
&& self.prefer_esm;
match &self.state {
NextDynamicPatcherState::Webpack => {
// Only use `require.resolveWebpack` to decouple modules for webpack,
// turbopack doesn't need this
// When it's not preferring to picking up ESM (in the pages router), we
// don't need to do it as it doesn't need to enter the non-ssr module.
//
// Also transforming it to `require.resolveWeak` doesn't work with ESM
// imports ( i.e. require.resolveWeak(esm asset)).
if should_skip_ssr_compile {
// if it's server components SSR layer
// Transform 1st argument `expr.args[0]` aka the module loader from:
// dynamic(() => import('./client-mod'), { ssr: false }))`
// into:
// dynamic(async () => {
// require.resolveWeak('./client-mod')
// }, { ssr: false }))`
let require_resolve_weak_expr = Expr::Call(CallExpr {
span: DUMMY_SP,
callee: quote_ident!("require.resolveWeak").as_callee(),
args: vec![ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: dynamically_imported_specifier.clone(),
raw: None,
}))),
}],
..Default::default()
});
let side_effect_free_loader_arg = Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Box::new(exec_expr_when_resolve_weak_available(
&require_resolve_weak_expr,
)),
})],
..Default::default()
})),
is_async: true,
is_generator: false,
..Default::default()
});
expr.args[0] = side_effect_free_loader_arg.as_arg();
}
}
NextDynamicPatcherState::Turbopack {
dynamic_transition_name,
..
} => {
// When `ssr: false`
// if it's server components SSR layer
// Transform 1st argument `expr.args[0]` aka the module loader from:
// dynamic(() => import('./client-mod'), { ssr: false }))`
// into:
// dynamic(async () => {}, { ssr: false }))`
if should_skip_ssr_compile {
let side_effect_free_loader_arg = Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts: vec![],
..Default::default()
})),
is_async: true,
is_generator: false,
..Default::default()
});
expr.args[0] = side_effect_free_loader_arg.as_arg();
} else {
// Add `{with:{turbopack-transition: ...}}` to the dynamic import
let mut visitor = DynamicImportTransitionAdder {
transition_name: dynamic_transition_name,
};
expr.args[0].visit_mut_with(&mut visitor);
}
}
}
let second_arg = ExprOrSpread {
spread: None,
expr: Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props,
})),
};
if expr.args.len() == 2 {
expr.args[1] = second_arg;
} else {
expr.args.push(second_arg)
}
}
}
self.is_next_dynamic_first_arg = true;
expr.args[0].expr.visit_mut_with(self);
self.is_next_dynamic_first_arg = false;
let Some((dynamically_imported_specifier, dynamically_imported_specifier_span)) =
self.dynamically_imported_specifier.take()
else {
return;
};
let project_dir = match self.pages_or_app_dir.as_deref() {
Some(pages_or_app) => pages_or_app.parent(),
_ => None,
};
let generated = Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: match &mut self.state {
NextDynamicPatcherState::Webpack => {
// dev client or server:
// loadableGenerated: {
// modules:
// ["/project/src/file-being-transformed.js -> " +
// '../components/hello'] }
//
// prod client
// loadableGenerated: {
// webpack: () => [require.resolveWeak('../components/hello')],
if self.is_development || self.is_server_compiler {
module_id_options(quote!(
"$left + $right" as Expr,
left: Expr = format!(
"{} -> ",
rel_filename(project_dir, &self.filename)
)
.into(),
right: Expr = dynamically_imported_specifier.clone().into(),
))
} else {
webpack_options(quote!(
"require.resolveWeak($id)" as Expr,
id: Expr = dynamically_imported_specifier.clone().into()
))
}
}
NextDynamicPatcherState::Turbopack { imports, .. } => {
// loadableGenerated: { modules: [
// ".../client.js [app-client] (ecmascript, next/dynamic entry)"
// ]}
let id_ident = private_ident!(dynamically_imported_specifier_span, "id");
imports.push(TurbopackImport::Import {
id_ident: id_ident.clone(),
specifier: dynamically_imported_specifier.clone(),
});
module_id_options(Expr::Ident(id_ident))
}
},
}));
let mut props = vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(IdentName::new(atom!("loadableGenerated"), DUMMY_SP)),
value: generated,
})))];
let mut has_ssr_false = false;
if expr.args.len() == 2
&& let Expr::Object(ObjectLit {
props: options_props,
..
}) = &*expr.args[1].expr
{
for prop in options_props.iter() {
if let Some(KeyValueProp { key, value }) = match prop {
PropOrSpread::Prop(prop) => match &**prop {
Prop::KeyValue(key_value_prop) => Some(key_value_prop),
_ => None,
},
_ => None,
} && let Some(IdentName { sym, span: _ }) = match key {
PropName::Ident(ident) => Some(ident),
_ => None,
} && sym == "ssr"
&& let Some(Lit::Bool(Bool {
value: false,
span: _,
})) = value.as_lit()
{
has_ssr_false = true
}
}
props.extend(options_props.iter().cloned());
}
let should_skip_ssr_compile = has_ssr_false
&& self.is_server_compiler
&& !self.is_react_server_layer
&& self.prefer_esm;
match &self.state {
NextDynamicPatcherState::Webpack => {
// Only use `require.resolveWebpack` to decouple modules for webpack,
// turbopack doesn't need this
// When it's not preferring to picking up ESM (in the pages router), we
// don't need to do it as it doesn't need to enter the non-ssr module.
//
// Also transforming it to `require.resolveWeak` doesn't work with ESM
// imports ( i.e. require.resolveWeak(esm asset)).
if should_skip_ssr_compile {
// if it's server components SSR layer
// Transform 1st argument `expr.args[0]` aka the module loader from:
// dynamic(() => import('./client-mod'), { ssr: false }))`
// into:
// dynamic(async () => {
// require.resolveWeak('./client-mod')
// }, { ssr: false }))`
let require_resolve_weak_expr = Expr::Call(CallExpr {
span: DUMMY_SP,
callee: quote_ident!("require.resolveWeak").as_callee(),
args: vec![ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: dynamically_imported_specifier.clone(),
raw: None,
}))),
}],
..Default::default()
});
let side_effect_free_loader_arg = Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Box::new(exec_expr_when_resolve_weak_available(
&require_resolve_weak_expr,
)),
})],
..Default::default()
})),
is_async: true,
is_generator: false,
..Default::default()
});
expr.args[0] = side_effect_free_loader_arg.as_arg();
}
}
NextDynamicPatcherState::Turbopack {
dynamic_transition_name,
..
} => {
// When `ssr: false`
// if it's server components SSR layer
// Transform 1st argument `expr.args[0]` aka the module loader from:
// dynamic(() => import('./client-mod'), { ssr: false }))`
// into:
// dynamic(async () => {}, { ssr: false }))`
if should_skip_ssr_compile {
let side_effect_free_loader_arg = Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts: vec![],
..Default::default()
})),
is_async: true,
is_generator: false,
..Default::default()
});
expr.args[0] = side_effect_free_loader_arg.as_arg();
} else {
// Add `{with:{turbopack-transition: ...}}` to the dynamic import
let mut visitor = DynamicImportTransitionAdder {
transition_name: dynamic_transition_name,
};
expr.args[0].visit_mut_with(&mut visitor);
}
}
}
let second_arg = ExprOrSpread {
spread: None,
expr: Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props,
})),
};
if expr.args.len() == 2 {
expr.args[1] = second_arg;
} else {
expr.args.push(second_arg)
}
}
}
}
@@ -2,7 +2,7 @@ use swc_core::{
common::errors::HANDLER,
ecma::{
ast::*,
visit::{noop_visit_type, Visit},
visit::{Visit, noop_visit_type},
},
};
pub struct FindFunctionsOutsideModuleScope<'a> {
@@ -3,7 +3,7 @@ use swc_core::{
common::errors::HANDLER,
ecma::{
ast::*,
visit::{noop_visit_type, Visit},
visit::{Visit, noop_visit_type},
},
};
@@ -1,10 +1,10 @@
use serde_json::Value;
use swc_core::{
atoms::Wtf8Atom,
common::{errors::HANDLER, Spanned, DUMMY_SP},
common::{DUMMY_SP, Spanned, errors::HANDLER},
ecma::{
ast::*,
visit::{noop_visit_type, Visit},
visit::{Visit, noop_visit_type},
},
};
@@ -19,69 +19,67 @@ impl FontImportsGenerator<'_> {
call_expr: &CallExpr,
variable_name: &Result<Ident, &Pat>,
) -> Option<ImportDecl> {
if let Callee::Expr(callee_expr) = &call_expr.callee {
if let Expr::Ident(ident) = &**callee_expr {
if let Some(font_function) = self.state.font_functions.get(&ident.to_id()) {
self.state
.font_functions_in_allowed_scope
.insert(ident.span.lo);
if let Callee::Expr(callee_expr) = &call_expr.callee
&& let Expr::Ident(ident) = &**callee_expr
&& let Some(font_function) = self.state.font_functions.get(&ident.to_id())
{
self.state
.font_functions_in_allowed_scope
.insert(ident.span.lo);
let json: Result<Vec<Value>, ()> = call_expr
.args
.iter()
.map(|expr_or_spread| {
if let Some(span) = expr_or_spread.spread {
HANDLER.with(|handler| {
handler
.struct_span_err(span, "Font loaders don't accept spreads")
.emit()
});
}
expr_to_json(&expr_or_spread.expr)
})
.collect();
if let Ok(json) = json {
let function_name = match &font_function.function_name {
Some(function) => String::from(&**function),
None => String::new(),
};
let mut query_json_values = serde_json::Map::new();
query_json_values.insert(
String::from("path"),
Value::String(self.relative_path.to_string()),
);
query_json_values
.insert(String::from("import"), Value::String(function_name));
query_json_values.insert(String::from("arguments"), Value::Array(json));
if let Ok(ident) = variable_name {
query_json_values.insert(
String::from("variableName"),
Value::String(ident.sym.to_string()),
);
}
let query_json = Value::Object(query_json_values);
return Some(ImportDecl {
src: Box::new(Str {
value: Wtf8Atom::from(format!(
"{}/target.css?{}",
font_function.loader.to_string_lossy(),
query_json
)),
raw: None,
span: DUMMY_SP,
}),
specifiers: vec![],
type_only: false,
with: None,
span: DUMMY_SP,
phase: Default::default(),
let json: Result<Vec<Value>, ()> = call_expr
.args
.iter()
.map(|expr_or_spread| {
if let Some(span) = expr_or_spread.spread {
HANDLER.with(|handler| {
handler
.struct_span_err(span, "Font loaders don't accept spreads")
.emit()
});
}
expr_to_json(&expr_or_spread.expr)
})
.collect();
if let Ok(json) = json {
let function_name = match &font_function.function_name {
Some(function) => String::from(&**function),
None => String::new(),
};
let mut query_json_values = serde_json::Map::new();
query_json_values.insert(
String::from("path"),
Value::String(self.relative_path.to_string()),
);
query_json_values.insert(String::from("import"), Value::String(function_name));
query_json_values.insert(String::from("arguments"), Value::Array(json));
if let Ok(ident) = variable_name {
query_json_values.insert(
String::from("variableName"),
Value::String(ident.sym.to_string()),
);
}
let query_json = Value::Object(query_json_values);
return Some(ImportDecl {
src: Box::new(Str {
value: Wtf8Atom::from(format!(
"{}/target.css?{}",
font_function.loader.to_string_lossy(),
query_json
)),
raw: None,
span: DUMMY_SP,
}),
specifiers: vec![],
type_only: false,
with: None,
span: DUMMY_SP,
phase: Default::default(),
});
}
}
@@ -94,49 +92,49 @@ impl FontImportsGenerator<'_> {
Pat::Ident(ident) => Ok(ident.id.clone()),
pattern => Err(pattern),
};
if let Some(expr) = &decl.init {
if let Expr::Call(call_expr) = &**expr {
let import_decl = self.check_call_expr(call_expr, &ident);
if let Some(expr) = &decl.init
&& let Expr::Call(call_expr) = &**expr
{
let import_decl = self.check_call_expr(call_expr, &ident);
if let Some(mut import_decl) = import_decl {
match var_decl.kind {
VarDeclKind::Const => {}
_ => {
HANDLER.with(|handler| {
handler
.struct_span_err(
var_decl.span,
"Font loader calls must be assigned to a const",
)
.emit()
});
}
if let Some(mut import_decl) = import_decl {
match var_decl.kind {
VarDeclKind::Const => {}
_ => {
HANDLER.with(|handler| {
handler
.struct_span_err(
var_decl.span,
"Font loader calls must be assigned to a const",
)
.emit()
});
}
}
match ident {
Ok(ident) => {
import_decl.specifiers =
vec![ImportSpecifier::Default(ImportDefaultSpecifier {
span: DUMMY_SP,
local: ident.clone(),
})];
match ident {
Ok(ident) => {
import_decl.specifiers =
vec![ImportSpecifier::Default(ImportDefaultSpecifier {
span: DUMMY_SP,
local: ident.clone(),
})];
self.state
.font_imports
.push(ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)));
self.state
.font_imports
.push(ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)));
return Some(ident);
}
Err(pattern) => {
HANDLER.with(|handler| {
handler
.struct_span_err(
pattern.span(),
"Font loader calls must be assigned to an identifier",
)
.emit()
});
}
return Some(ident);
}
Err(pattern) => {
HANDLER.with(|handler| {
handler
.struct_span_err(
pattern.span(),
"Font loader calls must be assigned to an identifier",
)
.emit()
});
}
}
}
@@ -157,14 +155,17 @@ impl Visit for FontImportsGenerator<'_> {
}
}
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => {
if let Decl::Var(var_decl) = &export_decl.decl {
if let Some(ident) = self.check_var_decl(var_decl) {
self.state
.removable_module_items
.insert(export_decl.span.lo);
if let Decl::Var(var_decl) = &export_decl.decl
&& let Some(ident) = self.check_var_decl(var_decl)
{
self.state
.removable_module_items
.insert(export_decl.span.lo);
self.state.font_exports.push(ModuleItem::ModuleDecl(
ModuleDecl::ExportNamed(NamedExport {
self.state
.font_exports
.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportSpecifier::Named(ExportNamedSpecifier {
orig: ModuleExportName::Ident(ident),
@@ -175,9 +176,8 @@ impl Visit for FontImportsGenerator<'_> {
src: None,
type_only: false,
with: None,
}),
));
}
},
)));
}
}
_ => {}
@@ -6,7 +6,7 @@ use swc_core::{
ecma::{
ast::{Id, ModuleItem, Pass},
atoms::Atom,
visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitWith},
visit::{VisitMut, VisitWith, noop_visit_mut_type, visit_mut_pass},
},
};
@@ -1,12 +1,12 @@
use rustc_hash::{FxHashMap, FxHashSet};
use swc_core::{
atoms::{atom, Atom, Wtf8Atom},
atoms::{Atom, Wtf8Atom, atom},
ecma::{
ast::{
Expr, Id, ImportDecl, ImportNamedSpecifier, ImportSpecifier, MemberExpr, MemberProp,
Module,
},
visit::{noop_visit_type, Visit, VisitWith},
visit::{Visit, VisitWith, noop_visit_type},
},
};
@@ -37,10 +37,10 @@ impl VisitMut for MiddlewareDynamic {
let callee = &call_expr.callee;
if let Callee::Expr(callee) = callee {
// `eval('some')`, or `Function('some')`
if let Expr::Ident(ident) = &**callee {
if ident.sym == "eval" || ident.sym == "Function" {
should_wrap = Some(WrappedExpr::Eval);
}
if let Expr::Ident(ident) = &**callee
&& (ident.sym == "eval" || ident.sym == "Function")
{
should_wrap = Some(WrappedExpr::Eval);
}
if let Expr::Member(MemberExpr {
@@ -70,16 +70,15 @@ impl VisitMut for MiddlewareDynamic {
prop: MemberProp::Ident(member_prop_ident),
..
}) = &**obj
&& let Expr::Ident(ident) = &**obj
{
if let Expr::Ident(ident) = &**obj {
// `global.WebAssembly.compile('some')` &
// `global.WebAssembly.instantiate('some')`
if ident.sym == "global" && member_prop_ident.sym == "WebAssembly" {
if prop_ident.sym == "compile" {
should_wrap = Some(WrappedExpr::WasmCompile);
} else if prop_ident.sym == "instantiate" {
should_wrap = Some(WrappedExpr::WasmInstantiate);
}
// `global.WebAssembly.compile('some')` &
// `global.WebAssembly.instantiate('some')`
if ident.sym == "global" && member_prop_ident.sym == "WebAssembly" {
if prop_ident.sym == "compile" {
should_wrap = Some(WrappedExpr::WasmCompile);
} else if prop_ident.sym == "instantiate" {
should_wrap = Some(WrappedExpr::WasmInstantiate);
}
}
}
@@ -100,12 +99,11 @@ impl VisitMut for MiddlewareDynamic {
}
}
if let Expr::New(NewExpr { callee, .. }) = &expr {
if let Expr::Ident(ident) = &**callee {
if ident.sym == "Function" {
*expr = quote!("__next_eval__(function() { return $orig_call });" as Expr, orig_call: Expr = expr.clone());
}
}
if let Expr::New(NewExpr { callee, .. }) = &expr
&& let Expr::Ident(ident) = &**callee
&& ident.sym == "Function"
{
*expr = quote!("__next_eval__(function() { return $orig_call });" as Expr, orig_call: Expr = expr.clone());
}
}
}
@@ -5,7 +5,7 @@ use swc_core::{
common::DUMMY_SP,
ecma::{
ast::*,
visit::{visit_mut_pass, VisitMut},
visit::{VisitMut, visit_mut_pass},
},
};
@@ -1,17 +1,17 @@
use std::{cell::RefCell, mem::take, rc::Rc};
use easy_error::{bail, Error};
use easy_error::{Error, bail};
use rustc_hash::FxHashSet;
use swc_core::{
atoms::{atom, Atom},
atoms::{Atom, atom},
common::{
DUMMY_SP,
errors::HANDLER,
pass::{Repeat, Repeated},
DUMMY_SP,
},
ecma::{
ast::*,
visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith},
visit::{VisitMut, VisitMutWith, noop_visit_mut_type, visit_mut_pass},
},
};
@@ -131,10 +131,10 @@ impl VisitMut for Analyzer<'_> {
}
fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) {
if let ModuleExportName::Ident(id) = &s.orig {
if !SSG_EXPORTS.contains(&&*id.sym) {
self.add_ref(id.to_id());
}
if let ModuleExportName::Ident(id) = &s.orig
&& !SSG_EXPORTS.contains(&&*id.sym)
{
self.add_ref(id.to_id());
}
}
@@ -145,10 +145,10 @@ impl VisitMut for Analyzer<'_> {
}
for decl in &d.decls {
if let Pat::Ident(id) = &decl.name {
if !SSG_EXPORTS.contains(&&*id.id.sym) {
self.add_ref(id.to_id());
}
if let Pat::Ident(id) = &decl.name
&& !SSG_EXPORTS.contains(&&*id.id.sym)
{
self.add_ref(id.to_id());
}
}
}
@@ -240,10 +240,10 @@ impl VisitMut for Analyzer<'_> {
match &e.decl {
Decl::Fn(f) => {
// Drop getStaticProps.
if let Ok(is_data_identifier) = self.state.is_data_identifier(&f.ident) {
if is_data_identifier {
*s = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
}
if let Ok(is_data_identifier) = self.state.is_data_identifier(&f.ident)
&& is_data_identifier
{
*s = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
}
}
@@ -468,18 +468,17 @@ impl VisitMut for NextSsg {
| ModuleDecl::ExportDefaultDecl(..)
| ModuleDecl::ExportDefaultExpr(..),
) = &item
&& let Some(var) = var.take()
{
if let Some(var) = var.take() {
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
span: DUMMY_SP,
decl: Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
decl: Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: vec![var],
..Default::default()
})),
})))
}
kind: VarDeclKind::Var,
decls: vec![var],
..Default::default()
})),
})))
}
new.push(item);
@@ -599,12 +598,12 @@ impl VisitMut for NextSsg {
#[allow(clippy::single_match)]
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
if let Stmt::Decl(Decl::Fn(f)) = s {
if self.should_remove(f.ident.to_id()) {
self.mark_as_candidate(&mut f.function);
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
return;
}
if let Stmt::Decl(Decl::Fn(f)) = s
&& self.should_remove(f.ident.to_id())
{
self.mark_as_candidate(&mut f.function);
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
return;
}
s.visit_mut_children_with(self);
@@ -2,12 +2,12 @@ use std::collections::HashMap;
use serde::Deserialize;
use swc_core::{
atoms::{atom, Atom, Wtf8Atom},
atoms::{Atom, Wtf8Atom, atom},
common::DUMMY_SP,
ecma::{
ast::*,
utils::private_ident,
visit::{fold_pass, Fold},
visit::{Fold, fold_pass},
},
};
@@ -10,7 +10,7 @@ use swc_core::{
common::DUMMY_SP,
ecma::{
ast::*,
visit::{fold_pass, Fold, FoldWith},
visit::{Fold, FoldWith, fold_pass},
},
};
@@ -139,31 +139,30 @@ impl Fold for OptimizeServerReact {
if let Expr::Call(call) = &expr {
if let Callee::Expr(box Expr::Ident(f)) = &call.callee {
// Mark `useEffect` as DCE'able
if let Some(use_effect_ident) = &self.use_effect_ident {
if &f.to_id() == use_effect_ident && !effect_has_side_effect_deps(call) {
// return Expr::Lit(Lit::Null(Null { span: DUMMY_SP }));
return wrap_expr_with_env_prod_condition(call.clone());
}
if let Some(use_effect_ident) = &self.use_effect_ident
&& &f.to_id() == use_effect_ident
&& !effect_has_side_effect_deps(call)
{
// return Expr::Lit(Lit::Null(Null { span: DUMMY_SP }));
return wrap_expr_with_env_prod_condition(call.clone());
}
// Mark `useLayoutEffect` as DCE'able
if let Some(use_layout_effect_ident) = &self.use_layout_effect_ident {
if &f.to_id() == use_layout_effect_ident && !effect_has_side_effect_deps(call) {
return wrap_expr_with_env_prod_condition(call.clone());
}
if let Some(use_layout_effect_ident) = &self.use_layout_effect_ident
&& &f.to_id() == use_layout_effect_ident
&& !effect_has_side_effect_deps(call)
{
return wrap_expr_with_env_prod_condition(call.clone());
}
} else if let Some(react_ident) = &self.react_ident {
if let Callee::Expr(box Expr::Member(member)) = &call.callee {
if let box Expr::Ident(f) = &member.obj {
if &f.to_id() == react_ident {
if let MemberProp::Ident(i) = &member.prop {
// Mark `React.useEffect` and `React.useLayoutEffect` as DCE'able
// calls in production
if i.sym == "useEffect" || i.sym == "useLayoutEffect" {
return wrap_expr_with_env_prod_condition(call.clone());
}
}
}
}
} else if let Some(react_ident) = &self.react_ident
&& let Callee::Expr(box Expr::Member(member)) = &call.callee
&& let box Expr::Ident(f) = &member.obj
&& &f.to_id() == react_ident
&& let MemberProp::Ident(i) = &member.prop
{
// Mark `React.useEffect` and `React.useLayoutEffect` as DCE'able
// calls in production
if i.sym == "useEffect" || i.sym == "useLayoutEffect" {
return wrap_expr_with_env_prod_condition(call.clone());
}
}
}
@@ -178,53 +177,48 @@ impl Fold for OptimizeServerReact {
return decl;
}
if let Pat::Array(array_pat) = &decl.name {
if array_pat.elems.len() == 2 {
if let Some(box Expr::Call(call)) = &decl.init {
if let Callee::Expr(box Expr::Ident(f)) = &call.callee {
if let Some(use_state_ident) = &self.use_state_ident {
if &f.to_id() == use_state_ident && call.args.len() == 1 {
// We do the optimization only if the arg is a literal or a
// type that we can
// be sure is not a function (e.g. {} or [] lit).
// This is because useState allows a function as the
// initialiser.
match &call.args[0].expr {
box Expr::Lit(_) | box Expr::Object(_) | box Expr::Array(_) => {
// const [state, setState] = [x, () => {}];
return VarDeclarator {
definite: false,
name: decl.name.clone(),
init: Some(Box::new(Expr::Array(ArrayLit {
elems: vec![
Some(call.args[0].expr.clone().into()),
Some(
Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
body: Box::new(BlockStmtOrExpr::Expr(
Box::new(Expr::Lit(Lit::Null(
Null { span: DUMMY_SP },
))),
)),
is_async: false,
is_generator: false,
params: vec![],
..Default::default()
})
.into(),
),
],
span: DUMMY_SP,
}))),
span: DUMMY_SP,
};
}
_ => {}
}
}
}
}
if let Pat::Array(array_pat) = &decl.name
&& array_pat.elems.len() == 2
&& let Some(box Expr::Call(call)) = &decl.init
&& let Callee::Expr(box Expr::Ident(f)) = &call.callee
&& let Some(use_state_ident) = &self.use_state_ident
&& &f.to_id() == use_state_ident
&& call.args.len() == 1
{
// We do the optimization only if the arg is a literal or a
// type that we can
// be sure is not a function (e.g. {} or [] lit).
// This is because useState allows a function as the
// initialiser.
match &call.args[0].expr {
box Expr::Lit(_) | box Expr::Object(_) | box Expr::Array(_) => {
// const [state, setState] = [x, () => {}];
return VarDeclarator {
definite: false,
name: decl.name.clone(),
init: Some(Box::new(Expr::Array(ArrayLit {
elems: vec![
Some(call.args[0].expr.clone().into()),
Some(
Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Lit(
Lit::Null(Null { span: DUMMY_SP }),
)))),
is_async: false,
is_generator: false,
params: vec![],
..Default::default()
})
.into(),
),
],
span: DUMMY_SP,
}))),
span: DUMMY_SP,
};
}
_ => {}
}
}
@@ -1,9 +1,9 @@
use chrono::Utc;
use swc_core::{
common::{errors::HANDLER, Span, DUMMY_SP},
common::{DUMMY_SP, Span, errors::HANDLER},
ecma::{
ast::*,
visit::{fold_pass, Fold, FoldWith},
visit::{Fold, FoldWith, fold_pass},
},
};
@@ -68,10 +68,10 @@ impl Fold for PageConfig {
if let Decl::Var(var_decl) = &export.decl {
for decl in &var_decl.decls {
let mut is_config = false;
if let Pat::Ident(ident) = &decl.name {
if ident.id.sym == CONFIG_KEY {
is_config = true;
}
if let Pat::Ident(ident) = &decl.name
&& ident.id.sym == CONFIG_KEY
{
is_config = true;
}
if is_config {
@@ -120,17 +120,17 @@ impl Fold for PageConfig {
) -> ExportNamedSpecifier {
match &specifier.exported {
Some(ident) => {
if let ModuleExportName::Ident(ident) = ident {
if ident.sym == CONFIG_KEY {
self.handle_error("Config cannot be re-exported.", specifier.span)
}
if let ModuleExportName::Ident(ident) = ident
&& ident.sym == CONFIG_KEY
{
self.handle_error("Config cannot be re-exported.", specifier.span)
}
}
None => {
if let ModuleExportName::Ident(ident) = &specifier.orig {
if ident.sym == CONFIG_KEY {
self.handle_error("Config cannot be re-exported.", specifier.span)
}
if let ModuleExportName::Ident(ident) = &specifier.orig
&& ident.sym == CONFIG_KEY
{
self.handle_error("Config cannot be re-exported.", specifier.span)
}
}
}
@@ -1,8 +1,8 @@
use swc_core::{
common::{comments::Comments, errors::HANDLER, util::take::Take, Span, Spanned, DUMMY_SP},
common::{DUMMY_SP, Span, Spanned, comments::Comments, errors::HANDLER, util::take::Take},
ecma::{
ast::{CallExpr, Callee, EmptyStmt, Expr, Module, ModuleDecl, ModuleItem, Stmt},
visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
visit::{VisitMut, VisitMutWith, noop_visit_mut_type},
},
};
@@ -74,11 +74,11 @@ where
}
fn visit_mut_module_item(&mut self, m: &mut ModuleItem) {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = m {
if import.src.value == MODULE {
*m = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
return;
}
if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = m
&& import.src.value == MODULE
{
*m = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
return;
}
m.visit_mut_children_with(self);
@@ -11,19 +11,19 @@ use regex::Regex;
use rustc_hash::FxHashMap;
use serde::Deserialize;
use swc_core::{
atoms::{atom, Atom, Wtf8Atom},
atoms::{Atom, Wtf8Atom, atom},
common::{
DUMMY_SP, FileName, Span, Spanned,
comments::{Comment, CommentKind, Comments},
errors::HANDLER,
util::take::Take,
FileName, Span, Spanned, DUMMY_SP,
},
ecma::{
ast::*,
utils::{prepend_stmts, quote_ident, quote_str, ExprFactory},
utils::{ExprFactory, prepend_stmts, quote_ident, quote_str},
visit::{
noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith,
VisitWith,
Visit, VisitMut, VisitMutWith, VisitWith, noop_visit_mut_type, noop_visit_type,
visit_mut_pass,
},
},
};
@@ -162,15 +162,13 @@ impl<C: Comments> ReactServerComponents<C> {
/// removes specific directive from the AST.
fn remove_top_level_directive(&mut self, module: &mut Module) {
module.body.retain(|item| {
if let ModuleItem::Stmt(stmt) = item {
if let Some(expr_stmt) = stmt.as_expr() {
if let Expr::Lit(Lit::Str(Str { value, .. })) = &*expr_stmt.expr {
if &**value == "use client" {
// Remove the directive.
return false;
}
}
}
if let ModuleItem::Stmt(stmt) = item
&& let Some(expr_stmt) = stmt.as_expr()
&& let Expr::Lit(Lit::Str(Str { value, .. })) = &*expr_stmt.expr
&& &**value == "use client"
{
// Remove the directive.
return false;
}
true
});
@@ -454,14 +452,14 @@ fn collect_module_info(
// an exception because they are not valid directives.
Expr::Paren(ParenExpr { expr, .. }) => {
finished_directives = true;
if let Expr::Lit(Lit::Str(Str { value, .. })) = &**expr {
if &**value == "use client" {
report_error(
app_dir,
filepath,
RSCErrorKind::NextRscErrClientDirective(expr_stmt.span),
);
}
if let Expr::Lit(Lit::Str(Str { value, .. })) = &**expr
&& &**value == "use client"
{
report_error(
app_dir,
filepath,
RSCErrorKind::NextRscErrClientDirective(expr_stmt.span),
);
}
}
_ => {
@@ -825,24 +823,22 @@ impl ReactServerComponentValidator {
let is_error_file = RE.is_match(&self.filepath);
if is_error_file {
if let Some(app_dir) = &self.app_dir {
if let Some(app_dir) = app_dir.to_str() {
if self.filepath.starts_with(app_dir) {
let span = if let Some(first_item) = module.body.first() {
first_item.span()
} else {
module.span
};
if is_error_file
&& let Some(app_dir) = &self.app_dir
&& let Some(app_dir) = app_dir.to_str()
&& self.filepath.starts_with(app_dir)
{
let span = if let Some(first_item) = module.body.first() {
first_item.span()
} else {
module.span
};
report_error(
&self.app_dir,
&self.filepath,
RSCErrorKind::NextRscErrErrorFileServerComponent(span),
);
}
}
}
report_error(
&self.app_dir,
&self.filepath,
RSCErrorKind::NextRscErrErrorFileServerComponent(span),
);
}
}
@@ -1,6 +1,6 @@
use std::{
cell::RefCell,
collections::{hash_map, BTreeMap},
collections::{BTreeMap, hash_map},
convert::{TryFrom, TryInto},
mem::{replace, take},
path::{Path, PathBuf},
@@ -16,23 +16,23 @@ use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
use sha1::{Digest, Sha1};
use swc_core::{
atoms::{atom, Atom, Wtf8Atom},
atoms::{Atom, Wtf8Atom, atom},
common::{
BytePos, DUMMY_SP, FileName, Mark, SourceMap, Span, SyntaxContext,
comments::{Comment, CommentKind, Comments, SingleThreadedComments},
errors::HANDLER,
source_map::{SourceMapGenConfig, PURE_SP},
source_map::{PURE_SP, SourceMapGenConfig},
util::take::Take,
BytePos, FileName, Mark, SourceMap, Span, SyntaxContext, DUMMY_SP,
},
ecma::{
ast::*,
codegen::{self, text_writer::JsWriter, Emitter},
utils::{private_ident, quote_ident, ExprFactory},
visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith},
codegen::{self, Emitter, text_writer::JsWriter},
utils::{ExprFactory, private_ident, quote_ident},
visit::{VisitMut, VisitMutWith, noop_visit_mut_type, visit_mut_pass},
},
quote,
};
use turbo_rcstr::{rcstr, RcStr};
use turbo_rcstr::{RcStr, rcstr};
use crate::FxIndexMap;
@@ -155,7 +155,7 @@ pub fn server_actions<C: Comments>(
cm: Arc<SourceMap>,
use_cache_telemetry_tracker: Rc<RefCell<FxHashMap<String, usize>>>,
mode: ServerActionsMode,
) -> impl Pass {
) -> impl Pass + use<C> {
visit_mut_pass(ServerActions {
config,
mode,
@@ -510,11 +510,11 @@ impl<C: Comments> ServerActions<C> {
// If this is an exported arrow, remove it from export_name_by_local_id so the
// post-pass doesn't register it again (it's already registered above).
if self.current_export_name.is_some() {
if let Some(arrow_ident) = &self.arrow_or_fn_expr_ident {
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
if self.current_export_name.is_some()
&& let Some(arrow_ident) = &self.arrow_or_fn_expr_ident
{
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
if let BlockStmtOrExpr::BlockStmt(block) = &mut *arrow.body {
@@ -677,10 +677,10 @@ impl<C: Comments> ServerActions<C> {
// If this is an exported function, remove it from export_name_by_local_id so the
// post-pass doesn't register it again (it's already registered above).
if self.current_export_name.is_some() {
if let Some(ref fn_name) = fn_name {
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
if self.current_export_name.is_some()
&& let Some(ref fn_name) = fn_name
{
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
function.body.visit_mut_with(&mut ClosureReplacer {
@@ -821,11 +821,11 @@ impl<C: Comments> ServerActions<C> {
// If this is an exported arrow, remove it from export_name_by_local_id so the
// post-pass doesn't register it again (it's already registered above).
if self.current_export_name.is_some() {
if let Some(arrow_ident) = &self.arrow_or_fn_expr_ident {
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
if self.current_export_name.is_some()
&& let Some(arrow_ident) = &self.arrow_or_fn_expr_ident
{
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
if let BlockStmtOrExpr::BlockStmt(block) = &mut *arrow.body {
@@ -926,10 +926,10 @@ impl<C: Comments> ServerActions<C> {
// If this is an exported function, remove it from export_name_by_local_id so the
// post-pass doesn't register it again (it's already registered above).
if self.current_export_name.is_some() {
if let Some(ref fn_name) = fn_name {
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
if self.current_export_name.is_some()
&& let Some(ref fn_name) = fn_name
{
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
function.body.visit_mut_with(&mut ClosureReplacer {
@@ -1247,10 +1247,10 @@ impl<C: Comments> VisitMut for ServerActions<C> {
if !self.validate_async_function(f.is_async, f.span, fn_name.as_ref(), &directive) {
// If this is an exported function that failed validation, remove it from
// export_name_by_local_id so the post-pass doesn't register it.
if self.current_export_name.is_some() {
if let Some(fn_name) = fn_name {
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
if self.current_export_name.is_some()
&& let Some(fn_name) = fn_name
{
self.export_name_by_local_id.swap_remove(&fn_name.to_id());
}
return;
@@ -1266,39 +1266,38 @@ impl<C: Comments> VisitMut for ServerActions<C> {
// client layers).
if matches!(self.file_directive, Some(Directive::UseServer))
&& matches!(directive, Directive::UseServer)
&& let Some(export_name) = self.current_export_name.clone()
{
if let Some(export_name) = self.current_export_name.clone() {
let params = f.params.clone();
let span = f.span;
let params = f.params.clone();
let span = f.span;
self.register_server_action_export(
&export_name,
fn_name.as_ref(),
Some(&params),
span,
&mut || {
Box::new(Expr::Fn(FnExpr {
ident: fn_name.clone(),
function: Box::new(f.take()),
}))
},
);
self.register_server_action_export(
&export_name,
fn_name.as_ref(),
Some(&params),
span,
&mut || {
Box::new(Expr::Fn(FnExpr {
ident: fn_name.clone(),
function: Box::new(f.take()),
}))
},
);
return;
}
return;
}
// For the client layer, register cache exports without hoisting.
if !self.config.is_react_server_layer {
if matches!(directive, Directive::UseCache { .. }) {
if let Some(export_name) = self.current_export_name.clone() {
self.register_cache_export_on_client(
&export_name,
fn_name.as_ref(),
Some(&f.params),
f.span,
);
}
if matches!(directive, Directive::UseCache { .. })
&& let Some(export_name) = self.current_export_name.clone()
{
self.register_cache_export_on_client(
&export_name,
fn_name.as_ref(),
Some(&f.params),
f.span,
);
}
return;
@@ -1399,10 +1398,10 @@ impl<C: Comments> VisitMut for ServerActions<C> {
fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
let old_this_status = replace(&mut self.this_status, ThisStatus::Allowed);
let old_current_export_name = self.current_export_name.take();
if self.in_module_level {
if let Some(export_name) = self.export_name_by_local_id.get(&f.ident.to_id()) {
self.current_export_name = Some(export_name.clone());
}
if self.in_module_level
&& let Some(export_name) = self.export_name_by_local_id.get(&f.ident.to_id())
{
self.current_export_name = Some(export_name.clone());
}
let old_fn_decl_ident = self.fn_decl_ident.replace(f.ident.clone());
f.visit_mut_children_with(self);
@@ -1460,11 +1459,11 @@ impl<C: Comments> VisitMut for ServerActions<C> {
if !self.validate_async_function(a.is_async, a.span, arrow_ident.as_ref(), &directive) {
// If this is an exported arrow function that failed validation, remove it from
// export_name_by_local_id so the post-pass doesn't register it.
if self.current_export_name.is_some() {
if let Some(arrow_ident) = arrow_ident {
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
if self.current_export_name.is_some()
&& let Some(arrow_ident) = arrow_ident
{
self.export_name_by_local_id
.swap_remove(&arrow_ident.to_id());
}
return;
@@ -1480,37 +1479,35 @@ impl<C: Comments> VisitMut for ServerActions<C> {
// client layers).
if matches!(self.file_directive, Some(Directive::UseServer))
&& matches!(directive, Directive::UseServer)
&& let Some(export_name) = self.current_export_name.clone()
{
if let Some(export_name) = self.current_export_name.clone() {
let params: Vec<Param> =
a.params.iter().map(|p| Param::from(p.clone())).collect();
let params: Vec<Param> = a.params.iter().map(|p| Param::from(p.clone())).collect();
self.register_server_action_export(
&export_name,
arrow_ident.as_ref(),
Some(&params),
a.span,
&mut || Box::new(Expr::Arrow(a.take())),
);
self.register_server_action_export(
&export_name,
arrow_ident.as_ref(),
Some(&params),
a.span,
&mut || Box::new(Expr::Arrow(a.take())),
);
return;
}
return;
}
// For the client layer, register cache exports without hoisting.
if !self.config.is_react_server_layer {
if matches!(directive, Directive::UseCache { .. }) {
if let Some(export_name) = self.current_export_name.clone() {
let params: Vec<Param> =
a.params.iter().map(|p| Param::from(p.clone())).collect();
if matches!(directive, Directive::UseCache { .. })
&& let Some(export_name) = self.current_export_name.clone()
{
let params: Vec<Param> =
a.params.iter().map(|p| Param::from(p.clone())).collect();
self.register_cache_export_on_client(
&export_name,
arrow_ident.as_ref(),
Some(&params),
a.span,
);
}
self.register_cache_export_on_client(
&export_name,
arrow_ident.as_ref(),
Some(&params),
a.span,
);
}
return;
@@ -1604,14 +1601,15 @@ impl<C: Comments> VisitMut for ServerActions<C> {
_ => {}
}
if !self.in_module_level && self.should_track_names {
if let PropOrSpread::Prop(box Prop::Shorthand(i)) = n {
self.names.push(Name::from(&*i));
self.should_track_names = false;
n.visit_mut_children_with(self);
self.should_track_names = true;
return;
}
if !self.in_module_level
&& self.should_track_names
&& let PropOrSpread::Prop(box Prop::Shorthand(i)) = n
{
self.names.push(Name::from(&*i));
self.should_track_names = false;
n.visit_mut_children_with(self);
self.should_track_names = true;
return;
}
n.visit_mut_children_with(self);
@@ -1688,17 +1686,17 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}
fn visit_mut_call_expr(&mut self, n: &mut CallExpr) {
if let Callee::Expr(box Expr::Ident(Ident { sym, .. })) = &mut n.callee {
if sym == "jsxDEV" || sym == "_jsxDEV" {
// Do not visit the 6th arg in a generated jsxDEV call, which is a `this`
// expression, to avoid emitting an error for using `this` if it's
// inside of a server function. https://github.com/facebook/react/blob/9106107/packages/react/src/jsx/ReactJSXElement.js#L429
if n.args.len() > 4 {
for arg in &mut n.args[0..4] {
arg.visit_mut_with(self);
}
return;
if let Callee::Expr(box Expr::Ident(Ident { sym, .. })) = &mut n.callee
&& (sym == "jsxDEV" || sym == "_jsxDEV")
{
// Do not visit the 6th arg in a generated jsxDEV call, which is a `this`
// expression, to avoid emitting an error for using `this` if it's
// inside of a server function. https://github.com/facebook/react/blob/9106107/packages/react/src/jsx/ReactJSXElement.js#L429
if n.args.len() > 4 {
for arg in &mut n.args[0..4] {
arg.visit_mut_with(self);
}
return;
}
}
@@ -1714,22 +1712,23 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}
fn visit_mut_expr(&mut self, n: &mut Expr) {
if !self.in_module_level && self.should_track_names {
if let Ok(mut name) = Name::try_from(&*n) {
if self.in_callee {
// This is a callee i.e. `foo.bar()`,
// we need to track the actual value instead of the method name.
if !name.1.is_empty() {
name.1.pop();
}
if !self.in_module_level
&& self.should_track_names
&& let Ok(mut name) = Name::try_from(&*n)
{
if self.in_callee {
// This is a callee i.e. `foo.bar()`,
// we need to track the actual value instead of the method name.
if !name.1.is_empty() {
name.1.pop();
}
self.names.push(name);
self.should_track_names = false;
n.visit_mut_children_with(self);
self.should_track_names = true;
return;
}
self.names.push(name);
self.should_track_names = false;
n.visit_mut_children_with(self);
self.should_track_names = true;
return;
}
self.rewrite_expr_to_proxy_expr = None;
@@ -1766,13 +1765,13 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export_default_decl)) => {
// export default function foo() {}
if let DefaultDecl::Fn(f) = &export_default_decl.decl {
if let Some(ident) = &f.ident {
self.export_name_by_local_id.insert(
ident.to_id(),
ModuleExportName::Ident(atom!("default").into()),
);
}
if let DefaultDecl::Fn(f) = &export_default_decl.decl
&& let Some(ident) = &f.ident
{
self.export_name_by_local_id.insert(
ident.to_id(),
ModuleExportName::Ident(atom!("default").into()),
);
}
}
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => {
@@ -1853,13 +1852,12 @@ impl<C: Comments> VisitMut for ServerActions<C> {
ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl))) => {
// Track which declarations need cache runtime wrappers if exported.
for decl in &var_decl.decls {
if let Pat::Ident(ident_pat) = &decl.name {
if let Some(init) = &decl.init {
if may_need_cache_runtime_wrapper(init) {
self.local_ids_that_need_cache_runtime_wrapper_if_exported
.insert(ident_pat.id.to_id());
}
}
if let Pat::Ident(ident_pat) = &decl.name
&& let Some(init) = &decl.init
&& may_need_cache_runtime_wrapper(init)
{
self.local_ids_that_need_cache_runtime_wrapper_if_exported
.insert(ident_pat.id.to_id());
}
}
}
@@ -1910,17 +1908,17 @@ impl<C: Comments> VisitMut for ServerActions<C> {
let mut has_export_needing_wrapper = false;
for decl in &var.decls {
if let Pat::Ident(_) = &decl.name {
if let Some(init) = &decl.init {
// Disallow exporting literals. Admittedly, this is
// pretty arbitrary. We don't disallow exporting object
// and array literals, as that would be too restrictive,
// especially for page and layout files with
// 'use cache', that may want to export metadata or
// viewport objects.
if let Expr::Lit(_) = &**init {
disallowed_export_span = *span;
}
if let Pat::Ident(_) = &decl.name
&& let Some(init) = &decl.init
{
// Disallow exporting literals. Admittedly, this is
// pretty arbitrary. We don't disallow exporting object
// and array literals, as that would be too restrictive,
// especially for page and layout files with
// 'use cache', that may want to export metadata or
// viewport objects.
if let Expr::Lit(_) = &**init {
disallowed_export_span = *span;
}
}
@@ -2857,10 +2855,10 @@ impl<C: Comments> VisitMut for ServerActions<C> {
if let (Pat::Ident(ident), Some(box Expr::Arrow(_) | box Expr::Fn(_))) =
(&var_declarator.name, &var_declarator.init)
{
if self.in_module_level {
if let Some(export_name) = self.export_name_by_local_id.get(&ident.to_id()) {
self.current_export_name = Some(export_name.clone());
}
if self.in_module_level
&& let Some(export_name) = self.export_name_by_local_id.get(&ident.to_id())
{
self.current_export_name = Some(export_name.clone());
}
self.arrow_or_fn_expr_ident = Some(ident.id.clone());
@@ -2908,14 +2906,14 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}
fn visit_mut_ident(&mut self, n: &mut Ident) {
if n.sym == *"arguments" {
if let ThisStatus::Forbidden { directive } = &self.this_status {
emit_error(ServerActionsErrorKind::ForbiddenExpression {
span: n.span,
expr: "arguments".into(),
directive: directive.clone(),
});
}
if n.sym == *"arguments"
&& let ThisStatus::Forbidden { directive } = &self.this_status
{
emit_error(ServerActionsErrorKind::ForbiddenExpression {
span: n.span,
expr: "arguments".into(),
directive: directive.clone(),
});
}
}
@@ -3,9 +3,9 @@ use swc_core::{
common::Mark,
ecma::{
ast::*,
atoms::{atom, Atom},
transforms::optimization::simplify::dce::{dce, Config as DCEConfig},
visit::{fold_pass, Fold, FoldWith, VisitMutWith},
atoms::{Atom, atom},
transforms::optimization::simplify::dce::{Config as DCEConfig, dce},
visit::{Fold, FoldWith, VisitMutWith, fold_pass},
},
};
@@ -63,10 +63,10 @@ impl Fold for ExportShaker {
.decls
.iter()
.filter_map(|var_decl| {
if let Pat::Ident(BindingIdent { id, .. }) = &var_decl.name {
if self.ignore.contains(&id.sym) {
return Some(var_decl.to_owned());
}
if let Pat::Ident(BindingIdent { id, .. }) = &var_decl.name
&& self.ignore.contains(&id.sym)
{
return Some(var_decl.to_owned());
}
None
})
@@ -87,15 +87,15 @@ impl Fold for ExportShaker {
.filter_map(|spec| {
if let ExportSpecifier::Named(named_spec) = spec {
if let Some(ident) = &named_spec.exported {
if let ModuleExportName::Ident(ident) = ident {
if self.ignore.contains(&ident.sym) {
return Some(ExportSpecifier::Named(named_spec));
}
}
} else if let ModuleExportName::Ident(ident) = &named_spec.orig {
if self.ignore.contains(&ident.sym) {
if let ModuleExportName::Ident(ident) = ident
&& self.ignore.contains(&ident.sym)
{
return Some(ExportSpecifier::Named(named_spec));
}
} else if let ModuleExportName::Ident(ident) = &named_spec.orig
&& self.ignore.contains(&ident.sym)
{
return Some(ExportSpecifier::Named(named_spec));
}
}
None
@@ -9,13 +9,13 @@ use rustc_hash::{FxHashMap, FxHashSet};
use swc_core::{
atoms::Atom,
common::{
DUMMY_SP,
errors::HANDLER,
pass::{Repeat, Repeated},
DUMMY_SP,
},
ecma::{
ast::*,
visit::{fold_pass, noop_fold_type, noop_visit_type, Fold, FoldWith, Visit, VisitWith},
visit::{Fold, FoldWith, Visit, VisitWith, fold_pass, noop_fold_type, noop_visit_type},
},
};
@@ -347,10 +347,10 @@ impl Visit for Analyzer<'_> {
self.state
.encounter_export(exported_ident, local_ident, export_type);
if let Some(local_ident) = local_ident {
if self.state.should_retain_export_type(export_type) {
self.add_ref(local_ident.to_id());
}
if let Some(local_ident) = local_ident
&& self.state.should_retain_export_type(export_type)
{
self.add_ref(local_ident.to_id());
}
}
}
@@ -793,13 +793,13 @@ impl Fold for NextSsg {
}
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(e)) => match &e.decl {
Decl::Fn(f) => {
if let Some(export_type) = self.state.export_type(&f.ident.to_id()) {
if self.state.dropping_export(export_type) {
tracing::trace!(
"Dropping an export specifier because it's an SSR/SSG function"
);
return ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
}
if let Some(export_type) = self.state.export_type(&f.ident.to_id())
&& self.state.dropping_export(export_type)
{
tracing::trace!(
"Dropping an export specifier because it's an SSR/SSG function"
);
return ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
}
}
@@ -950,20 +950,19 @@ impl Fold for NextSsg {
}
}
Pat::Expr(expr) => {
if let Expr::Member(member_expr) = &**expr {
if let Some(id) = find_member_root_id(member_expr) {
if self.should_remove(&id) {
self.state.should_run_again = true;
tracing::trace!(
"Dropping member expression object `{}{:?}` because it should \
be removed",
id.0,
id.1
);
if let Expr::Member(member_expr) = &**expr
&& let Some(id) = find_member_root_id(member_expr)
&& self.should_remove(&id)
{
self.state.should_run_again = true;
tracing::trace!(
"Dropping member expression object `{}{:?}` because it should be \
removed",
id.0,
id.1
);
return Pat::Invalid(Invalid { span: DUMMY_SP });
}
}
return Pat::Invalid(Invalid { span: DUMMY_SP });
}
}
_ => {}
@@ -976,32 +975,31 @@ impl Fold for NextSsg {
fn fold_simple_assign_target(&mut self, mut n: SimpleAssignTarget) -> SimpleAssignTarget {
n = n.fold_children_with(self);
if let SimpleAssignTarget::Ident(name) = &n {
if self.should_remove(&name.id.to_id()) {
self.state.should_run_again = true;
tracing::trace!(
"Dropping var `{}{:?}` because it should be removed",
name.id.sym,
name.id.ctxt
);
if let SimpleAssignTarget::Ident(name) = &n
&& self.should_remove(&name.id.to_id())
{
self.state.should_run_again = true;
tracing::trace!(
"Dropping var `{}{:?}` because it should be removed",
name.id.sym,
name.id.ctxt
);
return SimpleAssignTarget::Invalid(Invalid { span: DUMMY_SP });
}
return SimpleAssignTarget::Invalid(Invalid { span: DUMMY_SP });
}
if let SimpleAssignTarget::Member(member_expr) = &n {
if let Some(id) = find_member_root_id(member_expr) {
if self.should_remove(&id) {
self.state.should_run_again = true;
tracing::trace!(
"Dropping member expression object `{}{:?}` because it should be removed",
id.0,
id.1
);
if let SimpleAssignTarget::Member(member_expr) = &n
&& let Some(id) = find_member_root_id(member_expr)
&& self.should_remove(&id)
{
self.state.should_run_again = true;
tracing::trace!(
"Dropping member expression object `{}{:?}` because it should be removed",
id.0,
id.1
);
return SimpleAssignTarget::Invalid(Invalid { span: DUMMY_SP });
}
}
return SimpleAssignTarget::Invalid(Invalid { span: DUMMY_SP });
}
n
@@ -1,15 +1,15 @@
use rustc_hash::FxHashMap;
use swc_core::{
common::{
BytePos, DUMMY_SP, Mark, Span, Spanned, SyntaxContext,
comments::{Comment, CommentKind, Comments},
source_map::PURE_SP,
util::take::Take,
BytePos, Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
},
ecma::{
ast::*,
utils::{prepend_stmt, private_ident, quote_ident, quote_str},
visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith},
visit::{VisitMut, VisitMutWith, noop_visit_mut_type, visit_mut_pass},
},
quote,
};
@@ -129,14 +129,12 @@ impl<C: Comments> VisitMut for ImportReplacer<C> {
//
// Only extract names when `await` is present — without await, the
// destructuring targets the Promise, not the module namespace.
if let Some(init) = &decl.init {
if let Some(import_span) = find_awaited_import_call_span(init) {
if let Pat::Object(obj_pat) = &decl.name {
if let Some(names) = extract_export_names_from_pat(obj_pat) {
self.import_export_names.insert(import_span.lo, names);
}
}
}
if let Some(init) = &decl.init
&& let Some(import_span) = find_awaited_import_call_span(init)
&& let Pat::Object(obj_pat) = &decl.name
&& let Some(names) = extract_export_names_from_pat(obj_pat)
{
self.import_export_names.insert(import_span.lo, names);
}
decl.visit_mut_children_with(self);
@@ -158,24 +156,24 @@ impl<C: Comments> VisitMut for ImportReplacer<C> {
self.has_dynamic_import = true;
// Add /* webpackExports: [...] */ comment if we detected destructuring
if let Some(names) = self.import_export_names.remove(&call_expr.span.lo) {
if let Some(first_arg) = call_expr.args.first() {
let comment_text = if names.is_empty() {
" webpackExports: [] ".to_string()
} else {
let names_json: Vec<String> =
names.iter().map(|n| format!("\"{}\"", n)).collect();
format!(" webpackExports: [{}] ", names_json.join(", "))
};
self.comments.add_leading(
first_arg.span_lo(),
Comment {
span: DUMMY_SP,
kind: CommentKind::Block,
text: comment_text.into(),
},
);
}
if let Some(names) = self.import_export_names.remove(&call_expr.span.lo)
&& let Some(first_arg) = call_expr.args.first()
{
let comment_text = if names.is_empty() {
" webpackExports: [] ".to_string()
} else {
let names_json: Vec<String> =
names.iter().map(|n| format!("\"{}\"", n)).collect();
format!(" webpackExports: [{}] ", names_json.join(", "))
};
self.comments.add_leading(
first_arg.span_lo(),
Comment {
span: DUMMY_SP,
kind: CommentKind::Block,
text: comment_text.into(),
},
);
}
let replacement_expr = quote!(
@@ -2,11 +2,11 @@ use std::sync::Arc;
use swc_core::{
atoms::{Atom, Wtf8Atom},
common::{errors::HANDLER, SourceMap, Span},
common::{SourceMap, Span, errors::HANDLER},
ecma::{
ast::{
op, BinExpr, CallExpr, Callee, CondExpr, Expr, IdentName, IfStmt, ImportDecl, Lit,
MemberExpr, MemberProp, NamedExport, UnaryExpr,
BinExpr, CallExpr, Callee, CondExpr, Expr, IdentName, IfStmt, ImportDecl, Lit,
MemberExpr, MemberProp, NamedExport, UnaryExpr, op,
},
utils::{ExprCtx, ExprExt},
visit::{Visit, VisitWith},
@@ -272,19 +272,17 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime",
self.guarded_symbols.push(ident.sym.clone());
}
Expr::Member(member) => {
if member.prop.is_ident_with("NEXT_RUNTIME") {
if let Expr::Member(obj_member) = &*member.obj {
if obj_member.obj.is_global_ref_to(self.ctx, "process")
&& obj_member.prop.is_ident_with("env")
{
self.guarded_runtime = true;
}
}
if member.prop.is_ident_with("NEXT_RUNTIME")
&& let Expr::Member(obj_member) = &*member.obj
&& obj_member.obj.is_global_ref_to(self.ctx, "process")
&& obj_member.prop.is_ident_with("env")
{
self.guarded_runtime = true;
}
if member.obj.is_global_ref_to(self.ctx, "process") {
if let MemberProp::Ident(prop) = &member.prop {
self.guarded_process_props.push(prop.sym.clone());
}
if member.obj.is_global_ref_to(self.ctx, "process")
&& let MemberProp::Ident(prop) = &member.prop
{
self.guarded_process_props.push(prop.sym.clone());
}
}
Expr::Bin(BinExpr {
@@ -330,10 +328,10 @@ where
fn visit_call_expr(&mut self, n: &CallExpr) {
n.visit_children_with(self);
if let Callee::Import(_) = &n.callee {
if let Some(Expr::Lit(Lit::Str(s))) = n.args.first().map(|e| &*e.expr) {
self.warn_if_nodejs_module(n.span, &s.value);
}
if let Callee::Import(_) = &n.callee
&& let Some(Expr::Lit(Lit::Str(s))) = n.args.first().map(|e| &*e.expr)
{
self.warn_if_nodejs_module(n.span, &s.value);
}
}
@@ -371,18 +369,18 @@ where
}
fn visit_expr(&mut self, n: &Expr) {
if let Expr::Ident(ident) = n {
if ident.ctxt == self.ctx.unresolved_ctxt {
if ident.sym == "eval" {
self.emit_dynamic_not_allowed_error(ident.span);
return;
}
if let Expr::Ident(ident) = n
&& ident.ctxt == self.ctx.unresolved_ctxt
{
if ident.sym == "eval" {
self.emit_dynamic_not_allowed_error(ident.span);
return;
}
for api in EDGE_UNSUPPORTED_NODE_APIS {
if self.is_in_middleware_layer() && ident.sym == *api {
self.emit_unsupported_api_error(ident.span, api);
return;
}
for api in EDGE_UNSUPPORTED_NODE_APIS {
if self.is_in_middleware_layer() && ident.sym == *api {
self.emit_unsupported_api_error(ident.span, api);
return;
}
}
}
@@ -406,11 +404,11 @@ where
}
fn visit_member_expr(&mut self, n: &MemberExpr) {
if n.obj.is_global_ref_to(self.ctx, "process") {
if let MemberProp::Ident(prop) = &n.prop {
self.warn_for_unsupported_process_api(n.span, prop);
return;
}
if n.obj.is_global_ref_to(self.ctx, "process")
&& let MemberProp::Ident(prop) = &n.prop
{
self.warn_for_unsupported_process_api(n.span, prop);
return;
}
n.visit_children_with(self);
@@ -2,12 +2,12 @@ use std::{iter::FromIterator, path::PathBuf};
use next_custom_transforms::transforms::{
disallow_re_export_all_in_page::disallow_re_export_all_in_page,
dynamic::{next_dynamic, NextDynamicMode},
fonts::{next_font_loaders, Config as FontLoaderConfig},
dynamic::{NextDynamicMode, next_dynamic},
fonts::{Config as FontLoaderConfig, next_font_loaders},
next_ssg::next_ssg,
react_server_components::server_components,
server_actions::{self, server_actions, ServerActionsMode},
strip_page_exports::{next_transform_strip_page_exports, ExportFilter},
server_actions::{self, ServerActionsMode, server_actions},
strip_page_exports::{ExportFilter, next_transform_strip_page_exports},
};
use rustc_hash::FxHashSet;
use swc_core::{
@@ -17,7 +17,7 @@ use swc_core::{
parser::{EsSyntax, Syntax},
transforms::{
base::resolver,
testing::{test_fixture, FixtureTestConfig},
testing::{FixtureTestConfig, test_fixture},
},
},
};
@@ -9,17 +9,17 @@ use next_custom_transforms::transforms::{
cjs_optimizer::cjs_optimizer,
debug_fn_name::debug_fn_name,
debug_instant_stack::debug_instant_stack,
dynamic::{next_dynamic, NextDynamicMode},
fonts::{next_font_loaders, Config as FontLoaderConfig},
dynamic::{NextDynamicMode, next_dynamic},
fonts::{Config as FontLoaderConfig, next_font_loaders},
named_import_transform::named_import_transform,
next_ssg::next_ssg,
optimize_barrel::optimize_barrel,
optimize_server_react::{self, optimize_server_react},
pure::pure_magic,
react_server_components::server_components,
server_actions::{self, server_actions, ServerActionsMode},
shake_exports::{shake_exports, Config as ShakeExportsConfig},
strip_page_exports::{next_transform_strip_page_exports, ExportFilter},
server_actions::{self, ServerActionsMode, server_actions},
shake_exports::{Config as ShakeExportsConfig, shake_exports},
strip_page_exports::{ExportFilter, next_transform_strip_page_exports},
track_dynamic_imports::track_dynamic_imports,
warn_for_edge_runtime::warn_for_edge_runtime,
};
@@ -27,20 +27,20 @@ use rustc_hash::FxHashSet;
use serde::de::DeserializeOwned;
use swc_core::{
atoms::atom,
common::{comments::SingleThreadedComments, FileName, Mark, SyntaxContext},
common::{FileName, Mark, SyntaxContext, comments::SingleThreadedComments},
ecma::{
ast::Pass,
parser::{EsSyntax, Syntax, TsSyntax},
transforms::{
base::resolver,
react::jsx,
testing::{test_fixture, FixtureTestConfig},
testing::{FixtureTestConfig, test_fixture},
},
utils::ExprCtx,
visit::{visit_mut_pass, visit_pass, Visit},
visit::{Visit, visit_mut_pass, visit_pass},
},
};
use swc_relay::{relay, RelayLanguageConfig};
use swc_relay::{RelayLanguageConfig, relay};
use testing::fixture;
fn syntax() -> Syntax {
+2 -2
View File
@@ -1,10 +1,10 @@
use std::path::{Path, PathBuf};
use next_custom_transforms::chain_transforms::{custom_before_pass, TransformOptions};
use next_custom_transforms::chain_transforms::{TransformOptions, custom_before_pass};
use serde::de::DeserializeOwned;
use swc_core::{
base::Compiler,
common::{comments::SingleThreadedComments, Mark},
common::{Mark, comments::SingleThreadedComments},
ecma::{
ast::noop_pass,
parser::{Syntax, TsSyntax},
@@ -5,8 +5,8 @@ use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use swc_core::{
atoms::Atom,
base::{try_with_handler, Compiler},
common::{comments::SingleThreadedComments, FileName, FilePathMapping, SourceMap, GLOBALS},
base::{Compiler, try_with_handler},
common::{FileName, FilePathMapping, GLOBALS, SourceMap, comments::SingleThreadedComments},
ecma::ast::noop_pass,
};
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
edition = "2018"
edition = "2024"
name = "wasm"
publish = false
version = "0.0.0"
+4 -3
View File
@@ -2,17 +2,18 @@ use std::{fmt::Debug, sync::Arc};
use anyhow::Context;
use js_sys::JsString;
use next_custom_transforms::chain_transforms::{custom_before_pass, TransformOptions};
use next_custom_transforms::chain_transforms::{TransformOptions, custom_before_pass};
use rustc_hash::FxHashMap;
use swc_core::{
base::{
Compiler,
config::{JsMinifyOptions, ParseOptions},
try_with_handler, Compiler,
try_with_handler,
},
common::{
FileName, FilePathMapping, GLOBALS, Mark, SourceMap,
comments::{Comments, SingleThreadedComments},
errors::ColorConfig,
FileName, FilePathMapping, Mark, SourceMap, GLOBALS,
},
ecma::ast::noop_pass,
};
+1 -1
View File
@@ -1,5 +1,5 @@
use js_sys::JsString;
use mdxjs::{compile, Options};
use mdxjs::{Options, compile};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;