1use std::fmt::Display;
2
3use anyhow::{Result, bail};
4use bincode::{Decode, Encode};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{NonLocalValue, ResolvedVc, trace::TraceRawVcs};
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, TraceRawVcs, 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 #[turbo_tasks(trace_ignore)]
103 options: ResolvedVc<EcmascriptOptions>,
104 },
105 Typescript {
106 preprocess: ResolvedVc<EcmascriptInputTransforms>,
108 main: ResolvedVc<EcmascriptInputTransforms>,
110 postprocess: ResolvedVc<EcmascriptInputTransforms>,
112 tsx: bool,
114 analyze_types: bool,
116 #[turbo_tasks(trace_ignore)]
117 options: ResolvedVc<EcmascriptOptions>,
118 },
119 TypescriptDeclaration {
120 preprocess: ResolvedVc<EcmascriptInputTransforms>,
122 main: ResolvedVc<EcmascriptInputTransforms>,
124 postprocess: ResolvedVc<EcmascriptInputTransforms>,
126 #[turbo_tasks(trace_ignore)]
127 options: ResolvedVc<EcmascriptOptions>,
128 },
129 EcmascriptExtensionless {
130 preprocess: ResolvedVc<EcmascriptInputTransforms>,
132 main: ResolvedVc<EcmascriptInputTransforms>,
134 postprocess: ResolvedVc<EcmascriptInputTransforms>,
136 #[turbo_tasks(trace_ignore)]
137 options: ResolvedVc<EcmascriptOptions>,
138 },
139 Raw,
140 NodeAddon,
141 CssModule,
142 Css {
143 ty: CssModuleType,
144 environment: Option<ResolvedVc<Environment>>,
145 lightningcss_features: turbopack_css::LightningCssFeatureFlags,
146 module_css_debuggable_idents: bool,
147 },
148 StaticUrlJs {
149 tag: Option<RcStr>,
151 },
152 StaticUrlCss {
153 tag: Option<RcStr>,
155 },
156 WebAssembly {
157 source_ty: WebAssemblySourceType,
158 },
159 Custom(ResolvedVc<Box<dyn CustomModuleType>>),
160}
161
162impl Display for ModuleType {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match self {
165 ModuleType::Ecmascript { .. } => write!(f, "Ecmascript"),
166 ModuleType::Typescript { .. } => write!(f, "Typescript"),
167 ModuleType::TypescriptDeclaration { .. } => write!(f, "TypescriptDeclaration"),
168 ModuleType::EcmascriptExtensionless { .. } => write!(f, "EcmascriptExtensionless"),
169 ModuleType::Raw => write!(f, "Raw"),
170 ModuleType::NodeAddon => write!(f, "NodeAddon"),
171 ModuleType::CssModule => write!(f, "CssModule"),
172 ModuleType::Css { .. } => write!(f, "Css"),
173 ModuleType::StaticUrlJs { .. } => write!(f, "StaticUrlJs"),
174 ModuleType::StaticUrlCss { .. } => write!(f, "StaticUrlCss"),
175 ModuleType::WebAssembly { .. } => write!(f, "WebAssembly"),
176 ModuleType::Custom(_) => write!(f, "Custom"),
177 }
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq)]
187pub enum ConfiguredModuleType {
188 Asset,
189 Ecmascript,
190 Typescript,
191 Css,
192 CssModule,
193 Json,
196 Wasm,
197 Raw,
199 Node,
200 Bytes,
203 Text,
208}
209
210impl ConfiguredModuleType {
211 pub fn parse(type_str: &str) -> Result<Self> {
213 Ok(match type_str {
214 "asset" => ConfiguredModuleType::Asset,
215 "ecmascript" => ConfiguredModuleType::Ecmascript,
216 "typescript" => ConfiguredModuleType::Typescript,
217 "css" => ConfiguredModuleType::Css,
218 "css-module" => ConfiguredModuleType::CssModule,
219 "json" => ConfiguredModuleType::Json,
220 "wasm" => ConfiguredModuleType::Wasm,
221 "raw" => ConfiguredModuleType::Raw,
222 "node" => ConfiguredModuleType::Node,
223 "bytes" => ConfiguredModuleType::Bytes,
224 "text" => ConfiguredModuleType::Text,
225 _ => bail!(
226 "Unknown module type: {type_str:?}. Valid types are: asset, ecmascript, \
227 typescript, css, css-module, json, wasm, raw, node, bytes, text"
228 ),
229 })
230 }
231
232 pub async fn into_effect(
237 self,
238 preprocess: ResolvedVc<EcmascriptInputTransforms>,
239 main: ResolvedVc<EcmascriptInputTransforms>,
240 postprocess: ResolvedVc<EcmascriptInputTransforms>,
241 options: ResolvedVc<EcmascriptOptions>,
242 environment: Option<ResolvedVc<Environment>>,
243 lightningcss_features: turbopack_css::LightningCssFeatureFlags,
244 ) -> Result<ModuleRuleEffect> {
245 Ok(match self {
246 ConfiguredModuleType::Bytes => {
247 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
250 BytesSourceTransform::new().to_resolved().await?,
251 )]))
252 }
253 ConfiguredModuleType::Text | ConfiguredModuleType::Raw => {
256 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
259 TextSourceTransform::new().to_resolved().await?,
260 )]))
261 }
262 ConfiguredModuleType::Asset => {
263 ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs { tag: None })
264 }
265 ConfiguredModuleType::Ecmascript => {
266 ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
267 preprocess,
268 main,
269 postprocess,
270 options,
271 })
272 }
273 ConfiguredModuleType::Typescript => {
274 ModuleRuleEffect::ModuleType(ModuleType::Typescript {
275 preprocess,
276 main,
277 postprocess,
278 tsx: false,
279 analyze_types: false,
280 options,
281 })
282 }
283 ConfiguredModuleType::Css => ModuleRuleEffect::ModuleType(ModuleType::Css {
284 ty: CssModuleType::Default,
285 environment,
286 lightningcss_features,
287 module_css_debuggable_idents: false,
289 }),
290 ConfiguredModuleType::CssModule => ModuleRuleEffect::ModuleType(ModuleType::CssModule),
291 ConfiguredModuleType::Json => {
292 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
293 JsonSourceTransform::new_cjs().to_resolved().await?,
295 )]))
296 }
297 ConfiguredModuleType::Wasm => ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
298 source_ty: WebAssemblySourceType::Binary,
299 }),
300 ConfiguredModuleType::Node => ModuleRuleEffect::ModuleType(ModuleType::NodeAddon),
301 })
302 }
303}