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