turbopack_ecmascript/references/
unreachable.rs1use std::mem::take;
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use swc_core::{
6 common::{Spanned, util::take::Take},
7 ecma::{
8 ast::{
9 ArrayPat, ArrowExpr, AssignPat, AssignPatProp, BindingIdent, BlockStmt, ClassDecl,
10 Decl, FnDecl, Ident, KeyValuePatProp, ObjectPat, ObjectPatProp, Pat, RestPat, Stmt,
11 VarDecl, VarDeclKind, VarDeclarator,
12 },
13 visit::{
14 AstParentKind, VisitMut, VisitMutWith,
15 fields::{BlockStmtField, SwitchCaseField},
16 },
17 },
18 quote,
19};
20use turbo_tasks::{NonLocalValue, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
21use turbopack_core::{chunk::ChunkingContext, module_graph::ModuleGraph};
22
23use crate::{
24 code_gen::{CodeGen, CodeGeneration},
25 create_visitor,
26 utils::AstPathRange,
27};
28
29#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
30
31pub struct Unreachable {
32 range: AstPathRange,
33}
34
35impl Unreachable {
36 pub fn new(range: AstPathRange) -> Self {
37 Unreachable { range }
38 }
39
40 pub async fn code_generation(
41 &self,
42 _module_graph: Vc<ModuleGraph>,
43 _chunking_context: Vc<Box<dyn ChunkingContext>>,
44 ) -> Result<CodeGeneration> {
45 let visitors = match &self.range {
46 AstPathRange::Exact(path) => {
47 [
48 create_visitor!(exact path, visit_mut_expr(expr: &mut Expr) {
50 *expr = quote!("(\"TURBOPACK unreachable\", undefined)" as Expr);
51 }),
52 create_visitor!(exact path, visit_mut_stmt(stmt: &mut Stmt) {
53 let mut replacement = Vec::new();
56 replacement.push(quote!("\"TURBOPACK unreachable\";" as Stmt));
57 stmt.visit_mut_with(&mut ExtractDeclarations {
58 stmts: &mut replacement,
59 in_nested_block_scope: false,
60 });
61 *stmt = Stmt::Block(BlockStmt {
62 span: stmt.span(),
63 stmts: replacement,
64 ..Default::default()
65 });
66 }),
67 ]
68 .into()
69 }
70 AstPathRange::StartAfter(path) => {
71 let mut parent = &path[..];
72 while !parent.is_empty()
73 && !matches!(parent.last().unwrap(), AstParentKind::Stmt(_))
74 {
75 parent = &parent[0..parent.len() - 1];
76 }
77 if !parent.is_empty() {
78 parent = &parent[0..parent.len() - 1];
79 fn replace(stmts: &mut Vec<Stmt>, start_index: usize) {
80 if stmts.len() > start_index + 1 {
81 let unreachable = stmts
82 .splice(
83 start_index + 1..,
84 [quote!("\"TURBOPACK unreachable\";" as Stmt)].into_iter(),
85 )
86 .collect::<Vec<_>>();
87 for mut stmt in unreachable {
88 stmt.visit_mut_with(&mut ExtractDeclarations {
89 stmts,
90 in_nested_block_scope: false,
91 });
92 }
93 }
94 }
95 let (parent, [last]) = parent.split_at(parent.len() - 1) else {
96 unreachable!();
97 };
98 if let &AstParentKind::BlockStmt(BlockStmtField::Stmts(start_index)) = last {
99 [
100 create_visitor!(exact parent, visit_mut_block_stmt(block: &mut BlockStmt) {
101 replace(&mut block.stmts, start_index);
102 }),
103 ]
104 .into()
105 } else if let &AstParentKind::SwitchCase(SwitchCaseField::Cons(start_index)) =
106 last
107 {
108 [
109 create_visitor!(exact parent, visit_mut_switch_case(case: &mut SwitchCase) {
110 replace(&mut case.cons, start_index);
111 }),
112 ]
113 .into()
114 } else {
115 Vec::new()
116 }
117 } else {
118 Vec::new()
119 }
120 }
121 };
122
123 Ok(CodeGeneration::visitors(visitors))
124 }
125}
126
127impl From<Unreachable> for CodeGen {
128 fn from(val: Unreachable) -> Self {
129 CodeGen::Unreachable(val)
130 }
131}
132
133struct ExtractDeclarations<'a> {
134 stmts: &'a mut Vec<Stmt>,
135 in_nested_block_scope: bool,
136}
137
138impl VisitMut for ExtractDeclarations<'_> {
139 fn visit_mut_var_decl(&mut self, decl: &mut VarDecl) {
140 let VarDecl {
141 span,
142 kind,
143 declare,
144 decls,
145 ctxt,
146 } = decl;
147 if self.in_nested_block_scope && !matches!(kind, VarDeclKind::Var) {
148 return;
149 }
150 let mut idents = Vec::new();
151 for decl in take(decls) {
152 collect_idents(&decl.name, &mut idents);
153 }
154 let decls = idents
155 .into_iter()
156 .map(|ident| VarDeclarator {
157 span: ident.span,
158 name: Pat::Ident(BindingIdent {
159 id: ident,
160 type_ann: None,
161 }),
162 init: if matches!(kind, VarDeclKind::Const) {
163 Some(quote!("undefined" as Box<Expr>))
164 } else {
165 None
166 },
167 definite: false,
168 })
169 .collect();
170 self.stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
171 span: *span,
172 kind: *kind,
173 declare: *declare,
174 ctxt: *ctxt,
175 decls,
176 }))));
177 }
178
179 fn visit_mut_fn_decl(&mut self, decl: &mut FnDecl) {
180 let FnDecl {
181 declare,
182 ident,
183 function,
184 } = decl;
185 self.stmts.push(Stmt::Decl(Decl::Fn(FnDecl {
186 declare: *declare,
187 ident: ident.take(),
188 function: function.take(),
189 })));
190 }
191
192 fn visit_mut_constructor(&mut self, _: &mut swc_core::ecma::ast::Constructor) {
193 }
195
196 fn visit_mut_function(&mut self, _: &mut swc_core::ecma::ast::Function) {
197 }
199
200 fn visit_mut_getter_prop(&mut self, _: &mut swc_core::ecma::ast::GetterProp) {
201 }
203
204 fn visit_mut_setter_prop(&mut self, _: &mut swc_core::ecma::ast::SetterProp) {
205 }
207
208 fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {
209 }
211
212 fn visit_mut_class_decl(&mut self, decl: &mut ClassDecl) {
213 let ClassDecl { declare, ident, .. } = decl;
214 self.stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
215 span: ident.span,
216 declare: *declare,
217 decls: vec![VarDeclarator {
218 span: ident.span,
219 name: Pat::Ident(BindingIdent {
220 type_ann: None,
221 id: ident.clone(),
222 }),
223 init: None,
224 definite: false,
225 }],
226 kind: VarDeclKind::Let,
227 ..Default::default()
228 }))));
229 }
230
231 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
232 let old = self.in_nested_block_scope;
233 self.in_nested_block_scope = true;
234 n.visit_mut_children_with(self);
235 self.in_nested_block_scope = old;
236 }
237}
238
239fn collect_idents(pat: &Pat, idents: &mut Vec<Ident>) {
240 match pat {
241 Pat::Ident(ident) => {
242 idents.push(ident.id.clone());
243 }
244 Pat::Array(ArrayPat { elems, .. }) => {
245 for elem in elems.iter() {
246 if let Some(elem) = elem.as_ref() {
247 collect_idents(elem, idents);
248 }
249 }
250 }
251 Pat::Rest(RestPat { arg, .. }) => {
252 collect_idents(arg, idents);
253 }
254 Pat::Object(ObjectPat { props, .. }) => {
255 for prop in props.iter() {
256 match prop {
257 ObjectPatProp::KeyValue(KeyValuePatProp { value, .. }) => {
258 collect_idents(value, idents);
259 }
260 ObjectPatProp::Assign(AssignPatProp { key, .. }) => {
261 idents.push(key.id.clone());
262 }
263 ObjectPatProp::Rest(RestPat { arg, .. }) => {
264 collect_idents(arg, idents);
265 }
266 }
267 }
268 }
269 Pat::Assign(AssignPat { left, .. }) => {
270 collect_idents(left, idents);
271 }
272 Pat::Invalid(_) | Pat::Expr(_) => {
273 }
275 }
276}