1use std::time::Duration;
2
3use anyhow::{Context, Result, bail};
4use async_trait::async_trait;
5use bincode::{Decode, Encode};
6use either::Either;
7use rustc_hash::FxHashSet;
8use serde::{Deserialize, Deserializer, Serialize};
9use serde_json::Value as JsonValue;
10use turbo_esregex::{EsRegex, EsRegexSet};
11use turbo_rcstr::{RcStr, rcstr};
12use turbo_tasks::{
13 FxIndexMap, NonLocalValue, OperationValue, ResolvedVc, TryJoinIterExt, Vc,
14 debug::ValueDebugFormat, trace::TraceRawVcs,
15};
16use turbo_tasks_env::EnvMap;
17use turbo_tasks_fetch::FetchClientConfig;
18use turbo_tasks_fs::{
19 FileSystemPath,
20 glob::{Glob, GlobOptions},
21};
22use turbopack::module_options::{
23 ConditionContentType, ConditionItem, ConditionPath, ConditionQuery, LoaderRuleItem,
24 WebpackRules, module_options_context::MdxTransformOptions,
25};
26use turbopack_core::{
27 chunk::{CrossOrigin, SourceMapsType},
28 issue::{
29 IgnoreIssue, IgnoreIssuePattern, Issue, IssueExt, IssueSeverity, IssueStage, StyledString,
30 },
31 module_graph::{chunk_group_info::EntryHeuristics, style_groups::StyleGroupsAlgorithm},
32 resolve::ResolveAliasMap,
33};
34use turbopack_ecmascript::transform::{
35 OptionReactCompilerCompilationMode, ReactCompilerCompilationMode, ReactCompilerTarget,
36};
37use turbopack_ecmascript_plugins::transform::{
38 emotion::EmotionTransformConfig, relay::RelayConfig,
39 styled_components::StyledComponentsTransformConfig,
40};
41use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems};
42
43use crate::{
44 app_structure::FileSystemPathVec,
45 mode::NextMode,
46 next_import_map::mdx_import_source_file,
47 next_shared::{
48 transforms::ModularizeImportPackageConfig, webpack_rules::WebpackLoaderBuiltinCondition,
49 },
50 util::relativize_glob,
51};
52
53pub const DIST_PROFILES_DIR_NAME: &str = ".next-profiles";
57
58#[turbo_tasks::value(transparent)]
59pub struct ModularizeImports(
60 #[bincode(with = "turbo_bincode::indexmap")] FxIndexMap<String, ModularizeImportPackageConfig>,
61);
62
63#[turbo_tasks::value(transparent)]
64#[derive(Clone, Debug)]
65pub struct CacheKinds(FxHashSet<RcStr>);
66
67impl CacheKinds {
68 pub fn extend<I: IntoIterator<Item = RcStr>>(&mut self, iter: I) {
69 self.0.extend(iter);
70 }
71}
72
73impl Default for CacheKinds {
74 fn default() -> Self {
75 CacheKinds(
76 ["default", "remote", "private"]
77 .iter()
78 .map(|&s| s.into())
79 .collect(),
80 )
81 }
82}
83
84#[turbo_tasks::value(transparent)]
85pub struct CacheHandlersMap(#[bincode(with = "turbo_bincode::indexmap")] FxIndexMap<RcStr, RcStr>);
86
87#[turbo_tasks::value(eq = "manual")]
88#[derive(Clone, Debug, Default, PartialEq, Deserialize)]
89#[serde(default, rename_all = "camelCase")]
90pub struct NextConfig {
91 config_file: Option<RcStr>,
94 config_file_name: RcStr,
95
96 cache_max_memory_size: Option<f64>,
100 cache_handler: Option<RcStr>,
102 #[bincode(with_serde)]
103 cache_handlers: Option<FxIndexMap<RcStr, RcStr>>,
104 #[bincode(with = "turbo_bincode::serde_self_describing")]
105 env: FxIndexMap<String, JsonValue>,
106 experimental: ExperimentalConfig,
107 images: ImageConfig,
108 page_extensions: Vec<RcStr>,
109 instrumentation_client_inject: Option<Vec<RcStr>>,
110 react_compiler: Option<ReactCompilerOptionsOrBoolean>,
111 react_production_profiling: Option<bool>,
112 react_strict_mode: Option<bool>,
113 transpile_packages: Option<Vec<RcStr>>,
114 #[bincode(with = "turbo_bincode::serde_self_describing")]
115 modularize_imports: Option<FxIndexMap<String, ModularizeImportPackageConfig>>,
116 dist_dir: RcStr,
117 dist_dir_root: RcStr,
118 deployment_id: Option<RcStr>,
119 #[bincode(with = "turbo_bincode::serde_self_describing")]
120 sass_options: Option<serde_json::Value>,
121 trailing_slash: Option<bool>,
122 asset_prefix: Option<RcStr>,
123 base_path: Option<RcStr>,
124 skip_proxy_url_normalize: Option<bool>,
125 skip_trailing_slash_redirect: Option<bool>,
126 i18n: Option<I18NConfig>,
127 cross_origin: CrossOrigin,
128 dev_indicators: Option<DevIndicatorsConfig>,
129 output: Option<OutputType>,
130 turbopack: Option<TurbopackConfig>,
131 production_browser_source_maps: bool,
132 #[bincode(with = "turbo_bincode::serde_self_describing")]
133 output_file_tracing_includes: Option<serde_json::Value>,
134 #[bincode(with = "turbo_bincode::serde_self_describing")]
135 output_file_tracing_excludes: Option<serde_json::Value>,
136 output_file_tracing_root: Option<RcStr>,
138
139 bundle_pages_router_dependencies: Option<bool>,
144
145 server_external_packages: Option<Vec<RcStr>>,
150
151 output_hash_salt: Option<RcStr>,
154
155 #[serde(rename = "_originalRedirects")]
156 original_redirects: Option<Vec<Redirect>>,
157
158 compiler: Option<CompilerConfig>,
160
161 optimize_fonts: Option<bool>,
162
163 clean_dist_dir: bool,
164 compress: bool,
165 eslint: EslintConfig,
166 exclude_default_moment_locales: bool,
167 generate_etags: bool,
168 http_agent_options: HttpAgentConfig,
169 on_demand_entries: OnDemandEntriesConfig,
170 powered_by_header: bool,
171 #[bincode(with = "turbo_bincode::serde_self_describing")]
172 public_runtime_config: FxIndexMap<String, serde_json::Value>,
173 #[bincode(with = "turbo_bincode::serde_self_describing")]
174 server_runtime_config: FxIndexMap<String, serde_json::Value>,
175 static_page_generation_timeout: f64,
176 target: Option<String>,
177 typescript: TypeScriptConfig,
178 use_file_system_public_routes: bool,
179 cache_components: Option<bool>,
180 supports_immutable_assets: Option<bool>,
181
182 adapter_path: Option<RcStr>,
183 }
190
191#[turbo_tasks::value_impl]
192impl NextConfig {
193 #[turbo_tasks::function]
194 pub fn with_analyze_config(&self) -> Vc<Self> {
195 let mut new = self.clone();
196 new.experimental.turbopack_source_maps = Some(true);
197 new.experimental.turbopack_input_source_maps = Some(false);
198 new.cell()
199 }
200}
201
202#[derive(
203 Clone,
204 Debug,
205 Default,
206 PartialEq,
207 Deserialize,
208 TraceRawVcs,
209 NonLocalValue,
210 OperationValue,
211 Encode,
212 Decode,
213)]
214#[serde(rename_all = "camelCase")]
215struct EslintConfig {
216 dirs: Option<Vec<String>>,
217 ignore_during_builds: Option<bool>,
218}
219
220#[derive(
221 Clone,
222 Debug,
223 Default,
224 PartialEq,
225 Deserialize,
226 TraceRawVcs,
227 NonLocalValue,
228 OperationValue,
229 Encode,
230 Decode,
231)]
232#[serde(rename_all = "kebab-case")]
233pub enum BuildActivityPositions {
234 #[default]
235 BottomRight,
236 BottomLeft,
237 TopRight,
238 TopLeft,
239}
240
241#[derive(
242 Clone,
243 Debug,
244 Default,
245 PartialEq,
246 Deserialize,
247 TraceRawVcs,
248 NonLocalValue,
249 OperationValue,
250 Encode,
251 Decode,
252)]
253#[serde(rename_all = "camelCase")]
254pub struct DevIndicatorsOptions {
255 pub build_activity_position: Option<BuildActivityPositions>,
256 pub position: Option<BuildActivityPositions>,
257}
258
259#[derive(
260 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
261)]
262#[serde(untagged)]
263pub enum DevIndicatorsConfig {
264 WithOptions(DevIndicatorsOptions),
265 Boolean(bool),
266}
267
268#[derive(
269 Clone,
270 Debug,
271 Default,
272 PartialEq,
273 Deserialize,
274 TraceRawVcs,
275 NonLocalValue,
276 OperationValue,
277 Encode,
278 Decode,
279)]
280#[serde(rename_all = "camelCase")]
281struct OnDemandEntriesConfig {
282 max_inactive_age: f64,
283 pages_buffer_length: f64,
284}
285
286#[derive(
287 Clone,
288 Debug,
289 Default,
290 PartialEq,
291 Deserialize,
292 TraceRawVcs,
293 NonLocalValue,
294 OperationValue,
295 Encode,
296 Decode,
297)]
298#[serde(rename_all = "camelCase")]
299struct HttpAgentConfig {
300 keep_alive: bool,
301}
302
303#[derive(
304 Clone,
305 Debug,
306 PartialEq,
307 Eq,
308 Deserialize,
309 TraceRawVcs,
310 NonLocalValue,
311 OperationValue,
312 Encode,
313 Decode,
314)]
315#[serde(rename_all = "camelCase")]
316pub struct DomainLocale {
317 pub default_locale: String,
318 pub domain: String,
319 pub http: Option<bool>,
320 pub locales: Option<Vec<String>>,
321}
322
323#[derive(
324 Clone,
325 Debug,
326 PartialEq,
327 Eq,
328 Deserialize,
329 TraceRawVcs,
330 NonLocalValue,
331 OperationValue,
332 Encode,
333 Decode,
334)]
335#[serde(rename_all = "camelCase")]
336pub struct I18NConfig {
337 pub default_locale: String,
338 pub domains: Option<Vec<DomainLocale>>,
339 pub locale_detection: Option<bool>,
340 pub locales: Vec<String>,
341}
342
343#[turbo_tasks::value(transparent)]
344pub struct OptionI18NConfig(Option<I18NConfig>);
345
346#[derive(
347 Clone,
348 Debug,
349 PartialEq,
350 Eq,
351 Deserialize,
352 TraceRawVcs,
353 NonLocalValue,
354 OperationValue,
355 Encode,
356 Decode,
357)]
358#[serde(rename_all = "kebab-case")]
359pub enum OutputType {
360 Standalone,
361 Export,
362}
363
364#[turbo_tasks::value(transparent)]
365pub struct OptionOutputType(Option<OutputType>);
366
367#[turbo_tasks::task_input]
368#[derive(
369 Debug,
370 Clone,
371 Hash,
372 Eq,
373 PartialEq,
374 Ord,
375 PartialOrd,
376 TraceRawVcs,
377 Serialize,
378 Deserialize,
379 OperationValue,
380 Encode,
381 Decode,
382)]
383#[serde(tag = "type", rename_all = "kebab-case")]
384pub enum RouteHas {
385 Header {
386 key: RcStr,
387 #[serde(skip_serializing_if = "Option::is_none")]
388 value: Option<RcStr>,
389 },
390 Cookie {
391 key: RcStr,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 value: Option<RcStr>,
394 },
395 Query {
396 key: RcStr,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 value: Option<RcStr>,
399 },
400 Host {
401 value: RcStr,
402 },
403}
404
405#[derive(Clone, Debug, Default, PartialEq, Deserialize, TraceRawVcs, NonLocalValue)]
406#[serde(rename_all = "camelCase")]
407pub struct HeaderValue {
408 pub key: RcStr,
409 pub value: RcStr,
410}
411
412#[derive(Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue)]
413#[serde(rename_all = "camelCase")]
414pub struct Header {
415 pub source: String,
416 pub base_path: Option<bool>,
417 pub locale: Option<bool>,
418 pub headers: Vec<HeaderValue>,
419 pub has: Option<Vec<RouteHas>>,
420 pub missing: Option<Vec<RouteHas>>,
421}
422
423#[derive(
424 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
425)]
426#[serde(rename_all = "camelCase")]
427pub enum RedirectStatus {
428 StatusCode(f64),
429 Permanent(bool),
430}
431
432#[derive(
433 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
434)]
435#[serde(rename_all = "camelCase")]
436pub struct Redirect {
437 pub source: String,
438 pub destination: String,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub base_path: Option<bool>,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub locale: Option<bool>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub has: Option<Vec<RouteHas>>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub missing: Option<Vec<RouteHas>>,
447
448 #[serde(flatten)]
449 pub status: RedirectStatus,
450}
451
452#[derive(Clone, Debug)]
453pub struct Rewrite {
454 pub source: String,
455 pub destination: String,
456 pub base_path: Option<bool>,
457 pub locale: Option<bool>,
458 pub has: Option<Vec<RouteHas>>,
459 pub missing: Option<Vec<RouteHas>>,
460}
461
462#[derive(Clone, Debug)]
463pub struct Rewrites {
464 pub before_files: Vec<Rewrite>,
465 pub after_files: Vec<Rewrite>,
466 pub fallback: Vec<Rewrite>,
467}
468
469#[derive(
470 Clone,
471 Debug,
472 Default,
473 PartialEq,
474 Deserialize,
475 TraceRawVcs,
476 NonLocalValue,
477 OperationValue,
478 Encode,
479 Decode,
480)]
481#[serde(rename_all = "camelCase")]
482pub struct TypeScriptConfig {
483 pub ignore_build_errors: Option<bool>,
484 pub tsconfig_path: Option<String>,
485}
486
487#[turbo_tasks::value(eq = "manual", operation)]
488#[derive(Clone, Debug, PartialEq, Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub struct ImageConfig {
491 pub device_sizes: Vec<u16>,
492 pub image_sizes: Vec<u16>,
493 pub path: String,
494 pub loader: ImageLoader,
495 #[serde(deserialize_with = "empty_string_is_none")]
496 pub loader_file: Option<String>,
497 pub domains: Vec<String>,
498 pub disable_static_images: bool,
499 #[serde(rename = "minimumCacheTTL")]
500 pub minimum_cache_ttl: u64,
501 pub formats: Vec<ImageFormat>,
502 #[serde(rename = "dangerouslyAllowSVG")]
503 pub dangerously_allow_svg: bool,
504 pub content_security_policy: String,
505 pub remote_patterns: Vec<RemotePattern>,
506 pub unoptimized: bool,
507}
508
509fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
510where
511 D: Deserializer<'de>,
512{
513 let o = Option::<String>::deserialize(deserializer)?;
514 Ok(o.filter(|s| !s.is_empty()))
515}
516
517impl Default for ImageConfig {
518 fn default() -> Self {
519 Self {
521 device_sizes: vec![640, 750, 828, 1080, 1200, 1920, 2048, 3840],
522 image_sizes: vec![32, 48, 64, 96, 128, 256, 384],
523 path: "/_next/image".to_string(),
524 loader: ImageLoader::Default,
525 loader_file: None,
526 domains: vec![],
527 disable_static_images: false,
528 minimum_cache_ttl: 60,
529 formats: vec![ImageFormat::Webp],
530 dangerously_allow_svg: false,
531 content_security_policy: "".to_string(),
532 remote_patterns: vec![],
533 unoptimized: false,
534 }
535 }
536}
537
538#[derive(
539 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
540)]
541#[serde(rename_all = "kebab-case")]
542pub enum ImageLoader {
543 Default,
544 Imgix,
545 Cloudinary,
546 Akamai,
547 Custom,
548}
549
550#[derive(
551 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
552)]
553pub enum ImageFormat {
554 #[serde(rename = "image/webp")]
555 Webp,
556 #[serde(rename = "image/avif")]
557 Avif,
558}
559
560#[derive(
561 Clone,
562 Debug,
563 Default,
564 PartialEq,
565 Deserialize,
566 TraceRawVcs,
567 NonLocalValue,
568 OperationValue,
569 Encode,
570 Decode,
571)]
572#[serde(rename_all = "camelCase")]
573pub struct RemotePattern {
574 pub hostname: String,
575 #[serde(skip_serializing_if = "Option::is_none")]
576 pub protocol: Option<RemotePatternProtocol>,
577 #[serde(skip_serializing_if = "Option::is_none")]
578 pub port: Option<String>,
579 #[serde(skip_serializing_if = "Option::is_none")]
580 pub pathname: Option<String>,
581}
582
583#[derive(
584 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
585)]
586#[serde(rename_all = "kebab-case")]
587pub enum RemotePatternProtocol {
588 Http,
589 Https,
590}
591
592#[derive(
593 Clone,
594 Debug,
595 Default,
596 PartialEq,
597 Deserialize,
598 TraceRawVcs,
599 NonLocalValue,
600 OperationValue,
601 Encode,
602 Decode,
603)]
604#[serde(rename_all = "camelCase")]
605pub struct TurbopackConfig {
606 #[serde(default)]
607 #[bincode(with = "turbo_bincode::indexmap")]
608 pub rules: FxIndexMap<RcStr, RuleConfigCollection>,
609 #[bincode(with = "turbo_bincode::serde_self_describing")]
610 pub resolve_alias: Option<FxIndexMap<RcStr, JsonValue>>,
611 pub resolve_extensions: Option<Vec<RcStr>>,
612 pub debug_ids: Option<bool>,
613 pub chunk_loading_global: Option<RcStr>,
614 #[serde(default)]
616 pub ignore_issue: Option<Vec<TurbopackIgnoreIssueRule>>,
617}
618
619#[derive(
620 Deserialize,
621 Clone,
622 PartialEq,
623 Eq,
624 Debug,
625 TraceRawVcs,
626 NonLocalValue,
627 OperationValue,
628 Encode,
629 Decode,
630)]
631#[serde(deny_unknown_fields)]
632pub struct RegexComponents {
633 source: RcStr,
634 flags: RcStr,
635}
636
637#[derive(
642 Clone,
643 PartialEq,
644 Eq,
645 Debug,
646 Deserialize,
647 TraceRawVcs,
648 NonLocalValue,
649 OperationValue,
650 Encode,
651 Decode,
652)]
653#[serde(
654 tag = "type",
655 content = "value",
656 rename_all = "camelCase",
657 deny_unknown_fields
658)]
659pub enum ConfigConditionPath {
660 Glob(RcStr),
661 Regex(RegexComponents),
662}
663
664impl TryFrom<ConfigConditionPath> for ConditionPath {
665 type Error = anyhow::Error;
666
667 fn try_from(config: ConfigConditionPath) -> Result<ConditionPath> {
668 Ok(match config {
669 ConfigConditionPath::Glob(path) => ConditionPath::Glob(path),
670 ConfigConditionPath::Regex(path) => {
671 ConditionPath::Regex(EsRegex::try_from(path)?.resolved_cell())
672 }
673 })
674 }
675}
676
677impl TryFrom<RegexComponents> for EsRegex {
678 type Error = anyhow::Error;
679
680 fn try_from(components: RegexComponents) -> Result<EsRegex> {
681 EsRegex::new(&components.source, &components.flags)
682 }
683}
684
685#[derive(
686 Clone,
687 PartialEq,
688 Eq,
689 Debug,
690 Deserialize,
691 TraceRawVcs,
692 NonLocalValue,
693 OperationValue,
694 Encode,
695 Decode,
696)]
697#[serde(
698 tag = "type",
699 content = "value",
700 rename_all = "camelCase",
701 deny_unknown_fields
702)]
703pub enum ConfigConditionQuery {
704 Constant(RcStr),
705 Regex(RegexComponents),
706}
707
708impl TryFrom<ConfigConditionQuery> for ConditionQuery {
709 type Error = anyhow::Error;
710
711 fn try_from(config: ConfigConditionQuery) -> Result<ConditionQuery> {
712 Ok(match config {
713 ConfigConditionQuery::Constant(value) => ConditionQuery::Constant(value),
714 ConfigConditionQuery::Regex(regex) => {
715 ConditionQuery::Regex(EsRegex::try_from(regex)?.resolved_cell())
716 }
717 })
718 }
719}
720
721#[derive(
722 Clone,
723 PartialEq,
724 Eq,
725 Debug,
726 Deserialize,
727 TraceRawVcs,
728 NonLocalValue,
729 OperationValue,
730 Encode,
731 Decode,
732)]
733#[serde(
734 tag = "type",
735 content = "value",
736 rename_all = "camelCase",
737 deny_unknown_fields
738)]
739pub enum ConfigConditionContentType {
740 Glob(RcStr),
741 Regex(RegexComponents),
742}
743
744impl TryFrom<ConfigConditionContentType> for ConditionContentType {
745 type Error = anyhow::Error;
746
747 fn try_from(config: ConfigConditionContentType) -> Result<ConditionContentType> {
748 Ok(match config {
749 ConfigConditionContentType::Glob(value) => ConditionContentType::Glob(value),
750 ConfigConditionContentType::Regex(regex) => {
751 ConditionContentType::Regex(EsRegex::try_from(regex)?.resolved_cell())
752 }
753 })
754 }
755}
756
757#[derive(
758 Deserialize,
759 Clone,
760 PartialEq,
761 Eq,
762 Debug,
763 TraceRawVcs,
764 NonLocalValue,
765 OperationValue,
766 Encode,
767 Decode,
768)]
769#[serde(deny_unknown_fields)]
772pub enum ConfigConditionItem {
773 #[serde(rename = "all")]
774 All(Box<[ConfigConditionItem]>),
775 #[serde(rename = "any")]
776 Any(Box<[ConfigConditionItem]>),
777 #[serde(rename = "not")]
778 Not(Box<ConfigConditionItem>),
779 #[serde(untagged)]
780 Builtin(WebpackLoaderBuiltinCondition),
781 #[serde(untagged)]
782 Base {
783 #[serde(default)]
784 path: Option<ConfigConditionPath>,
785 #[serde(default)]
786 content: Option<RegexComponents>,
787 #[serde(default)]
788 query: Option<ConfigConditionQuery>,
789 #[serde(default, rename = "contentType")]
790 content_type: Option<ConfigConditionContentType>,
791 },
792}
793
794impl TryFrom<ConfigConditionItem> for ConditionItem {
795 type Error = anyhow::Error;
796
797 fn try_from(config: ConfigConditionItem) -> Result<Self> {
798 let try_from_vec = |conds: Box<[_]>| {
799 conds
800 .into_iter()
801 .map(ConditionItem::try_from)
802 .collect::<Result<_>>()
803 };
804 Ok(match config {
805 ConfigConditionItem::All(conds) => ConditionItem::All(try_from_vec(conds)?),
806 ConfigConditionItem::Any(conds) => ConditionItem::Any(try_from_vec(conds)?),
807 ConfigConditionItem::Not(cond) => ConditionItem::Not(Box::new((*cond).try_into()?)),
808 ConfigConditionItem::Builtin(cond) => {
809 ConditionItem::Builtin(RcStr::from(cond.as_str()))
810 }
811 ConfigConditionItem::Base {
812 path,
813 content,
814 query,
815 content_type,
816 } => ConditionItem::Base {
817 path: path.map(ConditionPath::try_from).transpose()?,
818 content: content
819 .map(EsRegex::try_from)
820 .transpose()?
821 .map(EsRegex::resolved_cell),
822 query: query.map(ConditionQuery::try_from).transpose()?,
823 content_type: content_type
824 .map(ConditionContentType::try_from)
825 .transpose()?,
826 },
827 })
828 }
829}
830
831#[derive(
832 Clone,
833 Debug,
834 PartialEq,
835 Eq,
836 Deserialize,
837 TraceRawVcs,
838 NonLocalValue,
839 OperationValue,
840 Encode,
841 Decode,
842)]
843#[serde(rename_all = "camelCase")]
844pub struct RuleConfigItem {
845 #[serde(default)]
846 pub loaders: Vec<LoaderItem>,
847 #[serde(default, alias = "as")]
848 pub rename_as: Option<RcStr>,
849 #[serde(default)]
850 pub condition: Option<ConfigConditionItem>,
851 #[serde(default, alias = "type")]
852 pub module_type: Option<RcStr>,
853}
854
855#[derive(
856 Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
857)]
858pub struct RuleConfigCollection(Vec<RuleConfigCollectionItem>);
859
860impl<'de> Deserialize<'de> for RuleConfigCollection {
861 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
862 where
863 D: Deserializer<'de>,
864 {
865 match either::serde_untagged::deserialize::<Vec<RuleConfigCollectionItem>, RuleConfigItem, D>(
866 deserializer,
867 )? {
868 Either::Left(collection) => Ok(RuleConfigCollection(collection)),
869 Either::Right(item) => Ok(RuleConfigCollection(vec![RuleConfigCollectionItem::Full(
870 item,
871 )])),
872 }
873 }
874}
875
876#[derive(
877 Clone,
878 Debug,
879 PartialEq,
880 Eq,
881 Deserialize,
882 TraceRawVcs,
883 NonLocalValue,
884 OperationValue,
885 Encode,
886 Decode,
887)]
888#[serde(untagged)]
889pub enum RuleConfigCollectionItem {
890 Shorthand(LoaderItem),
891 Full(RuleConfigItem),
892}
893
894#[derive(
895 Clone,
896 Debug,
897 PartialEq,
898 Eq,
899 Deserialize,
900 TraceRawVcs,
901 NonLocalValue,
902 OperationValue,
903 Encode,
904 Decode,
905)]
906#[serde(untagged)]
907pub enum LoaderItem {
908 LoaderName(RcStr),
909 LoaderOptions(WebpackLoaderItem),
910}
911
912#[turbo_tasks::value(operation)]
913#[derive(Copy, Clone, Debug, Deserialize)]
914#[serde(rename_all = "camelCase")]
915pub enum ModuleIds {
916 Named,
917 Deterministic,
918}
919
920#[turbo_tasks::value(operation)]
921#[derive(Copy, Clone, Debug, Deserialize)]
922#[serde(rename_all = "camelCase")]
923pub enum TurbopackPluginRuntimeStrategy {
924 #[cfg(feature = "worker_pool")]
925 WorkerThreads,
926 #[cfg(feature = "process_pool")]
927 ChildProcesses,
928}
929
930#[derive(
931 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
932)]
933#[serde(untagged)]
934pub enum MdxRsOptions {
935 Boolean(bool),
936 Option(MdxTransformOptions),
937}
938
939#[turbo_tasks::value(shared, operation)]
940#[derive(Clone, Debug, Default, Serialize, Deserialize)]
941#[serde(rename_all = "snake_case")]
942pub enum ReactCompilerPanicThreshold {
943 #[default]
944 None,
945 CriticalErrors,
946 AllErrors,
947}
948
949#[turbo_tasks::value(shared, operation)]
952#[derive(Clone, Debug, Default, Serialize, Deserialize)]
953#[serde(rename_all = "camelCase")]
954pub struct ReactCompilerOptions {
955 #[serde(default)]
956 pub compilation_mode: ReactCompilerCompilationMode,
957 #[serde(default)]
958 pub panic_threshold: ReactCompilerPanicThreshold,
959 #[serde(default, skip_deserializing, skip_serializing_if = "Option::is_none")]
960 pub target: Option<ReactCompilerTarget>,
961}
962
963#[derive(
964 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
965)]
966#[serde(untagged)]
967pub enum ReactCompilerOptionsOrBoolean {
968 Boolean(bool),
969 Option(ReactCompilerOptions),
970}
971
972#[turbo_tasks::value(transparent)]
973pub struct OptionalReactCompilerOptions(Option<ResolvedVc<ReactCompilerOptions>>);
974
975#[derive(
979 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
980)]
981#[serde(tag = "type")]
982pub enum TurbopackIgnoreIssuePathPattern {
983 #[serde(rename = "glob")]
984 Glob { value: RcStr },
985 #[serde(rename = "regex")]
986 Regex { source: RcStr, flags: RcStr },
987}
988
989impl TurbopackIgnoreIssuePathPattern {
990 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
991 match self {
992 TurbopackIgnoreIssuePathPattern::Glob { value } => Ok(IgnoreIssuePattern::Glob(
993 Glob::parse(value.clone(), GlobOptions::default())?,
994 )),
995 TurbopackIgnoreIssuePathPattern::Regex { source, flags } => {
996 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
997 }
998 }
999 }
1000}
1001
1002#[derive(
1007 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1008)]
1009#[serde(tag = "type")]
1010pub enum TurbopackIgnoreIssueTextPattern {
1011 #[serde(rename = "string")]
1012 String { value: RcStr },
1013 #[serde(rename = "regex")]
1014 Regex { source: RcStr, flags: RcStr },
1015}
1016
1017impl TurbopackIgnoreIssueTextPattern {
1018 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
1019 match self {
1020 TurbopackIgnoreIssueTextPattern::String { value } => {
1021 Ok(IgnoreIssuePattern::ExactString(value.clone()))
1022 }
1023 TurbopackIgnoreIssueTextPattern::Regex { source, flags } => {
1024 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
1025 }
1026 }
1027 }
1028}
1029
1030#[derive(
1032 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1033)]
1034pub struct TurbopackIgnoreIssueRule {
1035 pub path: TurbopackIgnoreIssuePathPattern,
1036 #[serde(default)]
1037 pub title: Option<TurbopackIgnoreIssueTextPattern>,
1038 #[serde(default)]
1039 pub description: Option<TurbopackIgnoreIssueTextPattern>,
1040}
1041
1042#[derive(
1051 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1052)]
1053#[serde(untagged)]
1054pub enum CssChunkingConfig {
1055 Bool(bool),
1056 String(CssChunkingMode),
1057 Object(CssChunkingObject),
1058}
1059
1060#[derive(
1062 Clone,
1063 Copy,
1064 Debug,
1065 PartialEq,
1066 Eq,
1067 Deserialize,
1068 TraceRawVcs,
1069 NonLocalValue,
1070 OperationValue,
1071 Encode,
1072 Decode,
1073)]
1074#[serde(rename_all = "lowercase")]
1075pub enum CssChunkingMode {
1076 Strict,
1077 Loose,
1078 Graph,
1079}
1080
1081#[derive(
1086 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1087)]
1088#[serde(tag = "type", rename_all = "lowercase")]
1089pub enum CssChunkingObject {
1090 #[serde(skip)]
1091 None,
1092 Strict,
1093 Loose,
1094 Graph(CssChunkingGraphOptions),
1095}
1096
1097#[derive(
1099 Clone,
1100 Debug,
1101 Default,
1102 PartialEq,
1103 Deserialize,
1104 TraceRawVcs,
1105 NonLocalValue,
1106 OperationValue,
1107 Encode,
1108 Decode,
1109)]
1110#[serde(rename_all = "camelCase")]
1111pub struct CssChunkingGraphOptions {
1112 pub request_cost: Option<f32>,
1113 pub weight_distribution: Option<f32>,
1114}
1115
1116impl CssChunkingConfig {
1117 pub fn normalize(&self) -> CssChunkingObject {
1120 match self {
1121 CssChunkingConfig::Bool(false) => CssChunkingObject::None,
1122 CssChunkingConfig::Bool(true) => CssChunkingObject::Loose,
1123 CssChunkingConfig::String(CssChunkingMode::Strict) => CssChunkingObject::Strict,
1124 CssChunkingConfig::String(CssChunkingMode::Loose) => CssChunkingObject::Loose,
1125 CssChunkingConfig::String(CssChunkingMode::Graph) => {
1126 CssChunkingObject::Graph(CssChunkingGraphOptions::default())
1127 }
1128 CssChunkingConfig::Object(obj) => obj.clone(),
1129 }
1130 }
1131}
1132
1133const DEFAULT_REQUEST_COST: f32 = 20_000.0;
1135const DEFAULT_WEIGHT_DISTRIBUTION: f32 = 0.1;
1137
1138#[derive(
1140 Clone,
1141 Debug,
1142 Default,
1143 PartialEq,
1144 Deserialize,
1145 TraceRawVcs,
1146 NonLocalValue,
1147 OperationValue,
1148 Encode,
1149 Decode,
1150)]
1151#[serde(rename_all = "camelCase")]
1152pub struct TurbopackChunkingConfig {
1153 clusters: Option<Vec<Vec<RegexComponents>>>,
1156 first_page_load_priority: Option<f64>,
1160 priority_routes: Option<Vec<RegexComponents>>,
1164 priority_boost: Option<f64>,
1167 request_cost: Option<u64>,
1171 min_chunk_size: Option<usize>,
1174 max_chunk_count_per_group: Option<usize>,
1176 max_merge_chunk_size: Option<usize>,
1179 generate_component_chunks: Option<bool>,
1182 min_component_chunk_size: Option<usize>,
1185}
1186
1187#[turbo_tasks::value]
1188pub struct TurbopackChunking {
1189 clusters: Vec<EsRegexSet>,
1191 pub first_page_load_priority: Option<u32>,
1193 priority_routes: EsRegexSet,
1195 pub priority_boost_percent: Option<u32>,
1198 pub request_cost: Option<u64>,
1200 pub min_chunk_size: Option<usize>,
1202 pub max_chunk_count_per_group: Option<usize>,
1204 pub max_merge_chunk_size: Option<usize>,
1206 pub min_component_chunk_size: Option<usize>,
1208 pub generate_component_chunks: bool,
1210}
1211
1212impl TurbopackChunking {
1213 pub fn entry_heuristics_for(&self, pathname: &str) -> EntryHeuristics {
1216 let clusters = self
1217 .clusters
1218 .iter()
1219 .enumerate()
1220 .filter(|(_, regexes)| regexes.is_match(pathname))
1221 .map(|(index, _)| index as u16)
1222 .collect();
1223 let high_priority = self.priority_routes.is_match(pathname);
1224 EntryHeuristics {
1225 clusters,
1226 high_priority,
1227 }
1228 }
1229}
1230
1231fn parse_route_regexes(patterns: &[RegexComponents]) -> Result<EsRegexSet> {
1234 let regexes = patterns
1235 .iter()
1236 .cloned()
1237 .map(|pattern| {
1238 EsRegex::try_from(pattern)
1239 .context("Invalid route pattern in `experimental.turbopackChunking`")
1240 })
1241 .collect::<Result<Vec<_>>>()?;
1242 Ok(EsRegexSet::new(regexes))
1243}
1244
1245fn resolve_css_chunking_algorithm(
1251 config: Option<&CssChunkingConfig>,
1252) -> Result<StyleGroupsAlgorithm> {
1253 let Some(config) = config else {
1254 return Ok(StyleGroupsAlgorithm::Default);
1255 };
1256 Ok(match config.normalize() {
1257 CssChunkingObject::None => {
1258 anyhow::bail!(
1259 "`experimental.cssChunking: false` is not supported by Turbopack; this should \
1260 have been rejected at config validation time"
1261 )
1262 }
1263 CssChunkingObject::Strict => {
1264 anyhow::bail!(
1265 "`experimental.cssChunking: \"strict\"` is not supported by Turbopack; this \
1266 should have been rejected at config validation time"
1267 )
1268 }
1269 CssChunkingObject::Loose => StyleGroupsAlgorithm::Default,
1270 CssChunkingObject::Graph(opts) => StyleGroupsAlgorithm::graph(
1271 opts.request_cost.unwrap_or(DEFAULT_REQUEST_COST),
1272 opts.weight_distribution
1273 .unwrap_or(DEFAULT_WEIGHT_DISTRIBUTION),
1274 ),
1275 })
1276}
1277
1278#[derive(
1279 Clone,
1280 Debug,
1281 Default,
1282 PartialEq,
1283 Deserialize,
1284 TraceRawVcs,
1285 ValueDebugFormat,
1286 NonLocalValue,
1287 OperationValue,
1288 Encode,
1289 Decode,
1290)]
1291#[serde(rename_all = "camelCase")]
1292pub struct ExperimentalConfig {
1293 allowed_revalidate_header_keys: Option<Vec<RcStr>>,
1296 client_router_filter: Option<bool>,
1297 client_router_filter_allowed_rate: Option<f64>,
1300 client_router_filter_redirects: Option<bool>,
1301 fetch_cache_key_prefix: Option<RcStr>,
1302 isr_flush_to_disk: Option<bool>,
1303 mdx_rs: Option<MdxRsOptions>,
1306 strict_next_head: Option<bool>,
1307 #[bincode(with = "turbo_bincode::serde_self_describing")]
1308 swc_plugins: Option<Vec<(RcStr, serde_json::Value)>>,
1309 swc_env_options: Option<SwcEnvOptions>,
1310 external_middleware_rewrites_resolve: Option<bool>,
1311 scroll_restoration: Option<bool>,
1312 manual_client_base_path: Option<bool>,
1313 optimistic_client_cache: Option<bool>,
1314 middleware_prefetch: Option<MiddlewarePrefetchType>,
1315 #[bincode(with = "turbo_bincode::serde_self_describing")]
1318 optimize_css: Option<serde_json::Value>,
1319 next_script_workers: Option<bool>,
1320 web_vitals_attribution: Option<Vec<RcStr>>,
1321 server_actions: Option<ServerActionsOrLegacyBool>,
1322 sri: Option<SubResourceIntegrity>,
1323 cache_components: Option<bool>,
1326 use_cache: Option<bool>,
1327 durable_use_cache_entries: Option<bool>,
1328 runtime_server_deployment_id: Option<bool>,
1329 expose_testing_api_in_production_build: Option<bool>,
1330
1331 css_chunking: Option<CssChunkingConfig>,
1333
1334 turbopack_chunking: Option<TurbopackChunkingConfig>,
1335
1336 adjust_font_fallbacks: Option<bool>,
1340 adjust_font_fallbacks_with_size_adjust: Option<bool>,
1341 after: Option<bool>,
1342 app_document_preloading: Option<bool>,
1343 case_sensitive_routes: Option<bool>,
1344 cpus: Option<f64>,
1345 cra_compat: Option<bool>,
1346 disable_optimized_loading: Option<bool>,
1347 disable_postcss_preset_env: Option<bool>,
1348 esm_externals: Option<EsmExternals>,
1349 #[bincode(with = "turbo_bincode::serde_self_describing")]
1350 extension_alias: Option<serde_json::Value>,
1351 external_dir: Option<bool>,
1352 fallback_node_polyfills: Option<bool>, force_swc_transforms: Option<bool>,
1357 fully_specified: Option<bool>,
1358 gzip_size: Option<bool>,
1359
1360 inline_css: Option<bool>,
1361 instrumentation_hook: Option<bool>,
1362 client_trace_metadata: Option<Vec<String>>,
1363 large_page_data_bytes: Option<f64>,
1364 #[bincode(with = "turbo_bincode::serde_self_describing")]
1365 logging: Option<serde_json::Value>,
1366 memory_based_workers_count: Option<bool>,
1367 optimize_server_react: Option<bool>,
1369 optimize_package_imports: Option<Vec<RcStr>>,
1372 taint: Option<bool>,
1373 proxy_timeout: Option<f64>,
1374 server_minification: Option<bool>,
1376 server_source_maps: Option<bool>,
1378 swc_trace_profiling: Option<bool>,
1379 transition_indicator: Option<bool>,
1380 gesture_transition: Option<bool>,
1381 concurrent_router_queue: Option<bool>,
1384 #[serde(rename = "blockingSSR")]
1387 blocking_ssr: Option<bool>,
1388 trust_host_header: Option<bool>,
1390
1391 #[bincode(with = "turbo_bincode::serde_self_describing")]
1392 url_imports: Option<serde_json::Value>,
1393 webpack_build_worker: Option<bool>,
1396 worker_threads: Option<bool>,
1397
1398 turbopack_minify: Option<TurbopackMinify>,
1399 turbopack_module_ids: Option<ModuleIds>,
1400 turbopack_plugin_runtime_strategy: Option<TurbopackPluginRuntimeStrategy>,
1401 turbopack_source_maps: Option<bool>,
1402 turbopack_input_source_maps: Option<bool>,
1403 turbopack_module_fragments: Option<bool>,
1404 turbopack_scope_hoisting: Option<bool>,
1405 turbopack_shared_runtime: Option<bool>,
1406 turbopack_worker_asset_prefix: Option<RcStr>,
1417 turbopack_client_side_nested_async_chunking: Option<bool>,
1418 turbopack_server_side_nested_async_chunking: Option<bool>,
1419 turbopack_import_type_bytes: Option<bool>,
1420 #[serde(default)]
1422 turbopack_use_builtin_sass: Option<bool>,
1423 #[serde(default)]
1426 turbopack_use_builtin_babel: Option<bool>,
1427 #[serde(default)]
1431 turbopack_local_postcss_config: Option<bool>,
1432 global_not_found: Option<bool>,
1434 turbopack_rust_react_compiler: Option<bool>,
1436 turbopack_remove_unused_imports: Option<bool>,
1438 turbopack_remove_unused_exports: Option<bool>,
1440 turbopack_infer_module_side_effects: Option<bool>,
1442 turbopack_cjs_tree_shaking: Option<bool>,
1444 turbopack_cjs_scope_hoisting: Option<bool>,
1446 turbopack_cross_module_constants: Option<bool>,
1448 devtool_segment_explorer: Option<bool>,
1450 report_system_env_inlining: Option<String>,
1452 lightning_css_features: Option<LightningCssFeatures>,
1456}
1457
1458#[derive(
1459 Clone,
1460 Debug,
1461 PartialEq,
1462 Eq,
1463 Deserialize,
1464 TraceRawVcs,
1465 NonLocalValue,
1466 OperationValue,
1467 Encode,
1468 Decode,
1469)]
1470#[serde(rename_all = "camelCase")]
1471pub struct SubResourceIntegrity {
1472 pub algorithm: Option<RcStr>,
1473}
1474
1475#[derive(
1476 Clone,
1477 Debug,
1478 Default,
1479 PartialEq,
1480 Eq,
1481 Deserialize,
1482 TraceRawVcs,
1483 NonLocalValue,
1484 OperationValue,
1485 Encode,
1486 Decode,
1487)]
1488#[serde(rename_all = "camelCase")]
1489pub struct LightningCssFeatures {
1490 pub include: Option<Vec<RcStr>>,
1491 pub exclude: Option<Vec<RcStr>>,
1492}
1493
1494#[derive(
1495 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1496)]
1497#[serde(untagged)]
1498pub enum ServerActionsOrLegacyBool {
1499 ServerActionsConfig(ServerActions),
1501
1502 LegacyBool(bool),
1505}
1506
1507#[derive(
1508 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1509)]
1510#[serde(rename_all = "kebab-case")]
1511pub enum EsmExternalsValue {
1512 Loose,
1513}
1514
1515#[derive(
1516 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1517)]
1518#[serde(untagged)]
1519pub enum EsmExternals {
1520 Loose(EsmExternalsValue),
1521 Bool(bool),
1522}
1523
1524#[test]
1526fn test_esm_externals_deserialization() {
1527 let json = serde_json::json!({
1528 "esmExternals": true
1529 });
1530 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1531 assert_eq!(config.esm_externals, Some(EsmExternals::Bool(true)));
1532
1533 let json = serde_json::json!({
1534 "esmExternals": "loose"
1535 });
1536 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1537 assert_eq!(
1538 config.esm_externals,
1539 Some(EsmExternals::Loose(EsmExternalsValue::Loose))
1540 );
1541}
1542
1543#[derive(
1544 Clone,
1545 Debug,
1546 Default,
1547 PartialEq,
1548 Eq,
1549 Deserialize,
1550 TraceRawVcs,
1551 NonLocalValue,
1552 OperationValue,
1553 Encode,
1554 Decode,
1555)]
1556#[serde(rename_all = "camelCase")]
1557pub struct ServerActions {
1558 pub body_size_limit: Option<SizeLimit>,
1560}
1561
1562#[derive(Clone, Debug, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode)]
1563#[serde(untagged)]
1564pub enum SizeLimit {
1565 Number(f64),
1566 WithUnit(String),
1567}
1568
1569impl PartialEq for SizeLimit {
1572 fn eq(&self, other: &Self) -> bool {
1573 match (self, other) {
1574 (SizeLimit::Number(a), SizeLimit::Number(b)) => a.to_bits() == b.to_bits(),
1575 (SizeLimit::WithUnit(a), SizeLimit::WithUnit(b)) => a == b,
1576 _ => false,
1577 }
1578 }
1579}
1580
1581impl Eq for SizeLimit {}
1582
1583#[derive(
1584 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1585)]
1586#[serde(rename_all = "kebab-case")]
1587pub enum MiddlewarePrefetchType {
1588 Strict,
1589 Flexible,
1590}
1591
1592#[derive(
1593 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1594)]
1595#[serde(untagged)]
1596pub enum EmotionTransformOptionsOrBoolean {
1597 Boolean(bool),
1598 Options(EmotionTransformConfig),
1599}
1600
1601impl EmotionTransformOptionsOrBoolean {
1602 pub fn is_enabled(&self) -> bool {
1603 match self {
1604 Self::Boolean(enabled) => *enabled,
1605 _ => true,
1606 }
1607 }
1608}
1609
1610#[derive(
1611 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1612)]
1613#[serde(untagged)]
1614pub enum StyledComponentsTransformOptionsOrBoolean {
1615 Boolean(bool),
1616 Options(StyledComponentsTransformConfig),
1617}
1618
1619impl StyledComponentsTransformOptionsOrBoolean {
1620 pub fn is_enabled(&self) -> bool {
1621 match self {
1622 Self::Boolean(enabled) => *enabled,
1623 _ => true,
1624 }
1625 }
1626}
1627
1628#[turbo_tasks::value(eq = "manual")]
1629#[derive(Clone, Debug, PartialEq, Default, OperationValue, Deserialize)]
1630#[serde(rename_all = "camelCase")]
1631pub struct CompilerConfig {
1632 pub react_remove_properties: Option<ReactRemoveProperties>,
1633 pub relay: Option<RelayConfig>,
1634 pub emotion: Option<EmotionTransformOptionsOrBoolean>,
1635 pub remove_console: Option<RemoveConsoleConfig>,
1636 pub styled_components: Option<StyledComponentsTransformOptionsOrBoolean>,
1637}
1638
1639#[derive(
1640 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1641)]
1642#[serde(untagged, rename_all = "camelCase")]
1643pub enum ReactRemoveProperties {
1644 Boolean(bool),
1645 Config { properties: Option<Vec<String>> },
1646}
1647
1648impl ReactRemoveProperties {
1649 pub fn is_enabled(&self) -> bool {
1650 match self {
1651 Self::Boolean(enabled) => *enabled,
1652 _ => true,
1653 }
1654 }
1655}
1656
1657#[derive(
1660 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1661)]
1662#[serde(untagged)]
1663pub enum TurbopackMinify {
1664 Boolean(bool),
1665 Config {
1666 server: Option<bool>,
1667 client: Option<bool>,
1668 edge: Option<bool>,
1669 },
1670}
1671
1672impl TurbopackMinify {
1673 fn client(&self) -> Option<bool> {
1675 match self {
1676 Self::Boolean(enabled) => Some(*enabled),
1677 Self::Config { client, .. } => *client,
1678 }
1679 }
1680
1681 fn server(&self) -> Option<bool> {
1683 match self {
1684 Self::Boolean(enabled) => Some(*enabled),
1685 Self::Config { server, .. } => *server,
1686 }
1687 }
1688
1689 fn edge(&self) -> Option<bool> {
1691 match self {
1692 Self::Boolean(enabled) => Some(*enabled),
1693 Self::Config { edge, .. } => *edge,
1694 }
1695 }
1696}
1697
1698#[derive(
1699 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1700)]
1701#[serde(untagged)]
1702pub enum RemoveConsoleConfig {
1703 Boolean(bool),
1704 Config { exclude: Option<Vec<String>> },
1705}
1706
1707impl RemoveConsoleConfig {
1708 pub fn is_enabled(&self) -> bool {
1709 match self {
1710 Self::Boolean(enabled) => *enabled,
1711 _ => true,
1712 }
1713 }
1714}
1715
1716#[turbo_tasks::value(transparent)]
1717pub struct ResolveExtensions(Option<Vec<RcStr>>);
1718
1719#[turbo_tasks::value(transparent)]
1720pub struct SwcPlugins(
1721 #[bincode(with = "turbo_bincode::serde_self_describing")] Vec<(RcStr, serde_json::Value)>,
1722);
1723
1724#[derive(
1726 Clone,
1727 Debug,
1728 Default,
1729 PartialEq,
1730 Eq,
1731 Serialize,
1732 Deserialize,
1733 TraceRawVcs,
1734 NonLocalValue,
1735 OperationValue,
1736 Encode,
1737 Decode,
1738)]
1739#[serde(rename_all = "camelCase")]
1740pub struct SwcEnvOptions {
1741 pub mode: Option<RcStr>,
1742 pub core_js: Option<RcStr>,
1743 pub skip: Option<Vec<RcStr>>,
1744 pub include: Option<Vec<RcStr>>,
1745 pub exclude: Option<Vec<RcStr>>,
1746 pub shipped_proposals: Option<bool>,
1747 pub force_all_transforms: Option<bool>,
1748 pub debug: Option<bool>,
1749 pub loose: Option<bool>,
1750}
1751
1752#[turbo_tasks::value(transparent)]
1753pub struct OptionSwcEnvOptions(Option<SwcEnvOptions>);
1754
1755#[turbo_tasks::value(transparent)]
1756pub struct OptionalMdxTransformOptions(Option<ResolvedVc<MdxTransformOptions>>);
1757
1758#[turbo_tasks::value(transparent)]
1759
1760pub struct OptionSubResourceIntegrity(Option<SubResourceIntegrity>);
1761
1762#[turbo_tasks::value(transparent)]
1763pub struct OptionFileSystemPath(Option<FileSystemPath>);
1764
1765#[turbo_tasks::value(transparent)]
1766pub struct IgnoreIssues(Box<[IgnoreIssue]>);
1767
1768#[turbo_tasks::value(transparent)]
1769pub struct OptionJsonValue(
1770 #[bincode(with = "turbo_bincode::serde_self_describing")] pub Option<serde_json::Value>,
1771);
1772
1773fn turbopack_config_documentation_link() -> RcStr {
1774 rcstr!(
1775 "https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#configuring-webpack-loaders"
1776 )
1777}
1778
1779#[turbo_tasks::value(shared)]
1780struct InvalidLoaderRuleRenameAsIssue {
1781 glob: RcStr,
1782 rename_as: RcStr,
1783 config_file_path: FileSystemPath,
1784}
1785
1786#[async_trait]
1787#[turbo_tasks::value_impl]
1788impl Issue for InvalidLoaderRuleRenameAsIssue {
1789 async fn file_path(&self) -> Result<FileSystemPath> {
1790 Ok(self.config_file_path.clone())
1791 }
1792
1793 fn stage(&self) -> IssueStage {
1794 IssueStage::Config
1795 }
1796
1797 async fn title(&self) -> Result<StyledString> {
1798 Ok(StyledString::Text(
1799 format!("Invalid loader rule for extension: {}", self.glob).into(),
1800 ))
1801 }
1802
1803 async fn description(&self) -> Result<Option<StyledString>> {
1804 Ok(Some(StyledString::Text(RcStr::from(format!(
1805 "The extension {} contains a wildcard, but the `as` option does not: {}",
1806 self.glob, self.rename_as,
1807 )))))
1808 }
1809
1810 fn documentation_link(&self) -> RcStr {
1811 turbopack_config_documentation_link()
1812 }
1813}
1814
1815#[turbo_tasks::value(shared)]
1816struct InvalidLoaderRuleConditionIssue {
1817 error_string: RcStr,
1818 condition: ConfigConditionItem,
1819 config_file_path: FileSystemPath,
1820}
1821
1822#[async_trait]
1823#[turbo_tasks::value_impl]
1824impl Issue for InvalidLoaderRuleConditionIssue {
1825 async fn file_path(&self) -> Result<FileSystemPath> {
1826 Ok(self.config_file_path.clone())
1827 }
1828
1829 fn stage(&self) -> IssueStage {
1830 IssueStage::Config
1831 }
1832
1833 async fn title(&self) -> Result<StyledString> {
1834 Ok(StyledString::Text(rcstr!(
1835 "Invalid condition for Turbopack loader rule"
1836 )))
1837 }
1838
1839 async fn description(&self) -> Result<Option<StyledString>> {
1840 Ok(Some(StyledString::Stack(vec![
1841 StyledString::Line(vec![
1842 StyledString::Text(rcstr!("Encountered the following error: ")),
1843 StyledString::Code(self.error_string.clone()),
1844 ]),
1845 StyledString::Text(rcstr!("While processing the condition:")),
1846 StyledString::Code(RcStr::from(format!("{:#?}", self.condition))),
1847 ])))
1848 }
1849
1850 fn documentation_link(&self) -> RcStr {
1851 turbopack_config_documentation_link()
1852 }
1853}
1854
1855#[turbo_tasks::value(transparent)]
1856pub struct OutputFileTracingIncludesExcludes(
1857 #[bincode(with = "turbo_bincode::indexmap")]
1858 FxIndexMap<ResolvedVc<Glob>, Vec<(RcStr, FileSystemPath)>>,
1859);
1860
1861impl OutputFileTracingIncludesExcludes {
1862 pub async fn parse(
1863 project_path: FileSystemPath,
1864 value: &Option<serde_json::Value>,
1865 ) -> Result<OutputFileTracingIncludesExcludes> {
1866 if let Some(value) = value
1867 && let Some(map) = value.as_object()
1868 {
1869 Ok(OutputFileTracingIncludesExcludes(
1870 map.iter()
1871 .map(async |(route_pattern, file_patterns)| {
1872 let route_pattern = Glob::new(
1873 RcStr::from(route_pattern.clone()),
1874 GlobOptions {
1875 contains: true,
1876 ..Default::default()
1877 },
1878 )
1879 .to_resolved()
1880 .await?;
1881 let file_patterns = file_patterns
1882 .as_array()
1883 .iter()
1884 .flat_map(|pattern| pattern.iter())
1885 .filter_map(|pattern| pattern.as_str())
1886 .map(async |pattern_str| {
1887 let (glob, root) = relativize_glob(pattern_str, &project_path)?;
1888 Ok((RcStr::from(glob), root))
1889 })
1890 .try_join()
1891 .await?;
1892 Ok((route_pattern, file_patterns))
1893 })
1894 .try_join()
1895 .await?
1896 .into_iter()
1897 .collect(),
1898 ))
1899 } else {
1900 Ok(OutputFileTracingIncludesExcludes(FxIndexMap::default()))
1901 }
1902 }
1903}
1904
1905#[turbo_tasks::value_impl]
1906impl NextConfig {
1907 #[turbo_tasks::function]
1908 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
1909 let string = string.await?;
1910 let mut jdeserializer = serde_json::Deserializer::from_str(&string);
1911 let config: NextConfig = serde_path_to_error::deserialize(&mut jdeserializer)
1912 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
1913 Ok(config.cell())
1914 }
1915
1916 #[turbo_tasks::function]
1917 pub async fn config_file_path(
1918 &self,
1919 project_path: FileSystemPath,
1920 ) -> Result<Vc<FileSystemPath>> {
1921 Ok(project_path.join(&self.config_file_name)?.cell())
1922 }
1923
1924 #[turbo_tasks::function]
1925 pub fn bundle_pages_router_dependencies(&self) -> Vc<bool> {
1926 Vc::cell(self.bundle_pages_router_dependencies.unwrap_or_default())
1927 }
1928
1929 #[turbo_tasks::function]
1930 pub fn enable_react_production_profiling(&self) -> Vc<bool> {
1931 Vc::cell(self.react_production_profiling.unwrap_or_default())
1932 }
1933
1934 #[turbo_tasks::function]
1935 pub fn server_external_packages(&self) -> Vc<Vec<RcStr>> {
1936 Vc::cell(
1937 self.server_external_packages
1938 .as_ref()
1939 .cloned()
1940 .unwrap_or_default(),
1941 )
1942 }
1943
1944 #[turbo_tasks::function]
1945 pub fn is_standalone(&self) -> Vc<bool> {
1946 Vc::cell(self.output == Some(OutputType::Standalone))
1947 }
1948
1949 #[turbo_tasks::function]
1950 pub fn base_path(&self) -> Vc<Option<RcStr>> {
1951 Vc::cell(self.base_path.clone())
1952 }
1953
1954 #[turbo_tasks::function]
1955 pub fn cache_handler(&self, project_path: FileSystemPath) -> Result<Vc<OptionFileSystemPath>> {
1956 if let Some(handler) = &self.cache_handler {
1957 Ok(Vc::cell(Some(project_path.join(handler)?)))
1958 } else {
1959 Ok(Vc::cell(None))
1960 }
1961 }
1962
1963 #[turbo_tasks::function]
1964 pub fn compiler(&self) -> Vc<CompilerConfig> {
1965 self.compiler.clone().unwrap_or_default().cell()
1966 }
1967
1968 #[turbo_tasks::function]
1969 pub fn env(&self) -> Vc<EnvMap> {
1970 let env = self
1974 .env
1975 .iter()
1976 .map(|(k, v)| {
1977 (
1978 k.as_str().into(),
1979 if let JsonValue::String(s) = v {
1980 s.as_str().into()
1982 } else {
1983 v.to_string().into()
1984 },
1985 )
1986 })
1987 .collect();
1988
1989 Vc::cell(env)
1990 }
1991
1992 #[turbo_tasks::function]
1993 pub fn image_config(&self) -> Vc<ImageConfig> {
1994 self.images.clone().cell()
1995 }
1996
1997 #[turbo_tasks::function]
1998 pub fn page_extensions(&self) -> Vc<Vec<RcStr>> {
1999 let mut extensions = self.page_extensions.clone();
2003 extensions.sort_by_key(|ext| std::cmp::Reverse(ext.len()));
2004 Vc::cell(extensions)
2005 }
2006
2007 #[turbo_tasks::function]
2008 pub fn instrumentation_client_inject(&self) -> Vc<Vec<RcStr>> {
2009 Vc::cell(
2010 self.instrumentation_client_inject
2011 .clone()
2012 .unwrap_or_default(),
2013 )
2014 }
2015
2016 #[turbo_tasks::function]
2017 pub fn is_global_not_found_enabled(&self) -> Vc<bool> {
2018 Vc::cell(self.experimental.global_not_found.unwrap_or_default())
2019 }
2020
2021 #[turbo_tasks::function]
2022 pub fn transpile_packages(&self) -> Vc<Vec<RcStr>> {
2023 Vc::cell(self.transpile_packages.clone().unwrap_or_default())
2024 }
2025
2026 #[turbo_tasks::function]
2027 pub async fn webpack_rules(
2028 self: Vc<Self>,
2029 project_path: FileSystemPath,
2030 ) -> Result<Vc<WebpackRules>> {
2031 let this = self.await?;
2032 let Some(turbo_rules) = this.turbopack.as_ref().map(|t| &t.rules) else {
2033 return Ok(Vc::cell(Vec::new()));
2034 };
2035 if turbo_rules.is_empty() {
2036 return Ok(Vc::cell(Vec::new()));
2037 }
2038 let mut rules = Vec::new();
2039 for (glob, rule_collection) in turbo_rules.iter() {
2040 fn transform_loaders(
2041 loaders: &mut dyn Iterator<Item = &LoaderItem>,
2042 ) -> ResolvedVc<WebpackLoaderItems> {
2043 ResolvedVc::cell(
2044 loaders
2045 .map(|item| match item {
2046 LoaderItem::LoaderName(name) => WebpackLoaderItem {
2047 loader: name.clone(),
2048 options: Default::default(),
2049 },
2050 LoaderItem::LoaderOptions(options) => options.clone(),
2051 })
2052 .collect(),
2053 )
2054 }
2055 for item in &rule_collection.0 {
2056 match item {
2057 RuleConfigCollectionItem::Shorthand(loaders) => {
2058 rules.push((
2059 glob.clone(),
2060 LoaderRuleItem {
2061 loaders: transform_loaders(&mut [loaders].into_iter()),
2062 rename_as: None,
2063 condition: None,
2064 module_type: None,
2065 },
2066 ));
2067 }
2068 RuleConfigCollectionItem::Full(RuleConfigItem {
2069 loaders,
2070 rename_as,
2071 condition,
2072 module_type,
2073 }) => {
2074 if glob.contains("*")
2078 && let Some(rename_as) = rename_as.as_ref()
2079 && !rename_as.contains("*")
2080 {
2081 InvalidLoaderRuleRenameAsIssue {
2082 glob: glob.clone(),
2083 config_file_path: self
2084 .config_file_path(project_path.clone())
2085 .owned()
2086 .await?,
2087 rename_as: rename_as.clone(),
2088 }
2089 .resolved_cell()
2090 .emit();
2091 }
2092
2093 let condition = if let Some(condition) = condition {
2096 match ConditionItem::try_from(condition.clone()) {
2097 Ok(cond) => Some(cond),
2098 Err(err) => {
2099 InvalidLoaderRuleConditionIssue {
2100 error_string: RcStr::from(err.to_string()),
2101 condition: condition.clone(),
2102 config_file_path: self
2103 .config_file_path(project_path.clone())
2104 .owned()
2105 .await?,
2106 }
2107 .resolved_cell()
2108 .emit();
2109 None
2110 }
2111 }
2112 } else {
2113 None
2114 };
2115 rules.push((
2116 glob.clone(),
2117 LoaderRuleItem {
2118 loaders: transform_loaders(&mut loaders.iter()),
2119 rename_as: rename_as.clone(),
2120 condition,
2121 module_type: module_type.clone(),
2122 },
2123 ));
2124 }
2125 }
2126 }
2127 }
2128 Ok(Vc::cell(rules))
2129 }
2130
2131 #[turbo_tasks::function]
2132 pub fn resolve_alias_options(&self) -> Result<Vc<ResolveAliasMap>> {
2133 let Some(resolve_alias) = self
2134 .turbopack
2135 .as_ref()
2136 .and_then(|t| t.resolve_alias.as_ref())
2137 else {
2138 return Ok(ResolveAliasMap::cell(ResolveAliasMap::default()));
2139 };
2140 let alias_map: ResolveAliasMap = resolve_alias.try_into()?;
2141 Ok(alias_map.cell())
2142 }
2143
2144 #[turbo_tasks::function]
2145 pub fn resolve_extension(&self) -> Vc<ResolveExtensions> {
2146 let Some(resolve_extensions) = self
2147 .turbopack
2148 .as_ref()
2149 .and_then(|t| t.resolve_extensions.as_ref())
2150 else {
2151 return Vc::cell(None);
2152 };
2153 Vc::cell(Some(resolve_extensions.clone()))
2154 }
2155
2156 #[turbo_tasks::function]
2157 pub fn import_externals(&self) -> Result<Vc<bool>> {
2158 Ok(Vc::cell(match self.experimental.esm_externals {
2159 Some(EsmExternals::Bool(b)) => b,
2160 Some(EsmExternals::Loose(_)) => bail!("esmExternals = \"loose\" is not supported"),
2161 None => true,
2162 }))
2163 }
2164
2165 #[turbo_tasks::function]
2166 pub fn inline_css(&self) -> Vc<bool> {
2167 Vc::cell(self.experimental.inline_css.unwrap_or(false))
2168 }
2169
2170 #[turbo_tasks::function]
2173 pub fn css_chunking(&self) -> Result<Vc<StyleGroupsAlgorithm>> {
2174 Ok(resolve_css_chunking_algorithm(self.experimental.css_chunking.as_ref())?.cell())
2175 }
2176
2177 #[turbo_tasks::function]
2178 pub fn turbopack_chunking(&self) -> Result<Vc<TurbopackChunking>> {
2179 let config = self.experimental.turbopack_chunking.as_ref();
2180 let clusters = config
2181 .and_then(|c| c.clusters.as_deref())
2182 .unwrap_or_default()
2183 .iter()
2184 .map(|patterns| parse_route_regexes(patterns))
2185 .collect::<Result<Vec<_>>>()?;
2186 let priority_routes = parse_route_regexes(
2187 config
2188 .and_then(|c| c.priority_routes.as_deref())
2189 .unwrap_or_default(),
2190 )?;
2191 Ok(TurbopackChunking {
2192 clusters,
2193 first_page_load_priority: config
2194 .and_then(|c| c.first_page_load_priority)
2195 .map(|priority| (priority.clamp(0.0, 1.0) * 100.0).round() as u32),
2196 priority_routes,
2197 priority_boost_percent: config
2198 .and_then(|c| c.priority_boost)
2199 .map(|boost| (boost.max(0.0) * 100.0).round() as u32),
2200 request_cost: config.and_then(|c| c.request_cost),
2201 min_chunk_size: config.and_then(|c| c.min_chunk_size),
2202 max_chunk_count_per_group: config.and_then(|c| c.max_chunk_count_per_group),
2203 max_merge_chunk_size: config.and_then(|c| c.max_merge_chunk_size),
2204 min_component_chunk_size: config.and_then(|c| c.min_component_chunk_size),
2205 generate_component_chunks: config
2206 .and_then(|c| c.generate_component_chunks)
2207 .unwrap_or(false),
2208 }
2209 .cell())
2210 }
2211
2212 #[turbo_tasks::function]
2213 pub fn mdx_rs(&self) -> Vc<OptionalMdxTransformOptions> {
2214 let options = &self.experimental.mdx_rs;
2215
2216 let options = match options {
2217 Some(MdxRsOptions::Boolean(true)) => OptionalMdxTransformOptions(Some(
2218 MdxTransformOptions {
2219 provider_import_source: Some(mdx_import_source_file()),
2220 ..Default::default()
2221 }
2222 .resolved_cell(),
2223 )),
2224 Some(MdxRsOptions::Option(options)) => OptionalMdxTransformOptions(Some(
2225 MdxTransformOptions {
2226 provider_import_source: Some(
2227 options
2228 .provider_import_source
2229 .clone()
2230 .unwrap_or(mdx_import_source_file()),
2231 ),
2232 ..options.clone()
2233 }
2234 .resolved_cell(),
2235 )),
2236 _ => OptionalMdxTransformOptions(None),
2237 };
2238
2239 options.cell()
2240 }
2241
2242 #[turbo_tasks::function]
2243 pub fn modularize_imports(&self) -> Vc<ModularizeImports> {
2244 Vc::cell(self.modularize_imports.clone().unwrap_or_default())
2245 }
2246
2247 #[turbo_tasks::function]
2248 pub fn dist_dir(&self) -> Vc<RcStr> {
2249 Vc::cell(self.dist_dir.clone())
2250 }
2251 #[turbo_tasks::function]
2252 pub fn dist_dir_root(&self) -> Vc<RcStr> {
2253 Vc::cell(self.dist_dir_root.clone())
2254 }
2255
2256 #[turbo_tasks::function]
2257 pub fn cache_handlers(&self, project_path: FileSystemPath) -> Result<Vc<FileSystemPathVec>> {
2258 if let Some(handlers) = &self.cache_handlers {
2259 Ok(Vc::cell(
2260 handlers
2261 .values()
2262 .map(|h| project_path.join(h))
2263 .collect::<Result<Vec<_>>>()?,
2264 ))
2265 } else {
2266 Ok(Vc::cell(vec![]))
2267 }
2268 }
2269
2270 #[turbo_tasks::function]
2271 pub fn cache_handlers_map(&self) -> Vc<CacheHandlersMap> {
2272 Vc::cell(self.cache_handlers.clone().unwrap_or_default())
2273 }
2274
2275 #[turbo_tasks::function]
2276 pub fn experimental_swc_plugins(&self) -> Vc<SwcPlugins> {
2277 Vc::cell(self.experimental.swc_plugins.clone().unwrap_or_default())
2278 }
2279
2280 #[turbo_tasks::function]
2281 pub fn experimental_swc_env_options(&self) -> Vc<OptionSwcEnvOptions> {
2282 Vc::cell(self.experimental.swc_env_options.clone())
2283 }
2284
2285 #[turbo_tasks::function]
2286 pub fn experimental_sri(&self) -> Vc<OptionSubResourceIntegrity> {
2287 Vc::cell(self.experimental.sri.clone())
2288 }
2289
2290 #[turbo_tasks::function]
2291 pub fn experimental_turbopack_use_builtin_babel(&self) -> Vc<Option<bool>> {
2292 Vc::cell(self.experimental.turbopack_use_builtin_babel)
2293 }
2294
2295 #[turbo_tasks::function]
2296 pub fn experimental_turbopack_use_builtin_sass(&self) -> Vc<Option<bool>> {
2297 Vc::cell(self.experimental.turbopack_use_builtin_sass)
2298 }
2299
2300 #[turbo_tasks::function]
2301 pub fn experimental_turbopack_local_postcss_config(&self) -> Vc<Option<bool>> {
2302 Vc::cell(self.experimental.turbopack_local_postcss_config)
2303 }
2304
2305 #[turbo_tasks::function]
2306 pub fn react_compiler_options(&self) -> Vc<OptionalReactCompilerOptions> {
2307 let options = &self.react_compiler;
2308
2309 let options = match options {
2310 Some(ReactCompilerOptionsOrBoolean::Boolean(true)) => {
2311 OptionalReactCompilerOptions(Some(ReactCompilerOptions::default().resolved_cell()))
2312 }
2313 Some(ReactCompilerOptionsOrBoolean::Option(options)) => OptionalReactCompilerOptions(
2314 Some(ReactCompilerOptions { ..options.clone() }.resolved_cell()),
2315 ),
2316 _ => OptionalReactCompilerOptions(None),
2317 };
2318
2319 options.cell()
2320 }
2321
2322 #[turbo_tasks::function]
2325 pub fn rust_react_compiler(&self) -> Vc<OptionReactCompilerCompilationMode> {
2326 let use_rust = self
2327 .experimental
2328 .turbopack_rust_react_compiler
2329 .unwrap_or(false);
2330 let mode = match (use_rust, &self.react_compiler) {
2331 (true, Some(ReactCompilerOptionsOrBoolean::Boolean(true))) => {
2332 Some(ReactCompilerCompilationMode::Infer)
2333 }
2334 (true, Some(ReactCompilerOptionsOrBoolean::Option(opts))) => {
2335 Some(opts.compilation_mode)
2336 }
2337 _ => None,
2338 };
2339 Vc::cell(mode)
2340 }
2341
2342 #[turbo_tasks::function]
2343 pub fn sass_config(&self) -> Vc<JsonValue> {
2344 Vc::cell(self.sass_options.clone().unwrap_or_default())
2345 }
2346
2347 #[turbo_tasks::function]
2348 pub fn skip_proxy_url_normalize(&self) -> Vc<bool> {
2349 Vc::cell(self.skip_proxy_url_normalize.unwrap_or(false))
2350 }
2351
2352 #[turbo_tasks::function]
2353 pub fn skip_trailing_slash_redirect(&self) -> Vc<bool> {
2354 Vc::cell(self.skip_trailing_slash_redirect.unwrap_or(false))
2355 }
2356
2357 #[turbo_tasks::function]
2360 pub async fn computed_asset_prefix(self: Vc<Self>) -> Result<Vc<RcStr>> {
2361 let this = self.await?;
2362
2363 Ok(Vc::cell(
2364 format!(
2365 "{}/_next/",
2366 if let Some(asset_prefix) = &this.asset_prefix {
2367 asset_prefix
2368 } else {
2369 this.base_path.as_ref().map_or("", |b| b.as_str())
2370 }
2371 .trim_end_matches('/')
2372 )
2373 .into(),
2374 ))
2375 }
2376
2377 #[turbo_tasks::function]
2379 pub fn asset_suffix_path(&self) -> Vc<Option<RcStr>> {
2380 let needs_dpl_id = self.supports_immutable_assets.is_none_or(|f| !f);
2381
2382 Vc::cell(
2383 needs_dpl_id
2384 .then_some(self.deployment_id.as_ref())
2385 .flatten()
2386 .map(|id| format!("?dpl={id}").into()),
2387 )
2388 }
2389
2390 #[turbo_tasks::function]
2393 pub fn enable_immutable_assets(&self) -> Vc<bool> {
2394 Vc::cell(self.supports_immutable_assets == Some(true))
2395 }
2396
2397 #[turbo_tasks::function]
2398 pub fn client_static_folder_name(&self) -> Vc<RcStr> {
2399 Vc::cell(if self.supports_immutable_assets == Some(true) {
2400 rcstr!("static/immutable")
2402 } else {
2403 rcstr!("static")
2404 })
2405 }
2406
2407 #[turbo_tasks::function]
2408 pub fn enable_taint(&self) -> Vc<bool> {
2409 Vc::cell(self.experimental.taint.unwrap_or(false))
2410 }
2411
2412 #[turbo_tasks::function]
2413 pub fn enable_transition_indicator(&self) -> Vc<bool> {
2414 Vc::cell(self.experimental.transition_indicator.unwrap_or(false))
2415 }
2416
2417 #[turbo_tasks::function]
2418 pub fn enable_gesture_transition(&self) -> Vc<bool> {
2419 Vc::cell(self.experimental.gesture_transition.unwrap_or(false))
2420 }
2421
2422 #[turbo_tasks::function]
2423 pub fn enable_blocking_ssr(&self) -> Vc<bool> {
2424 Vc::cell(self.experimental.blocking_ssr.unwrap_or(false))
2425 }
2426
2427 #[turbo_tasks::function]
2428 pub fn enable_expose_testing_api_in_production_build(&self) -> Vc<bool> {
2429 Vc::cell(
2430 self.experimental
2431 .expose_testing_api_in_production_build
2432 .unwrap_or(false),
2433 )
2434 }
2435
2436 #[turbo_tasks::function]
2437 pub fn enable_concurrent_router_queue(&self) -> Vc<bool> {
2438 Vc::cell(self.experimental.concurrent_router_queue.unwrap_or(false))
2439 }
2440
2441 #[turbo_tasks::function]
2442 pub fn enable_cache_components(&self) -> Vc<bool> {
2443 Vc::cell(self.cache_components.unwrap_or(false))
2444 }
2445
2446 #[turbo_tasks::function]
2447 pub fn enable_use_cache(&self) -> Vc<bool> {
2448 Vc::cell(
2449 self.experimental
2450 .use_cache
2451 .unwrap_or(self.cache_components.unwrap_or(false)),
2455 )
2456 }
2457
2458 #[turbo_tasks::function]
2459 pub async fn enable_durable_use_cache_entries(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2460 Ok(match *mode.await? {
2461 NextMode::Development => Vc::cell(false),
2463 NextMode::Build => {
2464 Vc::cell(self.experimental.durable_use_cache_entries.unwrap_or(false))
2465 }
2466 })
2467 }
2468
2469 #[turbo_tasks::function]
2470 pub fn is_using_adapter(&self) -> Vc<bool> {
2471 Vc::cell(self.adapter_path.is_some())
2472 }
2473
2474 #[turbo_tasks::function]
2475 pub fn should_append_server_deployment_id_at_runtime(&self) -> Vc<bool> {
2476 let needs_dpl_id = self.supports_immutable_assets.is_none_or(|f| !f);
2477
2478 Vc::cell(
2479 needs_dpl_id
2480 && self
2481 .experimental
2482 .runtime_server_deployment_id
2483 .unwrap_or(false),
2484 )
2485 }
2486
2487 #[turbo_tasks::function]
2488 pub fn cache_kinds(&self) -> Vc<CacheKinds> {
2489 let mut cache_kinds = CacheKinds::default();
2490
2491 if let Some(handlers) = self.cache_handlers.as_ref() {
2492 cache_kinds.extend(handlers.keys().cloned());
2493 }
2494
2495 cache_kinds.cell()
2496 }
2497
2498 #[turbo_tasks::function]
2499 pub fn optimize_package_imports(&self) -> Vc<Vec<RcStr>> {
2500 Vc::cell(
2501 self.experimental
2502 .optimize_package_imports
2503 .clone()
2504 .unwrap_or_default(),
2505 )
2506 }
2507
2508 #[turbo_tasks::function]
2509 pub fn module_fragments_enabled_for_foreign_code(&self, _is_development: bool) -> Vc<bool> {
2510 Vc::cell(matches!(
2511 self.experimental.turbopack_module_fragments,
2512 Some(true)
2513 ))
2514 }
2515
2516 #[turbo_tasks::function]
2517 pub fn module_fragments_enabled_for_user_code(&self, _is_development: bool) -> Vc<bool> {
2518 Vc::cell(matches!(
2519 self.experimental.turbopack_module_fragments,
2520 Some(true)
2521 ))
2522 }
2523
2524 #[turbo_tasks::function]
2525 pub async fn turbopack_remove_unused_imports(
2526 self: Vc<Self>,
2527 mode: Vc<NextMode>,
2528 ) -> Result<Vc<bool>> {
2529 let remove_unused_imports = self
2530 .await?
2531 .experimental
2532 .turbopack_remove_unused_imports
2533 .unwrap_or(matches!(*mode.await?, NextMode::Build));
2534
2535 if remove_unused_imports && !*self.turbopack_remove_unused_exports(mode).await? {
2536 bail!(
2537 "`experimental.turbopackRemoveUnusedImports` cannot be enabled without also \
2538 enabling `experimental.turbopackRemoveUnusedExports`"
2539 );
2540 }
2541
2542 Ok(Vc::cell(remove_unused_imports))
2543 }
2544
2545 #[turbo_tasks::function]
2546 pub async fn turbopack_remove_unused_exports(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2547 Ok(Vc::cell(
2548 self.experimental
2549 .turbopack_remove_unused_exports
2550 .unwrap_or(matches!(*mode.await?, NextMode::Build)),
2551 ))
2552 }
2553
2554 #[turbo_tasks::function]
2555 pub fn turbopack_infer_module_side_effects(&self) -> Vc<bool> {
2556 Vc::cell(
2557 self.experimental
2558 .turbopack_infer_module_side_effects
2559 .unwrap_or(true),
2560 )
2561 }
2562
2563 #[turbo_tasks::function]
2564 pub fn turbopack_cjs_tree_shaking(&self) -> Vc<bool> {
2565 Vc::cell(
2566 self.experimental
2567 .turbopack_cjs_tree_shaking
2568 .unwrap_or(false),
2569 )
2570 }
2571
2572 #[turbo_tasks::function]
2573 pub fn turbopack_cjs_scope_hoisting(&self) -> Vc<bool> {
2574 Vc::cell(
2575 self.experimental
2576 .turbopack_cjs_scope_hoisting
2577 .unwrap_or(false),
2578 )
2579 }
2580
2581 #[turbo_tasks::function]
2582 pub fn turbopack_cross_module_constants(&self) -> Vc<bool> {
2583 Vc::cell(
2584 self.experimental
2585 .turbopack_cross_module_constants
2586 .unwrap_or(false),
2587 )
2588 }
2589
2590 #[turbo_tasks::function]
2591 pub fn turbopack_plugin_runtime_strategy(&self) -> Vc<TurbopackPluginRuntimeStrategy> {
2592 #[cfg(feature = "process_pool")]
2593 let default = TurbopackPluginRuntimeStrategy::ChildProcesses;
2594 #[cfg(all(feature = "worker_pool", not(feature = "process_pool")))]
2595 let default = TurbopackPluginRuntimeStrategy::WorkerThreads;
2596
2597 self.experimental
2598 .turbopack_plugin_runtime_strategy
2599 .unwrap_or(default)
2600 .cell()
2601 }
2602
2603 #[turbo_tasks::function]
2604 pub async fn module_ids(&self, mode: Vc<NextMode>) -> Result<Vc<ModuleIds>> {
2605 Ok(match *mode.await? {
2606 NextMode::Development => ModuleIds::Named.cell(),
2608 NextMode::Build => self
2609 .experimental
2610 .turbopack_module_ids
2611 .unwrap_or(ModuleIds::Deterministic)
2612 .cell(),
2613 })
2614 }
2615
2616 #[turbo_tasks::function]
2618 pub async fn turbo_client_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2619 let default = matches!(*mode.await?, NextMode::Build);
2620 let minify = self
2621 .experimental
2622 .turbopack_minify
2623 .as_ref()
2624 .and_then(TurbopackMinify::client);
2625 Ok(Vc::cell(minify.unwrap_or(default)))
2626 }
2627
2628 #[turbo_tasks::function]
2631 pub async fn turbo_server_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2632 let default = matches!(*mode.await?, NextMode::Build)
2633 && self.experimental.server_minification.unwrap_or(true);
2634 let minify = self
2635 .experimental
2636 .turbopack_minify
2637 .as_ref()
2638 .and_then(TurbopackMinify::server);
2639 Ok(Vc::cell(minify.unwrap_or(default)))
2640 }
2641
2642 #[turbo_tasks::function]
2645 pub async fn turbo_edge_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2646 let default = matches!(*mode.await?, NextMode::Build);
2647 let minify = self
2648 .experimental
2649 .turbopack_minify
2650 .as_ref()
2651 .and_then(TurbopackMinify::edge);
2652 Ok(Vc::cell(minify.unwrap_or(default)))
2653 }
2654
2655 #[turbo_tasks::function]
2656 pub async fn turbo_scope_hoisting(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2657 Ok(Vc::cell(match *mode.await? {
2658 NextMode::Development => false,
2660 NextMode::Build => self.experimental.turbopack_scope_hoisting.unwrap_or(true),
2661 }))
2662 }
2663
2664 #[turbo_tasks::function]
2665 pub fn turbopack_generate_component_chunks(&self) -> Vc<bool> {
2666 Vc::cell(
2667 self.experimental
2668 .turbopack_chunking
2669 .as_ref()
2670 .and_then(|c| c.generate_component_chunks)
2671 .unwrap_or(false),
2672 )
2673 }
2674
2675 #[turbo_tasks::function]
2676 pub async fn turbo_shared_runtime(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2677 Ok(Vc::cell(match *mode.await? {
2678 NextMode::Development => false,
2681 NextMode::Build => self.experimental.turbopack_shared_runtime.unwrap_or(false),
2682 }))
2683 }
2684
2685 #[turbo_tasks::function]
2686 pub async fn turbo_nested_async_chunking(
2687 &self,
2688 mode: Vc<NextMode>,
2689 client_side: bool,
2690 ) -> Result<Vc<bool>> {
2691 let option = if client_side {
2692 self.experimental
2693 .turbopack_client_side_nested_async_chunking
2694 } else {
2695 self.experimental
2696 .turbopack_server_side_nested_async_chunking
2697 };
2698 Ok(Vc::cell(if let Some(value) = option {
2699 value
2700 } else {
2701 match *mode.await? {
2702 NextMode::Development => false,
2703 NextMode::Build => client_side,
2704 }
2705 }))
2706 }
2707
2708 #[turbo_tasks::function]
2709 pub async fn turbopack_import_type_bytes(&self) -> Vc<bool> {
2710 Vc::cell(
2711 self.experimental
2712 .turbopack_import_type_bytes
2713 .unwrap_or(false),
2714 )
2715 }
2716
2717 #[turbo_tasks::function]
2718 pub fn lightningcss_feature_flags(
2719 &self,
2720 ) -> Result<Vc<turbopack_css::LightningCssFeatureFlags>> {
2721 Ok(turbopack_css::LightningCssFeatureFlags {
2722 include: lightningcss_features_field_mask(
2723 &self.experimental.lightning_css_features,
2724 |f| f.include.as_ref(),
2725 )?,
2726 exclude: lightningcss_features_field_mask(
2727 &self.experimental.lightning_css_features,
2728 |f| f.exclude.as_ref(),
2729 )?,
2730 }
2731 .cell())
2732 }
2733
2734 #[turbo_tasks::function]
2735 pub async fn client_source_maps(&self, mode: Vc<NextMode>) -> Result<Vc<SourceMapsType>> {
2736 let input_source_maps = self
2737 .experimental
2738 .turbopack_input_source_maps
2739 .unwrap_or(true);
2740 let source_maps = self
2741 .experimental
2742 .turbopack_source_maps
2743 .unwrap_or(match &*mode.await? {
2744 NextMode::Development => true,
2745 NextMode::Build => self.production_browser_source_maps,
2746 });
2747 Ok(match (source_maps, input_source_maps) {
2748 (true, true) => SourceMapsType::Full,
2749 (true, false) => SourceMapsType::Partial,
2750 (false, _) => SourceMapsType::None,
2751 }
2752 .cell())
2753 }
2754
2755 #[turbo_tasks::function]
2756 pub fn server_source_maps(&self) -> Result<Vc<SourceMapsType>> {
2757 let input_source_maps = self
2758 .experimental
2759 .turbopack_input_source_maps
2760 .unwrap_or(true);
2761 let source_maps = self
2762 .experimental
2763 .turbopack_source_maps
2764 .or(self.experimental.server_source_maps)
2765 .unwrap_or(true);
2766 Ok(match (source_maps, input_source_maps) {
2767 (true, true) => SourceMapsType::Full,
2768 (true, false) => SourceMapsType::Partial,
2769 (false, _) => SourceMapsType::None,
2770 }
2771 .cell())
2772 }
2773
2774 #[turbo_tasks::function]
2775 pub fn turbopack_debug_ids(&self) -> Vc<bool> {
2776 Vc::cell(
2777 self.turbopack
2778 .as_ref()
2779 .and_then(|turbopack| turbopack.debug_ids)
2780 .unwrap_or(false),
2781 )
2782 }
2783
2784 #[turbo_tasks::function]
2787 pub fn turbopack_worker_asset_prefix(&self) -> Vc<Option<RcStr>> {
2788 Vc::cell(
2789 self.experimental
2790 .turbopack_worker_asset_prefix
2791 .as_ref()
2792 .map(|prefix| format!("{}/_next/", prefix.trim_end_matches('/')).into()),
2793 )
2794 }
2795
2796 #[turbo_tasks::function]
2797 pub fn turbopack_chunk_loading_global(&self) -> Vc<Option<RcStr>> {
2798 Vc::cell(
2799 self.turbopack
2800 .as_ref()
2801 .and_then(|t| t.chunk_loading_global.clone()),
2802 )
2803 }
2804
2805 #[turbo_tasks::function]
2806 pub fn typescript_tsconfig_path(&self) -> Result<Vc<Option<RcStr>>> {
2807 Ok(Vc::cell(
2808 self.typescript
2809 .tsconfig_path
2810 .as_ref()
2811 .map(|path| path.to_owned().into()),
2812 ))
2813 }
2814
2815 #[turbo_tasks::function]
2816 pub fn cross_origin(&self) -> Vc<CrossOrigin> {
2817 *self.cross_origin.resolved_cell()
2818 }
2819
2820 #[turbo_tasks::function]
2821 pub fn i18n(&self) -> Vc<OptionI18NConfig> {
2822 Vc::cell(self.i18n.clone())
2823 }
2824
2825 #[turbo_tasks::function]
2826 pub fn output(&self) -> Vc<OptionOutputType> {
2827 Vc::cell(self.output.clone())
2828 }
2829
2830 #[turbo_tasks::function]
2831 pub async fn output_file_tracing_includes(
2832 &self,
2833 project_path: FileSystemPath,
2834 ) -> Result<Vc<OutputFileTracingIncludesExcludes>> {
2835 Ok(OutputFileTracingIncludesExcludes::parse(
2836 project_path,
2837 &self.output_file_tracing_includes,
2838 )
2839 .await?
2840 .cell())
2841 }
2842
2843 #[turbo_tasks::function]
2844 pub async fn output_file_tracing_excludes(
2845 &self,
2846 project_path: FileSystemPath,
2847 ) -> Result<Vc<OutputFileTracingIncludesExcludes>> {
2848 Ok(OutputFileTracingIncludesExcludes::parse(
2849 project_path,
2850 &self.output_file_tracing_excludes,
2851 )
2852 .await?
2853 .cell())
2854 }
2855
2856 #[turbo_tasks::function]
2857 pub async fn fetch_client(&self, next_mode: Vc<NextMode>) -> Result<Vc<FetchClientConfig>> {
2858 let (connect_timeout, timeout) = if matches!(*next_mode.await?, NextMode::Development) {
2861 (Duration::from_secs(5), Duration::from_secs(10))
2862 } else {
2863 (Duration::from_secs(10), Duration::from_secs(30))
2864 };
2865 Ok(FetchClientConfig {
2866 connect_timeout,
2867 timeout,
2868 max_retries: 1,
2869 ..Default::default()
2870 }
2871 .cell())
2872 }
2873
2874 #[turbo_tasks::function]
2875 pub async fn report_system_env_inlining(&self) -> Result<Vc<IssueSeverity>> {
2876 match self.experimental.report_system_env_inlining.as_deref() {
2877 None => Ok(IssueSeverity::Suggestion.cell()),
2878 Some("warn") => Ok(IssueSeverity::Warning.cell()),
2879 Some("error") => Ok(IssueSeverity::Error.cell()),
2880 _ => bail!(
2881 "`experimental.reportSystemEnvInlining` must be undefined, \"error\", or \"warn\""
2882 ),
2883 }
2884 }
2885
2886 #[turbo_tasks::function]
2889 pub fn turbopack_ignore_issue_rules(&self) -> Result<Vc<IgnoreIssues>> {
2890 let rules = self
2891 .turbopack
2892 .as_ref()
2893 .and_then(|tp| tp.ignore_issue.as_deref())
2894 .unwrap_or_default()
2895 .iter()
2896 .map(|rule| {
2897 Ok(IgnoreIssue {
2898 path: rule.path.to_ignore_pattern()?,
2899 title: rule
2900 .title
2901 .as_ref()
2902 .map(|t| t.to_ignore_pattern())
2903 .transpose()?,
2904 description: rule
2905 .description
2906 .as_ref()
2907 .map(|d| d.to_ignore_pattern())
2908 .transpose()?,
2909 })
2910 })
2911 .collect::<Result<_>>()?;
2912 Ok(Vc::cell(rules))
2913 }
2914
2915 #[turbo_tasks::function]
2916 pub fn output_hash_salt(&self) -> Vc<RcStr> {
2917 Vc::cell(self.output_hash_salt.clone().unwrap_or_default())
2918 }
2919}
2920
2921#[turbo_tasks::value(serialization = "custom", eq = "manual")]
2924#[derive(Clone, Debug, Default, PartialEq, Deserialize, Encode, Decode)]
2925#[serde(rename_all = "camelCase")]
2926pub struct JsConfig {
2927 #[bincode(with = "turbo_bincode::serde_self_describing")]
2928 compiler_options: Option<serde_json::Value>,
2929}
2930
2931#[turbo_tasks::value_impl]
2932impl JsConfig {
2933 #[turbo_tasks::function]
2934 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
2935 let string = string.await?;
2936 let config: JsConfig = serde_json::from_str(&string)
2937 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
2938
2939 Ok(config.cell())
2940 }
2941
2942 #[turbo_tasks::function]
2943 pub fn compiler_options(&self) -> Vc<serde_json::Value> {
2944 Vc::cell(self.compiler_options.clone().unwrap_or_default())
2945 }
2946}
2947
2948fn lightningcss_features_field_mask(
2951 features: &Option<LightningCssFeatures>,
2952 field: impl FnOnce(&LightningCssFeatures) -> Option<&Vec<RcStr>>,
2953) -> Result<u32> {
2954 features
2955 .as_ref()
2956 .and_then(field)
2957 .map(|names| lightningcss_feature_names_to_mask(names))
2958 .unwrap_or(Ok(0))
2959}
2960
2961pub fn lightningcss_feature_names_to_mask(
2970 names: &[impl std::ops::Deref<Target = str>],
2971) -> Result<u32> {
2972 use lightningcss::targets::Features;
2973 let mut mask = Features::empty();
2974 for name in names {
2975 mask |= match &**name {
2976 "nesting" => Features::Nesting,
2977 "not-selector-list" => Features::NotSelectorList,
2978 "dir-selector" => Features::DirSelector,
2979 "lang-selector-list" => Features::LangSelectorList,
2980 "is-selector" => Features::IsSelector,
2981 "text-decoration-thickness-percent" => Features::TextDecorationThicknessPercent,
2982 "media-interval-syntax" => Features::MediaIntervalSyntax,
2983 "media-range-syntax" => Features::MediaRangeSyntax,
2984 "custom-media-queries" => Features::CustomMediaQueries,
2985 "clamp-function" => Features::ClampFunction,
2986 "color-function" => Features::ColorFunction,
2987 "oklab-colors" => Features::OklabColors,
2988 "lab-colors" => Features::LabColors,
2989 "p3-colors" => Features::P3Colors,
2990 "hex-alpha-colors" => Features::HexAlphaColors,
2991 "space-separated-color-notation" => Features::SpaceSeparatedColorNotation,
2992 "font-family-system-ui" => Features::FontFamilySystemUi,
2993 "double-position-gradients" => Features::DoublePositionGradients,
2994 "vendor-prefixes" => Features::VendorPrefixes,
2995 "logical-properties" => Features::LogicalProperties,
2996 "light-dark" => Features::LightDark,
2997 "selectors" => Features::Selectors,
2999 "media-queries" => Features::MediaQueries,
3000 "colors" => Features::Colors,
3001 _ => bail!("Unknown lightningcss feature: {}", &**name),
3002 };
3003 }
3004 Ok(mask.bits())
3005}
3006
3007#[cfg(test)]
3008mod tests {
3009 use super::*;
3010
3011 #[test]
3012 fn test_serde_rule_config_item_options() {
3013 let json_value = serde_json::json!({
3014 "loaders": [],
3015 "as": "*.js",
3016 "condition": {
3017 "all": [
3018 "production",
3019 {"not": "foreign"},
3020 {"any": [
3021 "browser",
3022 {
3023 "path": { "type": "glob", "value": "*.svg"},
3024 "query": {
3025 "type": "regex",
3026 "value": {
3027 "source": "@someQuery",
3028 "flags": ""
3029 }
3030 },
3031 "content": {
3032 "source": "@someTag",
3033 "flags": ""
3034 }
3035 }
3036 ]},
3037 ],
3038 }
3039 });
3040
3041 let rule_config: RuleConfigItem = serde_json::from_value(json_value).unwrap();
3042
3043 assert_eq!(
3044 rule_config,
3045 RuleConfigItem {
3046 loaders: vec![],
3047 rename_as: Some(rcstr!("*.js")),
3048 module_type: None,
3049 condition: Some(ConfigConditionItem::All(
3050 [
3051 ConfigConditionItem::Builtin(WebpackLoaderBuiltinCondition::Production),
3052 ConfigConditionItem::Not(Box::new(ConfigConditionItem::Builtin(
3053 WebpackLoaderBuiltinCondition::Foreign
3054 ))),
3055 ConfigConditionItem::Any(
3056 vec![
3057 ConfigConditionItem::Builtin(
3058 WebpackLoaderBuiltinCondition::Browser
3059 ),
3060 ConfigConditionItem::Base {
3061 path: Some(ConfigConditionPath::Glob(rcstr!("*.svg"))),
3062 content: Some(RegexComponents {
3063 source: rcstr!("@someTag"),
3064 flags: rcstr!(""),
3065 }),
3066 query: Some(ConfigConditionQuery::Regex(RegexComponents {
3067 source: rcstr!("@someQuery"),
3068 flags: rcstr!(""),
3069 })),
3070 content_type: None,
3071 },
3072 ]
3073 .into(),
3074 ),
3075 ]
3076 .into(),
3077 )),
3078 }
3079 );
3080 }
3081}