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