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,
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}