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