turbopack_ecmascript/
utils.rs1use serde::{Deserialize, Serialize};
2use swc_core::{
3 common::DUMMY_SP,
4 ecma::{
5 ast::{Expr, Lit, Str},
6 visit::AstParentKind,
7 },
8};
9use turbo_tasks::{NonLocalValue, trace::TraceRawVcs};
10use turbopack_core::{chunk::ModuleId, resolve::pattern::Pattern};
11
12use crate::analyzer::{
13 ConstantNumber, ConstantValue, JsValue, JsValueUrlKind, ModuleValue, WellKnownFunctionKind,
14 WellKnownObjectKind,
15};
16
17pub fn unparen(expr: &Expr) -> &Expr {
18 if let Some(expr) = expr.as_paren() {
19 return unparen(&expr.expr);
20 }
21 if let Expr::Seq(seq) = expr {
22 return unparen(seq.exprs.last().unwrap());
23 }
24 expr
25}
26
27pub fn js_value_to_pattern(value: &JsValue) -> Pattern {
28 let mut result = match value {
29 JsValue::Constant(v) => Pattern::Constant(match v {
30 ConstantValue::Str(str) => str.as_str().into(),
31 ConstantValue::True => "true".into(),
32 ConstantValue::False => "false".into(),
33 ConstantValue::Null => "null".into(),
34 ConstantValue::Num(ConstantNumber(n)) => n.to_string().into(),
35 ConstantValue::BigInt(n) => n.to_string().into(),
36 ConstantValue::Regex(box (exp, flags)) => format!("/{exp}/{flags}").into(),
37 ConstantValue::Undefined => "undefined".into(),
38 }),
39 JsValue::Url(v, JsValueUrlKind::Relative) => Pattern::Constant(v.as_str().into()),
40 JsValue::Alternatives {
41 total_nodes: _,
42 values,
43 logical_property: _,
44 } => Pattern::Alternatives(values.iter().map(js_value_to_pattern).collect()),
45 JsValue::Concat(_, parts) => {
46 Pattern::Concatenation(parts.iter().map(js_value_to_pattern).collect())
47 }
48 JsValue::Add(..) => {
49 Pattern::Dynamic
52 }
53 _ => Pattern::Dynamic,
54 };
55 result.normalize();
56 result
57}
58
59const JS_MAX_SAFE_INTEGER: u64 = (1u64 << 53) - 1;
60
61pub fn module_id_to_lit(module_id: &ModuleId) -> Expr {
62 Expr::Lit(match module_id {
63 ModuleId::Number(n) => {
64 if *n <= JS_MAX_SAFE_INTEGER {
65 Lit::Num((*n as f64).into())
66 } else {
67 Lit::Str(Str {
68 span: DUMMY_SP,
69 value: n.to_string().into(),
70 raw: None,
71 })
72 }
73 }
74 ModuleId::String(s) => Lit::Str(Str {
75 span: DUMMY_SP,
76 value: (s as &str).into(),
77 raw: None,
78 }),
79 })
80}
81
82pub struct StringifyModuleId<'a>(pub &'a ModuleId);
83
84impl std::fmt::Display for StringifyModuleId<'_> {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 match self.0 {
87 ModuleId::Number(n) => {
88 if *n <= JS_MAX_SAFE_INTEGER {
89 n.fmt(f)
90 } else {
91 write!(f, "\"{n}\"")
92 }
93 }
94 ModuleId::String(s) => StringifyJs(s).fmt(f),
95 }
96 }
97}
98
99pub struct StringifyJs<'a, T>(pub &'a T)
100where
101 T: ?Sized;
102
103impl<T> std::fmt::Display for StringifyJs<'_, T>
104where
105 T: Serialize + ?Sized,
106{
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 struct DisplayWriter<'a, 'b> {
111 f: &'a mut std::fmt::Formatter<'b>,
112 }
113
114 impl std::io::Write for DisplayWriter<'_, '_> {
115 fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
116 self.f
117 .write_str(std::str::from_utf8(bytes).map_err(std::io::Error::other)?)
118 .map_err(std::io::Error::other)?;
119 Ok(bytes.len())
120 }
121
122 fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
123 unreachable!()
124 }
125 }
126
127 let to_writer = match f.alternate() {
128 true => serde_json::to_writer_pretty,
129 false => serde_json::to_writer,
130 };
131
132 to_writer(DisplayWriter { f }, self.0).map_err(|_err| std::fmt::Error)
133 }
134}
135
136pub struct FormatIter<T: Iterator, F: Fn() -> T>(pub F);
137
138macro_rules! format_iter {
139 ($trait:path) => {
140 impl<T: Iterator, F: Fn() -> T> $trait for FormatIter<T, F>
141 where
142 T::Item: $trait,
143 {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 for item in self.0() {
146 item.fmt(f)?;
147 }
148 Ok(())
149 }
150 }
151 };
152}
153
154format_iter!(std::fmt::Binary);
155format_iter!(std::fmt::Debug);
156format_iter!(std::fmt::Display);
157format_iter!(std::fmt::LowerExp);
158format_iter!(std::fmt::LowerHex);
159format_iter!(std::fmt::Octal);
160format_iter!(std::fmt::Pointer);
161format_iter!(std::fmt::UpperExp);
162format_iter!(std::fmt::UpperHex);
163
164#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, Debug, NonLocalValue)]
165pub enum AstPathRange {
166 Exact(#[turbo_tasks(trace_ignore)] Vec<AstParentKind>),
168 StartAfter(#[turbo_tasks(trace_ignore)] Vec<AstParentKind>),
171}
172
173pub fn module_value_to_well_known_object(module_value: &ModuleValue) -> Option<JsValue> {
176 Some(match &*module_value.module {
177 "node:path" | "path" => JsValue::WellKnownObject(WellKnownObjectKind::PathModule),
178 "node:fs/promises" | "fs/promises" => {
179 JsValue::WellKnownObject(WellKnownObjectKind::FsModule)
180 }
181 "node:fs" | "fs" => JsValue::WellKnownObject(WellKnownObjectKind::FsModule),
182 "node:child_process" | "child_process" => {
183 JsValue::WellKnownObject(WellKnownObjectKind::ChildProcess)
184 }
185 "node:os" | "os" => JsValue::WellKnownObject(WellKnownObjectKind::OsModule),
186 "node:process" | "process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess),
187 "node-pre-gyp" | "@mapbox/node-pre-gyp" => {
188 JsValue::WellKnownObject(WellKnownObjectKind::NodePreGyp)
189 }
190 "node-gyp-build" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeGypBuild),
191 "node:bindings" | "bindings" => {
192 JsValue::WellKnownFunction(WellKnownFunctionKind::NodeBindings)
193 }
194 "express" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeExpress),
195 "strong-globalize" => {
196 JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalize)
197 }
198 "resolve-from" => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom),
199 "@grpc/proto-loader" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProtobufLoader),
200 _ => return None,
201 })
202}