1use std::fmt::Display;
2
3use anyhow::{Result, bail};
4use bincode::{Decode, Encode};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{NonLocalValue, ResolvedVc};
7use turbo_tasks_fs::FileSystemPath;
8use turbopack_core::{
9 environment::Environment, reference_type::ReferenceType, source::Source,
10 source_transform::SourceTransforms,
11};
12use turbopack_css::CssModuleType;
13use turbopack_ecmascript::{
14 EcmascriptInputTransforms, EcmascriptOptions, bytes_source_transform::BytesSourceTransform,
15 json_source_transform::JsonSourceTransform, text_source_transform::TextSourceTransform,
16};
17use turbopack_wasm::source::WebAssemblySourceType;
18
19use crate::module_options::{CustomModuleType, RuleCondition, match_mode::MatchMode};
20
21#[derive(Debug, Clone, PartialEq, Eq, NonLocalValue, Encode, Decode)]
22pub struct ModuleRule {
23 condition: RuleCondition,
24 effects: Vec<ModuleRuleEffect>,
25 match_mode: MatchMode,
26}
27
28impl ModuleRule {
29 pub fn new(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
31 condition.flatten();
32 ModuleRule {
33 condition,
34 effects,
35 match_mode: MatchMode::NonInternal,
36 }
37 }
38
39 pub fn new_internal(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
41 condition.flatten();
42 ModuleRule {
43 condition,
44 effects,
45 match_mode: MatchMode::Internal,
46 }
47 }
48
49 pub fn new_all(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
51 condition.flatten();
52 ModuleRule {
53 condition,
54 effects,
55 match_mode: MatchMode::All,
56 }
57 }
58
59 pub fn effects(&self) -> impl Iterator<Item = &ModuleRuleEffect> {
60 self.effects.iter()
61 }
62
63 pub async fn matches(
64 &self,
65 source: ResolvedVc<Box<dyn Source>>,
66 path: &FileSystemPath,
67 reference_type: &ReferenceType,
68 ) -> Result<bool> {
69 Ok(self.match_mode.matches(reference_type)
70 && self.condition.matches(source, path, reference_type).await?)
71 }
72}
73
74#[turbo_tasks::value(shared)]
75#[derive(Debug, Clone)]
76pub enum ModuleRuleEffect {
77 ModuleType(ModuleType),
78 ExtendEcmascriptTransforms {
81 preprocess: ResolvedVc<EcmascriptInputTransforms>,
83 main: ResolvedVc<EcmascriptInputTransforms>,
85 postprocess: ResolvedVc<EcmascriptInputTransforms>,
87 },
88 SourceTransforms(ResolvedVc<SourceTransforms>),
89 Ignore,
90}
91
92#[turbo_tasks::value(shared)]
93#[derive(Hash, Debug, Clone)]
94pub enum ModuleType {
95 Ecmascript {
96 preprocess: ResolvedVc<EcmascriptInputTransforms>,
98 main: ResolvedVc<EcmascriptInputTransforms>,
100 postprocess: ResolvedVc<EcmascriptInputTransforms>,
102 options: ResolvedVc<EcmascriptOptions>,
103 },
104 Typescript {
105 preprocess: ResolvedVc<EcmascriptInputTransforms>,
107 main: ResolvedVc<EcmascriptInputTransforms>,
109 postprocess: ResolvedVc<EcmascriptInputTransforms>,
111 tsx: bool,
113 analyze_types: bool,
115 options: ResolvedVc<EcmascriptOptions>,
116 },
117 TypescriptDeclaration {
118 preprocess: ResolvedVc<EcmascriptInputTransforms>,
120 main: ResolvedVc<EcmascriptInputTransforms>,
122 postprocess: ResolvedVc<EcmascriptInputTransforms>,
124 options: ResolvedVc<EcmascriptOptions>,
125 },
126 EcmascriptExtensionless {
127 preprocess: ResolvedVc<EcmascriptInputTransforms>,
129 main: ResolvedVc<EcmascriptInputTransforms>,
131 postprocess: ResolvedVc<EcmascriptInputTransforms>,
133 options: ResolvedVc<EcmascriptOptions>,
134 },
135 Raw,
136 NodeAddon,
137 CssModule,
138 Css {
139 ty: CssModuleType,
140 environment: Option<ResolvedVc<Environment>>,
141 lightningcss_features: turbopack_css::LightningCssFeatureFlags,
142 module_css_debuggable_idents: bool,
143 },
144 StaticUrlJs {
145 tag: Option<RcStr>,
147 },
148 StaticUrlCss {
149 tag: Option<RcStr>,
151 },
152 WebAssembly {
153 source_ty: WebAssemblySourceType,
154 },
155 Custom(ResolvedVc<Box<dyn CustomModuleType>>),
156}
157
158impl Display for ModuleType {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 ModuleType::Ecmascript { .. } => write!(f, "Ecmascript"),
162 ModuleType::Typescript { .. } => write!(f, "Typescript"),
163 ModuleType::TypescriptDeclaration { .. } => write!(f, "TypescriptDeclaration"),
164 ModuleType::EcmascriptExtensionless { .. } => write!(f, "EcmascriptExtensionless"),
165 ModuleType::Raw => write!(f, "Raw"),
166 ModuleType::NodeAddon => write!(f, "NodeAddon"),
167 ModuleType::CssModule => write!(f, "CssModule"),
168 ModuleType::Css { .. } => write!(f, "Css"),
169 ModuleType::StaticUrlJs { .. } => write!(f, "StaticUrlJs"),
170 ModuleType::StaticUrlCss { .. } => write!(f, "StaticUrlCss"),
171 ModuleType::WebAssembly { .. } => write!(f, "WebAssembly"),
172 ModuleType::Custom(_) => write!(f, "Custom"),
173 }
174 }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
183pub enum ConfiguredModuleType {
184 Asset,
185 Ecmascript,
186 Typescript,
187 Css,
188 CssModule,
189 Json,
192 Wasm,
193 Raw,
195 Node,
196 Bytes,
199 Text,
204}
205
206impl ConfiguredModuleType {
207 pub fn parse(type_str: &str) -> Result<Self> {
209 Ok(match type_str {
210 "asset" => ConfiguredModuleType::Asset,
211 "ecmascript" => ConfiguredModuleType::Ecmascript,
212 "typescript" => ConfiguredModuleType::Typescript,
213 "css" => ConfiguredModuleType::Css,
214 "css-module" => ConfiguredModuleType::CssModule,
215 "json" => ConfiguredModuleType::Json,
216 "wasm" => ConfiguredModuleType::Wasm,
217 "raw" => ConfiguredModuleType::Raw,
218 "node" => ConfiguredModuleType::Node,
219 "bytes" => ConfiguredModuleType::Bytes,
220 "text" => ConfiguredModuleType::Text,
221 _ => bail!(
222 "Unknown module type: {type_str:?}. Valid types are: asset, ecmascript, \
223 typescript, css, css-module, json, wasm, raw, node, bytes, text"
224 ),
225 })
226 }
227
228 pub async fn into_effect(
233 self,
234 preprocess: ResolvedVc<EcmascriptInputTransforms>,
235 main: ResolvedVc<EcmascriptInputTransforms>,
236 postprocess: ResolvedVc<EcmascriptInputTransforms>,
237 options: ResolvedVc<EcmascriptOptions>,
238 environment: Option<ResolvedVc<Environment>>,
239 lightningcss_features: turbopack_css::LightningCssFeatureFlags,
240 ) -> Result<ModuleRuleEffect> {
241 Ok(match self {
242 ConfiguredModuleType::Bytes => {
243 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
246 BytesSourceTransform::new().to_resolved().await?,
247 )]))
248 }
249 ConfiguredModuleType::Text | ConfiguredModuleType::Raw => {
252 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
255 TextSourceTransform::new().to_resolved().await?,
256 )]))
257 }
258 ConfiguredModuleType::Asset => {
259 ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs { tag: None })
260 }
261 ConfiguredModuleType::Ecmascript => {
262 ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
263 preprocess,
264 main,
265 postprocess,
266 options,
267 })
268 }
269 ConfiguredModuleType::Typescript => {
270 ModuleRuleEffect::ModuleType(ModuleType::Typescript {
271 preprocess,
272 main,
273 postprocess,
274 tsx: false,
275 analyze_types: false,
276 options,
277 })
278 }
279 ConfiguredModuleType::Css => ModuleRuleEffect::ModuleType(ModuleType::Css {
280 ty: CssModuleType::Default,
281 environment,
282 lightningcss_features,
283 module_css_debuggable_idents: false,
285 }),
286 ConfiguredModuleType::CssModule => ModuleRuleEffect::ModuleType(ModuleType::CssModule),
287 ConfiguredModuleType::Json => {
288 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
289 JsonSourceTransform::new_cjs().to_resolved().await?,
291 )]))
292 }
293 ConfiguredModuleType::Wasm => ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
294 source_ty: WebAssemblySourceType::Binary,
295 }),
296 ConfiguredModuleType::Node => ModuleRuleEffect::ModuleType(ModuleType::NodeAddon),
297 })
298 }
299}