1use anyhow::{Context, Result, bail};
2use bincode::{Decode, Encode};
3use either::Either;
4use rustc_hash::FxHashSet;
5use serde::{Deserialize, Deserializer, Serialize};
6use serde_json::Value as JsonValue;
7use turbo_esregex::EsRegex;
8use turbo_rcstr::{RcStr, rcstr};
9use turbo_tasks::{
10 FxIndexMap, NonLocalValue, OperationValue, ResolvedVc, TaskInput, Vc, debug::ValueDebugFormat,
11 trace::TraceRawVcs,
12};
13use turbo_tasks_env::EnvMap;
14use turbo_tasks_fetch::FetchClientConfig;
15use turbo_tasks_fs::{
16 FileSystemPath,
17 glob::{Glob, GlobOptions},
18};
19use turbopack::module_options::{
20 ConditionContentType, ConditionItem, ConditionPath, ConditionQuery, LoaderRuleItem,
21 WebpackRules, module_options_context::MdxTransformOptions,
22};
23use turbopack_core::{
24 chunk::SourceMapsType,
25 issue::{
26 IgnoreIssue, IgnoreIssuePattern, Issue, IssueExt, IssueSeverity, IssueStage,
27 OptionStyledString, StyledString,
28 },
29 resolve::ResolveAliasMap,
30};
31use turbopack_ecmascript::{OptionTreeShaking, TreeShakingMode};
32use turbopack_ecmascript_plugins::transform::{
33 emotion::EmotionTransformConfig, relay::RelayConfig,
34 styled_components::StyledComponentsTransformConfig,
35};
36use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems};
37
38use crate::{
39 app_structure::FileSystemPathVec,
40 mode::NextMode,
41 next_import_map::mdx_import_source_file,
42 next_shared::{
43 transforms::ModularizeImportPackageConfig, webpack_rules::WebpackLoaderBuiltinCondition,
44 },
45};
46
47#[turbo_tasks::value(transparent)]
48pub struct ModularizeImports(
49 #[bincode(with = "turbo_bincode::indexmap")] FxIndexMap<String, ModularizeImportPackageConfig>,
50);
51
52#[turbo_tasks::value(transparent)]
53#[derive(Clone, Debug)]
54pub struct CacheKinds(FxHashSet<RcStr>);
55
56impl CacheKinds {
57 pub fn extend<I: IntoIterator<Item = RcStr>>(&mut self, iter: I) {
58 self.0.extend(iter);
59 }
60}
61
62impl Default for CacheKinds {
63 fn default() -> Self {
64 CacheKinds(
65 ["default", "remote", "private"]
66 .iter()
67 .map(|&s| s.into())
68 .collect(),
69 )
70 }
71}
72
73#[turbo_tasks::value(transparent)]
74pub struct CacheHandlersMap(#[bincode(with = "turbo_bincode::indexmap")] FxIndexMap<RcStr, RcStr>);
75
76#[turbo_tasks::value(eq = "manual")]
77#[derive(Clone, Debug, Default, PartialEq, Deserialize)]
78#[serde(default, rename_all = "camelCase")]
79pub struct NextConfig {
80 config_file: Option<RcStr>,
83 config_file_name: RcStr,
84
85 cache_max_memory_size: Option<f64>,
89 cache_handler: Option<RcStr>,
91 #[bincode(with_serde)]
92 cache_handlers: Option<FxIndexMap<RcStr, RcStr>>,
93 #[bincode(with = "turbo_bincode::serde_self_describing")]
94 env: FxIndexMap<String, JsonValue>,
95 experimental: ExperimentalConfig,
96 images: ImageConfig,
97 page_extensions: Vec<RcStr>,
98 react_compiler: Option<ReactCompilerOptionsOrBoolean>,
99 react_production_profiling: Option<bool>,
100 react_strict_mode: Option<bool>,
101 transpile_packages: Option<Vec<RcStr>>,
102 #[bincode(with = "turbo_bincode::serde_self_describing")]
103 modularize_imports: Option<FxIndexMap<String, ModularizeImportPackageConfig>>,
104 dist_dir: RcStr,
105 dist_dir_root: RcStr,
106 deployment_id: Option<RcStr>,
107 #[bincode(with = "turbo_bincode::serde_self_describing")]
108 sass_options: Option<serde_json::Value>,
109 trailing_slash: Option<bool>,
110 asset_prefix: Option<RcStr>,
111 base_path: Option<RcStr>,
112 skip_proxy_url_normalize: Option<bool>,
113 skip_trailing_slash_redirect: Option<bool>,
114 i18n: Option<I18NConfig>,
115 cross_origin: Option<CrossOriginConfig>,
116 dev_indicators: Option<DevIndicatorsConfig>,
117 output: Option<OutputType>,
118 turbopack: Option<TurbopackConfig>,
119 production_browser_source_maps: bool,
120 #[bincode(with = "turbo_bincode::serde_self_describing")]
121 output_file_tracing_includes: Option<serde_json::Value>,
122 #[bincode(with = "turbo_bincode::serde_self_describing")]
123 output_file_tracing_excludes: Option<serde_json::Value>,
124 output_file_tracing_root: Option<RcStr>,
126
127 bundle_pages_router_dependencies: Option<bool>,
132
133 server_external_packages: Option<Vec<RcStr>>,
138
139 #[serde(rename = "_originalRedirects")]
140 original_redirects: Option<Vec<Redirect>>,
141
142 compiler: Option<CompilerConfig>,
144
145 optimize_fonts: Option<bool>,
146
147 clean_dist_dir: bool,
148 compress: bool,
149 eslint: EslintConfig,
150 exclude_default_moment_locales: bool,
151 generate_etags: bool,
152 http_agent_options: HttpAgentConfig,
153 on_demand_entries: OnDemandEntriesConfig,
154 powered_by_header: bool,
155 #[bincode(with = "turbo_bincode::serde_self_describing")]
156 public_runtime_config: FxIndexMap<String, serde_json::Value>,
157 #[bincode(with = "turbo_bincode::serde_self_describing")]
158 server_runtime_config: FxIndexMap<String, serde_json::Value>,
159 static_page_generation_timeout: f64,
160 target: Option<String>,
161 typescript: TypeScriptConfig,
162 use_file_system_public_routes: bool,
163 cache_components: Option<bool>,
164 }
171
172#[turbo_tasks::value_impl]
173impl NextConfig {
174 #[turbo_tasks::function]
175 pub fn with_analyze_config(&self) -> Vc<Self> {
176 let mut new = self.clone();
177 new.experimental.turbopack_source_maps = Some(true);
178 new.experimental.turbopack_input_source_maps = Some(false);
179 new.cell()
180 }
181}
182
183#[derive(
184 Clone,
185 Debug,
186 PartialEq,
187 Eq,
188 Serialize,
189 Deserialize,
190 TraceRawVcs,
191 NonLocalValue,
192 OperationValue,
193 Encode,
194 Decode,
195)]
196#[serde(rename_all = "kebab-case")]
197pub enum CrossOriginConfig {
198 Anonymous,
199 UseCredentials,
200}
201
202#[turbo_tasks::value(transparent)]
203pub struct OptionCrossOriginConfig(Option<CrossOriginConfig>);
204
205#[derive(
206 Clone,
207 Debug,
208 Default,
209 PartialEq,
210 Deserialize,
211 TraceRawVcs,
212 NonLocalValue,
213 OperationValue,
214 Encode,
215 Decode,
216)]
217#[serde(rename_all = "camelCase")]
218struct EslintConfig {
219 dirs: Option<Vec<String>>,
220 ignore_during_builds: Option<bool>,
221}
222
223#[derive(
224 Clone,
225 Debug,
226 Default,
227 PartialEq,
228 Deserialize,
229 TraceRawVcs,
230 NonLocalValue,
231 OperationValue,
232 Encode,
233 Decode,
234)]
235#[serde(rename_all = "kebab-case")]
236pub enum BuildActivityPositions {
237 #[default]
238 BottomRight,
239 BottomLeft,
240 TopRight,
241 TopLeft,
242}
243
244#[derive(
245 Clone,
246 Debug,
247 Default,
248 PartialEq,
249 Deserialize,
250 TraceRawVcs,
251 NonLocalValue,
252 OperationValue,
253 Encode,
254 Decode,
255)]
256#[serde(rename_all = "camelCase")]
257pub struct DevIndicatorsOptions {
258 pub build_activity_position: Option<BuildActivityPositions>,
259 pub position: Option<BuildActivityPositions>,
260}
261
262#[derive(
263 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
264)]
265#[serde(untagged)]
266pub enum DevIndicatorsConfig {
267 WithOptions(DevIndicatorsOptions),
268 Boolean(bool),
269}
270
271#[derive(
272 Clone,
273 Debug,
274 Default,
275 PartialEq,
276 Deserialize,
277 TraceRawVcs,
278 NonLocalValue,
279 OperationValue,
280 Encode,
281 Decode,
282)]
283#[serde(rename_all = "camelCase")]
284struct OnDemandEntriesConfig {
285 max_inactive_age: f64,
286 pages_buffer_length: f64,
287}
288
289#[derive(
290 Clone,
291 Debug,
292 Default,
293 PartialEq,
294 Deserialize,
295 TraceRawVcs,
296 NonLocalValue,
297 OperationValue,
298 Encode,
299 Decode,
300)]
301#[serde(rename_all = "camelCase")]
302struct HttpAgentConfig {
303 keep_alive: bool,
304}
305
306#[derive(
307 Clone,
308 Debug,
309 PartialEq,
310 Eq,
311 Deserialize,
312 TraceRawVcs,
313 NonLocalValue,
314 OperationValue,
315 Encode,
316 Decode,
317)]
318#[serde(rename_all = "camelCase")]
319pub struct DomainLocale {
320 pub default_locale: String,
321 pub domain: String,
322 pub http: Option<bool>,
323 pub locales: Option<Vec<String>>,
324}
325
326#[derive(
327 Clone,
328 Debug,
329 PartialEq,
330 Eq,
331 Deserialize,
332 TraceRawVcs,
333 NonLocalValue,
334 OperationValue,
335 Encode,
336 Decode,
337)]
338#[serde(rename_all = "camelCase")]
339pub struct I18NConfig {
340 pub default_locale: String,
341 pub domains: Option<Vec<DomainLocale>>,
342 pub locale_detection: Option<bool>,
343 pub locales: Vec<String>,
344}
345
346#[turbo_tasks::value(transparent)]
347pub struct OptionI18NConfig(Option<I18NConfig>);
348
349#[derive(
350 Clone,
351 Debug,
352 PartialEq,
353 Eq,
354 Deserialize,
355 TraceRawVcs,
356 NonLocalValue,
357 OperationValue,
358 Encode,
359 Decode,
360)]
361#[serde(rename_all = "kebab-case")]
362pub enum OutputType {
363 Standalone,
364 Export,
365}
366
367#[turbo_tasks::value(transparent)]
368pub struct OptionOutputType(Option<OutputType>);
369
370#[derive(
371 Debug,
372 Clone,
373 Hash,
374 Eq,
375 PartialEq,
376 Ord,
377 PartialOrd,
378 TaskInput,
379 TraceRawVcs,
380 Serialize,
381 Deserialize,
382 NonLocalValue,
383 OperationValue,
384 Encode,
385 Decode,
386)]
387#[serde(tag = "type", rename_all = "kebab-case")]
388pub enum RouteHas {
389 Header {
390 key: RcStr,
391 #[serde(skip_serializing_if = "Option::is_none")]
392 value: Option<RcStr>,
393 },
394 Cookie {
395 key: RcStr,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 value: Option<RcStr>,
398 },
399 Query {
400 key: RcStr,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 value: Option<RcStr>,
403 },
404 Host {
405 value: RcStr,
406 },
407}
408
409#[derive(Clone, Debug, Default, PartialEq, Deserialize, TraceRawVcs, NonLocalValue)]
410#[serde(rename_all = "camelCase")]
411pub struct HeaderValue {
412 pub key: RcStr,
413 pub value: RcStr,
414}
415
416#[derive(Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue)]
417#[serde(rename_all = "camelCase")]
418pub struct Header {
419 pub source: String,
420 pub base_path: Option<bool>,
421 pub locale: Option<bool>,
422 pub headers: Vec<HeaderValue>,
423 pub has: Option<Vec<RouteHas>>,
424 pub missing: Option<Vec<RouteHas>>,
425}
426
427#[derive(
428 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
429)]
430#[serde(rename_all = "camelCase")]
431pub enum RedirectStatus {
432 StatusCode(f64),
433 Permanent(bool),
434}
435
436#[derive(
437 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
438)]
439#[serde(rename_all = "camelCase")]
440pub struct Redirect {
441 pub source: String,
442 pub destination: String,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub base_path: Option<bool>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub locale: Option<bool>,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub has: Option<Vec<RouteHas>>,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub missing: Option<Vec<RouteHas>>,
451
452 #[serde(flatten)]
453 pub status: RedirectStatus,
454}
455
456#[derive(Clone, Debug)]
457pub struct Rewrite {
458 pub source: String,
459 pub destination: String,
460 pub base_path: Option<bool>,
461 pub locale: Option<bool>,
462 pub has: Option<Vec<RouteHas>>,
463 pub missing: Option<Vec<RouteHas>>,
464}
465
466#[derive(Clone, Debug)]
467pub struct Rewrites {
468 pub before_files: Vec<Rewrite>,
469 pub after_files: Vec<Rewrite>,
470 pub fallback: Vec<Rewrite>,
471}
472
473#[derive(
474 Clone,
475 Debug,
476 Default,
477 PartialEq,
478 Deserialize,
479 TraceRawVcs,
480 NonLocalValue,
481 OperationValue,
482 Encode,
483 Decode,
484)]
485#[serde(rename_all = "camelCase")]
486pub struct TypeScriptConfig {
487 pub ignore_build_errors: Option<bool>,
488 pub tsconfig_path: Option<String>,
489}
490
491#[turbo_tasks::value(eq = "manual", operation)]
492#[derive(Clone, Debug, PartialEq, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct ImageConfig {
495 pub device_sizes: Vec<u16>,
496 pub image_sizes: Vec<u16>,
497 pub path: String,
498 pub loader: ImageLoader,
499 #[serde(deserialize_with = "empty_string_is_none")]
500 pub loader_file: Option<String>,
501 pub domains: Vec<String>,
502 pub disable_static_images: bool,
503 #[serde(rename = "minimumCacheTTL")]
504 pub minimum_cache_ttl: u64,
505 pub formats: Vec<ImageFormat>,
506 #[serde(rename = "dangerouslyAllowSVG")]
507 pub dangerously_allow_svg: bool,
508 pub content_security_policy: String,
509 pub remote_patterns: Vec<RemotePattern>,
510 pub unoptimized: bool,
511}
512
513fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
514where
515 D: Deserializer<'de>,
516{
517 let o = Option::<String>::deserialize(deserializer)?;
518 Ok(o.filter(|s| !s.is_empty()))
519}
520
521impl Default for ImageConfig {
522 fn default() -> Self {
523 Self {
525 device_sizes: vec![640, 750, 828, 1080, 1200, 1920, 2048, 3840],
526 image_sizes: vec![32, 48, 64, 96, 128, 256, 384],
527 path: "/_next/image".to_string(),
528 loader: ImageLoader::Default,
529 loader_file: None,
530 domains: vec![],
531 disable_static_images: false,
532 minimum_cache_ttl: 60,
533 formats: vec![ImageFormat::Webp],
534 dangerously_allow_svg: false,
535 content_security_policy: "".to_string(),
536 remote_patterns: vec![],
537 unoptimized: false,
538 }
539 }
540}
541
542#[derive(
543 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
544)]
545#[serde(rename_all = "kebab-case")]
546pub enum ImageLoader {
547 Default,
548 Imgix,
549 Cloudinary,
550 Akamai,
551 Custom,
552}
553
554#[derive(
555 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
556)]
557pub enum ImageFormat {
558 #[serde(rename = "image/webp")]
559 Webp,
560 #[serde(rename = "image/avif")]
561 Avif,
562}
563
564#[derive(
565 Clone,
566 Debug,
567 Default,
568 PartialEq,
569 Deserialize,
570 TraceRawVcs,
571 NonLocalValue,
572 OperationValue,
573 Encode,
574 Decode,
575)]
576#[serde(rename_all = "camelCase")]
577pub struct RemotePattern {
578 pub hostname: String,
579 #[serde(skip_serializing_if = "Option::is_none")]
580 pub protocol: Option<RemotePatternProtocol>,
581 #[serde(skip_serializing_if = "Option::is_none")]
582 pub port: Option<String>,
583 #[serde(skip_serializing_if = "Option::is_none")]
584 pub pathname: Option<String>,
585}
586
587#[derive(
588 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
589)]
590#[serde(rename_all = "kebab-case")]
591pub enum RemotePatternProtocol {
592 Http,
593 Https,
594}
595
596#[derive(
597 Clone,
598 Debug,
599 Default,
600 PartialEq,
601 Deserialize,
602 TraceRawVcs,
603 NonLocalValue,
604 OperationValue,
605 Encode,
606 Decode,
607)]
608#[serde(rename_all = "camelCase")]
609pub struct TurbopackConfig {
610 #[serde(default)]
611 #[bincode(with = "turbo_bincode::indexmap")]
612 pub rules: FxIndexMap<RcStr, RuleConfigCollection>,
613 #[bincode(with = "turbo_bincode::serde_self_describing")]
614 pub resolve_alias: Option<FxIndexMap<RcStr, JsonValue>>,
615 pub resolve_extensions: Option<Vec<RcStr>>,
616 pub debug_ids: Option<bool>,
617 #[serde(default)]
619 pub ignore_issue: Option<Vec<TurbopackIgnoreIssueRule>>,
620}
621
622#[derive(
623 Deserialize,
624 Clone,
625 PartialEq,
626 Eq,
627 Debug,
628 TraceRawVcs,
629 NonLocalValue,
630 OperationValue,
631 Encode,
632 Decode,
633)]
634#[serde(deny_unknown_fields)]
635pub struct RegexComponents {
636 source: RcStr,
637 flags: RcStr,
638}
639
640#[derive(
645 Clone,
646 PartialEq,
647 Eq,
648 Debug,
649 Deserialize,
650 TraceRawVcs,
651 NonLocalValue,
652 OperationValue,
653 Encode,
654 Decode,
655)]
656#[serde(
657 tag = "type",
658 content = "value",
659 rename_all = "camelCase",
660 deny_unknown_fields
661)]
662pub enum ConfigConditionPath {
663 Glob(RcStr),
664 Regex(RegexComponents),
665}
666
667impl TryFrom<ConfigConditionPath> for ConditionPath {
668 type Error = anyhow::Error;
669
670 fn try_from(config: ConfigConditionPath) -> Result<ConditionPath> {
671 Ok(match config {
672 ConfigConditionPath::Glob(path) => ConditionPath::Glob(path),
673 ConfigConditionPath::Regex(path) => {
674 ConditionPath::Regex(EsRegex::try_from(path)?.resolved_cell())
675 }
676 })
677 }
678}
679
680impl TryFrom<RegexComponents> for EsRegex {
681 type Error = anyhow::Error;
682
683 fn try_from(components: RegexComponents) -> Result<EsRegex> {
684 EsRegex::new(&components.source, &components.flags)
685 }
686}
687
688#[derive(
689 Clone,
690 PartialEq,
691 Eq,
692 Debug,
693 Deserialize,
694 TraceRawVcs,
695 NonLocalValue,
696 OperationValue,
697 Encode,
698 Decode,
699)]
700#[serde(
701 tag = "type",
702 content = "value",
703 rename_all = "camelCase",
704 deny_unknown_fields
705)]
706pub enum ConfigConditionQuery {
707 Constant(RcStr),
708 Regex(RegexComponents),
709}
710
711impl TryFrom<ConfigConditionQuery> for ConditionQuery {
712 type Error = anyhow::Error;
713
714 fn try_from(config: ConfigConditionQuery) -> Result<ConditionQuery> {
715 Ok(match config {
716 ConfigConditionQuery::Constant(value) => ConditionQuery::Constant(value),
717 ConfigConditionQuery::Regex(regex) => {
718 ConditionQuery::Regex(EsRegex::try_from(regex)?.resolved_cell())
719 }
720 })
721 }
722}
723
724#[derive(
725 Clone,
726 PartialEq,
727 Eq,
728 Debug,
729 Deserialize,
730 TraceRawVcs,
731 NonLocalValue,
732 OperationValue,
733 Encode,
734 Decode,
735)]
736#[serde(
737 tag = "type",
738 content = "value",
739 rename_all = "camelCase",
740 deny_unknown_fields
741)]
742pub enum ConfigConditionContentType {
743 Glob(RcStr),
744 Regex(RegexComponents),
745}
746
747impl TryFrom<ConfigConditionContentType> for ConditionContentType {
748 type Error = anyhow::Error;
749
750 fn try_from(config: ConfigConditionContentType) -> Result<ConditionContentType> {
751 Ok(match config {
752 ConfigConditionContentType::Glob(value) => ConditionContentType::Glob(value),
753 ConfigConditionContentType::Regex(regex) => {
754 ConditionContentType::Regex(EsRegex::try_from(regex)?.resolved_cell())
755 }
756 })
757 }
758}
759
760#[derive(
761 Deserialize,
762 Clone,
763 PartialEq,
764 Eq,
765 Debug,
766 TraceRawVcs,
767 NonLocalValue,
768 OperationValue,
769 Encode,
770 Decode,
771)]
772#[serde(deny_unknown_fields)]
775pub enum ConfigConditionItem {
776 #[serde(rename = "all")]
777 All(Box<[ConfigConditionItem]>),
778 #[serde(rename = "any")]
779 Any(Box<[ConfigConditionItem]>),
780 #[serde(rename = "not")]
781 Not(Box<ConfigConditionItem>),
782 #[serde(untagged)]
783 Builtin(WebpackLoaderBuiltinCondition),
784 #[serde(untagged)]
785 Base {
786 #[serde(default)]
787 path: Option<ConfigConditionPath>,
788 #[serde(default)]
789 content: Option<RegexComponents>,
790 #[serde(default)]
791 query: Option<ConfigConditionQuery>,
792 #[serde(default, rename = "contentType")]
793 content_type: Option<ConfigConditionContentType>,
794 },
795}
796
797impl TryFrom<ConfigConditionItem> for ConditionItem {
798 type Error = anyhow::Error;
799
800 fn try_from(config: ConfigConditionItem) -> Result<Self> {
801 let try_from_vec = |conds: Box<[_]>| {
802 conds
803 .into_iter()
804 .map(ConditionItem::try_from)
805 .collect::<Result<_>>()
806 };
807 Ok(match config {
808 ConfigConditionItem::All(conds) => ConditionItem::All(try_from_vec(conds)?),
809 ConfigConditionItem::Any(conds) => ConditionItem::Any(try_from_vec(conds)?),
810 ConfigConditionItem::Not(cond) => ConditionItem::Not(Box::new((*cond).try_into()?)),
811 ConfigConditionItem::Builtin(cond) => {
812 ConditionItem::Builtin(RcStr::from(cond.as_str()))
813 }
814 ConfigConditionItem::Base {
815 path,
816 content,
817 query,
818 content_type,
819 } => ConditionItem::Base {
820 path: path.map(ConditionPath::try_from).transpose()?,
821 content: content
822 .map(EsRegex::try_from)
823 .transpose()?
824 .map(EsRegex::resolved_cell),
825 query: query.map(ConditionQuery::try_from).transpose()?,
826 content_type: content_type
827 .map(ConditionContentType::try_from)
828 .transpose()?,
829 },
830 })
831 }
832}
833
834#[derive(
835 Clone,
836 Debug,
837 PartialEq,
838 Eq,
839 Deserialize,
840 TraceRawVcs,
841 NonLocalValue,
842 OperationValue,
843 Encode,
844 Decode,
845)]
846#[serde(rename_all = "camelCase")]
847pub struct RuleConfigItem {
848 #[serde(default)]
849 pub loaders: Vec<LoaderItem>,
850 #[serde(default, alias = "as")]
851 pub rename_as: Option<RcStr>,
852 #[serde(default)]
853 pub condition: Option<ConfigConditionItem>,
854 #[serde(default, alias = "type")]
855 pub module_type: Option<RcStr>,
856}
857
858#[derive(
859 Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
860)]
861pub struct RuleConfigCollection(Vec<RuleConfigCollectionItem>);
862
863impl<'de> Deserialize<'de> for RuleConfigCollection {
864 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
865 where
866 D: Deserializer<'de>,
867 {
868 match either::serde_untagged::deserialize::<Vec<RuleConfigCollectionItem>, RuleConfigItem, D>(
869 deserializer,
870 )? {
871 Either::Left(collection) => Ok(RuleConfigCollection(collection)),
872 Either::Right(item) => Ok(RuleConfigCollection(vec![RuleConfigCollectionItem::Full(
873 item,
874 )])),
875 }
876 }
877}
878
879#[derive(
880 Clone,
881 Debug,
882 PartialEq,
883 Eq,
884 Deserialize,
885 TraceRawVcs,
886 NonLocalValue,
887 OperationValue,
888 Encode,
889 Decode,
890)]
891#[serde(untagged)]
892pub enum RuleConfigCollectionItem {
893 Shorthand(LoaderItem),
894 Full(RuleConfigItem),
895}
896
897#[derive(
898 Clone,
899 Debug,
900 PartialEq,
901 Eq,
902 Deserialize,
903 TraceRawVcs,
904 NonLocalValue,
905 OperationValue,
906 Encode,
907 Decode,
908)]
909#[serde(untagged)]
910pub enum LoaderItem {
911 LoaderName(RcStr),
912 LoaderOptions(WebpackLoaderItem),
913}
914
915#[turbo_tasks::value(operation)]
916#[derive(Copy, Clone, Debug, Deserialize)]
917#[serde(rename_all = "camelCase")]
918pub enum ModuleIds {
919 Named,
920 Deterministic,
921}
922
923#[turbo_tasks::value(operation)]
924#[derive(Copy, Clone, Debug, Deserialize)]
925#[serde(rename_all = "camelCase")]
926pub enum TurbopackPluginRuntimeStrategy {
927 #[cfg(feature = "worker_pool")]
928 WorkerThreads,
929 #[cfg(feature = "process_pool")]
930 ChildProcesses,
931}
932
933#[derive(
934 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
935)]
936#[serde(untagged)]
937pub enum MdxRsOptions {
938 Boolean(bool),
939 Option(MdxTransformOptions),
940}
941
942#[turbo_tasks::value(shared, operation)]
943#[derive(Clone, Debug, Default, Serialize, Deserialize)]
944#[serde(rename_all = "camelCase")]
945pub enum ReactCompilerCompilationMode {
946 #[default]
947 Infer,
948 Annotation,
949 All,
950}
951
952#[turbo_tasks::value(shared, operation)]
953#[derive(Clone, Debug, Default, Serialize, Deserialize)]
954#[serde(rename_all = "snake_case")]
955pub enum ReactCompilerPanicThreshold {
956 #[default]
957 None,
958 CriticalErrors,
959 AllErrors,
960}
961
962#[turbo_tasks::value(shared, operation)]
963#[derive(Clone, Debug, Serialize, Deserialize)]
964pub enum ReactCompilerTarget {
965 #[serde(rename = "18")]
966 React18,
967}
968
969#[turbo_tasks::value(shared, operation)]
972#[derive(Clone, Debug, Default, Serialize, Deserialize)]
973#[serde(rename_all = "camelCase")]
974pub struct ReactCompilerOptions {
975 #[serde(default)]
976 pub compilation_mode: ReactCompilerCompilationMode,
977 #[serde(default)]
978 pub panic_threshold: ReactCompilerPanicThreshold,
979 #[serde(default, skip_deserializing, skip_serializing_if = "Option::is_none")]
980 pub target: Option<ReactCompilerTarget>,
981}
982
983#[derive(
984 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
985)]
986#[serde(untagged)]
987pub enum ReactCompilerOptionsOrBoolean {
988 Boolean(bool),
989 Option(ReactCompilerOptions),
990}
991
992#[turbo_tasks::value(transparent)]
993pub struct OptionalReactCompilerOptions(Option<ResolvedVc<ReactCompilerOptions>>);
994
995#[derive(
999 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1000)]
1001#[serde(tag = "type")]
1002pub enum TurbopackIgnoreIssuePathPattern {
1003 #[serde(rename = "glob")]
1004 Glob { value: RcStr },
1005 #[serde(rename = "regex")]
1006 Regex { source: RcStr, flags: RcStr },
1007}
1008
1009impl TurbopackIgnoreIssuePathPattern {
1010 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
1011 match self {
1012 TurbopackIgnoreIssuePathPattern::Glob { value } => Ok(IgnoreIssuePattern::Glob(
1013 Glob::parse(value.clone(), GlobOptions::default())?,
1014 )),
1015 TurbopackIgnoreIssuePathPattern::Regex { source, flags } => {
1016 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
1017 }
1018 }
1019 }
1020}
1021
1022#[derive(
1027 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1028)]
1029#[serde(tag = "type")]
1030pub enum TurbopackIgnoreIssueTextPattern {
1031 #[serde(rename = "string")]
1032 String { value: RcStr },
1033 #[serde(rename = "regex")]
1034 Regex { source: RcStr, flags: RcStr },
1035}
1036
1037impl TurbopackIgnoreIssueTextPattern {
1038 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
1039 match self {
1040 TurbopackIgnoreIssueTextPattern::String { value } => {
1041 Ok(IgnoreIssuePattern::ExactString(value.clone()))
1042 }
1043 TurbopackIgnoreIssueTextPattern::Regex { source, flags } => {
1044 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
1045 }
1046 }
1047 }
1048}
1049
1050#[derive(
1052 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1053)]
1054pub struct TurbopackIgnoreIssueRule {
1055 pub path: TurbopackIgnoreIssuePathPattern,
1056 #[serde(default)]
1057 pub title: Option<TurbopackIgnoreIssueTextPattern>,
1058 #[serde(default)]
1059 pub description: Option<TurbopackIgnoreIssueTextPattern>,
1060}
1061
1062#[derive(
1063 Clone,
1064 Debug,
1065 Default,
1066 PartialEq,
1067 Deserialize,
1068 TraceRawVcs,
1069 ValueDebugFormat,
1070 NonLocalValue,
1071 OperationValue,
1072 Encode,
1073 Decode,
1074)]
1075#[serde(rename_all = "camelCase")]
1076pub struct ExperimentalConfig {
1077 allowed_revalidate_header_keys: Option<Vec<RcStr>>,
1080 client_router_filter: Option<bool>,
1081 client_router_filter_allowed_rate: Option<f64>,
1084 client_router_filter_redirects: Option<bool>,
1085 fetch_cache_key_prefix: Option<RcStr>,
1086 isr_flush_to_disk: Option<bool>,
1087 mdx_rs: Option<MdxRsOptions>,
1090 strict_next_head: Option<bool>,
1091 #[bincode(with = "turbo_bincode::serde_self_describing")]
1092 swc_plugins: Option<Vec<(RcStr, serde_json::Value)>>,
1093 swc_env_options: Option<SwcEnvOptions>,
1094 external_middleware_rewrites_resolve: Option<bool>,
1095 scroll_restoration: Option<bool>,
1096 manual_client_base_path: Option<bool>,
1097 optimistic_client_cache: Option<bool>,
1098 middleware_prefetch: Option<MiddlewarePrefetchType>,
1099 #[bincode(with = "turbo_bincode::serde_self_describing")]
1102 optimize_css: Option<serde_json::Value>,
1103 next_script_workers: Option<bool>,
1104 web_vitals_attribution: Option<Vec<RcStr>>,
1105 server_actions: Option<ServerActionsOrLegacyBool>,
1106 sri: Option<SubResourceIntegrity>,
1107 cache_components: Option<bool>,
1110 use_cache: Option<bool>,
1111 root_params: Option<bool>,
1112 runtime_server_deployment_id: Option<bool>,
1113 supports_immutable_assets: Option<bool>,
1114
1115 adjust_font_fallbacks: Option<bool>,
1119 adjust_font_fallbacks_with_size_adjust: Option<bool>,
1120 after: Option<bool>,
1121 app_document_preloading: Option<bool>,
1122 app_new_scroll_handler: Option<bool>,
1123 case_sensitive_routes: Option<bool>,
1124 cpus: Option<f64>,
1125 cra_compat: Option<bool>,
1126 disable_optimized_loading: Option<bool>,
1127 disable_postcss_preset_env: Option<bool>,
1128 esm_externals: Option<EsmExternals>,
1129 #[bincode(with = "turbo_bincode::serde_self_describing")]
1130 extension_alias: Option<serde_json::Value>,
1131 external_dir: Option<bool>,
1132 fallback_node_polyfills: Option<bool>, force_swc_transforms: Option<bool>,
1137 fully_specified: Option<bool>,
1138 gzip_size: Option<bool>,
1139
1140 inline_css: Option<bool>,
1141 instrumentation_hook: Option<bool>,
1142 client_trace_metadata: Option<Vec<String>>,
1143 large_page_data_bytes: Option<f64>,
1144 #[bincode(with = "turbo_bincode::serde_self_describing")]
1145 logging: Option<serde_json::Value>,
1146 memory_based_workers_count: Option<bool>,
1147 optimize_server_react: Option<bool>,
1149 optimize_package_imports: Option<Vec<RcStr>>,
1152 taint: Option<bool>,
1153 proxy_timeout: Option<f64>,
1154 server_minification: Option<bool>,
1156 server_source_maps: Option<bool>,
1158 swc_trace_profiling: Option<bool>,
1159 transition_indicator: Option<bool>,
1160 gesture_transition: Option<bool>,
1161 trust_host_header: Option<bool>,
1163
1164 #[bincode(with = "turbo_bincode::serde_self_describing")]
1165 url_imports: Option<serde_json::Value>,
1166 webpack_build_worker: Option<bool>,
1169 worker_threads: Option<bool>,
1170
1171 turbopack_minify: Option<bool>,
1172 turbopack_module_ids: Option<ModuleIds>,
1173 turbopack_plugin_runtime_strategy: Option<TurbopackPluginRuntimeStrategy>,
1174 turbopack_source_maps: Option<bool>,
1175 turbopack_input_source_maps: Option<bool>,
1176 turbopack_tree_shaking: Option<bool>,
1177 turbopack_scope_hoisting: Option<bool>,
1178 turbopack_client_side_nested_async_chunking: Option<bool>,
1179 turbopack_server_side_nested_async_chunking: Option<bool>,
1180 turbopack_import_type_bytes: Option<bool>,
1181 turbopack_import_type_text: Option<bool>,
1182 #[serde(default)]
1184 turbopack_use_builtin_sass: Option<bool>,
1185 #[serde(default)]
1188 turbopack_use_builtin_babel: Option<bool>,
1189 global_not_found: Option<bool>,
1191 turbopack_remove_unused_imports: Option<bool>,
1193 turbopack_remove_unused_exports: Option<bool>,
1195 turbopack_infer_module_side_effects: Option<bool>,
1197 devtool_segment_explorer: Option<bool>,
1199 report_system_env_inlining: Option<String>,
1201 lightning_css_features: Option<LightningCssFeatures>,
1205}
1206
1207#[derive(
1208 Clone,
1209 Debug,
1210 PartialEq,
1211 Eq,
1212 Deserialize,
1213 TraceRawVcs,
1214 NonLocalValue,
1215 OperationValue,
1216 Encode,
1217 Decode,
1218)]
1219#[serde(rename_all = "camelCase")]
1220pub struct SubResourceIntegrity {
1221 pub algorithm: Option<RcStr>,
1222}
1223
1224#[derive(
1225 Clone,
1226 Debug,
1227 Default,
1228 PartialEq,
1229 Eq,
1230 Deserialize,
1231 TraceRawVcs,
1232 NonLocalValue,
1233 OperationValue,
1234 Encode,
1235 Decode,
1236)]
1237#[serde(rename_all = "camelCase")]
1238pub struct LightningCssFeatures {
1239 pub include: Option<Vec<RcStr>>,
1240 pub exclude: Option<Vec<RcStr>>,
1241}
1242
1243#[derive(
1244 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1245)]
1246#[serde(untagged)]
1247pub enum ServerActionsOrLegacyBool {
1248 ServerActionsConfig(ServerActions),
1250
1251 LegacyBool(bool),
1254}
1255
1256#[derive(
1257 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1258)]
1259#[serde(rename_all = "kebab-case")]
1260pub enum EsmExternalsValue {
1261 Loose,
1262}
1263
1264#[derive(
1265 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1266)]
1267#[serde(untagged)]
1268pub enum EsmExternals {
1269 Loose(EsmExternalsValue),
1270 Bool(bool),
1271}
1272
1273#[test]
1275fn test_esm_externals_deserialization() {
1276 let json = serde_json::json!({
1277 "esmExternals": true
1278 });
1279 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1280 assert_eq!(config.esm_externals, Some(EsmExternals::Bool(true)));
1281
1282 let json = serde_json::json!({
1283 "esmExternals": "loose"
1284 });
1285 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1286 assert_eq!(
1287 config.esm_externals,
1288 Some(EsmExternals::Loose(EsmExternalsValue::Loose))
1289 );
1290}
1291
1292#[derive(
1293 Clone,
1294 Debug,
1295 Default,
1296 PartialEq,
1297 Eq,
1298 Deserialize,
1299 TraceRawVcs,
1300 NonLocalValue,
1301 OperationValue,
1302 Encode,
1303 Decode,
1304)]
1305#[serde(rename_all = "camelCase")]
1306pub struct ServerActions {
1307 pub body_size_limit: Option<SizeLimit>,
1309}
1310
1311#[derive(Clone, Debug, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode)]
1312#[serde(untagged)]
1313pub enum SizeLimit {
1314 Number(f64),
1315 WithUnit(String),
1316}
1317
1318impl PartialEq for SizeLimit {
1321 fn eq(&self, other: &Self) -> bool {
1322 match (self, other) {
1323 (SizeLimit::Number(a), SizeLimit::Number(b)) => a.to_bits() == b.to_bits(),
1324 (SizeLimit::WithUnit(a), SizeLimit::WithUnit(b)) => a == b,
1325 _ => false,
1326 }
1327 }
1328}
1329
1330impl Eq for SizeLimit {}
1331
1332#[derive(
1333 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1334)]
1335#[serde(rename_all = "kebab-case")]
1336pub enum MiddlewarePrefetchType {
1337 Strict,
1338 Flexible,
1339}
1340
1341#[derive(
1342 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1343)]
1344#[serde(untagged)]
1345pub enum EmotionTransformOptionsOrBoolean {
1346 Boolean(bool),
1347 Options(EmotionTransformConfig),
1348}
1349
1350impl EmotionTransformOptionsOrBoolean {
1351 pub fn is_enabled(&self) -> bool {
1352 match self {
1353 Self::Boolean(enabled) => *enabled,
1354 _ => true,
1355 }
1356 }
1357}
1358
1359#[derive(
1360 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1361)]
1362#[serde(untagged)]
1363pub enum StyledComponentsTransformOptionsOrBoolean {
1364 Boolean(bool),
1365 Options(StyledComponentsTransformConfig),
1366}
1367
1368impl StyledComponentsTransformOptionsOrBoolean {
1369 pub fn is_enabled(&self) -> bool {
1370 match self {
1371 Self::Boolean(enabled) => *enabled,
1372 _ => true,
1373 }
1374 }
1375}
1376
1377#[turbo_tasks::value(eq = "manual")]
1378#[derive(Clone, Debug, PartialEq, Default, OperationValue, Deserialize)]
1379#[serde(rename_all = "camelCase")]
1380pub struct CompilerConfig {
1381 pub react_remove_properties: Option<ReactRemoveProperties>,
1382 pub relay: Option<RelayConfig>,
1383 pub emotion: Option<EmotionTransformOptionsOrBoolean>,
1384 pub remove_console: Option<RemoveConsoleConfig>,
1385 pub styled_components: Option<StyledComponentsTransformOptionsOrBoolean>,
1386}
1387
1388#[derive(
1389 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1390)]
1391#[serde(untagged, rename_all = "camelCase")]
1392pub enum ReactRemoveProperties {
1393 Boolean(bool),
1394 Config { properties: Option<Vec<String>> },
1395}
1396
1397impl ReactRemoveProperties {
1398 pub fn is_enabled(&self) -> bool {
1399 match self {
1400 Self::Boolean(enabled) => *enabled,
1401 _ => true,
1402 }
1403 }
1404}
1405
1406#[derive(
1407 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1408)]
1409#[serde(untagged)]
1410pub enum RemoveConsoleConfig {
1411 Boolean(bool),
1412 Config { exclude: Option<Vec<String>> },
1413}
1414
1415impl RemoveConsoleConfig {
1416 pub fn is_enabled(&self) -> bool {
1417 match self {
1418 Self::Boolean(enabled) => *enabled,
1419 _ => true,
1420 }
1421 }
1422}
1423
1424#[turbo_tasks::value(transparent)]
1425pub struct ResolveExtensions(Option<Vec<RcStr>>);
1426
1427#[turbo_tasks::value(transparent)]
1428pub struct SwcPlugins(
1429 #[bincode(with = "turbo_bincode::serde_self_describing")] Vec<(RcStr, serde_json::Value)>,
1430);
1431
1432#[derive(
1434 Clone,
1435 Debug,
1436 Default,
1437 PartialEq,
1438 Eq,
1439 Serialize,
1440 Deserialize,
1441 TraceRawVcs,
1442 NonLocalValue,
1443 OperationValue,
1444 Encode,
1445 Decode,
1446)]
1447#[serde(rename_all = "camelCase")]
1448pub struct SwcEnvOptions {
1449 pub mode: Option<RcStr>,
1450 pub core_js: Option<RcStr>,
1451 pub skip: Option<Vec<RcStr>>,
1452 pub include: Option<Vec<RcStr>>,
1453 pub exclude: Option<Vec<RcStr>>,
1454 pub shipped_proposals: Option<bool>,
1455 pub force_all_transforms: Option<bool>,
1456 pub debug: Option<bool>,
1457 pub loose: Option<bool>,
1458}
1459
1460#[turbo_tasks::value(transparent)]
1461pub struct OptionSwcEnvOptions(Option<SwcEnvOptions>);
1462
1463#[turbo_tasks::value(transparent)]
1464pub struct OptionalMdxTransformOptions(Option<ResolvedVc<MdxTransformOptions>>);
1465
1466#[turbo_tasks::value(transparent)]
1467
1468pub struct OptionSubResourceIntegrity(Option<SubResourceIntegrity>);
1469
1470#[turbo_tasks::value(transparent)]
1471pub struct OptionFileSystemPath(Option<FileSystemPath>);
1472
1473#[turbo_tasks::value(transparent)]
1474pub struct IgnoreIssues(Vec<IgnoreIssue>);
1475
1476#[turbo_tasks::value(transparent)]
1477pub struct OptionJsonValue(
1478 #[bincode(with = "turbo_bincode::serde_self_describing")] pub Option<serde_json::Value>,
1479);
1480
1481fn turbopack_config_documentation_link() -> RcStr {
1482 rcstr!("https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#configuring-webpack-loaders")
1483}
1484
1485#[turbo_tasks::value(shared)]
1486struct InvalidLoaderRuleRenameAsIssue {
1487 glob: RcStr,
1488 rename_as: RcStr,
1489 config_file_path: FileSystemPath,
1490}
1491
1492#[turbo_tasks::value_impl]
1493impl Issue for InvalidLoaderRuleRenameAsIssue {
1494 #[turbo_tasks::function]
1495 async fn file_path(&self) -> Result<Vc<FileSystemPath>> {
1496 Ok(self.config_file_path.clone().cell())
1497 }
1498
1499 #[turbo_tasks::function]
1500 fn stage(&self) -> Vc<IssueStage> {
1501 IssueStage::Config.cell()
1502 }
1503
1504 #[turbo_tasks::function]
1505 async fn title(&self) -> Result<Vc<StyledString>> {
1506 Ok(
1507 StyledString::Text(format!("Invalid loader rule for extension: {}", self.glob).into())
1508 .cell(),
1509 )
1510 }
1511
1512 #[turbo_tasks::function]
1513 async fn description(&self) -> Result<Vc<OptionStyledString>> {
1514 Ok(Vc::cell(Some(
1515 StyledString::Text(RcStr::from(format!(
1516 "The extension {} contains a wildcard, but the `as` option does not: {}",
1517 self.glob, self.rename_as,
1518 )))
1519 .resolved_cell(),
1520 )))
1521 }
1522
1523 #[turbo_tasks::function]
1524 fn documentation_link(&self) -> Vc<RcStr> {
1525 Vc::cell(turbopack_config_documentation_link())
1526 }
1527}
1528
1529#[turbo_tasks::value(shared)]
1530struct InvalidLoaderRuleConditionIssue {
1531 error_string: RcStr,
1532 condition: ConfigConditionItem,
1533 config_file_path: FileSystemPath,
1534}
1535
1536#[turbo_tasks::value_impl]
1537impl Issue for InvalidLoaderRuleConditionIssue {
1538 #[turbo_tasks::function]
1539 async fn file_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1540 Ok(self.await?.config_file_path.clone().cell())
1541 }
1542
1543 #[turbo_tasks::function]
1544 fn stage(self: Vc<Self>) -> Vc<IssueStage> {
1545 IssueStage::Config.cell()
1546 }
1547
1548 #[turbo_tasks::function]
1549 async fn title(&self) -> Result<Vc<StyledString>> {
1550 Ok(StyledString::Text(rcstr!("Invalid condition for Turbopack loader rule")).cell())
1551 }
1552
1553 #[turbo_tasks::function]
1554 async fn description(&self) -> Result<Vc<OptionStyledString>> {
1555 Ok(Vc::cell(Some(
1556 StyledString::Stack(vec![
1557 StyledString::Line(vec![
1558 StyledString::Text(rcstr!("Encountered the following error: ")),
1559 StyledString::Code(self.error_string.clone()),
1560 ]),
1561 StyledString::Text(rcstr!("While processing the condition:")),
1562 StyledString::Code(RcStr::from(format!("{:#?}", self.condition))),
1563 ])
1564 .resolved_cell(),
1565 )))
1566 }
1567
1568 #[turbo_tasks::function]
1569 fn documentation_link(&self) -> Vc<RcStr> {
1570 Vc::cell(turbopack_config_documentation_link())
1571 }
1572}
1573
1574#[turbo_tasks::value_impl]
1575impl NextConfig {
1576 #[turbo_tasks::function]
1577 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
1578 let string = string.await?;
1579 let mut jdeserializer = serde_json::Deserializer::from_str(&string);
1580 let config: NextConfig = serde_path_to_error::deserialize(&mut jdeserializer)
1581 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
1582 Ok(config.cell())
1583 }
1584
1585 #[turbo_tasks::function]
1586 pub async fn config_file_path(
1587 &self,
1588 project_path: FileSystemPath,
1589 ) -> Result<Vc<FileSystemPath>> {
1590 Ok(project_path.join(&self.config_file_name)?.cell())
1591 }
1592
1593 #[turbo_tasks::function]
1594 pub fn bundle_pages_router_dependencies(&self) -> Vc<bool> {
1595 Vc::cell(self.bundle_pages_router_dependencies.unwrap_or_default())
1596 }
1597
1598 #[turbo_tasks::function]
1599 pub fn enable_react_production_profiling(&self) -> Vc<bool> {
1600 Vc::cell(self.react_production_profiling.unwrap_or_default())
1601 }
1602
1603 #[turbo_tasks::function]
1604 pub fn server_external_packages(&self) -> Vc<Vec<RcStr>> {
1605 Vc::cell(
1606 self.server_external_packages
1607 .as_ref()
1608 .cloned()
1609 .unwrap_or_default(),
1610 )
1611 }
1612
1613 #[turbo_tasks::function]
1614 pub fn is_standalone(&self) -> Vc<bool> {
1615 Vc::cell(self.output == Some(OutputType::Standalone))
1616 }
1617
1618 #[turbo_tasks::function]
1619 pub fn base_path(&self) -> Vc<Option<RcStr>> {
1620 Vc::cell(self.base_path.clone())
1621 }
1622
1623 #[turbo_tasks::function]
1624 pub fn cache_handler(&self, project_path: FileSystemPath) -> Result<Vc<OptionFileSystemPath>> {
1625 if let Some(handler) = &self.cache_handler {
1626 Ok(Vc::cell(Some(project_path.join(handler)?)))
1627 } else {
1628 Ok(Vc::cell(None))
1629 }
1630 }
1631
1632 #[turbo_tasks::function]
1633 pub fn compiler(&self) -> Vc<CompilerConfig> {
1634 self.compiler.clone().unwrap_or_default().cell()
1635 }
1636
1637 #[turbo_tasks::function]
1638 pub fn env(&self) -> Vc<EnvMap> {
1639 let env = self
1643 .env
1644 .iter()
1645 .map(|(k, v)| {
1646 (
1647 k.as_str().into(),
1648 if let JsonValue::String(s) = v {
1649 s.as_str().into()
1651 } else {
1652 v.to_string().into()
1653 },
1654 )
1655 })
1656 .collect();
1657
1658 Vc::cell(env)
1659 }
1660
1661 #[turbo_tasks::function]
1662 pub fn image_config(&self) -> Vc<ImageConfig> {
1663 self.images.clone().cell()
1664 }
1665
1666 #[turbo_tasks::function]
1667 pub fn page_extensions(&self) -> Vc<Vec<RcStr>> {
1668 let mut extensions = self.page_extensions.clone();
1672 extensions.sort_by_key(|ext| std::cmp::Reverse(ext.len()));
1673 Vc::cell(extensions)
1674 }
1675
1676 #[turbo_tasks::function]
1677 pub fn is_global_not_found_enabled(&self) -> Vc<bool> {
1678 Vc::cell(self.experimental.global_not_found.unwrap_or_default())
1679 }
1680
1681 #[turbo_tasks::function]
1682 pub fn transpile_packages(&self) -> Vc<Vec<RcStr>> {
1683 Vc::cell(self.transpile_packages.clone().unwrap_or_default())
1684 }
1685
1686 #[turbo_tasks::function]
1687 pub async fn webpack_rules(
1688 self: Vc<Self>,
1689 project_path: FileSystemPath,
1690 ) -> Result<Vc<WebpackRules>> {
1691 let this = self.await?;
1692 let Some(turbo_rules) = this.turbopack.as_ref().map(|t| &t.rules) else {
1693 return Ok(Vc::cell(Vec::new()));
1694 };
1695 if turbo_rules.is_empty() {
1696 return Ok(Vc::cell(Vec::new()));
1697 }
1698 let mut rules = Vec::new();
1699 for (glob, rule_collection) in turbo_rules.iter() {
1700 fn transform_loaders(
1701 loaders: &mut dyn Iterator<Item = &LoaderItem>,
1702 ) -> ResolvedVc<WebpackLoaderItems> {
1703 ResolvedVc::cell(
1704 loaders
1705 .map(|item| match item {
1706 LoaderItem::LoaderName(name) => WebpackLoaderItem {
1707 loader: name.clone(),
1708 options: Default::default(),
1709 },
1710 LoaderItem::LoaderOptions(options) => options.clone(),
1711 })
1712 .collect(),
1713 )
1714 }
1715 for item in &rule_collection.0 {
1716 match item {
1717 RuleConfigCollectionItem::Shorthand(loaders) => {
1718 rules.push((
1719 glob.clone(),
1720 LoaderRuleItem {
1721 loaders: transform_loaders(&mut [loaders].into_iter()),
1722 rename_as: None,
1723 condition: None,
1724 module_type: None,
1725 },
1726 ));
1727 }
1728 RuleConfigCollectionItem::Full(RuleConfigItem {
1729 loaders,
1730 rename_as,
1731 condition,
1732 module_type,
1733 }) => {
1734 if glob.contains("*")
1738 && let Some(rename_as) = rename_as.as_ref()
1739 && !rename_as.contains("*")
1740 {
1741 InvalidLoaderRuleRenameAsIssue {
1742 glob: glob.clone(),
1743 config_file_path: self
1744 .config_file_path(project_path.clone())
1745 .owned()
1746 .await?,
1747 rename_as: rename_as.clone(),
1748 }
1749 .resolved_cell()
1750 .emit();
1751 }
1752
1753 let condition = if let Some(condition) = condition {
1756 match ConditionItem::try_from(condition.clone()) {
1757 Ok(cond) => Some(cond),
1758 Err(err) => {
1759 InvalidLoaderRuleConditionIssue {
1760 error_string: RcStr::from(err.to_string()),
1761 condition: condition.clone(),
1762 config_file_path: self
1763 .config_file_path(project_path.clone())
1764 .owned()
1765 .await?,
1766 }
1767 .resolved_cell()
1768 .emit();
1769 None
1770 }
1771 }
1772 } else {
1773 None
1774 };
1775 rules.push((
1776 glob.clone(),
1777 LoaderRuleItem {
1778 loaders: transform_loaders(&mut loaders.iter()),
1779 rename_as: rename_as.clone(),
1780 condition,
1781 module_type: module_type.clone(),
1782 },
1783 ));
1784 }
1785 }
1786 }
1787 }
1788 Ok(Vc::cell(rules))
1789 }
1790
1791 #[turbo_tasks::function]
1792 pub fn resolve_alias_options(&self) -> Result<Vc<ResolveAliasMap>> {
1793 let Some(resolve_alias) = self
1794 .turbopack
1795 .as_ref()
1796 .and_then(|t| t.resolve_alias.as_ref())
1797 else {
1798 return Ok(ResolveAliasMap::cell(ResolveAliasMap::default()));
1799 };
1800 let alias_map: ResolveAliasMap = resolve_alias.try_into()?;
1801 Ok(alias_map.cell())
1802 }
1803
1804 #[turbo_tasks::function]
1805 pub fn resolve_extension(&self) -> Vc<ResolveExtensions> {
1806 let Some(resolve_extensions) = self
1807 .turbopack
1808 .as_ref()
1809 .and_then(|t| t.resolve_extensions.as_ref())
1810 else {
1811 return Vc::cell(None);
1812 };
1813 Vc::cell(Some(resolve_extensions.clone()))
1814 }
1815
1816 #[turbo_tasks::function]
1817 pub fn import_externals(&self) -> Result<Vc<bool>> {
1818 Ok(Vc::cell(match self.experimental.esm_externals {
1819 Some(EsmExternals::Bool(b)) => b,
1820 Some(EsmExternals::Loose(_)) => bail!("esmExternals = \"loose\" is not supported"),
1821 None => true,
1822 }))
1823 }
1824
1825 #[turbo_tasks::function]
1826 pub fn inline_css(&self) -> Vc<bool> {
1827 Vc::cell(self.experimental.inline_css.unwrap_or(false))
1828 }
1829
1830 #[turbo_tasks::function]
1831 pub fn mdx_rs(&self) -> Vc<OptionalMdxTransformOptions> {
1832 let options = &self.experimental.mdx_rs;
1833
1834 let options = match options {
1835 Some(MdxRsOptions::Boolean(true)) => OptionalMdxTransformOptions(Some(
1836 MdxTransformOptions {
1837 provider_import_source: Some(mdx_import_source_file()),
1838 ..Default::default()
1839 }
1840 .resolved_cell(),
1841 )),
1842 Some(MdxRsOptions::Option(options)) => OptionalMdxTransformOptions(Some(
1843 MdxTransformOptions {
1844 provider_import_source: Some(
1845 options
1846 .provider_import_source
1847 .clone()
1848 .unwrap_or(mdx_import_source_file()),
1849 ),
1850 ..options.clone()
1851 }
1852 .resolved_cell(),
1853 )),
1854 _ => OptionalMdxTransformOptions(None),
1855 };
1856
1857 options.cell()
1858 }
1859
1860 #[turbo_tasks::function]
1861 pub fn modularize_imports(&self) -> Vc<ModularizeImports> {
1862 Vc::cell(self.modularize_imports.clone().unwrap_or_default())
1863 }
1864
1865 #[turbo_tasks::function]
1866 pub fn dist_dir(&self) -> Vc<RcStr> {
1867 Vc::cell(self.dist_dir.clone())
1868 }
1869 #[turbo_tasks::function]
1870 pub fn dist_dir_root(&self) -> Vc<RcStr> {
1871 Vc::cell(self.dist_dir_root.clone())
1872 }
1873
1874 #[turbo_tasks::function]
1875 pub fn cache_handlers(&self, project_path: FileSystemPath) -> Result<Vc<FileSystemPathVec>> {
1876 if let Some(handlers) = &self.cache_handlers {
1877 Ok(Vc::cell(
1878 handlers
1879 .values()
1880 .map(|h| project_path.join(h))
1881 .collect::<Result<Vec<_>>>()?,
1882 ))
1883 } else {
1884 Ok(Vc::cell(vec![]))
1885 }
1886 }
1887
1888 #[turbo_tasks::function]
1889 pub fn cache_handlers_map(&self) -> Vc<CacheHandlersMap> {
1890 Vc::cell(self.cache_handlers.clone().unwrap_or_default())
1891 }
1892
1893 #[turbo_tasks::function]
1894 pub fn experimental_swc_plugins(&self) -> Vc<SwcPlugins> {
1895 Vc::cell(self.experimental.swc_plugins.clone().unwrap_or_default())
1896 }
1897
1898 #[turbo_tasks::function]
1899 pub fn experimental_swc_env_options(&self) -> Vc<OptionSwcEnvOptions> {
1900 Vc::cell(self.experimental.swc_env_options.clone())
1901 }
1902
1903 #[turbo_tasks::function]
1904 pub fn experimental_sri(&self) -> Vc<OptionSubResourceIntegrity> {
1905 Vc::cell(self.experimental.sri.clone())
1906 }
1907
1908 #[turbo_tasks::function]
1909 pub fn experimental_turbopack_use_builtin_babel(&self) -> Vc<Option<bool>> {
1910 Vc::cell(self.experimental.turbopack_use_builtin_babel)
1911 }
1912
1913 #[turbo_tasks::function]
1914 pub fn experimental_turbopack_use_builtin_sass(&self) -> Vc<Option<bool>> {
1915 Vc::cell(self.experimental.turbopack_use_builtin_sass)
1916 }
1917
1918 #[turbo_tasks::function]
1919 pub fn react_compiler_options(&self) -> Vc<OptionalReactCompilerOptions> {
1920 let options = &self.react_compiler;
1921
1922 let options = match options {
1923 Some(ReactCompilerOptionsOrBoolean::Boolean(true)) => {
1924 OptionalReactCompilerOptions(Some(ReactCompilerOptions::default().resolved_cell()))
1925 }
1926 Some(ReactCompilerOptionsOrBoolean::Option(options)) => OptionalReactCompilerOptions(
1927 Some(ReactCompilerOptions { ..options.clone() }.resolved_cell()),
1928 ),
1929 _ => OptionalReactCompilerOptions(None),
1930 };
1931
1932 options.cell()
1933 }
1934
1935 #[turbo_tasks::function]
1936 pub fn sass_config(&self) -> Vc<JsonValue> {
1937 Vc::cell(self.sass_options.clone().unwrap_or_default())
1938 }
1939
1940 #[turbo_tasks::function]
1941 pub fn skip_proxy_url_normalize(&self) -> Vc<bool> {
1942 Vc::cell(self.skip_proxy_url_normalize.unwrap_or(false))
1943 }
1944
1945 #[turbo_tasks::function]
1946 pub fn skip_trailing_slash_redirect(&self) -> Vc<bool> {
1947 Vc::cell(self.skip_trailing_slash_redirect.unwrap_or(false))
1948 }
1949
1950 #[turbo_tasks::function]
1953 pub async fn computed_asset_prefix(self: Vc<Self>) -> Result<Vc<RcStr>> {
1954 let this = self.await?;
1955
1956 Ok(Vc::cell(
1957 format!(
1958 "{}/_next/",
1959 if let Some(asset_prefix) = &this.asset_prefix {
1960 asset_prefix
1961 } else {
1962 this.base_path.as_ref().map_or("", |b| b.as_str())
1963 }
1964 .trim_end_matches('/')
1965 )
1966 .into(),
1967 ))
1968 }
1969
1970 #[turbo_tasks::function]
1972 pub fn asset_suffix_path(&self) -> Vc<Option<RcStr>> {
1973 let needs_dpl_id = self
1974 .experimental
1975 .supports_immutable_assets
1976 .is_none_or(|f| !f);
1977
1978 Vc::cell(
1979 needs_dpl_id
1980 .then_some(self.deployment_id.as_ref())
1981 .flatten()
1982 .map(|id| format!("?dpl={id}").into()),
1983 )
1984 }
1985
1986 #[turbo_tasks::function]
1989 pub fn enable_immutable_assets(&self) -> Vc<bool> {
1990 Vc::cell(self.experimental.supports_immutable_assets == Some(true))
1991 }
1992
1993 #[turbo_tasks::function]
1994 pub fn client_static_folder_name(&self) -> Vc<RcStr> {
1995 Vc::cell(
1996 if self.experimental.supports_immutable_assets == Some(true) {
1997 rcstr!("static/immutable")
1999 } else {
2000 rcstr!("static")
2001 },
2002 )
2003 }
2004
2005 #[turbo_tasks::function]
2006 pub fn enable_taint(&self) -> Vc<bool> {
2007 Vc::cell(self.experimental.taint.unwrap_or(false))
2008 }
2009
2010 #[turbo_tasks::function]
2011 pub fn enable_transition_indicator(&self) -> Vc<bool> {
2012 Vc::cell(self.experimental.transition_indicator.unwrap_or(false))
2013 }
2014
2015 #[turbo_tasks::function]
2016 pub fn enable_gesture_transition(&self) -> Vc<bool> {
2017 Vc::cell(self.experimental.gesture_transition.unwrap_or(false))
2018 }
2019
2020 #[turbo_tasks::function]
2021 pub fn enable_cache_components(&self) -> Vc<bool> {
2022 Vc::cell(self.cache_components.unwrap_or(false))
2023 }
2024
2025 #[turbo_tasks::function]
2026 pub fn enable_use_cache(&self) -> Vc<bool> {
2027 Vc::cell(
2028 self.experimental
2029 .use_cache
2030 .unwrap_or(self.cache_components.unwrap_or(false)),
2034 )
2035 }
2036
2037 #[turbo_tasks::function]
2038 pub fn enable_root_params(&self) -> Vc<bool> {
2039 Vc::cell(
2040 self.experimental
2041 .root_params
2042 .unwrap_or(self.cache_components.unwrap_or(false)),
2044 )
2045 }
2046
2047 #[turbo_tasks::function]
2048 pub fn should_append_server_deployment_id_at_runtime(&self) -> Vc<bool> {
2049 let needs_dpl_id = self
2050 .experimental
2051 .supports_immutable_assets
2052 .is_none_or(|f| !f);
2053
2054 Vc::cell(
2055 needs_dpl_id
2056 && self
2057 .experimental
2058 .runtime_server_deployment_id
2059 .unwrap_or(false),
2060 )
2061 }
2062
2063 #[turbo_tasks::function]
2064 pub fn cache_kinds(&self) -> Vc<CacheKinds> {
2065 let mut cache_kinds = CacheKinds::default();
2066
2067 if let Some(handlers) = self.cache_handlers.as_ref() {
2068 cache_kinds.extend(handlers.keys().cloned());
2069 }
2070
2071 cache_kinds.cell()
2072 }
2073
2074 #[turbo_tasks::function]
2075 pub fn optimize_package_imports(&self) -> Vc<Vec<RcStr>> {
2076 Vc::cell(
2077 self.experimental
2078 .optimize_package_imports
2079 .clone()
2080 .unwrap_or_default(),
2081 )
2082 }
2083
2084 #[turbo_tasks::function]
2085 pub fn tree_shaking_mode_for_foreign_code(
2086 &self,
2087 _is_development: bool,
2088 ) -> Vc<OptionTreeShaking> {
2089 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
2090 Some(false) => Some(TreeShakingMode::ReexportsOnly),
2091 Some(true) => Some(TreeShakingMode::ModuleFragments),
2092 None => Some(TreeShakingMode::ReexportsOnly),
2093 })
2094 .cell()
2095 }
2096
2097 #[turbo_tasks::function]
2098 pub fn tree_shaking_mode_for_user_code(&self, _is_development: bool) -> Vc<OptionTreeShaking> {
2099 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
2100 Some(false) => Some(TreeShakingMode::ReexportsOnly),
2101 Some(true) => Some(TreeShakingMode::ModuleFragments),
2102 None => Some(TreeShakingMode::ReexportsOnly),
2103 })
2104 .cell()
2105 }
2106
2107 #[turbo_tasks::function]
2108 pub async fn turbopack_remove_unused_imports(
2109 self: Vc<Self>,
2110 mode: Vc<NextMode>,
2111 ) -> Result<Vc<bool>> {
2112 let remove_unused_imports = self
2113 .await?
2114 .experimental
2115 .turbopack_remove_unused_imports
2116 .unwrap_or(matches!(*mode.await?, NextMode::Build));
2117
2118 if remove_unused_imports && !*self.turbopack_remove_unused_exports(mode).await? {
2119 bail!(
2120 "`experimental.turbopackRemoveUnusedImports` cannot be enabled without also \
2121 enabling `experimental.turbopackRemoveUnusedExports`"
2122 );
2123 }
2124
2125 Ok(Vc::cell(remove_unused_imports))
2126 }
2127
2128 #[turbo_tasks::function]
2129 pub async fn turbopack_remove_unused_exports(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2130 Ok(Vc::cell(
2131 self.experimental
2132 .turbopack_remove_unused_exports
2133 .unwrap_or(matches!(*mode.await?, NextMode::Build)),
2134 ))
2135 }
2136
2137 #[turbo_tasks::function]
2138 pub fn turbopack_infer_module_side_effects(&self) -> Vc<bool> {
2139 Vc::cell(
2140 self.experimental
2141 .turbopack_infer_module_side_effects
2142 .unwrap_or(true),
2143 )
2144 }
2145
2146 #[turbo_tasks::function]
2147 pub fn turbopack_plugin_runtime_strategy(&self) -> Vc<TurbopackPluginRuntimeStrategy> {
2148 #[cfg(feature = "process_pool")]
2149 let default = TurbopackPluginRuntimeStrategy::ChildProcesses;
2150 #[cfg(all(feature = "worker_pool", not(feature = "process_pool")))]
2151 let default = TurbopackPluginRuntimeStrategy::WorkerThreads;
2152
2153 self.experimental
2154 .turbopack_plugin_runtime_strategy
2155 .unwrap_or(default)
2156 .cell()
2157 }
2158
2159 #[turbo_tasks::function]
2160 pub async fn module_ids(&self, mode: Vc<NextMode>) -> Result<Vc<ModuleIds>> {
2161 Ok(match *mode.await? {
2162 NextMode::Development => ModuleIds::Named.cell(),
2164 NextMode::Build => self
2165 .experimental
2166 .turbopack_module_ids
2167 .unwrap_or(ModuleIds::Deterministic)
2168 .cell(),
2169 })
2170 }
2171
2172 #[turbo_tasks::function]
2173 pub async fn turbo_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2174 let minify = self.experimental.turbopack_minify;
2175 Ok(Vc::cell(
2176 minify.unwrap_or(matches!(*mode.await?, NextMode::Build)),
2177 ))
2178 }
2179
2180 #[turbo_tasks::function]
2181 pub async fn turbo_scope_hoisting(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2182 Ok(Vc::cell(match *mode.await? {
2183 NextMode::Development => false,
2185 NextMode::Build => self.experimental.turbopack_scope_hoisting.unwrap_or(true),
2186 }))
2187 }
2188
2189 #[turbo_tasks::function]
2190 pub async fn turbo_nested_async_chunking(
2191 &self,
2192 mode: Vc<NextMode>,
2193 client_side: bool,
2194 ) -> Result<Vc<bool>> {
2195 let option = if client_side {
2196 self.experimental
2197 .turbopack_client_side_nested_async_chunking
2198 } else {
2199 self.experimental
2200 .turbopack_server_side_nested_async_chunking
2201 };
2202 Ok(Vc::cell(if let Some(value) = option {
2203 value
2204 } else {
2205 match *mode.await? {
2206 NextMode::Development => false,
2207 NextMode::Build => client_side,
2208 }
2209 }))
2210 }
2211
2212 #[turbo_tasks::function]
2213 pub async fn turbopack_import_type_bytes(&self) -> Vc<bool> {
2214 Vc::cell(
2215 self.experimental
2216 .turbopack_import_type_bytes
2217 .unwrap_or(false),
2218 )
2219 }
2220
2221 #[turbo_tasks::function]
2222 pub async fn turbopack_import_type_text(&self) -> Vc<bool> {
2223 Vc::cell(
2224 self.experimental
2225 .turbopack_import_type_text
2226 .unwrap_or(false),
2227 )
2228 }
2229
2230 #[turbo_tasks::function]
2231 pub fn lightningcss_feature_flags(
2232 &self,
2233 ) -> Result<Vc<turbopack_css::LightningCssFeatureFlags>> {
2234 Ok(turbopack_css::LightningCssFeatureFlags {
2235 include: lightningcss_features_field_mask(
2236 &self.experimental.lightning_css_features,
2237 |f| f.include.as_ref(),
2238 )?,
2239 exclude: lightningcss_features_field_mask(
2240 &self.experimental.lightning_css_features,
2241 |f| f.exclude.as_ref(),
2242 )?,
2243 }
2244 .cell())
2245 }
2246
2247 #[turbo_tasks::function]
2248 pub async fn client_source_maps(&self, mode: Vc<NextMode>) -> Result<Vc<SourceMapsType>> {
2249 let input_source_maps = self
2250 .experimental
2251 .turbopack_input_source_maps
2252 .unwrap_or(true);
2253 let source_maps = self
2254 .experimental
2255 .turbopack_source_maps
2256 .unwrap_or(match &*mode.await? {
2257 NextMode::Development => true,
2258 NextMode::Build => self.production_browser_source_maps,
2259 });
2260 Ok(match (source_maps, input_source_maps) {
2261 (true, true) => SourceMapsType::Full,
2262 (true, false) => SourceMapsType::Partial,
2263 (false, _) => SourceMapsType::None,
2264 }
2265 .cell())
2266 }
2267
2268 #[turbo_tasks::function]
2269 pub fn server_source_maps(&self) -> Result<Vc<SourceMapsType>> {
2270 let input_source_maps = self
2271 .experimental
2272 .turbopack_input_source_maps
2273 .unwrap_or(true);
2274 let source_maps = self
2275 .experimental
2276 .turbopack_source_maps
2277 .or(self.experimental.server_source_maps)
2278 .unwrap_or(true);
2279 Ok(match (source_maps, input_source_maps) {
2280 (true, true) => SourceMapsType::Full,
2281 (true, false) => SourceMapsType::Partial,
2282 (false, _) => SourceMapsType::None,
2283 }
2284 .cell())
2285 }
2286
2287 #[turbo_tasks::function]
2288 pub fn turbopack_debug_ids(&self) -> Vc<bool> {
2289 Vc::cell(
2290 self.turbopack
2291 .as_ref()
2292 .and_then(|turbopack| turbopack.debug_ids)
2293 .unwrap_or(false),
2294 )
2295 }
2296
2297 #[turbo_tasks::function]
2298 pub fn typescript_tsconfig_path(&self) -> Result<Vc<Option<RcStr>>> {
2299 Ok(Vc::cell(
2300 self.typescript
2301 .tsconfig_path
2302 .as_ref()
2303 .map(|path| path.to_owned().into()),
2304 ))
2305 }
2306
2307 #[turbo_tasks::function]
2308 pub fn cross_origin(&self) -> Vc<OptionCrossOriginConfig> {
2309 Vc::cell(self.cross_origin.clone())
2310 }
2311
2312 #[turbo_tasks::function]
2313 pub fn i18n(&self) -> Vc<OptionI18NConfig> {
2314 Vc::cell(self.i18n.clone())
2315 }
2316
2317 #[turbo_tasks::function]
2318 pub fn output(&self) -> Vc<OptionOutputType> {
2319 Vc::cell(self.output.clone())
2320 }
2321
2322 #[turbo_tasks::function]
2323 pub fn output_file_tracing_includes(&self) -> Vc<OptionJsonValue> {
2324 Vc::cell(self.output_file_tracing_includes.clone())
2325 }
2326
2327 #[turbo_tasks::function]
2328 pub fn output_file_tracing_excludes(&self) -> Vc<OptionJsonValue> {
2329 Vc::cell(self.output_file_tracing_excludes.clone())
2330 }
2331
2332 #[turbo_tasks::function]
2333 pub fn fetch_client(&self) -> Vc<FetchClientConfig> {
2334 FetchClientConfig::default().cell()
2335 }
2336
2337 #[turbo_tasks::function]
2338 pub async fn report_system_env_inlining(&self) -> Result<Vc<IssueSeverity>> {
2339 match self.experimental.report_system_env_inlining.as_deref() {
2340 None => Ok(IssueSeverity::Suggestion.cell()),
2341 Some("warn") => Ok(IssueSeverity::Warning.cell()),
2342 Some("error") => Ok(IssueSeverity::Error.cell()),
2343 _ => bail!(
2344 "`experimental.reportSystemEnvInlining` must be undefined, \"error\", or \"warn\""
2345 ),
2346 }
2347 }
2348
2349 #[turbo_tasks::function]
2352 pub fn turbopack_ignore_issue_rules(&self) -> Result<Vc<IgnoreIssues>> {
2353 let rules = self
2354 .turbopack
2355 .as_ref()
2356 .and_then(|tp| tp.ignore_issue.as_deref())
2357 .unwrap_or_default()
2358 .iter()
2359 .map(|rule| {
2360 Ok(IgnoreIssue {
2361 path: rule.path.to_ignore_pattern()?,
2362 title: rule
2363 .title
2364 .as_ref()
2365 .map(|t| t.to_ignore_pattern())
2366 .transpose()?,
2367 description: rule
2368 .description
2369 .as_ref()
2370 .map(|d| d.to_ignore_pattern())
2371 .transpose()?,
2372 })
2373 })
2374 .collect::<Result<Vec<_>>>()?;
2375 Ok(Vc::cell(rules))
2376 }
2377}
2378
2379#[turbo_tasks::value(serialization = "custom", eq = "manual")]
2382#[derive(Clone, Debug, Default, PartialEq, Deserialize, Encode, Decode)]
2383#[serde(rename_all = "camelCase")]
2384pub struct JsConfig {
2385 #[bincode(with = "turbo_bincode::serde_self_describing")]
2386 compiler_options: Option<serde_json::Value>,
2387}
2388
2389#[turbo_tasks::value_impl]
2390impl JsConfig {
2391 #[turbo_tasks::function]
2392 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
2393 let string = string.await?;
2394 let config: JsConfig = serde_json::from_str(&string)
2395 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
2396
2397 Ok(config.cell())
2398 }
2399
2400 #[turbo_tasks::function]
2401 pub fn compiler_options(&self) -> Vc<serde_json::Value> {
2402 Vc::cell(self.compiler_options.clone().unwrap_or_default())
2403 }
2404}
2405
2406fn lightningcss_features_field_mask(
2409 features: &Option<LightningCssFeatures>,
2410 field: impl FnOnce(&LightningCssFeatures) -> Option<&Vec<RcStr>>,
2411) -> Result<u32> {
2412 features
2413 .as_ref()
2414 .and_then(field)
2415 .map(|names| lightningcss_feature_names_to_mask(names))
2416 .unwrap_or(Ok(0))
2417}
2418
2419pub fn lightningcss_feature_names_to_mask(
2428 names: &[impl std::ops::Deref<Target = str>],
2429) -> Result<u32> {
2430 use lightningcss::targets::Features;
2431 let mut mask = Features::empty();
2432 for name in names {
2433 mask |= match &**name {
2434 "nesting" => Features::Nesting,
2435 "not-selector-list" => Features::NotSelectorList,
2436 "dir-selector" => Features::DirSelector,
2437 "lang-selector-list" => Features::LangSelectorList,
2438 "is-selector" => Features::IsSelector,
2439 "text-decoration-thickness-percent" => Features::TextDecorationThicknessPercent,
2440 "media-interval-syntax" => Features::MediaIntervalSyntax,
2441 "media-range-syntax" => Features::MediaRangeSyntax,
2442 "custom-media-queries" => Features::CustomMediaQueries,
2443 "clamp-function" => Features::ClampFunction,
2444 "color-function" => Features::ColorFunction,
2445 "oklab-colors" => Features::OklabColors,
2446 "lab-colors" => Features::LabColors,
2447 "p3-colors" => Features::P3Colors,
2448 "hex-alpha-colors" => Features::HexAlphaColors,
2449 "space-separated-color-notation" => Features::SpaceSeparatedColorNotation,
2450 "font-family-system-ui" => Features::FontFamilySystemUi,
2451 "double-position-gradients" => Features::DoublePositionGradients,
2452 "vendor-prefixes" => Features::VendorPrefixes,
2453 "logical-properties" => Features::LogicalProperties,
2454 "light-dark" => Features::LightDark,
2455 "selectors" => Features::Selectors,
2457 "media-queries" => Features::MediaQueries,
2458 "colors" => Features::Colors,
2459 _ => bail!("Unknown lightningcss feature: {}", &**name),
2460 };
2461 }
2462 Ok(mask.bits())
2463}
2464
2465#[cfg(test)]
2466mod tests {
2467 use super::*;
2468
2469 #[test]
2470 fn test_serde_rule_config_item_options() {
2471 let json_value = serde_json::json!({
2472 "loaders": [],
2473 "as": "*.js",
2474 "condition": {
2475 "all": [
2476 "production",
2477 {"not": "foreign"},
2478 {"any": [
2479 "browser",
2480 {
2481 "path": { "type": "glob", "value": "*.svg"},
2482 "query": {
2483 "type": "regex",
2484 "value": {
2485 "source": "@someQuery",
2486 "flags": ""
2487 }
2488 },
2489 "content": {
2490 "source": "@someTag",
2491 "flags": ""
2492 }
2493 }
2494 ]},
2495 ],
2496 }
2497 });
2498
2499 let rule_config: RuleConfigItem = serde_json::from_value(json_value).unwrap();
2500
2501 assert_eq!(
2502 rule_config,
2503 RuleConfigItem {
2504 loaders: vec![],
2505 rename_as: Some(rcstr!("*.js")),
2506 module_type: None,
2507 condition: Some(ConfigConditionItem::All(
2508 [
2509 ConfigConditionItem::Builtin(WebpackLoaderBuiltinCondition::Production),
2510 ConfigConditionItem::Not(Box::new(ConfigConditionItem::Builtin(
2511 WebpackLoaderBuiltinCondition::Foreign
2512 ))),
2513 ConfigConditionItem::Any(
2514 vec![
2515 ConfigConditionItem::Builtin(
2516 WebpackLoaderBuiltinCondition::Browser
2517 ),
2518 ConfigConditionItem::Base {
2519 path: Some(ConfigConditionPath::Glob(rcstr!("*.svg"))),
2520 content: Some(RegexComponents {
2521 source: rcstr!("@someTag"),
2522 flags: rcstr!(""),
2523 }),
2524 query: Some(ConfigConditionQuery::Regex(RegexComponents {
2525 source: rcstr!("@someQuery"),
2526 flags: rcstr!(""),
2527 })),
2528 content_type: None,
2529 },
2530 ]
2531 .into(),
2532 ),
2533 ]
2534 .into(),
2535 )),
2536 }
2537 );
2538 }
2539}