turbopack_ecmascript/webpack/
parse.rs1use anyhow::Result;
2use swc_core::ecma::ast::{
3 ArrowExpr, CallExpr, Callee, Expr, ExprStmt, FnExpr, Module, ModuleItem, Program, Script, Stmt,
4};
5use turbo_rcstr::rcstr;
6use turbo_tasks::Vc;
7use turbo_tasks_fs::FileSystemPath;
8use turbopack_core::{compile_time_info::CompileTimeInfo, source::Source};
9
10use crate::{
11 EcmascriptInputTransforms, EcmascriptModuleAssetType,
12 parse::{ParseResult, parse},
13 utils::unparen,
14};
15
16#[turbo_tasks::value(shared, serialization = "skip")]
17#[derive(Debug)]
18pub enum WebpackRuntime {
19 Webpack5 { context_path: FileSystemPath },
20 None,
21}
22
23fn iife(stmt: &Stmt) -> Option<&Vec<Stmt>> {
24 if let Stmt::Expr(ExprStmt { expr, .. }) = &stmt
25 && let Expr::Call(CallExpr {
26 callee: Callee::Expr(callee),
27 args,
28 ..
29 }) = unparen(expr)
30 {
31 if !args.is_empty() {
32 return None;
33 }
34 return get_fn_body(callee);
35 }
36 None
37}
38
39fn program_iife(program: &Program) -> Option<&Vec<Stmt>> {
40 match program {
41 Program::Module(Module { body, .. }) => {
42 if let [ModuleItem::Stmt(stmt)] = &body[..] {
43 return iife(stmt);
44 }
45 }
46 Program::Script(Script { body, .. }) => {
47 if let [stmt] = &body[..] {
48 return iife(stmt);
49 }
50 }
51 }
52 None
53}
54
55fn is_webpack_require_decl(stmt: &Stmt) -> bool {
56 if let Some(decl) = stmt.as_decl()
57 && let Some(fn_decl) = decl.as_fn_decl()
58 {
59 return &*fn_decl.ident.sym == "__webpack_require__";
60 }
61 false
62}
63
64fn get_fn_body(expr: &Expr) -> Option<&Vec<Stmt>> {
65 let expr = unparen(expr);
66 if let Some(FnExpr { function, .. }) = expr.as_fn_expr()
67 && let Some(body) = &function.body
68 {
69 return Some(&body.stmts);
70 }
71 if let Some(ArrowExpr { body, .. }) = expr.as_arrow()
72 && let Some(block) = body.as_block_stmt()
73 {
74 return Some(&block.stmts);
75 }
76 None
77}
78
79#[turbo_tasks::function]
80pub async fn webpack_runtime(
81 source: Vc<Box<dyn Source>>,
82 transforms: Vc<EcmascriptInputTransforms>,
83 compile_time_info: Vc<CompileTimeInfo>,
84) -> Result<Vc<WebpackRuntime>> {
85 let node_env = compile_time_info
86 .await?
87 .defines
88 .read_process_env(rcstr!("NODE_ENV"))
89 .owned()
90 .await?
91 .unwrap_or_else(|| rcstr!("development"));
92 let parsed = parse(
93 source,
94 EcmascriptModuleAssetType::Ecmascript,
95 transforms,
96 node_env,
97 false,
98 false,
99 )
100 .await?;
101 match &*parsed {
102 ParseResult::Ok { program, .. } => {
103 if let Some(stmts) = program_iife(program)
104 && stmts.iter().any(is_webpack_require_decl)
105 {
106 return Ok(WebpackRuntime::Webpack5 {
107 context_path: source.ident().await?.path.parent(),
108 }
109 .cell());
110 }
111 }
112 ParseResult::Unparsable { .. } | ParseResult::NotFound => {}
113 }
114 Ok(WebpackRuntime::None.cell())
115}