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, ValueToString)]
182#[value_to_string("compiler reference {request}")]
183pub struct CompilerReference {
184 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
185 pub request: ResolvedVc<Request>,
186}
187
188#[turbo_tasks::value_impl]
189impl CompilerReference {
190 #[turbo_tasks::function]
191 pub fn new(
192 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
193 request: ResolvedVc<Request>,
194 ) -> Vc<Self> {
195 Self::cell(CompilerReference { origin, request })
196 }
197}
198
199#[turbo_tasks::value_impl]
200impl ModuleReference for CompilerReference {
201 #[turbo_tasks::function]
202 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
203 cjs_resolve(
204 *self.origin,
205 *self.request,
206 CommonJsReferenceSubType::Undefined,
207 None,
208 ResolveErrorMode::Error,
209 )
210 }
211}
212
213#[turbo_tasks::value]
214#[derive(Hash, Debug, ValueToString)]
215#[value_to_string("tsconfig extends {}", self.config.ident())]
216pub struct TsExtendsReference {
217 pub config: ResolvedVc<Box<dyn Source>>,
218}
219
220#[turbo_tasks::value_impl]
221impl TsExtendsReference {
222 #[turbo_tasks::function]
223 pub fn new(config: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
224 Self::cell(TsExtendsReference { config })
225 }
226}
227
228#[turbo_tasks::value_impl]
229impl ModuleReference for TsExtendsReference {
230 #[turbo_tasks::function]
231 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
232 Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
233 RawModule::new(*self.config).to_resolved().await?,
234 )))
235 }
236}
237
238#[turbo_tasks::value]
239#[derive(Hash, Debug, ValueToString)]
240#[value_to_string("tsconfig tsnode require {request}")]
241pub struct TsNodeRequireReference {
242 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
243 pub request: ResolvedVc<Request>,
244}
245
246#[turbo_tasks::value_impl]
247impl TsNodeRequireReference {
248 #[turbo_tasks::function]
249 pub fn new(
250 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
251 request: ResolvedVc<Request>,
252 ) -> Vc<Self> {
253 Self::cell(TsNodeRequireReference { origin, request })
254 }
255}
256
257#[turbo_tasks::value_impl]
258impl ModuleReference for TsNodeRequireReference {
259 #[turbo_tasks::function]
260 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
261 cjs_resolve(
262 *self.origin,
263 *self.request,
264 CommonJsReferenceSubType::Undefined,
265 None,
266 ResolveErrorMode::Error,
267 )
268 }
269}
270
271#[turbo_tasks::value]
272#[derive(Hash, Debug, ValueToString)]
273#[value_to_string("tsconfig types {request}")]
274pub struct TsConfigTypesReference {
275 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
276 pub request: ResolvedVc<Request>,
277}
278
279#[turbo_tasks::value_impl]
280impl TsConfigTypesReference {
281 #[turbo_tasks::function]
282 pub fn new(
283 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
284 request: ResolvedVc<Request>,
285 ) -> Vc<Self> {
286 Self::cell(TsConfigTypesReference { origin, request })
287 }
288}
289
290#[turbo_tasks::value_impl]
291impl ModuleReference for TsConfigTypesReference {
292 #[turbo_tasks::function]
293 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
294 type_resolve(*self.origin, *self.request)
295 }
296}