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