turbopack/module_options/
module_options_context.rs

1use std::fmt::Debug;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use turbo_esregex::EsRegex;
6use turbo_rcstr::{RcStr, rcstr};
7use turbo_tasks::{NonLocalValue, ResolvedVc, ValueDefault, Vc, trace::TraceRawVcs};
8use turbo_tasks_fs::{
9    FileSystemPath,
10    glob::{Glob, GlobOptions},
11};
12use turbopack_core::{
13    chunk::SourceMapsType, compile_time_info::CompileTimeInfo, condition::ContextCondition,
14    environment::Environment, resolve::options::ImportMapping,
15};
16use turbopack_ecmascript::{
17    AnalyzeMode, TreeShakingMode, TypeofWindow, references::esm::UrlRewriteBehavior,
18};
19pub use turbopack_mdx::MdxTransformOptions;
20use turbopack_node::{
21    execution_context::ExecutionContext,
22    transforms::{postcss::PostCssTransformOptions, webpack::WebpackLoaderItems},
23};
24
25use super::ModuleRule;
26use crate::module_options::RuleCondition;
27
28#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
29pub struct LoaderRuleItem {
30    pub loaders: ResolvedVc<WebpackLoaderItems>,
31    pub rename_as: Option<RcStr>,
32    pub condition: Option<ConditionItem>,
33}
34
35/// This is a list of instructions for the rule engine to process. The first element in each tuple
36/// is a glob to match against, and the second is a rule to execute if that glob matches.
37///
38/// This is not a map, since multiple rules can be configured for the same glob, and since execution
39/// order matters.
40#[derive(Default)]
41#[turbo_tasks::value(transparent)]
42pub struct WebpackRules(Vec<(RcStr, LoaderRuleItem)>);
43
44#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
45pub enum ConditionPath {
46    Glob(RcStr),
47    Regex(ResolvedVc<EsRegex>),
48}
49
50#[turbo_tasks::value(shared)]
51#[derive(Clone, Debug)]
52pub enum ConditionItem {
53    All(Box<[ConditionItem]>),
54    Any(Box<[ConditionItem]>),
55    Not(Box<ConditionItem>),
56    Builtin(RcStr),
57    Base {
58        path: Option<ConditionPath>,
59        content: Option<ResolvedVc<EsRegex>>,
60    },
61}
62
63#[turbo_tasks::value(shared)]
64#[derive(Clone, Debug)]
65pub struct WebpackLoadersOptions {
66    pub rules: ResolvedVc<WebpackRules>,
67    pub builtin_conditions: ResolvedVc<Box<dyn WebpackLoaderBuiltinConditionSet>>,
68    pub loader_runner_package: Option<ResolvedVc<ImportMapping>>,
69}
70
71pub enum WebpackLoaderBuiltinConditionSetMatch {
72    Matched,
73    Unmatched,
74    /// The given condition is not supported by the framework.
75    Invalid,
76}
77
78/// A collection of framework-provided conditions for user (or framework) specified loader rules
79/// ([`WebpackRules`]) to match against.
80#[turbo_tasks::value_trait]
81pub trait WebpackLoaderBuiltinConditionSet {
82    /// Determines if the string representation of this condition is in the set. If it's not valid,
83    /// an issue will be emitted as a collectible.
84    fn match_condition(&self, condition: &str) -> WebpackLoaderBuiltinConditionSetMatch;
85}
86
87/// A no-op implementation of `WebpackLoaderBuiltinConditionSet` that always returns
88/// `WebpackLoaderBuiltinConditionSetMatch::Invalid`.
89#[turbo_tasks::value]
90pub struct EmptyWebpackLoaderBuiltinConditionSet;
91
92#[turbo_tasks::value_impl]
93impl EmptyWebpackLoaderBuiltinConditionSet {
94    #[turbo_tasks::function]
95    fn new() -> Vc<Box<dyn WebpackLoaderBuiltinConditionSet>> {
96        Vc::upcast::<Box<dyn WebpackLoaderBuiltinConditionSet>>(
97            EmptyWebpackLoaderBuiltinConditionSet.cell(),
98        )
99    }
100}
101
102#[turbo_tasks::value_impl]
103impl WebpackLoaderBuiltinConditionSet for EmptyWebpackLoaderBuiltinConditionSet {
104    fn match_condition(&self, _condition: &str) -> WebpackLoaderBuiltinConditionSetMatch {
105        WebpackLoaderBuiltinConditionSetMatch::Invalid
106    }
107}
108
109/// The kind of decorators transform to use.
110/// [TODO]: might need bikeshed for the name (Ecma)
111#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
112pub enum DecoratorsKind {
113    Legacy,
114    Ecma,
115}
116
117/// Configuration options for the decorators transform.
118///
119/// This is not part of Typescript transform: while there are typescript
120/// specific transforms (legay decorators), there is an ecma decorator transform
121/// as well for the JS.
122#[turbo_tasks::value(shared)]
123#[derive(Default, Clone, Debug)]
124pub struct DecoratorsOptions {
125    pub decorators_kind: Option<DecoratorsKind>,
126    /// Option to control whether to emit decorator metadata.
127    /// (https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata)
128    /// This'll be applied only if `decorators_type` and
129    /// `enable_typescript_transform` is enabled.
130    pub emit_decorators_metadata: bool,
131    /// Mimic babel's `decorators.decoratorsBeforeExport` option.
132    /// This'll be applied only if `decorators_type` is enabled.
133    /// ref: https://github.com/swc-project/swc/blob/d4ebb5e6efbed0758f25e46e8f74d7c47ec6cb8f/crates/swc_ecma_parser/src/lib.rs#L327
134    /// [TODO]: this option is not actively being used currently.
135    pub decorators_before_export: bool,
136    pub use_define_for_class_fields: bool,
137}
138
139#[turbo_tasks::value_impl]
140impl ValueDefault for DecoratorsOptions {
141    #[turbo_tasks::function]
142    fn value_default() -> Vc<Self> {
143        Self::default().cell()
144    }
145}
146
147/// Subset of Typescript options configured via tsconfig.json or jsconfig.json,
148/// which affects the runtime transform output.
149#[turbo_tasks::value(shared)]
150#[derive(Default, Clone, Debug)]
151pub struct TypescriptTransformOptions {
152    pub use_define_for_class_fields: bool,
153}
154
155#[turbo_tasks::value_impl]
156impl ValueDefault for TypescriptTransformOptions {
157    #[turbo_tasks::function]
158    fn value_default() -> Vc<Self> {
159        Self::default().cell()
160    }
161}
162
163#[turbo_tasks::value(shared)]
164#[derive(Default, Clone, Debug)]
165pub struct JsxTransformOptions {
166    pub development: bool,
167    pub react_refresh: bool,
168    pub import_source: Option<RcStr>,
169    pub runtime: Option<RcStr>,
170}
171
172#[turbo_tasks::value(shared)]
173#[derive(Clone, Debug)]
174pub struct ExternalsTracingOptions {
175    /// The directory from which the bundled files will require the externals at runtime.
176    pub tracing_root: FileSystemPath,
177    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
178}
179
180#[turbo_tasks::value(shared)]
181#[derive(Clone, Default)]
182pub struct ModuleOptionsContext {
183    pub ecmascript: EcmascriptOptionsContext,
184    pub css: CssOptionsContext,
185
186    pub enable_postcss_transform: Option<ResolvedVc<PostCssTransformOptions>>,
187    pub enable_webpack_loaders: Option<ResolvedVc<WebpackLoadersOptions>>,
188    // [Note]: currently mdx, and mdx_rs have different configuration entrypoint from next.config.js,
189    // however we might want to unify them in the future.
190    pub enable_mdx: bool,
191    pub enable_mdx_rs: Option<ResolvedVc<MdxTransformOptions>>,
192
193    pub environment: Option<ResolvedVc<Environment>>,
194    pub execution_context: Option<ResolvedVc<ExecutionContext>>,
195    pub side_effect_free_packages: Option<ResolvedVc<Glob>>,
196    pub tree_shaking_mode: Option<TreeShakingMode>,
197
198    /// Generate (non-emitted) output assets for static assets and externals, to facilitate
199    /// generating a list of all non-bundled files that will be required at runtime.
200    pub enable_externals_tracing: Option<ResolvedVc<ExternalsTracingOptions>>,
201
202    /// If true, it stores the last successful parse result in state and keeps using it when
203    /// parsing fails. This is useful to keep the module graph structure intact when syntax errors
204    /// are temporarily introduced.
205    pub keep_last_successful_parse: bool,
206
207    /// Custom rules to be applied after all default rules.
208    pub module_rules: Vec<ModuleRule>,
209    /// A list of rules to use a different module option context for certain
210    /// context paths. The first matching is used.
211    pub rules: Vec<(ContextCondition, ResolvedVc<ModuleOptionsContext>)>,
212
213    /// Whether the modules in this context are never chunked/codegen-ed, but only used for
214    /// tracing.
215    pub analyze_mode: AnalyzeMode,
216
217    pub placeholder_for_future_extensions: (),
218}
219
220#[turbo_tasks::value(shared)]
221#[derive(Clone, Default)]
222pub struct EcmascriptOptionsContext {
223    // TODO this should just be handled via CompileTimeInfo FreeVarReferences, but then it
224    // (currently) wouldn't be possible to have different replacement values in user code vs
225    // node_modules.
226    pub enable_typeof_window_inlining: Option<TypeofWindow>,
227    pub enable_jsx: Option<ResolvedVc<JsxTransformOptions>>,
228    /// Follow type references and resolve declaration files in additional to
229    /// normal resolution.
230    pub enable_types: bool,
231    pub enable_typescript_transform: Option<ResolvedVc<TypescriptTransformOptions>>,
232    pub enable_decorators: Option<ResolvedVc<DecoratorsOptions>>,
233    pub esm_url_rewrite_behavior: Option<UrlRewriteBehavior>,
234    /// References to externals from ESM imports should use `import()` and make
235    /// async modules.
236    pub import_externals: bool,
237    /// Ignore very dynamic requests which doesn't have any static known part.
238    /// If false, they will reference the whole directory. If true, they won't
239    /// reference anything and lead to an runtime error instead.
240    pub ignore_dynamic_requests: bool,
241    /// Specifies how Source Maps are handled.
242    pub source_maps: SourceMapsType,
243
244    /// Whether to allow accessing exports info via `__webpack_exports_info__`.
245    pub enable_exports_info_inlining: bool,
246
247    // TODO should this be a part of Environment instead?
248    pub inline_helpers: bool,
249
250    /// Whether to infer side effect free modules via local analysis. Defaults to true.
251    pub infer_module_side_effects: bool,
252
253    pub placeholder_for_future_extensions: (),
254}
255
256#[turbo_tasks::value(shared)]
257#[derive(Clone, Default)]
258pub struct CssOptionsContext {
259    /// This skips `GlobalCss` and `ModuleCss` module assets from being
260    /// generated in the module graph, generating only `Css` module assets.
261    ///
262    /// This is useful for node-file-trace, which tries to emit all assets in
263    /// the module graph, but neither asset types can be emitted directly.
264    pub enable_raw_css: bool,
265
266    /// Specifies how Source Maps are handled.
267    pub source_maps: SourceMapsType,
268
269    /// Override the conditions for module CSS (doesn't have any effect if `enable_raw_css` is
270    /// true). By default (for `None`), it uses
271    /// `Any(ResourcePathEndsWith(".module.css"), ContentTypeStartsWith("text/css+module"))`
272    pub module_css_condition: Option<RuleCondition>,
273
274    pub placeholder_for_future_extensions: (),
275}
276
277#[turbo_tasks::value_impl]
278impl ValueDefault for ModuleOptionsContext {
279    #[turbo_tasks::function]
280    fn value_default() -> Vc<Self> {
281        Self::cell(Default::default())
282    }
283}
284
285#[turbo_tasks::function]
286pub async fn side_effect_free_packages_glob(
287    side_effect_free_packages: ResolvedVc<Vec<RcStr>>,
288) -> Result<Vc<Glob>> {
289    let side_effect_free_packages = &*side_effect_free_packages.await?;
290    if side_effect_free_packages.is_empty() {
291        return Ok(Glob::new(rcstr!(""), GlobOptions::default()));
292    }
293
294    let mut globs = String::new();
295    globs.push_str("**/node_modules/{");
296    globs.push_str(&side_effect_free_packages.join(","));
297    globs.push_str("}/**");
298
299    Ok(Glob::new(globs.into(), GlobOptions::default()))
300}