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)]
965#[derive(Clone, Debug, Default, Serialize, Deserialize)]
966#[serde(rename_all = "camelCase")]
967pub struct ReactCompilerOptions {
968 #[serde(default)]
969 pub compilation_mode: ReactCompilerCompilationMode,
970 #[serde(default)]
971 pub panic_threshold: ReactCompilerPanicThreshold,
972}
973
974#[derive(
975 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
976)]
977#[serde(untagged)]
978pub enum ReactCompilerOptionsOrBoolean {
979 Boolean(bool),
980 Option(ReactCompilerOptions),
981}
982
983#[turbo_tasks::value(transparent)]
984pub struct OptionalReactCompilerOptions(Option<ResolvedVc<ReactCompilerOptions>>);
985
986#[derive(
990 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
991)]
992#[serde(tag = "type")]
993pub enum TurbopackIgnoreIssuePathPattern {
994 #[serde(rename = "glob")]
995 Glob { value: RcStr },
996 #[serde(rename = "regex")]
997 Regex { source: RcStr, flags: RcStr },
998}
999
1000impl TurbopackIgnoreIssuePathPattern {
1001 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
1002 match self {
1003 TurbopackIgnoreIssuePathPattern::Glob { value } => Ok(IgnoreIssuePattern::Glob(
1004 Glob::parse(value.clone(), GlobOptions::default())?,
1005 )),
1006 TurbopackIgnoreIssuePathPattern::Regex { source, flags } => {
1007 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
1008 }
1009 }
1010 }
1011}
1012
1013#[derive(
1018 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1019)]
1020#[serde(tag = "type")]
1021pub enum TurbopackIgnoreIssueTextPattern {
1022 #[serde(rename = "string")]
1023 String { value: RcStr },
1024 #[serde(rename = "regex")]
1025 Regex { source: RcStr, flags: RcStr },
1026}
1027
1028impl TurbopackIgnoreIssueTextPattern {
1029 fn to_ignore_pattern(&self) -> Result<IgnoreIssuePattern> {
1030 match self {
1031 TurbopackIgnoreIssueTextPattern::String { value } => {
1032 Ok(IgnoreIssuePattern::ExactString(value.clone()))
1033 }
1034 TurbopackIgnoreIssueTextPattern::Regex { source, flags } => {
1035 Ok(IgnoreIssuePattern::Regex(EsRegex::new(source, flags)?))
1036 }
1037 }
1038 }
1039}
1040
1041#[derive(
1043 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1044)]
1045pub struct TurbopackIgnoreIssueRule {
1046 pub path: TurbopackIgnoreIssuePathPattern,
1047 #[serde(default)]
1048 pub title: Option<TurbopackIgnoreIssueTextPattern>,
1049 #[serde(default)]
1050 pub description: Option<TurbopackIgnoreIssueTextPattern>,
1051}
1052
1053#[derive(
1054 Clone,
1055 Debug,
1056 Default,
1057 PartialEq,
1058 Deserialize,
1059 TraceRawVcs,
1060 ValueDebugFormat,
1061 NonLocalValue,
1062 OperationValue,
1063 Encode,
1064 Decode,
1065)]
1066#[serde(rename_all = "camelCase")]
1067pub struct ExperimentalConfig {
1068 allowed_revalidate_header_keys: Option<Vec<RcStr>>,
1071 client_router_filter: Option<bool>,
1072 client_router_filter_allowed_rate: Option<f64>,
1075 client_router_filter_redirects: Option<bool>,
1076 fetch_cache_key_prefix: Option<RcStr>,
1077 isr_flush_to_disk: Option<bool>,
1078 mdx_rs: Option<MdxRsOptions>,
1081 strict_next_head: Option<bool>,
1082 #[bincode(with = "turbo_bincode::serde_self_describing")]
1083 swc_plugins: Option<Vec<(RcStr, serde_json::Value)>>,
1084 external_middleware_rewrites_resolve: Option<bool>,
1085 scroll_restoration: Option<bool>,
1086 manual_client_base_path: Option<bool>,
1087 optimistic_client_cache: Option<bool>,
1088 middleware_prefetch: Option<MiddlewarePrefetchType>,
1089 #[bincode(with = "turbo_bincode::serde_self_describing")]
1092 optimize_css: Option<serde_json::Value>,
1093 next_script_workers: Option<bool>,
1094 web_vitals_attribution: Option<Vec<RcStr>>,
1095 server_actions: Option<ServerActionsOrLegacyBool>,
1096 sri: Option<SubResourceIntegrity>,
1097 cache_components: Option<bool>,
1100 use_cache: Option<bool>,
1101 root_params: Option<bool>,
1102 runtime_server_deployment_id: Option<bool>,
1103 immutable_asset_token: Option<RcStr>,
1104
1105 adjust_font_fallbacks: Option<bool>,
1109 adjust_font_fallbacks_with_size_adjust: Option<bool>,
1110 after: Option<bool>,
1111 app_document_preloading: Option<bool>,
1112 app_new_scroll_handler: Option<bool>,
1113 case_sensitive_routes: Option<bool>,
1114 cpus: Option<f64>,
1115 cra_compat: Option<bool>,
1116 disable_optimized_loading: Option<bool>,
1117 disable_postcss_preset_env: Option<bool>,
1118 esm_externals: Option<EsmExternals>,
1119 #[bincode(with = "turbo_bincode::serde_self_describing")]
1120 extension_alias: Option<serde_json::Value>,
1121 external_dir: Option<bool>,
1122 fallback_node_polyfills: Option<bool>, force_swc_transforms: Option<bool>,
1127 fully_specified: Option<bool>,
1128 gzip_size: Option<bool>,
1129
1130 inline_css: Option<bool>,
1131 instrumentation_hook: Option<bool>,
1132 client_trace_metadata: Option<Vec<String>>,
1133 large_page_data_bytes: Option<f64>,
1134 #[bincode(with = "turbo_bincode::serde_self_describing")]
1135 logging: Option<serde_json::Value>,
1136 memory_based_workers_count: Option<bool>,
1137 optimize_server_react: Option<bool>,
1139 optimize_package_imports: Option<Vec<RcStr>>,
1142 taint: Option<bool>,
1143 proxy_timeout: Option<f64>,
1144 server_minification: Option<bool>,
1146 server_source_maps: Option<bool>,
1148 swc_trace_profiling: Option<bool>,
1149 transition_indicator: Option<bool>,
1150 gesture_transition: Option<bool>,
1151 trust_host_header: Option<bool>,
1153
1154 #[bincode(with = "turbo_bincode::serde_self_describing")]
1155 url_imports: Option<serde_json::Value>,
1156 webpack_build_worker: Option<bool>,
1159 worker_threads: Option<bool>,
1160
1161 turbopack_minify: Option<bool>,
1162 turbopack_module_ids: Option<ModuleIds>,
1163 turbopack_plugin_runtime_strategy: Option<TurbopackPluginRuntimeStrategy>,
1164 turbopack_source_maps: Option<bool>,
1165 turbopack_input_source_maps: Option<bool>,
1166 turbopack_tree_shaking: Option<bool>,
1167 turbopack_scope_hoisting: Option<bool>,
1168 turbopack_client_side_nested_async_chunking: Option<bool>,
1169 turbopack_server_side_nested_async_chunking: Option<bool>,
1170 turbopack_import_type_bytes: Option<bool>,
1171 turbopack_import_type_text: Option<bool>,
1172 #[serde(default)]
1174 turbopack_use_builtin_sass: Option<bool>,
1175 #[serde(default)]
1178 turbopack_use_builtin_babel: Option<bool>,
1179 global_not_found: Option<bool>,
1181 turbopack_remove_unused_imports: Option<bool>,
1183 turbopack_remove_unused_exports: Option<bool>,
1185 turbopack_infer_module_side_effects: Option<bool>,
1187 devtool_segment_explorer: Option<bool>,
1189 report_system_env_inlining: Option<String>,
1191 lightning_css_features: Option<LightningCssFeatures>,
1195}
1196
1197#[derive(
1198 Clone,
1199 Debug,
1200 PartialEq,
1201 Eq,
1202 Deserialize,
1203 TraceRawVcs,
1204 NonLocalValue,
1205 OperationValue,
1206 Encode,
1207 Decode,
1208)]
1209#[serde(rename_all = "camelCase")]
1210pub struct SubResourceIntegrity {
1211 pub algorithm: Option<RcStr>,
1212}
1213
1214#[derive(
1215 Clone,
1216 Debug,
1217 Default,
1218 PartialEq,
1219 Eq,
1220 Deserialize,
1221 TraceRawVcs,
1222 NonLocalValue,
1223 OperationValue,
1224 Encode,
1225 Decode,
1226)]
1227#[serde(rename_all = "camelCase")]
1228pub struct LightningCssFeatures {
1229 pub include: Option<Vec<RcStr>>,
1230 pub exclude: Option<Vec<RcStr>>,
1231}
1232
1233#[derive(
1234 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1235)]
1236#[serde(untagged)]
1237pub enum ServerActionsOrLegacyBool {
1238 ServerActionsConfig(ServerActions),
1240
1241 LegacyBool(bool),
1244}
1245
1246#[derive(
1247 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1248)]
1249#[serde(rename_all = "kebab-case")]
1250pub enum EsmExternalsValue {
1251 Loose,
1252}
1253
1254#[derive(
1255 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1256)]
1257#[serde(untagged)]
1258pub enum EsmExternals {
1259 Loose(EsmExternalsValue),
1260 Bool(bool),
1261}
1262
1263#[test]
1265fn test_esm_externals_deserialization() {
1266 let json = serde_json::json!({
1267 "esmExternals": true
1268 });
1269 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1270 assert_eq!(config.esm_externals, Some(EsmExternals::Bool(true)));
1271
1272 let json = serde_json::json!({
1273 "esmExternals": "loose"
1274 });
1275 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
1276 assert_eq!(
1277 config.esm_externals,
1278 Some(EsmExternals::Loose(EsmExternalsValue::Loose))
1279 );
1280}
1281
1282#[derive(
1283 Clone,
1284 Debug,
1285 Default,
1286 PartialEq,
1287 Eq,
1288 Deserialize,
1289 TraceRawVcs,
1290 NonLocalValue,
1291 OperationValue,
1292 Encode,
1293 Decode,
1294)]
1295#[serde(rename_all = "camelCase")]
1296pub struct ServerActions {
1297 pub body_size_limit: Option<SizeLimit>,
1299}
1300
1301#[derive(Clone, Debug, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode)]
1302#[serde(untagged)]
1303pub enum SizeLimit {
1304 Number(f64),
1305 WithUnit(String),
1306}
1307
1308impl PartialEq for SizeLimit {
1311 fn eq(&self, other: &Self) -> bool {
1312 match (self, other) {
1313 (SizeLimit::Number(a), SizeLimit::Number(b)) => a.to_bits() == b.to_bits(),
1314 (SizeLimit::WithUnit(a), SizeLimit::WithUnit(b)) => a == b,
1315 _ => false,
1316 }
1317 }
1318}
1319
1320impl Eq for SizeLimit {}
1321
1322#[derive(
1323 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1324)]
1325#[serde(rename_all = "kebab-case")]
1326pub enum MiddlewarePrefetchType {
1327 Strict,
1328 Flexible,
1329}
1330
1331#[derive(
1332 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1333)]
1334#[serde(untagged)]
1335pub enum EmotionTransformOptionsOrBoolean {
1336 Boolean(bool),
1337 Options(EmotionTransformConfig),
1338}
1339
1340impl EmotionTransformOptionsOrBoolean {
1341 pub fn is_enabled(&self) -> bool {
1342 match self {
1343 Self::Boolean(enabled) => *enabled,
1344 _ => true,
1345 }
1346 }
1347}
1348
1349#[derive(
1350 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1351)]
1352#[serde(untagged)]
1353pub enum StyledComponentsTransformOptionsOrBoolean {
1354 Boolean(bool),
1355 Options(StyledComponentsTransformConfig),
1356}
1357
1358impl StyledComponentsTransformOptionsOrBoolean {
1359 pub fn is_enabled(&self) -> bool {
1360 match self {
1361 Self::Boolean(enabled) => *enabled,
1362 _ => true,
1363 }
1364 }
1365}
1366
1367#[turbo_tasks::value(eq = "manual")]
1368#[derive(Clone, Debug, PartialEq, Default, OperationValue, Deserialize)]
1369#[serde(rename_all = "camelCase")]
1370pub struct CompilerConfig {
1371 pub react_remove_properties: Option<ReactRemoveProperties>,
1372 pub relay: Option<RelayConfig>,
1373 pub emotion: Option<EmotionTransformOptionsOrBoolean>,
1374 pub remove_console: Option<RemoveConsoleConfig>,
1375 pub styled_components: Option<StyledComponentsTransformOptionsOrBoolean>,
1376}
1377
1378#[derive(
1379 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1380)]
1381#[serde(untagged, rename_all = "camelCase")]
1382pub enum ReactRemoveProperties {
1383 Boolean(bool),
1384 Config { properties: Option<Vec<String>> },
1385}
1386
1387impl ReactRemoveProperties {
1388 pub fn is_enabled(&self) -> bool {
1389 match self {
1390 Self::Boolean(enabled) => *enabled,
1391 _ => true,
1392 }
1393 }
1394}
1395
1396#[derive(
1397 Clone, Debug, PartialEq, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, Encode, Decode,
1398)]
1399#[serde(untagged)]
1400pub enum RemoveConsoleConfig {
1401 Boolean(bool),
1402 Config { exclude: Option<Vec<String>> },
1403}
1404
1405impl RemoveConsoleConfig {
1406 pub fn is_enabled(&self) -> bool {
1407 match self {
1408 Self::Boolean(enabled) => *enabled,
1409 _ => true,
1410 }
1411 }
1412}
1413
1414#[turbo_tasks::value(transparent)]
1415pub struct ResolveExtensions(Option<Vec<RcStr>>);
1416
1417#[turbo_tasks::value(transparent)]
1418pub struct SwcPlugins(
1419 #[bincode(with = "turbo_bincode::serde_self_describing")] Vec<(RcStr, serde_json::Value)>,
1420);
1421
1422#[turbo_tasks::value(transparent)]
1423pub struct OptionalMdxTransformOptions(Option<ResolvedVc<MdxTransformOptions>>);
1424
1425#[turbo_tasks::value(transparent)]
1426
1427pub struct OptionSubResourceIntegrity(Option<SubResourceIntegrity>);
1428
1429#[turbo_tasks::value(transparent)]
1430pub struct OptionFileSystemPath(Option<FileSystemPath>);
1431
1432#[turbo_tasks::value(transparent)]
1433pub struct IgnoreIssues(Vec<IgnoreIssue>);
1434
1435#[turbo_tasks::value(transparent)]
1436pub struct OptionJsonValue(
1437 #[bincode(with = "turbo_bincode::serde_self_describing")] pub Option<serde_json::Value>,
1438);
1439
1440fn turbopack_config_documentation_link() -> RcStr {
1441 rcstr!("https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#configuring-webpack-loaders")
1442}
1443
1444#[turbo_tasks::value(shared)]
1445struct InvalidLoaderRuleRenameAsIssue {
1446 glob: RcStr,
1447 rename_as: RcStr,
1448 config_file_path: FileSystemPath,
1449}
1450
1451#[turbo_tasks::value_impl]
1452impl Issue for InvalidLoaderRuleRenameAsIssue {
1453 #[turbo_tasks::function]
1454 async fn file_path(&self) -> Result<Vc<FileSystemPath>> {
1455 Ok(self.config_file_path.clone().cell())
1456 }
1457
1458 #[turbo_tasks::function]
1459 fn stage(&self) -> Vc<IssueStage> {
1460 IssueStage::Config.cell()
1461 }
1462
1463 #[turbo_tasks::function]
1464 async fn title(&self) -> Result<Vc<StyledString>> {
1465 Ok(
1466 StyledString::Text(format!("Invalid loader rule for extension: {}", self.glob).into())
1467 .cell(),
1468 )
1469 }
1470
1471 #[turbo_tasks::function]
1472 async fn description(&self) -> Result<Vc<OptionStyledString>> {
1473 Ok(Vc::cell(Some(
1474 StyledString::Text(RcStr::from(format!(
1475 "The extension {} contains a wildcard, but the `as` option does not: {}",
1476 self.glob, self.rename_as,
1477 )))
1478 .resolved_cell(),
1479 )))
1480 }
1481
1482 #[turbo_tasks::function]
1483 fn documentation_link(&self) -> Vc<RcStr> {
1484 Vc::cell(turbopack_config_documentation_link())
1485 }
1486}
1487
1488#[turbo_tasks::value(shared)]
1489struct InvalidLoaderRuleConditionIssue {
1490 error_string: RcStr,
1491 condition: ConfigConditionItem,
1492 config_file_path: FileSystemPath,
1493}
1494
1495#[turbo_tasks::value_impl]
1496impl Issue for InvalidLoaderRuleConditionIssue {
1497 #[turbo_tasks::function]
1498 async fn file_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1499 Ok(self.await?.config_file_path.clone().cell())
1500 }
1501
1502 #[turbo_tasks::function]
1503 fn stage(self: Vc<Self>) -> Vc<IssueStage> {
1504 IssueStage::Config.cell()
1505 }
1506
1507 #[turbo_tasks::function]
1508 async fn title(&self) -> Result<Vc<StyledString>> {
1509 Ok(StyledString::Text(rcstr!("Invalid condition for Turbopack loader rule")).cell())
1510 }
1511
1512 #[turbo_tasks::function]
1513 async fn description(&self) -> Result<Vc<OptionStyledString>> {
1514 Ok(Vc::cell(Some(
1515 StyledString::Stack(vec![
1516 StyledString::Line(vec![
1517 StyledString::Text(rcstr!("Encountered the following error: ")),
1518 StyledString::Code(self.error_string.clone()),
1519 ]),
1520 StyledString::Text(rcstr!("While processing the condition:")),
1521 StyledString::Code(RcStr::from(format!("{:#?}", self.condition))),
1522 ])
1523 .resolved_cell(),
1524 )))
1525 }
1526
1527 #[turbo_tasks::function]
1528 fn documentation_link(&self) -> Vc<RcStr> {
1529 Vc::cell(turbopack_config_documentation_link())
1530 }
1531}
1532
1533#[turbo_tasks::value_impl]
1534impl NextConfig {
1535 #[turbo_tasks::function]
1536 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
1537 let string = string.await?;
1538 let mut jdeserializer = serde_json::Deserializer::from_str(&string);
1539 let config: NextConfig = serde_path_to_error::deserialize(&mut jdeserializer)
1540 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
1541 Ok(config.cell())
1542 }
1543
1544 #[turbo_tasks::function]
1545 pub async fn config_file_path(
1546 &self,
1547 project_path: FileSystemPath,
1548 ) -> Result<Vc<FileSystemPath>> {
1549 Ok(project_path.join(&self.config_file_name)?.cell())
1550 }
1551
1552 #[turbo_tasks::function]
1553 pub fn bundle_pages_router_dependencies(&self) -> Vc<bool> {
1554 Vc::cell(self.bundle_pages_router_dependencies.unwrap_or_default())
1555 }
1556
1557 #[turbo_tasks::function]
1558 pub fn enable_react_production_profiling(&self) -> Vc<bool> {
1559 Vc::cell(self.react_production_profiling.unwrap_or_default())
1560 }
1561
1562 #[turbo_tasks::function]
1563 pub fn server_external_packages(&self) -> Vc<Vec<RcStr>> {
1564 Vc::cell(
1565 self.server_external_packages
1566 .as_ref()
1567 .cloned()
1568 .unwrap_or_default(),
1569 )
1570 }
1571
1572 #[turbo_tasks::function]
1573 pub fn is_standalone(&self) -> Vc<bool> {
1574 Vc::cell(self.output == Some(OutputType::Standalone))
1575 }
1576
1577 #[turbo_tasks::function]
1578 pub fn base_path(&self) -> Vc<Option<RcStr>> {
1579 Vc::cell(self.base_path.clone())
1580 }
1581
1582 #[turbo_tasks::function]
1583 pub fn cache_handler(&self, project_path: FileSystemPath) -> Result<Vc<OptionFileSystemPath>> {
1584 if let Some(handler) = &self.cache_handler {
1585 Ok(Vc::cell(Some(project_path.join(handler)?)))
1586 } else {
1587 Ok(Vc::cell(None))
1588 }
1589 }
1590
1591 #[turbo_tasks::function]
1592 pub fn compiler(&self) -> Vc<CompilerConfig> {
1593 self.compiler.clone().unwrap_or_default().cell()
1594 }
1595
1596 #[turbo_tasks::function]
1597 pub fn env(&self) -> Vc<EnvMap> {
1598 let env = self
1602 .env
1603 .iter()
1604 .map(|(k, v)| {
1605 (
1606 k.as_str().into(),
1607 if let JsonValue::String(s) = v {
1608 s.as_str().into()
1610 } else {
1611 v.to_string().into()
1612 },
1613 )
1614 })
1615 .collect();
1616
1617 Vc::cell(env)
1618 }
1619
1620 #[turbo_tasks::function]
1621 pub fn image_config(&self) -> Vc<ImageConfig> {
1622 self.images.clone().cell()
1623 }
1624
1625 #[turbo_tasks::function]
1626 pub fn page_extensions(&self) -> Vc<Vec<RcStr>> {
1627 let mut extensions = self.page_extensions.clone();
1631 extensions.sort_by_key(|ext| std::cmp::Reverse(ext.len()));
1632 Vc::cell(extensions)
1633 }
1634
1635 #[turbo_tasks::function]
1636 pub fn is_global_not_found_enabled(&self) -> Vc<bool> {
1637 Vc::cell(self.experimental.global_not_found.unwrap_or_default())
1638 }
1639
1640 #[turbo_tasks::function]
1641 pub fn transpile_packages(&self) -> Vc<Vec<RcStr>> {
1642 Vc::cell(self.transpile_packages.clone().unwrap_or_default())
1643 }
1644
1645 #[turbo_tasks::function]
1646 pub async fn webpack_rules(
1647 self: Vc<Self>,
1648 project_path: FileSystemPath,
1649 ) -> Result<Vc<WebpackRules>> {
1650 let this = self.await?;
1651 let Some(turbo_rules) = this.turbopack.as_ref().map(|t| &t.rules) else {
1652 return Ok(Vc::cell(Vec::new()));
1653 };
1654 if turbo_rules.is_empty() {
1655 return Ok(Vc::cell(Vec::new()));
1656 }
1657 let mut rules = Vec::new();
1658 for (glob, rule_collection) in turbo_rules.iter() {
1659 fn transform_loaders(
1660 loaders: &mut dyn Iterator<Item = &LoaderItem>,
1661 ) -> ResolvedVc<WebpackLoaderItems> {
1662 ResolvedVc::cell(
1663 loaders
1664 .map(|item| match item {
1665 LoaderItem::LoaderName(name) => WebpackLoaderItem {
1666 loader: name.clone(),
1667 options: Default::default(),
1668 },
1669 LoaderItem::LoaderOptions(options) => options.clone(),
1670 })
1671 .collect(),
1672 )
1673 }
1674 for item in &rule_collection.0 {
1675 match item {
1676 RuleConfigCollectionItem::Shorthand(loaders) => {
1677 rules.push((
1678 glob.clone(),
1679 LoaderRuleItem {
1680 loaders: transform_loaders(&mut [loaders].into_iter()),
1681 rename_as: None,
1682 condition: None,
1683 module_type: None,
1684 },
1685 ));
1686 }
1687 RuleConfigCollectionItem::Full(RuleConfigItem {
1688 loaders,
1689 rename_as,
1690 condition,
1691 module_type,
1692 }) => {
1693 if glob.contains("*")
1697 && let Some(rename_as) = rename_as.as_ref()
1698 && !rename_as.contains("*")
1699 {
1700 InvalidLoaderRuleRenameAsIssue {
1701 glob: glob.clone(),
1702 config_file_path: self
1703 .config_file_path(project_path.clone())
1704 .owned()
1705 .await?,
1706 rename_as: rename_as.clone(),
1707 }
1708 .resolved_cell()
1709 .emit();
1710 }
1711
1712 let condition = if let Some(condition) = condition {
1715 match ConditionItem::try_from(condition.clone()) {
1716 Ok(cond) => Some(cond),
1717 Err(err) => {
1718 InvalidLoaderRuleConditionIssue {
1719 error_string: RcStr::from(err.to_string()),
1720 condition: condition.clone(),
1721 config_file_path: self
1722 .config_file_path(project_path.clone())
1723 .owned()
1724 .await?,
1725 }
1726 .resolved_cell()
1727 .emit();
1728 None
1729 }
1730 }
1731 } else {
1732 None
1733 };
1734 rules.push((
1735 glob.clone(),
1736 LoaderRuleItem {
1737 loaders: transform_loaders(&mut loaders.iter()),
1738 rename_as: rename_as.clone(),
1739 condition,
1740 module_type: module_type.clone(),
1741 },
1742 ));
1743 }
1744 }
1745 }
1746 }
1747 Ok(Vc::cell(rules))
1748 }
1749
1750 #[turbo_tasks::function]
1751 pub fn resolve_alias_options(&self) -> Result<Vc<ResolveAliasMap>> {
1752 let Some(resolve_alias) = self
1753 .turbopack
1754 .as_ref()
1755 .and_then(|t| t.resolve_alias.as_ref())
1756 else {
1757 return Ok(ResolveAliasMap::cell(ResolveAliasMap::default()));
1758 };
1759 let alias_map: ResolveAliasMap = resolve_alias.try_into()?;
1760 Ok(alias_map.cell())
1761 }
1762
1763 #[turbo_tasks::function]
1764 pub fn resolve_extension(&self) -> Vc<ResolveExtensions> {
1765 let Some(resolve_extensions) = self
1766 .turbopack
1767 .as_ref()
1768 .and_then(|t| t.resolve_extensions.as_ref())
1769 else {
1770 return Vc::cell(None);
1771 };
1772 Vc::cell(Some(resolve_extensions.clone()))
1773 }
1774
1775 #[turbo_tasks::function]
1776 pub fn import_externals(&self) -> Result<Vc<bool>> {
1777 Ok(Vc::cell(match self.experimental.esm_externals {
1778 Some(EsmExternals::Bool(b)) => b,
1779 Some(EsmExternals::Loose(_)) => bail!("esmExternals = \"loose\" is not supported"),
1780 None => true,
1781 }))
1782 }
1783
1784 #[turbo_tasks::function]
1785 pub fn inline_css(&self) -> Vc<bool> {
1786 Vc::cell(self.experimental.inline_css.unwrap_or(false))
1787 }
1788
1789 #[turbo_tasks::function]
1790 pub fn mdx_rs(&self) -> Vc<OptionalMdxTransformOptions> {
1791 let options = &self.experimental.mdx_rs;
1792
1793 let options = match options {
1794 Some(MdxRsOptions::Boolean(true)) => OptionalMdxTransformOptions(Some(
1795 MdxTransformOptions {
1796 provider_import_source: Some(mdx_import_source_file()),
1797 ..Default::default()
1798 }
1799 .resolved_cell(),
1800 )),
1801 Some(MdxRsOptions::Option(options)) => OptionalMdxTransformOptions(Some(
1802 MdxTransformOptions {
1803 provider_import_source: Some(
1804 options
1805 .provider_import_source
1806 .clone()
1807 .unwrap_or(mdx_import_source_file()),
1808 ),
1809 ..options.clone()
1810 }
1811 .resolved_cell(),
1812 )),
1813 _ => OptionalMdxTransformOptions(None),
1814 };
1815
1816 options.cell()
1817 }
1818
1819 #[turbo_tasks::function]
1820 pub fn modularize_imports(&self) -> Vc<ModularizeImports> {
1821 Vc::cell(self.modularize_imports.clone().unwrap_or_default())
1822 }
1823
1824 #[turbo_tasks::function]
1825 pub fn dist_dir(&self) -> Vc<RcStr> {
1826 Vc::cell(self.dist_dir.clone())
1827 }
1828 #[turbo_tasks::function]
1829 pub fn dist_dir_root(&self) -> Vc<RcStr> {
1830 Vc::cell(self.dist_dir_root.clone())
1831 }
1832
1833 #[turbo_tasks::function]
1834 pub fn cache_handlers(&self, project_path: FileSystemPath) -> Result<Vc<FileSystemPathVec>> {
1835 if let Some(handlers) = &self.cache_handlers {
1836 Ok(Vc::cell(
1837 handlers
1838 .values()
1839 .map(|h| project_path.join(h))
1840 .collect::<Result<Vec<_>>>()?,
1841 ))
1842 } else {
1843 Ok(Vc::cell(vec![]))
1844 }
1845 }
1846
1847 #[turbo_tasks::function]
1848 pub fn cache_handlers_map(&self) -> Vc<CacheHandlersMap> {
1849 Vc::cell(self.cache_handlers.clone().unwrap_or_default())
1850 }
1851
1852 #[turbo_tasks::function]
1853 pub fn experimental_swc_plugins(&self) -> Vc<SwcPlugins> {
1854 Vc::cell(self.experimental.swc_plugins.clone().unwrap_or_default())
1855 }
1856
1857 #[turbo_tasks::function]
1858 pub fn experimental_sri(&self) -> Vc<OptionSubResourceIntegrity> {
1859 Vc::cell(self.experimental.sri.clone())
1860 }
1861
1862 #[turbo_tasks::function]
1863 pub fn experimental_turbopack_use_builtin_babel(&self) -> Vc<Option<bool>> {
1864 Vc::cell(self.experimental.turbopack_use_builtin_babel)
1865 }
1866
1867 #[turbo_tasks::function]
1868 pub fn experimental_turbopack_use_builtin_sass(&self) -> Vc<Option<bool>> {
1869 Vc::cell(self.experimental.turbopack_use_builtin_sass)
1870 }
1871
1872 #[turbo_tasks::function]
1873 pub fn react_compiler_options(&self) -> Vc<OptionalReactCompilerOptions> {
1874 let options = &self.react_compiler;
1875
1876 let options = match options {
1877 Some(ReactCompilerOptionsOrBoolean::Boolean(true)) => {
1878 OptionalReactCompilerOptions(Some(ReactCompilerOptions::default().resolved_cell()))
1879 }
1880 Some(ReactCompilerOptionsOrBoolean::Option(options)) => OptionalReactCompilerOptions(
1881 Some(ReactCompilerOptions { ..options.clone() }.resolved_cell()),
1882 ),
1883 _ => OptionalReactCompilerOptions(None),
1884 };
1885
1886 options.cell()
1887 }
1888
1889 #[turbo_tasks::function]
1890 pub fn sass_config(&self) -> Vc<JsonValue> {
1891 Vc::cell(self.sass_options.clone().unwrap_or_default())
1892 }
1893
1894 #[turbo_tasks::function]
1895 pub fn skip_proxy_url_normalize(&self) -> Vc<bool> {
1896 Vc::cell(self.skip_proxy_url_normalize.unwrap_or(false))
1897 }
1898
1899 #[turbo_tasks::function]
1900 pub fn skip_trailing_slash_redirect(&self) -> Vc<bool> {
1901 Vc::cell(self.skip_trailing_slash_redirect.unwrap_or(false))
1902 }
1903
1904 #[turbo_tasks::function]
1907 pub async fn computed_asset_prefix(self: Vc<Self>) -> Result<Vc<RcStr>> {
1908 let this = self.await?;
1909
1910 Ok(Vc::cell(
1911 format!(
1912 "{}/_next/",
1913 if let Some(asset_prefix) = &this.asset_prefix {
1914 asset_prefix
1915 } else {
1916 this.base_path.as_ref().map_or("", |b| b.as_str())
1917 }
1918 .trim_end_matches('/')
1919 )
1920 .into(),
1921 ))
1922 }
1923
1924 #[turbo_tasks::function]
1926 pub fn asset_suffix_path(&self) -> Vc<Option<RcStr>> {
1927 let id = self
1928 .experimental
1929 .immutable_asset_token
1930 .as_ref()
1931 .or(self.deployment_id.as_ref());
1932
1933 Vc::cell(id.as_ref().map(|id| format!("?dpl={id}").into()))
1934 }
1935
1936 #[turbo_tasks::function]
1939 pub fn enable_immutable_assets(&self) -> Vc<bool> {
1940 Vc::cell(self.experimental.immutable_asset_token.is_some())
1941 }
1942
1943 #[turbo_tasks::function]
1944 pub fn enable_taint(&self) -> Vc<bool> {
1945 Vc::cell(self.experimental.taint.unwrap_or(false))
1946 }
1947
1948 #[turbo_tasks::function]
1949 pub fn enable_transition_indicator(&self) -> Vc<bool> {
1950 Vc::cell(self.experimental.transition_indicator.unwrap_or(false))
1951 }
1952
1953 #[turbo_tasks::function]
1954 pub fn enable_gesture_transition(&self) -> Vc<bool> {
1955 Vc::cell(self.experimental.gesture_transition.unwrap_or(false))
1956 }
1957
1958 #[turbo_tasks::function]
1959 pub fn enable_cache_components(&self) -> Vc<bool> {
1960 Vc::cell(self.cache_components.unwrap_or(false))
1961 }
1962
1963 #[turbo_tasks::function]
1964 pub fn enable_use_cache(&self) -> Vc<bool> {
1965 Vc::cell(
1966 self.experimental
1967 .use_cache
1968 .unwrap_or(self.cache_components.unwrap_or(false)),
1972 )
1973 }
1974
1975 #[turbo_tasks::function]
1976 pub fn enable_root_params(&self) -> Vc<bool> {
1977 Vc::cell(
1978 self.experimental
1979 .root_params
1980 .unwrap_or(self.cache_components.unwrap_or(false)),
1982 )
1983 }
1984
1985 #[turbo_tasks::function]
1986 pub fn runtime_server_deployment_id_available(&self) -> Vc<bool> {
1987 Vc::cell(
1988 self.experimental
1989 .runtime_server_deployment_id
1990 .unwrap_or(false),
1991 )
1992 }
1993
1994 #[turbo_tasks::function]
1995 pub fn cache_kinds(&self) -> Vc<CacheKinds> {
1996 let mut cache_kinds = CacheKinds::default();
1997
1998 if let Some(handlers) = self.cache_handlers.as_ref() {
1999 cache_kinds.extend(handlers.keys().cloned());
2000 }
2001
2002 cache_kinds.cell()
2003 }
2004
2005 #[turbo_tasks::function]
2006 pub fn optimize_package_imports(&self) -> Vc<Vec<RcStr>> {
2007 Vc::cell(
2008 self.experimental
2009 .optimize_package_imports
2010 .clone()
2011 .unwrap_or_default(),
2012 )
2013 }
2014
2015 #[turbo_tasks::function]
2016 pub fn tree_shaking_mode_for_foreign_code(
2017 &self,
2018 _is_development: bool,
2019 ) -> Vc<OptionTreeShaking> {
2020 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
2021 Some(false) => Some(TreeShakingMode::ReexportsOnly),
2022 Some(true) => Some(TreeShakingMode::ModuleFragments),
2023 None => Some(TreeShakingMode::ReexportsOnly),
2024 })
2025 .cell()
2026 }
2027
2028 #[turbo_tasks::function]
2029 pub fn tree_shaking_mode_for_user_code(&self, _is_development: bool) -> Vc<OptionTreeShaking> {
2030 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
2031 Some(false) => Some(TreeShakingMode::ReexportsOnly),
2032 Some(true) => Some(TreeShakingMode::ModuleFragments),
2033 None => Some(TreeShakingMode::ReexportsOnly),
2034 })
2035 .cell()
2036 }
2037
2038 #[turbo_tasks::function]
2039 pub async fn turbopack_remove_unused_imports(
2040 self: Vc<Self>,
2041 mode: Vc<NextMode>,
2042 ) -> Result<Vc<bool>> {
2043 let remove_unused_imports = self
2044 .await?
2045 .experimental
2046 .turbopack_remove_unused_imports
2047 .unwrap_or(matches!(*mode.await?, NextMode::Build));
2048
2049 if remove_unused_imports && !*self.turbopack_remove_unused_exports(mode).await? {
2050 bail!(
2051 "`experimental.turbopackRemoveUnusedImports` cannot be enabled without also \
2052 enabling `experimental.turbopackRemoveUnusedExports`"
2053 );
2054 }
2055
2056 Ok(Vc::cell(remove_unused_imports))
2057 }
2058
2059 #[turbo_tasks::function]
2060 pub async fn turbopack_remove_unused_exports(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2061 Ok(Vc::cell(
2062 self.experimental
2063 .turbopack_remove_unused_exports
2064 .unwrap_or(matches!(*mode.await?, NextMode::Build)),
2065 ))
2066 }
2067
2068 #[turbo_tasks::function]
2069 pub fn turbopack_infer_module_side_effects(&self) -> Vc<bool> {
2070 Vc::cell(
2071 self.experimental
2072 .turbopack_infer_module_side_effects
2073 .unwrap_or(true),
2074 )
2075 }
2076
2077 #[turbo_tasks::function]
2078 pub fn turbopack_plugin_runtime_strategy(&self) -> Vc<TurbopackPluginRuntimeStrategy> {
2079 #[cfg(feature = "process_pool")]
2080 let default = TurbopackPluginRuntimeStrategy::ChildProcesses;
2081 #[cfg(all(feature = "worker_pool", not(feature = "process_pool")))]
2082 let default = TurbopackPluginRuntimeStrategy::WorkerThreads;
2083
2084 self.experimental
2085 .turbopack_plugin_runtime_strategy
2086 .unwrap_or(default)
2087 .cell()
2088 }
2089
2090 #[turbo_tasks::function]
2091 pub async fn module_ids(&self, mode: Vc<NextMode>) -> Result<Vc<ModuleIds>> {
2092 Ok(match *mode.await? {
2093 NextMode::Development => ModuleIds::Named.cell(),
2095 NextMode::Build => self
2096 .experimental
2097 .turbopack_module_ids
2098 .unwrap_or(ModuleIds::Deterministic)
2099 .cell(),
2100 })
2101 }
2102
2103 #[turbo_tasks::function]
2104 pub async fn turbo_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2105 let minify = self.experimental.turbopack_minify;
2106 Ok(Vc::cell(
2107 minify.unwrap_or(matches!(*mode.await?, NextMode::Build)),
2108 ))
2109 }
2110
2111 #[turbo_tasks::function]
2112 pub async fn turbo_scope_hoisting(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
2113 Ok(Vc::cell(match *mode.await? {
2114 NextMode::Development => false,
2116 NextMode::Build => self.experimental.turbopack_scope_hoisting.unwrap_or(true),
2117 }))
2118 }
2119
2120 #[turbo_tasks::function]
2121 pub async fn turbo_nested_async_chunking(
2122 &self,
2123 mode: Vc<NextMode>,
2124 client_side: bool,
2125 ) -> Result<Vc<bool>> {
2126 let option = if client_side {
2127 self.experimental
2128 .turbopack_client_side_nested_async_chunking
2129 } else {
2130 self.experimental
2131 .turbopack_server_side_nested_async_chunking
2132 };
2133 Ok(Vc::cell(if let Some(value) = option {
2134 value
2135 } else {
2136 match *mode.await? {
2137 NextMode::Development => false,
2138 NextMode::Build => client_side,
2139 }
2140 }))
2141 }
2142
2143 #[turbo_tasks::function]
2144 pub async fn turbopack_import_type_bytes(&self) -> Vc<bool> {
2145 Vc::cell(
2146 self.experimental
2147 .turbopack_import_type_bytes
2148 .unwrap_or(false),
2149 )
2150 }
2151
2152 #[turbo_tasks::function]
2153 pub async fn turbopack_import_type_text(&self) -> Vc<bool> {
2154 Vc::cell(
2155 self.experimental
2156 .turbopack_import_type_text
2157 .unwrap_or(false),
2158 )
2159 }
2160
2161 #[turbo_tasks::function]
2162 pub fn lightningcss_feature_flags(
2163 &self,
2164 ) -> Result<Vc<turbopack_css::LightningCssFeatureFlags>> {
2165 Ok(turbopack_css::LightningCssFeatureFlags {
2166 include: lightningcss_features_field_mask(
2167 &self.experimental.lightning_css_features,
2168 |f| f.include.as_ref(),
2169 )?,
2170 exclude: lightningcss_features_field_mask(
2171 &self.experimental.lightning_css_features,
2172 |f| f.exclude.as_ref(),
2173 )?,
2174 }
2175 .cell())
2176 }
2177
2178 #[turbo_tasks::function]
2179 pub async fn client_source_maps(&self, mode: Vc<NextMode>) -> Result<Vc<SourceMapsType>> {
2180 let input_source_maps = self
2181 .experimental
2182 .turbopack_input_source_maps
2183 .unwrap_or(true);
2184 let source_maps = self
2185 .experimental
2186 .turbopack_source_maps
2187 .unwrap_or(match &*mode.await? {
2188 NextMode::Development => true,
2189 NextMode::Build => self.production_browser_source_maps,
2190 });
2191 Ok(match (source_maps, input_source_maps) {
2192 (true, true) => SourceMapsType::Full,
2193 (true, false) => SourceMapsType::Partial,
2194 (false, _) => SourceMapsType::None,
2195 }
2196 .cell())
2197 }
2198
2199 #[turbo_tasks::function]
2200 pub fn server_source_maps(&self) -> Result<Vc<SourceMapsType>> {
2201 let input_source_maps = self
2202 .experimental
2203 .turbopack_input_source_maps
2204 .unwrap_or(true);
2205 let source_maps = self
2206 .experimental
2207 .turbopack_source_maps
2208 .or(self.experimental.server_source_maps)
2209 .unwrap_or(true);
2210 Ok(match (source_maps, input_source_maps) {
2211 (true, true) => SourceMapsType::Full,
2212 (true, false) => SourceMapsType::Partial,
2213 (false, _) => SourceMapsType::None,
2214 }
2215 .cell())
2216 }
2217
2218 #[turbo_tasks::function]
2219 pub fn turbopack_debug_ids(&self) -> Vc<bool> {
2220 Vc::cell(
2221 self.turbopack
2222 .as_ref()
2223 .and_then(|turbopack| turbopack.debug_ids)
2224 .unwrap_or(false),
2225 )
2226 }
2227
2228 #[turbo_tasks::function]
2229 pub fn typescript_tsconfig_path(&self) -> Result<Vc<Option<RcStr>>> {
2230 Ok(Vc::cell(
2231 self.typescript
2232 .tsconfig_path
2233 .as_ref()
2234 .map(|path| path.to_owned().into()),
2235 ))
2236 }
2237
2238 #[turbo_tasks::function]
2239 pub fn cross_origin(&self) -> Vc<OptionCrossOriginConfig> {
2240 Vc::cell(self.cross_origin.clone())
2241 }
2242
2243 #[turbo_tasks::function]
2244 pub fn i18n(&self) -> Vc<OptionI18NConfig> {
2245 Vc::cell(self.i18n.clone())
2246 }
2247
2248 #[turbo_tasks::function]
2249 pub fn output(&self) -> Vc<OptionOutputType> {
2250 Vc::cell(self.output.clone())
2251 }
2252
2253 #[turbo_tasks::function]
2254 pub fn output_file_tracing_includes(&self) -> Vc<OptionJsonValue> {
2255 Vc::cell(self.output_file_tracing_includes.clone())
2256 }
2257
2258 #[turbo_tasks::function]
2259 pub fn output_file_tracing_excludes(&self) -> Vc<OptionJsonValue> {
2260 Vc::cell(self.output_file_tracing_excludes.clone())
2261 }
2262
2263 #[turbo_tasks::function]
2264 pub fn fetch_client(&self) -> Vc<FetchClientConfig> {
2265 FetchClientConfig::default().cell()
2266 }
2267
2268 #[turbo_tasks::function]
2269 pub async fn report_system_env_inlining(&self) -> Result<Vc<IssueSeverity>> {
2270 match self.experimental.report_system_env_inlining.as_deref() {
2271 None => Ok(IssueSeverity::Suggestion.cell()),
2272 Some("warn") => Ok(IssueSeverity::Warning.cell()),
2273 Some("error") => Ok(IssueSeverity::Error.cell()),
2274 _ => bail!(
2275 "`experimental.reportSystemEnvInlining` must be undefined, \"error\", or \"warn\""
2276 ),
2277 }
2278 }
2279
2280 #[turbo_tasks::function]
2283 pub fn turbopack_ignore_issue_rules(&self) -> Result<Vc<IgnoreIssues>> {
2284 let rules = self
2285 .turbopack
2286 .as_ref()
2287 .and_then(|tp| tp.ignore_issue.as_deref())
2288 .unwrap_or_default()
2289 .iter()
2290 .map(|rule| {
2291 Ok(IgnoreIssue {
2292 path: rule.path.to_ignore_pattern()?,
2293 title: rule
2294 .title
2295 .as_ref()
2296 .map(|t| t.to_ignore_pattern())
2297 .transpose()?,
2298 description: rule
2299 .description
2300 .as_ref()
2301 .map(|d| d.to_ignore_pattern())
2302 .transpose()?,
2303 })
2304 })
2305 .collect::<Result<Vec<_>>>()?;
2306 Ok(Vc::cell(rules))
2307 }
2308}
2309
2310#[turbo_tasks::value(serialization = "custom", eq = "manual")]
2313#[derive(Clone, Debug, Default, PartialEq, Deserialize, Encode, Decode)]
2314#[serde(rename_all = "camelCase")]
2315pub struct JsConfig {
2316 #[bincode(with = "turbo_bincode::serde_self_describing")]
2317 compiler_options: Option<serde_json::Value>,
2318}
2319
2320#[turbo_tasks::value_impl]
2321impl JsConfig {
2322 #[turbo_tasks::function]
2323 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
2324 let string = string.await?;
2325 let config: JsConfig = serde_json::from_str(&string)
2326 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
2327
2328 Ok(config.cell())
2329 }
2330
2331 #[turbo_tasks::function]
2332 pub fn compiler_options(&self) -> Vc<serde_json::Value> {
2333 Vc::cell(self.compiler_options.clone().unwrap_or_default())
2334 }
2335}
2336
2337fn lightningcss_features_field_mask(
2340 features: &Option<LightningCssFeatures>,
2341 field: impl FnOnce(&LightningCssFeatures) -> Option<&Vec<RcStr>>,
2342) -> Result<u32> {
2343 features
2344 .as_ref()
2345 .and_then(field)
2346 .map(|names| lightningcss_feature_names_to_mask(names))
2347 .unwrap_or(Ok(0))
2348}
2349
2350pub fn lightningcss_feature_names_to_mask(
2359 names: &[impl std::ops::Deref<Target = str>],
2360) -> Result<u32> {
2361 use lightningcss::targets::Features;
2362 let mut mask = Features::empty();
2363 for name in names {
2364 mask |= match &**name {
2365 "nesting" => Features::Nesting,
2366 "not-selector-list" => Features::NotSelectorList,
2367 "dir-selector" => Features::DirSelector,
2368 "lang-selector-list" => Features::LangSelectorList,
2369 "is-selector" => Features::IsSelector,
2370 "text-decoration-thickness-percent" => Features::TextDecorationThicknessPercent,
2371 "media-interval-syntax" => Features::MediaIntervalSyntax,
2372 "media-range-syntax" => Features::MediaRangeSyntax,
2373 "custom-media-queries" => Features::CustomMediaQueries,
2374 "clamp-function" => Features::ClampFunction,
2375 "color-function" => Features::ColorFunction,
2376 "oklab-colors" => Features::OklabColors,
2377 "lab-colors" => Features::LabColors,
2378 "p3-colors" => Features::P3Colors,
2379 "hex-alpha-colors" => Features::HexAlphaColors,
2380 "space-separated-color-notation" => Features::SpaceSeparatedColorNotation,
2381 "font-family-system-ui" => Features::FontFamilySystemUi,
2382 "double-position-gradients" => Features::DoublePositionGradients,
2383 "vendor-prefixes" => Features::VendorPrefixes,
2384 "logical-properties" => Features::LogicalProperties,
2385 "light-dark" => Features::LightDark,
2386 "selectors" => Features::Selectors,
2388 "media-queries" => Features::MediaQueries,
2389 "colors" => Features::Colors,
2390 _ => bail!("Unknown lightningcss feature: {}", &**name),
2391 };
2392 }
2393 Ok(mask.bits())
2394}
2395
2396#[cfg(test)]
2397mod tests {
2398 use super::*;
2399
2400 #[test]
2401 fn test_serde_rule_config_item_options() {
2402 let json_value = serde_json::json!({
2403 "loaders": [],
2404 "as": "*.js",
2405 "condition": {
2406 "all": [
2407 "production",
2408 {"not": "foreign"},
2409 {"any": [
2410 "browser",
2411 {
2412 "path": { "type": "glob", "value": "*.svg"},
2413 "query": {
2414 "type": "regex",
2415 "value": {
2416 "source": "@someQuery",
2417 "flags": ""
2418 }
2419 },
2420 "content": {
2421 "source": "@someTag",
2422 "flags": ""
2423 }
2424 }
2425 ]},
2426 ],
2427 }
2428 });
2429
2430 let rule_config: RuleConfigItem = serde_json::from_value(json_value).unwrap();
2431
2432 assert_eq!(
2433 rule_config,
2434 RuleConfigItem {
2435 loaders: vec![],
2436 rename_as: Some(rcstr!("*.js")),
2437 module_type: None,
2438 condition: Some(ConfigConditionItem::All(
2439 [
2440 ConfigConditionItem::Builtin(WebpackLoaderBuiltinCondition::Production),
2441 ConfigConditionItem::Not(Box::new(ConfigConditionItem::Builtin(
2442 WebpackLoaderBuiltinCondition::Foreign
2443 ))),
2444 ConfigConditionItem::Any(
2445 vec![
2446 ConfigConditionItem::Builtin(
2447 WebpackLoaderBuiltinCondition::Browser
2448 ),
2449 ConfigConditionItem::Base {
2450 path: Some(ConfigConditionPath::Glob(rcstr!("*.svg"))),
2451 content: Some(RegexComponents {
2452 source: rcstr!("@someTag"),
2453 flags: rcstr!(""),
2454 }),
2455 query: Some(ConfigConditionQuery::Regex(RegexComponents {
2456 source: rcstr!("@someQuery"),
2457 flags: rcstr!(""),
2458 })),
2459 content_type: None,
2460 },
2461 ]
2462 .into(),
2463 ),
2464 ]
2465 .into(),
2466 )),
2467 }
2468 );
2469 }
2470}