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 ident::AssetIdent,
9 module::{Module, ModuleSideEffects},
10 raw_module::RawModule,
11 reference::{ModuleReference, ModuleReferences},
12 reference_type::CommonJsReferenceSubType,
13 resolve::{ModuleResolveResult, ResolveErrorMode, 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(self.origin.resolve_options()),
59 )
60 .await?;
61 references.extend(
62 configs[1..]
63 .iter()
64 .map(|(_, config_asset)| async move {
65 Ok(ResolvedVc::upcast(
66 TsExtendsReference::new(**config_asset)
67 .to_resolved()
68 .await?,
69 ))
70 })
71 .try_join()
72 .await?,
73 );
74
75 {
77 let compiler = read_from_tsconfigs(&configs, |json, source| {
78 json["ts-node"]["compiler"]
79 .as_str()
80 .map(|s| (source, s.to_string()))
81 })
82 .await?;
83 let compiler = match compiler {
84 Some((_, c)) => RcStr::from(c),
85 None => rcstr!("typescript"),
86 };
87 references.push(ResolvedVc::upcast(
88 CompilerReference::new(*self.origin, Request::parse(compiler.into()))
89 .to_resolved()
90 .await?,
91 ));
92 let require = read_from_tsconfigs(&configs, |json, source| {
93 if let JsonValue::Array(array) = &json["ts-node"]["require"] {
94 Some(
95 array
96 .iter()
97 .filter_map(|name| name.as_str().map(|s| (source, RcStr::from(s))))
98 .collect::<Vec<_>>(),
99 )
100 } else {
101 None
102 }
103 })
104 .await?;
105 if let Some(require) = require {
106 for (_, request) in require {
107 references.push(ResolvedVc::upcast(
108 TsNodeRequireReference::new(*self.origin, Request::parse(request.into()))
109 .to_resolved()
110 .await?,
111 ));
112 }
113 }
114 }
115 {
117 let types = read_from_tsconfigs(&configs, |json, source| {
118 if let JsonValue::Array(array) = &json["compilerOptions"]["types"] {
119 Some(
120 array
121 .iter()
122 .filter_map(|name| name.as_str().map(|s| (source, RcStr::from(s))))
123 .collect::<Vec<_>>(),
124 )
125 } else {
126 None
127 }
128 })
129 .await?;
130 let types = if let Some(types) = types {
131 types
132 } else {
133 let mut all_types = Vec::new();
134 let mut current = self.source.ident().path().await?.parent();
135 loop {
136 if let DirectoryContent::Entries(entries) =
137 &*current.join("node_modules/@types")?.read_dir().await?
138 {
139 all_types.extend(entries.iter().filter_map(|(name, _)| {
140 if name.starts_with('.') {
141 None
142 } else {
143 Some((self.source, name.clone()))
144 }
145 }));
146 }
147 let parent = current.parent();
148 if parent == current {
149 break;
150 }
151 current = parent;
152 }
153 all_types
154 };
155 for (_, name) in types {
156 references.push(ResolvedVc::upcast(
157 TsConfigTypesReference::new(
158 *self.origin,
159 Request::module(
160 name.into(),
161 RcStr::default().into(),
162 RcStr::default(),
163 RcStr::default(),
164 ),
165 )
166 .to_resolved()
167 .await?,
168 ));
169 }
170 }
171 Ok(Vc::cell(references))
172 }
173
174 #[turbo_tasks::function]
175 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
176 ModuleSideEffects::SideEffectful.cell()
177 }
178}
179
180#[turbo_tasks::value]
181#[derive(Hash, Debug)]
182pub struct CompilerReference {
183 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
184 pub request: ResolvedVc<Request>,
185}
186
187#[turbo_tasks::value_impl]
188impl CompilerReference {
189 #[turbo_tasks::function]
190 pub fn new(
191 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
192 request: ResolvedVc<Request>,
193 ) -> Vc<Self> {
194 Self::cell(CompilerReference { origin, request })
195 }
196}
197
198#[turbo_tasks::value_impl]
199impl ModuleReference for CompilerReference {
200 #[turbo_tasks::function]
201 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
202 cjs_resolve(
203 *self.origin,
204 *self.request,
205 CommonJsReferenceSubType::Undefined,
206 None,
207 ResolveErrorMode::Error,
208 )
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(*self.config).to_resolved().await?,
242 )))
243 }
244}
245
246#[turbo_tasks::value_impl]
247impl ValueToString for TsExtendsReference {
248 #[turbo_tasks::function]
249 async fn to_string(&self) -> Result<Vc<RcStr>> {
250 Ok(Vc::cell(
251 format!(
252 "tsconfig extends {}",
253 self.config.ident().to_string().await?,
254 )
255 .into(),
256 ))
257 }
258}
259
260#[turbo_tasks::value]
261#[derive(Hash, Debug)]
262pub struct TsNodeRequireReference {
263 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
264 pub request: ResolvedVc<Request>,
265}
266
267#[turbo_tasks::value_impl]
268impl TsNodeRequireReference {
269 #[turbo_tasks::function]
270 pub fn new(
271 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
272 request: ResolvedVc<Request>,
273 ) -> Vc<Self> {
274 Self::cell(TsNodeRequireReference { origin, request })
275 }
276}
277
278#[turbo_tasks::value_impl]
279impl ModuleReference for TsNodeRequireReference {
280 #[turbo_tasks::function]
281 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
282 cjs_resolve(
283 *self.origin,
284 *self.request,
285 CommonJsReferenceSubType::Undefined,
286 None,
287 ResolveErrorMode::Error,
288 )
289 }
290}
291
292#[turbo_tasks::value_impl]
293impl ValueToString for TsNodeRequireReference {
294 #[turbo_tasks::function]
295 async fn to_string(&self) -> Result<Vc<RcStr>> {
296 Ok(Vc::cell(
297 format!(
298 "tsconfig tsnode require {}",
299 self.request.to_string().await?
300 )
301 .into(),
302 ))
303 }
304}
305
306#[turbo_tasks::value]
307#[derive(Hash, Debug)]
308pub struct TsConfigTypesReference {
309 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
310 pub request: ResolvedVc<Request>,
311}
312
313#[turbo_tasks::value_impl]
314impl TsConfigTypesReference {
315 #[turbo_tasks::function]
316 pub fn new(
317 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
318 request: ResolvedVc<Request>,
319 ) -> Vc<Self> {
320 Self::cell(TsConfigTypesReference { origin, request })
321 }
322}
323
324#[turbo_tasks::value_impl]
325impl ModuleReference for TsConfigTypesReference {
326 #[turbo_tasks::function]
327 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
328 type_resolve(*self.origin, *self.request)
329 }
330}
331
332#[turbo_tasks::value_impl]
333impl ValueToString for TsConfigTypesReference {
334 #[turbo_tasks::function]
335 async fn to_string(&self) -> Result<Vc<RcStr>> {
336 Ok(Vc::cell(
337 format!("tsconfig types {}", self.request.to_string().await?,).into(),
338 ))
339 }
340}