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