Skip to main content

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    pub module_type: Option<RcStr>,
34}
35
36/// This is a list of instructions for the rule engine to process. The first element in each tuple
37/// is a glob to match against, and the second is a rule to execute if that glob matches.
38///
39/// This is not a map, since multiple rules can be configured for the same glob, and since execution
40/// order matters.
41#[derive(Default)]
42#[turbo_tasks::value(transparent)]
43pub struct WebpackRules(Vec<(RcStr, LoaderRuleItem)>);
44
45#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
46pub enum ConditionPath {
47    Glob(RcStr),
48    Regex(ResolvedVc<EsRegex>),
49}
50
51#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
52pub enum ConditionQuery {
53    Constant(RcStr),
54    Regex(ResolvedVc<EsRegex>),
55}
56
57#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
58pub enum ConditionContentType {
59    Glob(RcStr),
60    Regex(ResolvedVc<EsRegex>),
61}
62
63#[turbo_tasks::value(shared)]
64#[derive(Clone, Debug)]
65pub enum ConditionItem {
66    All(Box<[ConditionItem]>),
67    Any(Box<[ConditionItem]>),
68    Not(Box<ConditionItem>),
69    Builtin(RcStr),
70    Base {
71        path: Option<ConditionPath>,
72        content: Option<ResolvedVc<EsRegex>>,
73        query: Option<ConditionQuery>,
74        content_type: Option<ConditionContentType>,
75    },
76}
77
78#[turbo_tasks::value(shared)]
79#[derive(Clone, Debug)]
80pub struct WebpackLoadersOptions {
81    pub rules: ResolvedVc<WebpackRules>,
82    pub builtin_conditions: ResolvedVc<Box<dyn WebpackLoaderBuiltinConditionSet>>,
83    pub loader_runner_package: Option<ResolvedVc<ImportMapping>>,
84}
85
86pub enum WebpackLoaderBuiltinConditionSetMatch {
87    Matched,
88    Unmatched,
89    /// The given condition is not supported by the framework.
90    Invalid,
91}
92
93/// A collection of framework-provided conditions for user (or framework) specified loader rules
94/// ([`WebpackRules`]) to match against.
95#[turbo_tasks::value_trait]
96pub trait WebpackLoaderBuiltinConditionSet {
97    /// Determines if the string representation of this condition is in the set. If it's not valid,
98    /// an issue will be emitted as a collectible.
99    fn match_condition(&self, condition: &str) -> WebpackLoaderBuiltinConditionSetMatch;
100}
101
102/// A no-op implementation of `WebpackLoaderBuiltinConditionSet` that always returns
103/// `WebpackLoaderBuiltinConditionSetMatch::Invalid`.
104#[turbo_tasks::value]
105pub struct EmptyWebpackLoaderBuiltinConditionSet;
106
107#[turbo_tasks::value_impl]
108impl EmptyWebpackLoaderBuiltinConditionSet {
109    #[turbo_tasks::function]
110    fn new() -> Vc<Box<dyn WebpackLoaderBuiltinConditionSet>> {
111        Vc::upcast::<Box<dyn WebpackLoaderBuiltinConditionSet>>(
112            EmptyWebpackLoaderBuiltinConditionSet.cell(),
113        )
114    }
115}
116
117#[turbo_tasks::value_impl]
118impl WebpackLoaderBuiltinConditionSet for EmptyWebpackLoaderBuiltinConditionSet {
119    fn match_condition(&self, _condition: &str) -> WebpackLoaderBuiltinConditionSetMatch {
120        WebpackLoaderBuiltinConditionSetMatch::Invalid
121    }
122}
123
124/// The kind of ECMAScript class decorators transform to use.
125///
126/// TODO: might need bikeshed for the name (Ecma)
127#[derive(Clone, PartialEq, Eq, Debug, TraceRawVcs, NonLocalValue, Encode, Decode)]
128pub enum DecoratorsKind {
129    /// Enables the syntax and behavior of the modern [stage 3 proposal]. This is the recommended
130    /// transform with JavaScript or [TypeScript 5.0][ts5] or later.
131    ///
132    /// [stage 3 proposal]: https://github.com/tc39/proposal-decorators
133    /// [ts5]: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#differences-with-experimental-legacy-decorators
134    Ecma,
135
136    /// Enables the legacy class decorator syntax and behavior, as it was defined during the [stage
137    /// 1 proposal].
138    ///
139    /// This is the same as setting [`jsx.transform.legacyDecorator` in SWC][swc].
140    ///
141    /// This option exists for compatibility with the TypeScript compiler's legacy
142    /// `--experimentalDecorators` feature.
143    ///
144    /// [stage 1 proposal]: https://github.com/wycats/javascript-decorators/blob/e1bf8d41bfa2591d9/README.md
145    /// [swc]: https://swc.rs/docs/configuration/compilation#jsctransformlegacydecorator
146    Legacy,
147}
148
149/// Configuration for the ECMAScript class decorators transform.
150///
151/// This is not part of TypeScript transform. It can be used with or without TypeScript.
152///
153/// There is a [legacy TypeScript-specific transform][DecoratorsKind::Legacy] available for when
154/// decorators are used with TypeScript.
155#[turbo_tasks::value(shared)]
156#[derive(Default, Clone, Debug)]
157pub struct DecoratorsOptions {
158    pub decorators_kind: Option<DecoratorsKind>,
159    /// Option to control whether to [emit decorator metadata]. This will be applied only when
160    /// using [`DecoratorsKind::Legacy`].
161    ///
162    /// [emit decorator metadata]: https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata
163    pub emit_decorators_metadata: bool,
164    /// Mimic [Babel's `decorators.decoratorsBeforeExport` option][babel]. This'll be applied only
165    /// if `decorators_type` is enabled.
166    ///
167    /// TODO: this option is not currently used.
168    ///
169    /// Ref: <https://github.com/swc-project/swc/blob/d4ebb5e6efbed0/crates/swc_ecma_parser/src/lib.rs#L327>
170    ///
171    /// [babel]: https://babeljs.io/docs/babel-plugin-proposal-decorators#decoratorsbeforeexport
172    pub decorators_before_export: bool,
173    pub use_define_for_class_fields: bool,
174}
175
176#[turbo_tasks::value_impl]
177impl ValueDefault for DecoratorsOptions {
178    #[turbo_tasks::function]
179    fn value_default() -> Vc<Self> {
180        Self::default().cell()
181    }
182}
183
184/// Subset of Typescript options configured via tsconfig.json or jsconfig.json,
185/// which affects the runtime transform output.
186#[turbo_tasks::value(shared)]
187#[derive(Default, Clone, Debug)]
188pub struct TypescriptTransformOptions {
189    pub use_define_for_class_fields: bool,
190    pub verbatim_module_syntax: bool,
191}
192
193#[turbo_tasks::value_impl]
194impl ValueDefault for TypescriptTransformOptions {
195    #[turbo_tasks::function]
196    fn value_default() -> Vc<Self> {
197        Self::default().cell()
198    }
199}
200
201#[turbo_tasks::value(shared)]
202#[derive(Default, Clone, Debug)]
203pub struct JsxTransformOptions {
204    pub development: bool,
205    pub react_refresh: bool,
206    pub import_source: Option<RcStr>,
207    pub runtime: Option<RcStr>,
208}
209
210#[turbo_tasks::value(shared)]
211#[derive(Clone, Debug)]
212pub struct ExternalsTracingOptions {
213    /// The directory from which the bundled files will require the externals at runtime.
214    pub tracing_root: FileSystemPath,
215    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
216}
217
218#[turbo_tasks::value(shared)]
219#[derive(Clone, Default)]
220pub struct ModuleOptionsContext {
221    pub ecmascript: EcmascriptOptionsContext,
222    pub css: CssOptionsContext,
223
224    pub enable_postcss_transform: Option<ResolvedVc<PostCssTransformOptions>>,
225    pub enable_webpack_loaders: Option<ResolvedVc<WebpackLoadersOptions>>,
226    // [Note]: currently mdx, and mdx_rs have different configuration entrypoint from next.config.js,
227    // however we might want to unify them in the future.
228    pub enable_mdx: bool,
229    pub enable_mdx_rs: Option<ResolvedVc<MdxTransformOptions>>,
230
231    pub environment: Option<ResolvedVc<Environment>>,
232    pub execution_context: Option<ResolvedVc<ExecutionContext>>,
233    pub side_effect_free_packages: Option<ResolvedVc<Glob>>,
234    pub tree_shaking_mode: Option<TreeShakingMode>,
235
236    pub static_url_tag: Option<RcStr>,
237
238    /// Generate (non-emitted) output assets for static assets and externals, to facilitate
239    /// generating a list of all non-bundled files that will be required at runtime.
240    pub enable_externals_tracing: Option<ResolvedVc<ExternalsTracingOptions>>,
241
242    /// If true, it stores the last successful parse result in state and keeps using it when
243    /// parsing fails. This is useful to keep the module graph structure intact when syntax errors
244    /// are temporarily introduced.
245    pub keep_last_successful_parse: bool,
246
247    /// Custom rules to be applied after all default rules.
248    pub module_rules: Vec<ModuleRule>,
249    /// A list of rules to use a different module option context for certain
250    /// context paths. The first matching is used.
251    pub rules: Vec<(ContextCondition, ResolvedVc<ModuleOptionsContext>)>,
252
253    /// Whether the modules in this context are never chunked/codegen-ed, but only used for
254    /// tracing.
255    pub analyze_mode: AnalyzeMode,
256
257    pub placeholder_for_future_extensions: (),
258}
259
260#[turbo_tasks::value(shared)]
261#[derive(Clone, Default)]
262pub struct EcmascriptOptionsContext {
263    // TODO this should just be handled via CompileTimeInfo FreeVarReferences, but then it
264    // (currently) wouldn't be possible to have different replacement values in user code vs
265    // node_modules.
266    pub enable_typeof_window_inlining: Option<TypeofWindow>,
267    pub enable_jsx: Option<ResolvedVc<JsxTransformOptions>>,
268    /// Follow type references and resolve declaration files in additional to
269    /// normal resolution.
270    pub enable_types: bool,
271    pub enable_typescript_transform: Option<ResolvedVc<TypescriptTransformOptions>>,
272    pub enable_decorators: Option<ResolvedVc<DecoratorsOptions>>,
273    pub esm_url_rewrite_behavior: Option<UrlRewriteBehavior>,
274    /// References to externals from ESM imports should use `import()` and make
275    /// async modules.
276    pub import_externals: bool,
277    /// Ignore very dynamic requests which doesn't have any static known part.
278    /// If false, they will reference the whole directory. If true, they won't
279    /// reference anything and lead to an runtime error instead.
280    pub ignore_dynamic_requests: bool,
281    /// Specifies how Source Maps are handled.
282    pub source_maps: SourceMapsType,
283
284    /// Whether to allow accessing exports info via `__webpack_exports_info__`.
285    pub enable_exports_info_inlining: bool,
286
287    /// Whether to enable `import bytes from 'module' with { type: "bytes" }` syntax.
288    pub enable_import_as_bytes: bool,
289
290    /// Whether to enable `import text from 'module' with { type: "text" }` syntax.
291    pub enable_import_as_text: bool,
292
293    // TODO should this be a part of Environment instead?
294    pub inline_helpers: bool,
295
296    /// Whether to infer side effect free modules via local analysis. Defaults to true.
297    pub infer_module_side_effects: bool,
298
299    pub placeholder_for_future_extensions: (),
300}
301
302#[turbo_tasks::value(shared)]
303#[derive(Clone, Default)]
304pub struct CssOptionsContext {
305    /// This skips `GlobalCss` and `ModuleCss` module assets from being
306    /// generated in the module graph, generating only `Css` module assets.
307    ///
308    /// This is useful for node-file-trace, which tries to emit all assets in
309    /// the module graph, but neither asset types can be emitted directly.
310    pub enable_raw_css: bool,
311
312    /// Specifies how Source Maps are handled.
313    pub source_maps: SourceMapsType,
314
315    /// Override the conditions for module CSS (doesn't have any effect if `enable_raw_css` is
316    /// true). By default (for `None`), it uses
317    /// `Any(ResourcePathEndsWith(".module.css"), ContentTypeStartsWith("text/css+module"))`
318    pub module_css_condition: Option<RuleCondition>,
319
320    /// User-specified lightningcss feature flags (include/exclude bitmasks).
321    pub lightningcss_features: turbopack_css::LightningCssFeatureFlags,
322
323    pub placeholder_for_future_extensions: (),
324}
325
326#[turbo_tasks::value_impl]
327impl ValueDefault for ModuleOptionsContext {
328    #[turbo_tasks::function]
329    fn value_default() -> Vc<Self> {
330        Self::cell(Default::default())
331    }
332}
333
334#[turbo_tasks::function]
335pub async fn side_effect_free_packages_glob(
336    side_effect_free_packages: ResolvedVc<Vec<RcStr>>,
337) -> Result<Vc<Glob>> {
338    let side_effect_free_packages = &*side_effect_free_packages.await?;
339    if side_effect_free_packages.is_empty() {
340        return Ok(Glob::new(rcstr!(""), GlobOptions::default()));
341    }
342
343    let mut globs = String::new();
344    globs.push_str("**/node_modules/{");
345    globs.push_str(&side_effect_free_packages.join(","));
346    globs.push_str("}/**");
347
348    Ok(Glob::new(globs.into(), GlobOptions::default()))
349}