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,
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 },
147 StaticUrlJs {
148 tag: Option<RcStr>,
150 },
151 StaticUrlCss {
152 tag: Option<RcStr>,
154 },
155 WebAssembly {
156 source_ty: WebAssemblySourceType,
157 },
158 Custom(ResolvedVc<Box<dyn CustomModuleType>>),
159}
160
161impl Display for ModuleType {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match self {
164 ModuleType::Ecmascript { .. } => write!(f, "Ecmascript"),
165 ModuleType::Typescript { .. } => write!(f, "Typescript"),
166 ModuleType::TypescriptDeclaration { .. } => write!(f, "TypescriptDeclaration"),
167 ModuleType::EcmascriptExtensionless { .. } => write!(f, "EcmascriptExtensionless"),
168 ModuleType::Raw => write!(f, "Raw"),
169 ModuleType::NodeAddon => write!(f, "NodeAddon"),
170 ModuleType::CssModule => write!(f, "CssModule"),
171 ModuleType::Css { .. } => write!(f, "Css"),
172 ModuleType::StaticUrlJs { .. } => write!(f, "StaticUrlJs"),
173 ModuleType::StaticUrlCss { .. } => write!(f, "StaticUrlCss"),
174 ModuleType::WebAssembly { .. } => write!(f, "WebAssembly"),
175 ModuleType::Custom(_) => write!(f, "Custom"),
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
186pub enum ConfiguredModuleType {
187 Asset,
188 Ecmascript,
189 Typescript,
190 Css,
191 CssModule,
192 Json,
195 Wasm,
196 Raw,
197 Node,
198 Bytes,
201}
202
203impl ConfiguredModuleType {
204 pub fn parse(type_str: &str) -> Result<Self> {
206 Ok(match type_str {
207 "asset" => ConfiguredModuleType::Asset,
208 "ecmascript" => ConfiguredModuleType::Ecmascript,
209 "typescript" => ConfiguredModuleType::Typescript,
210 "css" => ConfiguredModuleType::Css,
211 "css-module" => ConfiguredModuleType::CssModule,
212 "json" => ConfiguredModuleType::Json,
213 "wasm" => ConfiguredModuleType::Wasm,
214 "raw" => ConfiguredModuleType::Raw,
215 "node" => ConfiguredModuleType::Node,
216 "bytes" => ConfiguredModuleType::Bytes,
217 _ => bail!(
218 "Unknown module type: {type_str:?}. Valid types are: asset, ecmascript, \
219 typescript, css, css-module, json, wasm, raw, node, bytes"
220 ),
221 })
222 }
223
224 pub async fn into_effect(
229 self,
230 preprocess: ResolvedVc<EcmascriptInputTransforms>,
231 main: ResolvedVc<EcmascriptInputTransforms>,
232 postprocess: ResolvedVc<EcmascriptInputTransforms>,
233 options: ResolvedVc<EcmascriptOptions>,
234 environment: Option<ResolvedVc<Environment>>,
235 lightningcss_features: turbopack_css::LightningCssFeatureFlags,
236 ) -> Result<ModuleRuleEffect> {
237 Ok(match self {
238 ConfiguredModuleType::Bytes => {
239 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
242 BytesSourceTransform::new().to_resolved().await?,
243 )]))
244 }
245 ConfiguredModuleType::Asset => {
246 ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs { tag: None })
247 }
248 ConfiguredModuleType::Ecmascript => {
249 ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
250 preprocess,
251 main,
252 postprocess,
253 options,
254 })
255 }
256 ConfiguredModuleType::Typescript => {
257 ModuleRuleEffect::ModuleType(ModuleType::Typescript {
258 preprocess,
259 main,
260 postprocess,
261 tsx: false,
262 analyze_types: false,
263 options,
264 })
265 }
266 ConfiguredModuleType::Css => ModuleRuleEffect::ModuleType(ModuleType::Css {
267 ty: CssModuleType::Default,
268 environment,
269 lightningcss_features,
270 }),
271 ConfiguredModuleType::CssModule => ModuleRuleEffect::ModuleType(ModuleType::CssModule),
272 ConfiguredModuleType::Json => {
273 ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![ResolvedVc::upcast(
274 JsonSourceTransform::new_cjs().to_resolved().await?,
276 )]))
277 }
278 ConfiguredModuleType::Wasm => ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
279 source_ty: WebAssemblySourceType::Binary,
280 }),
281 ConfiguredModuleType::Raw => ModuleRuleEffect::ModuleType(ModuleType::Raw),
282 ConfiguredModuleType::Node => ModuleRuleEffect::ModuleType(ModuleType::NodeAddon),
283 })
284 }
285}