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,
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};
20pub 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 {
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
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(
209 *self.origin,
210 *self.request,
211 CommonJsReferenceSubType::Undefined,
212 None,
213 false,
214 )
215 }
216}
217
218#[turbo_tasks::value_impl]
219impl ValueToString for CompilerReference {
220 #[turbo_tasks::function]
221 async fn to_string(&self) -> Result<Vc<RcStr>> {
222 Ok(Vc::cell(
223 format!("compiler reference {}", self.request.to_string().await?).into(),
224 ))
225 }
226}
227
228#[turbo_tasks::value]
229#[derive(Hash, Debug)]
230pub struct TsExtendsReference {
231 pub config: ResolvedVc<Box<dyn Source>>,
232}
233
234#[turbo_tasks::value_impl]
235impl TsExtendsReference {
236 #[turbo_tasks::function]
237 pub fn new(config: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
238 Self::cell(TsExtendsReference { config })
239 }
240}
241
242#[turbo_tasks::value_impl]
243impl ModuleReference for TsExtendsReference {
244 #[turbo_tasks::function]
245 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
246 Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
247 RawModule::new(*ResolvedVc::upcast(self.config))
248 .to_resolved()
249 .await?,
250 )))
251 }
252}
253
254#[turbo_tasks::value_impl]
255impl ValueToString for TsExtendsReference {
256 #[turbo_tasks::function]
257 async fn to_string(&self) -> Result<Vc<RcStr>> {
258 Ok(Vc::cell(
259 format!(
260 "tsconfig extends {}",
261 self.config.ident().to_string().await?,
262 )
263 .into(),
264 ))
265 }
266}
267
268#[turbo_tasks::value]
269#[derive(Hash, Debug)]
270pub struct TsNodeRequireReference {
271 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
272 pub request: ResolvedVc<Request>,
273}
274
275#[turbo_tasks::value_impl]
276impl TsNodeRequireReference {
277 #[turbo_tasks::function]
278 pub fn new(
279 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
280 request: ResolvedVc<Request>,
281 ) -> Vc<Self> {
282 Self::cell(TsNodeRequireReference { origin, request })
283 }
284}
285
286#[turbo_tasks::value_impl]
287impl ModuleReference for TsNodeRequireReference {
288 #[turbo_tasks::function]
289 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
290 cjs_resolve(
291 *self.origin,
292 *self.request,
293 CommonJsReferenceSubType::Undefined,
294 None,
295 false,
296 )
297 }
298}
299
300#[turbo_tasks::value_impl]
301impl ValueToString for TsNodeRequireReference {
302 #[turbo_tasks::function]
303 async fn to_string(&self) -> Result<Vc<RcStr>> {
304 Ok(Vc::cell(
305 format!(
306 "tsconfig tsnode require {}",
307 self.request.to_string().await?
308 )
309 .into(),
310 ))
311 }
312}
313
314#[turbo_tasks::value]
315#[derive(Hash, Debug)]
316pub struct TsConfigTypesReference {
317 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
318 pub request: ResolvedVc<Request>,
319}
320
321#[turbo_tasks::value_impl]
322impl TsConfigTypesReference {
323 #[turbo_tasks::function]
324 pub fn new(
325 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
326 request: ResolvedVc<Request>,
327 ) -> Vc<Self> {
328 Self::cell(TsConfigTypesReference { origin, request })
329 }
330}
331
332#[turbo_tasks::value_impl]
333impl ModuleReference for TsConfigTypesReference {
334 #[turbo_tasks::function]
335 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
336 type_resolve(*self.origin, *self.request)
337 }
338}
339
340#[turbo_tasks::value_impl]
341impl ValueToString for TsConfigTypesReference {
342 #[turbo_tasks::function]
343 async fn to_string(&self) -> Result<Vc<RcStr>> {
344 Ok(Vc::cell(
345 format!("tsconfig types {}", self.request.to_string().await?,).into(),
346 ))
347 }
348}