Skip to main content

turbopack/module_options/
module_rule.rs

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::CssModuleAssetType;
13use turbopack_ecmascript::{EcmascriptInputTransforms, EcmascriptOptions};
14use turbopack_wasm::source::WebAssemblySourceType;
15
16use crate::module_options::{CustomModuleType, RuleCondition, match_mode::MatchMode};
17
18#[derive(Debug, Clone, TraceRawVcs, PartialEq, Eq, NonLocalValue, Encode, Decode)]
19pub struct ModuleRule {
20    condition: RuleCondition,
21    effects: Vec<ModuleRuleEffect>,
22    match_mode: MatchMode,
23}
24
25impl ModuleRule {
26    /// Creates a new module rule. Will not match internal references.
27    pub fn new(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
28        condition.flatten();
29        ModuleRule {
30            condition,
31            effects,
32            match_mode: MatchMode::NonInternal,
33        }
34    }
35
36    /// Creates a new module rule. Will only match internal references.
37    pub fn new_internal(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
38        condition.flatten();
39        ModuleRule {
40            condition,
41            effects,
42            match_mode: MatchMode::Internal,
43        }
44    }
45
46    /// Creates a new module rule. Will match all references.
47    pub fn new_all(mut condition: RuleCondition, effects: Vec<ModuleRuleEffect>) -> Self {
48        condition.flatten();
49        ModuleRule {
50            condition,
51            effects,
52            match_mode: MatchMode::All,
53        }
54    }
55
56    pub fn effects(&self) -> impl Iterator<Item = &ModuleRuleEffect> {
57        self.effects.iter()
58    }
59
60    pub async fn matches(
61        &self,
62        source: ResolvedVc<Box<dyn Source>>,
63        path: &FileSystemPath,
64        reference_type: &ReferenceType,
65    ) -> Result<bool> {
66        Ok(self.match_mode.matches(reference_type)
67            && self.condition.matches(source, path, reference_type).await?)
68    }
69}
70
71#[turbo_tasks::value(shared)]
72#[derive(Debug, Clone)]
73pub enum ModuleRuleEffect {
74    ModuleType(ModuleType),
75    /// Allow to extend an existing Ecmascript module rules for the additional
76    /// transforms
77    ExtendEcmascriptTransforms {
78        /// Transforms to run first: transpile TypeScript, decorators, ...
79        preprocess: ResolvedVc<EcmascriptInputTransforms>,
80        /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ...
81        main: ResolvedVc<EcmascriptInputTransforms>,
82        /// Transforms to run last: JSX, preset-env, scan for imports, ...
83        postprocess: ResolvedVc<EcmascriptInputTransforms>,
84    },
85    SourceTransforms(ResolvedVc<SourceTransforms>),
86    Ignore,
87}
88
89#[turbo_tasks::value(shared)]
90#[derive(Hash, Debug, Clone)]
91pub enum ModuleType {
92    Ecmascript {
93        /// Transforms to run first: transpile TypeScript, decorators, ...
94        preprocess: ResolvedVc<EcmascriptInputTransforms>,
95        /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ...
96        main: ResolvedVc<EcmascriptInputTransforms>,
97        /// Transforms to run last: JSX, preset-env, scan for imports, ...
98        postprocess: ResolvedVc<EcmascriptInputTransforms>,
99        #[turbo_tasks(trace_ignore)]
100        options: ResolvedVc<EcmascriptOptions>,
101    },
102    Typescript {
103        /// Transforms to run first: transpile TypeScript, decorators, ...
104        preprocess: ResolvedVc<EcmascriptInputTransforms>,
105        /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ...
106        main: ResolvedVc<EcmascriptInputTransforms>,
107        /// Transforms to run last: JSX, preset-env, scan for imports, ...
108        postprocess: ResolvedVc<EcmascriptInputTransforms>,
109        // parse JSX syntax.
110        tsx: bool,
111        // follow references to imported types.
112        analyze_types: bool,
113        #[turbo_tasks(trace_ignore)]
114        options: ResolvedVc<EcmascriptOptions>,
115    },
116    TypescriptDeclaration {
117        /// Transforms to run first: transpile TypeScript, decorators, ...
118        preprocess: ResolvedVc<EcmascriptInputTransforms>,
119        /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ...
120        main: ResolvedVc<EcmascriptInputTransforms>,
121        /// Transforms to run last: JSX, preset-env, scan for imports, ...
122        postprocess: ResolvedVc<EcmascriptInputTransforms>,
123        #[turbo_tasks(trace_ignore)]
124        options: ResolvedVc<EcmascriptOptions>,
125    },
126    EcmascriptExtensionless {
127        /// Transforms to run first: transpile TypeScript, decorators, ...
128        preprocess: ResolvedVc<EcmascriptInputTransforms>,
129        /// Transforms to execute on standard EcmaScript (plus JSX): styled-jsx, swc plugins, ...
130        main: ResolvedVc<EcmascriptInputTransforms>,
131        /// Transforms to run last: JSX, preset-env, scan for imports, ...
132        postprocess: ResolvedVc<EcmascriptInputTransforms>,
133        #[turbo_tasks(trace_ignore)]
134        options: ResolvedVc<EcmascriptOptions>,
135    },
136    Json,
137    Raw,
138    NodeAddon,
139    CssModule,
140    Css {
141        ty: CssModuleAssetType,
142        environment: Option<ResolvedVc<Environment>>,
143    },
144    StaticUrlJs {
145        /// The tag that is passed to ChunkingContext::asset_url
146        tag: Option<RcStr>,
147    },
148    StaticUrlCss {
149        /// The tag that is passed to ChunkingContext::asset_url
150        tag: Option<RcStr>,
151    },
152    InlinedBytesJs,
153    WebAssembly {
154        source_ty: WebAssemblySourceType,
155    },
156    Custom(ResolvedVc<Box<dyn CustomModuleType>>),
157}
158
159impl Display for ModuleType {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        match self {
162            ModuleType::Ecmascript { .. } => write!(f, "Ecmascript"),
163            ModuleType::Typescript { .. } => write!(f, "Typescript"),
164            ModuleType::TypescriptDeclaration { .. } => write!(f, "TypescriptDeclaration"),
165            ModuleType::EcmascriptExtensionless { .. } => write!(f, "EcmascriptExtensionless"),
166            ModuleType::Json => write!(f, "Json"),
167            ModuleType::Raw => write!(f, "Raw"),
168            ModuleType::NodeAddon => write!(f, "NodeAddon"),
169            ModuleType::CssModule => write!(f, "CssModule"),
170            ModuleType::Css { .. } => write!(f, "Css"),
171            ModuleType::StaticUrlJs { .. } => write!(f, "StaticUrlJs"),
172            ModuleType::StaticUrlCss { .. } => write!(f, "StaticUrlCss"),
173            ModuleType::InlinedBytesJs => write!(f, "InlinedBytesJs"),
174            ModuleType::WebAssembly { .. } => write!(f, "WebAssembly"),
175            ModuleType::Custom(_) => write!(f, "Custom"),
176        }
177    }
178}
179
180impl ModuleType {
181    /// Creates a ModuleType from a string identifier.
182    /// This is used for user-configured module types in turbopack rules.
183    pub fn from_str_with_defaults(
184        type_str: &str,
185        preprocess: ResolvedVc<EcmascriptInputTransforms>,
186        main: ResolvedVc<EcmascriptInputTransforms>,
187        postprocess: ResolvedVc<EcmascriptInputTransforms>,
188        options: ResolvedVc<EcmascriptOptions>,
189        environment: Option<ResolvedVc<Environment>>,
190    ) -> Result<Self> {
191        Ok(match type_str {
192            "asset" => ModuleType::StaticUrlJs { tag: None },
193            "ecmascript" => ModuleType::Ecmascript {
194                preprocess,
195                main,
196                postprocess,
197                options,
198            },
199            "typescript" => ModuleType::Typescript {
200                preprocess,
201                main,
202                postprocess,
203                tsx: false,
204                analyze_types: false,
205                options,
206            },
207            "css" => ModuleType::Css {
208                ty: CssModuleAssetType::Default,
209                environment,
210            },
211            "css-module" => ModuleType::CssModule,
212            "wasm" => ModuleType::WebAssembly {
213                source_ty: WebAssemblySourceType::Binary,
214            },
215            "raw" => ModuleType::Raw,
216            "node" => ModuleType::NodeAddon,
217            "bytes" => ModuleType::InlinedBytesJs,
218            _ => bail!(
219                "Unknown module type: {type_str:?}. Valid types are: asset, ecmascript, \
220                 typescript, css, css-module, wasm, raw, node, bytes"
221            ),
222        })
223    }
224}