turbopack_ecmascript/typescript/
mod.rs1use 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, ModuleSideEffects},
10 raw_module::RawModule,
11 reference::{ModuleReference, ModuleReferences},
12 reference_type::{CommonJsReferenceSubType, ReferenceType},
13 resolve::{ModuleResolveResult, origin::ResolveOrigin, parse::Request},
14 source::Source,
15};
16pub use turbopack_resolve::typescript as resolve;
18use turbopack_resolve::{
19 ecmascript::{apply_cjs_specific_options, cjs_resolve},
20 typescript::{read_from_tsconfigs, read_tsconfigs, type_resolve},
21};
22
23#[turbo_tasks::value]
24pub struct TsConfigModuleAsset {
25 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
26 pub source: ResolvedVc<Box<dyn Source>>,
27}
28
29#[turbo_tasks::value_impl]
30impl TsConfigModuleAsset {
31 #[turbo_tasks::function]
32 pub fn new(
33 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
34 source: ResolvedVc<Box<dyn Source>>,
35 ) -> Vc<Self> {
36 Self::cell(TsConfigModuleAsset { origin, source })
37 }
38}
39
40#[turbo_tasks::value_impl]
41impl Module for TsConfigModuleAsset {
42 #[turbo_tasks::function]
43 fn ident(&self) -> Vc<AssetIdent> {
44 self.source.ident()
45 }
46
47 #[turbo_tasks::function]
48 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
49 Vc::cell(Some(self.source))
50 }
51
52 #[turbo_tasks::function]
53 async fn references(&self) -> Result<Vc<ModuleReferences>> {
54 let mut references = Vec::new();
55 let configs = read_tsconfigs(
56 self.source.content().file_content(),
57 self.source,
58 apply_cjs_specific_options(
59 self.origin
60 .resolve_options(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)),
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 {
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 {
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.into(),
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 #[turbo_tasks::function]
178 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
179 ModuleSideEffects::SideEffectful.cell()
180 }
181}
182
183#[turbo_tasks::value_impl]
184impl Asset for TsConfigModuleAsset {
185 #[turbo_tasks::function]
186 fn content(&self) -> Vc<AssetContent> {
187 self.source.content()
188 }
189}
190
191#[turbo_tasks::value]
192#[derive(Hash, Debug)]
193pub struct CompilerReference {
194 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
195 pub request: ResolvedVc<Request>,
196}
197
198#[turbo_tasks::value_impl]
199impl CompilerReference {
200 #[turbo_tasks::function]
201 pub fn new(
202 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
203 request: ResolvedVc<Request>,
204 ) -> Vc<Self> {
205 Self::cell(CompilerReference { origin, request })
206 }
207}
208
209#[turbo_tasks::value_impl]
210impl ModuleReference for CompilerReference {
211 #[turbo_tasks::function]
212 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
213 cjs_resolve(
214 *self.origin,
215 *self.request,
216 CommonJsReferenceSubType::Undefined,
217 None,
218 false,
219 )
220 }
221}
222
223#[turbo_tasks::value_impl]
224impl ValueToString for CompilerReference {
225 #[turbo_tasks::function]
226 async fn to_string(&self) -> Result<Vc<RcStr>> {
227 Ok(Vc::cell(
228 format!("compiler reference {}", self.request.to_string().await?).into(),
229 ))
230 }
231}
232
233#[turbo_tasks::value]
234#[derive(Hash, Debug)]
235pub struct TsExtendsReference {
236 pub config: ResolvedVc<Box<dyn Source>>,
237}
238
239#[turbo_tasks::value_impl]
240impl TsExtendsReference {
241 #[turbo_tasks::function]
242 pub fn new(config: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
243 Self::cell(TsExtendsReference { config })
244 }
245}
246
247#[turbo_tasks::value_impl]
248impl ModuleReference for TsExtendsReference {
249 #[turbo_tasks::function]
250 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
251 Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
252 RawModule::new(*self.config).to_resolved().await?,
253 )))
254 }
255}
256
257#[turbo_tasks::value_impl]
258impl ValueToString for TsExtendsReference {
259 #[turbo_tasks::function]
260 async fn to_string(&self) -> Result<Vc<RcStr>> {
261 Ok(Vc::cell(
262 format!(
263 "tsconfig extends {}",
264 self.config.ident().to_string().await?,
265 )
266 .into(),
267 ))
268 }
269}
270
271#[turbo_tasks::value]
272#[derive(Hash, Debug)]
273pub struct TsNodeRequireReference {
274 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
275 pub request: ResolvedVc<Request>,
276}
277
278#[turbo_tasks::value_impl]
279impl TsNodeRequireReference {
280 #[turbo_tasks::function]
281 pub fn new(
282 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
283 request: ResolvedVc<Request>,
284 ) -> Vc<Self> {
285 Self::cell(TsNodeRequireReference { origin, request })
286 }
287}
288
289#[turbo_tasks::value_impl]
290impl ModuleReference for TsNodeRequireReference {
291 #[turbo_tasks::function]
292 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
293 cjs_resolve(
294 *self.origin,
295 *self.request,
296 CommonJsReferenceSubType::Undefined,
297 None,
298 false,
299 )
300 }
301}
302
303#[turbo_tasks::value_impl]
304impl ValueToString for TsNodeRequireReference {
305 #[turbo_tasks::function]
306 async fn to_string(&self) -> Result<Vc<RcStr>> {
307 Ok(Vc::cell(
308 format!(
309 "tsconfig tsnode require {}",
310 self.request.to_string().await?
311 )
312 .into(),
313 ))
314 }
315}
316
317#[turbo_tasks::value]
318#[derive(Hash, Debug)]
319pub struct TsConfigTypesReference {
320 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
321 pub request: ResolvedVc<Request>,
322}
323
324#[turbo_tasks::value_impl]
325impl TsConfigTypesReference {
326 #[turbo_tasks::function]
327 pub fn new(
328 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
329 request: ResolvedVc<Request>,
330 ) -> Vc<Self> {
331 Self::cell(TsConfigTypesReference { origin, request })
332 }
333}
334
335#[turbo_tasks::value_impl]
336impl ModuleReference for TsConfigTypesReference {
337 #[turbo_tasks::function]
338 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
339 type_resolve(*self.origin, *self.request)
340 }
341}
342
343#[turbo_tasks::value_impl]
344impl ValueToString for TsConfigTypesReference {
345 #[turbo_tasks::function]
346 async fn to_string(&self) -> Result<Vc<RcStr>> {
347 Ok(Vc::cell(
348 format!("tsconfig types {}", self.request.to_string().await?,).into(),
349 ))
350 }
351}