turbopack_ecmascript/typescript/
mod.rs

1use anyhow::Result;
2use serde_json::Value as JsonValue;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, TryJoinIterExt, ValueToString, Vc};
5use turbo_tasks_fs::DirectoryContent;
6use turbopack_core::{
7    asset::{Asset, AssetContent},
8    ident::AssetIdent,
9    module::Module,
10    raw_module::RawModule,
11    reference::{ModuleReference, ModuleReferences},
12    reference_type::{CommonJsReferenceSubType, ReferenceType},
13    resolve::{
14        ModuleResolveResult,
15        origin::{ResolveOrigin, ResolveOriginExt},
16        parse::Request,
17    },
18    source::Source,
19};
20// TODO remove this
21pub use turbopack_resolve::typescript as resolve;
22use turbopack_resolve::{
23    ecmascript::{apply_cjs_specific_options, cjs_resolve},
24    typescript::{read_from_tsconfigs, read_tsconfigs, type_resolve},
25};
26
27#[turbo_tasks::value]
28pub struct TsConfigModuleAsset {
29    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
30    pub source: ResolvedVc<Box<dyn Source>>,
31}
32
33#[turbo_tasks::value_impl]
34impl TsConfigModuleAsset {
35    #[turbo_tasks::function]
36    pub fn new(
37        origin: ResolvedVc<Box<dyn ResolveOrigin>>,
38        source: ResolvedVc<Box<dyn Source>>,
39    ) -> Vc<Self> {
40        Self::cell(TsConfigModuleAsset { origin, source })
41    }
42}
43
44#[turbo_tasks::value_impl]
45impl Module for TsConfigModuleAsset {
46    #[turbo_tasks::function]
47    fn ident(&self) -> Vc<AssetIdent> {
48        self.source.ident()
49    }
50
51    #[turbo_tasks::function]
52    async fn references(&self) -> Result<Vc<ModuleReferences>> {
53        let mut references = Vec::new();
54        let configs = read_tsconfigs(
55            self.source.content().file_content(),
56            self.source,
57            apply_cjs_specific_options(
58                self.origin
59                    .resolve_options(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined))
60                    .await?,
61            ),
62        )
63        .await?;
64        references.extend(
65            configs[1..]
66                .iter()
67                .map(|(_, config_asset)| async move {
68                    Ok(ResolvedVc::upcast(
69                        TsExtendsReference::new(**config_asset)
70                            .to_resolved()
71                            .await?,
72                    ))
73                })
74                .try_join()
75                .await?,
76        );
77
78        // ts-node options
79        {
80            let compiler = read_from_tsconfigs(&configs, |json, source| {
81                json["ts-node"]["compiler"]
82                    .as_str()
83                    .map(|s| (source, s.to_string()))
84            })
85            .await?;
86            let compiler = match compiler {
87                Some((_, c)) => RcStr::from(c),
88                None => rcstr!("typescript"),
89            };
90            references.push(ResolvedVc::upcast(
91                CompilerReference::new(*self.origin, Request::parse(compiler.into()))
92                    .to_resolved()
93                    .await?,
94            ));
95            let require = read_from_tsconfigs(&configs, |json, source| {
96                if let JsonValue::Array(array) = &json["ts-node"]["require"] {
97                    Some(
98                        array
99                            .iter()
100                            .filter_map(|name| name.as_str().map(|s| (source, RcStr::from(s))))
101                            .collect::<Vec<_>>(),
102                    )
103                } else {
104                    None
105                }
106            })
107            .await?;
108            if let Some(require) = require {
109                for (_, request) in require {
110                    references.push(ResolvedVc::upcast(
111                        TsNodeRequireReference::new(*self.origin, Request::parse(request.into()))
112                            .to_resolved()
113                            .await?,
114                    ));
115                }
116            }
117        }
118        // compilerOptions
119        {
120            let types = read_from_tsconfigs(&configs, |json, source| {
121                if let JsonValue::Array(array) = &json["compilerOptions"]["types"] {
122                    Some(
123                        array
124                            .iter()
125                            .filter_map(|name| name.as_str().map(|s| (source, RcStr::from(s))))
126                            .collect::<Vec<_>>(),
127                    )
128                } else {
129                    None
130                }
131            })
132            .await?;
133            let types = if let Some(types) = types {
134                types
135            } else {
136                let mut all_types = Vec::new();
137                let mut current = self.source.ident().path().await?.parent();
138                loop {
139                    if let DirectoryContent::Entries(entries) =
140                        &*current.join("node_modules/@types")?.read_dir().await?
141                    {
142                        all_types.extend(entries.iter().filter_map(|(name, _)| {
143                            if name.starts_with('.') {
144                                None
145                            } else {
146                                Some((self.source, name.clone()))
147                            }
148                        }));
149                    }
150                    let parent = current.parent();
151                    if parent == current {
152                        break;
153                    }
154                    current = parent;
155                }
156                all_types
157            };
158            for (_, name) in types {
159                references.push(ResolvedVc::upcast(
160                    TsConfigTypesReference::new(
161                        *self.origin,
162                        Request::module(
163                            name,
164                            RcStr::default().into(),
165                            RcStr::default(),
166                            RcStr::default(),
167                        ),
168                    )
169                    .to_resolved()
170                    .await?,
171                ));
172            }
173        }
174        Ok(Vc::cell(references))
175    }
176}
177
178#[turbo_tasks::value_impl]
179impl Asset for TsConfigModuleAsset {
180    #[turbo_tasks::function]
181    fn content(&self) -> Vc<AssetContent> {
182        self.source.content()
183    }
184}
185
186#[turbo_tasks::value]
187#[derive(Hash, Debug)]
188pub struct CompilerReference {
189    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
190    pub request: ResolvedVc<Request>,
191}
192
193#[turbo_tasks::value_impl]
194impl CompilerReference {
195    #[turbo_tasks::function]
196    pub fn new(
197        origin: ResolvedVc<Box<dyn ResolveOrigin>>,
198        request: ResolvedVc<Request>,
199    ) -> Vc<Self> {
200        Self::cell(CompilerReference { origin, request })
201    }
202}
203
204#[turbo_tasks::value_impl]
205impl ModuleReference for CompilerReference {
206    #[turbo_tasks::function]
207    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
208        cjs_resolve(*self.origin, *self.request, None, false)
209    }
210}
211
212#[turbo_tasks::value_impl]
213impl ValueToString for CompilerReference {
214    #[turbo_tasks::function]
215    async fn to_string(&self) -> Result<Vc<RcStr>> {
216        Ok(Vc::cell(
217            format!("compiler reference {}", self.request.to_string().await?).into(),
218        ))
219    }
220}
221
222#[turbo_tasks::value]
223#[derive(Hash, Debug)]
224pub struct TsExtendsReference {
225    pub config: ResolvedVc<Box<dyn Source>>,
226}
227
228#[turbo_tasks::value_impl]
229impl TsExtendsReference {
230    #[turbo_tasks::function]
231    pub fn new(config: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
232        Self::cell(TsExtendsReference { config })
233    }
234}
235
236#[turbo_tasks::value_impl]
237impl ModuleReference for TsExtendsReference {
238    #[turbo_tasks::function]
239    async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
240        Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
241            RawModule::new(*ResolvedVc::upcast(self.config))
242                .to_resolved()
243                .await?,
244        )))
245    }
246}
247
248#[turbo_tasks::value_impl]
249impl ValueToString for TsExtendsReference {
250    #[turbo_tasks::function]
251    async fn to_string(&self) -> Result<Vc<RcStr>> {
252        Ok(Vc::cell(
253            format!(
254                "tsconfig extends {}",
255                self.config.ident().to_string().await?,
256            )
257            .into(),
258        ))
259    }
260}
261
262#[turbo_tasks::value]
263#[derive(Hash, Debug)]
264pub struct TsNodeRequireReference {
265    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
266    pub request: ResolvedVc<Request>,
267}
268
269#[turbo_tasks::value_impl]
270impl TsNodeRequireReference {
271    #[turbo_tasks::function]
272    pub fn new(
273        origin: ResolvedVc<Box<dyn ResolveOrigin>>,
274        request: ResolvedVc<Request>,
275    ) -> Vc<Self> {
276        Self::cell(TsNodeRequireReference { origin, request })
277    }
278}
279
280#[turbo_tasks::value_impl]
281impl ModuleReference for TsNodeRequireReference {
282    #[turbo_tasks::function]
283    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
284        cjs_resolve(*self.origin, *self.request, None, false)
285    }
286}
287
288#[turbo_tasks::value_impl]
289impl ValueToString for TsNodeRequireReference {
290    #[turbo_tasks::function]
291    async fn to_string(&self) -> Result<Vc<RcStr>> {
292        Ok(Vc::cell(
293            format!(
294                "tsconfig tsnode require {}",
295                self.request.to_string().await?
296            )
297            .into(),
298        ))
299    }
300}
301
302#[turbo_tasks::value]
303#[derive(Hash, Debug)]
304pub struct TsConfigTypesReference {
305    pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
306    pub request: ResolvedVc<Request>,
307}
308
309#[turbo_tasks::value_impl]
310impl TsConfigTypesReference {
311    #[turbo_tasks::function]
312    pub fn new(
313        origin: ResolvedVc<Box<dyn ResolveOrigin>>,
314        request: ResolvedVc<Request>,
315    ) -> Vc<Self> {
316        Self::cell(TsConfigTypesReference { origin, request })
317    }
318}
319
320#[turbo_tasks::value_impl]
321impl ModuleReference for TsConfigTypesReference {
322    #[turbo_tasks::function]
323    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
324        type_resolve(*self.origin, *self.request)
325    }
326}
327
328#[turbo_tasks::value_impl]
329impl ValueToString for TsConfigTypesReference {
330    #[turbo_tasks::function]
331    async fn to_string(&self) -> Result<Vc<RcStr>> {
332        Ok(Vc::cell(
333            format!("tsconfig types {}", self.request.to_string().await?,).into(),
334        ))
335    }
336}