1use anyhow::{Context, Result, bail};
2use rustc_hash::FxHashSet;
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_json::Value as JsonValue;
5use turbo_esregex::EsRegex;
6use turbo_rcstr::RcStr;
7use turbo_tasks::{
8 FxIndexMap, NonLocalValue, OperationValue, ResolvedVc, TaskInput, Vc, debug::ValueDebugFormat,
9 trace::TraceRawVcs,
10};
11use turbo_tasks_env::EnvMap;
12use turbopack::module_options::{
13 ConditionItem, ConditionPath, LoaderRuleItem, OptionWebpackRules,
14 module_options_context::{MdxTransformOptions, OptionWebpackConditions},
15};
16use turbopack_core::resolve::ResolveAliasMap;
17use turbopack_ecmascript::{OptionTreeShaking, TreeShakingMode};
18use turbopack_ecmascript_plugins::transform::{
19 emotion::EmotionTransformConfig, relay::RelayConfig,
20 styled_components::StyledComponentsTransformConfig,
21};
22use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems};
23
24use crate::{
25 mode::NextMode, next_import_map::mdx_import_source_file,
26 next_shared::transforms::ModularizeImportPackageConfig,
27};
28
29#[turbo_tasks::value]
30struct NextConfigAndCustomRoutes {
31 config: ResolvedVc<NextConfig>,
32 custom_routes: ResolvedVc<CustomRoutes>,
33}
34
35#[turbo_tasks::value]
36struct CustomRoutes {
37 rewrites: ResolvedVc<Rewrites>,
38}
39
40#[turbo_tasks::value(transparent)]
41pub struct ModularizeImports(FxIndexMap<String, ModularizeImportPackageConfig>);
42
43#[turbo_tasks::value(transparent)]
44#[derive(Clone, Debug)]
45pub struct CacheKinds(FxHashSet<RcStr>);
46
47impl CacheKinds {
48 pub fn extend<I: IntoIterator<Item = RcStr>>(&mut self, iter: I) {
49 self.0.extend(iter);
50 }
51}
52
53impl Default for CacheKinds {
54 fn default() -> Self {
55 CacheKinds(["default", "remote"].iter().map(|&s| s.into()).collect())
56 }
57}
58
59#[turbo_tasks::value(eq = "manual")]
60#[derive(Clone, Debug, Default, PartialEq)]
61#[serde(default, rename_all = "camelCase")]
62pub struct NextConfig {
63 pub config_file: Option<RcStr>,
66 pub config_file_name: RcStr,
67
68 pub cache_max_memory_size: Option<f64>,
72 pub cache_handler: Option<RcStr>,
74
75 pub env: FxIndexMap<String, JsonValue>,
76 pub experimental: ExperimentalConfig,
77 pub images: ImageConfig,
78 pub page_extensions: Vec<RcStr>,
79 pub react_production_profiling: Option<bool>,
80 pub react_strict_mode: Option<bool>,
81 pub transpile_packages: Option<Vec<RcStr>>,
82 pub modularize_imports: Option<FxIndexMap<String, ModularizeImportPackageConfig>>,
83 pub dist_dir: Option<RcStr>,
84 pub deployment_id: Option<RcStr>,
85 sass_options: Option<serde_json::Value>,
86 pub trailing_slash: Option<bool>,
87 pub asset_prefix: Option<RcStr>,
88 pub base_path: Option<RcStr>,
89 pub skip_middleware_url_normalize: Option<bool>,
90 pub skip_trailing_slash_redirect: Option<bool>,
91 pub i18n: Option<I18NConfig>,
92 pub cross_origin: Option<CrossOriginConfig>,
93 pub dev_indicators: Option<DevIndicatorsConfig>,
94 pub output: Option<OutputType>,
95 pub turbopack: Option<TurbopackConfig>,
96 production_browser_source_maps: bool,
97 output_file_tracing_includes: Option<serde_json::Value>,
98 output_file_tracing_excludes: Option<serde_json::Value>,
99 output_file_tracing_root: Option<RcStr>,
101
102 pub bundle_pages_router_dependencies: Option<bool>,
107
108 pub server_external_packages: Option<Vec<RcStr>>,
113
114 #[serde(rename = "_originalRedirects")]
115 pub original_redirects: Option<Vec<Redirect>>,
116
117 pub compiler: Option<CompilerConfig>,
119
120 pub optimize_fonts: Option<bool>,
121
122 amp: AmpConfig,
124 clean_dist_dir: bool,
125 compress: bool,
126 eslint: EslintConfig,
127 exclude_default_moment_locales: bool,
128 export_path_map: Option<serde_json::Value>,
130 generate_build_id: Option<serde_json::Value>,
132 generate_etags: bool,
133 http_agent_options: HttpAgentConfig,
134 on_demand_entries: OnDemandEntriesConfig,
135 powered_by_header: bool,
136 public_runtime_config: FxIndexMap<String, serde_json::Value>,
137 server_runtime_config: FxIndexMap<String, serde_json::Value>,
138 static_page_generation_timeout: f64,
139 target: Option<String>,
140 typescript: TypeScriptConfig,
141 use_file_system_public_routes: bool,
142 webpack: Option<serde_json::Value>,
143}
144
145#[derive(
146 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
147)]
148#[serde(rename_all = "kebab-case")]
149pub enum CrossOriginConfig {
150 Anonymous,
151 UseCredentials,
152}
153
154#[derive(
155 Clone,
156 Debug,
157 Default,
158 PartialEq,
159 Serialize,
160 Deserialize,
161 TraceRawVcs,
162 NonLocalValue,
163 OperationValue,
164)]
165#[serde(rename_all = "camelCase")]
166struct AmpConfig {
167 canonical_base: Option<String>,
168}
169
170#[derive(
171 Clone,
172 Debug,
173 Default,
174 PartialEq,
175 Serialize,
176 Deserialize,
177 TraceRawVcs,
178 NonLocalValue,
179 OperationValue,
180)]
181#[serde(rename_all = "camelCase")]
182struct EslintConfig {
183 dirs: Option<Vec<String>>,
184 ignore_during_builds: Option<bool>,
185}
186
187#[derive(
188 Clone,
189 Debug,
190 Default,
191 PartialEq,
192 Serialize,
193 Deserialize,
194 TraceRawVcs,
195 NonLocalValue,
196 OperationValue,
197)]
198#[serde(rename_all = "kebab-case")]
199pub enum BuildActivityPositions {
200 #[default]
201 BottomRight,
202 BottomLeft,
203 TopRight,
204 TopLeft,
205}
206
207#[derive(
208 Clone,
209 Debug,
210 Default,
211 PartialEq,
212 Serialize,
213 Deserialize,
214 TraceRawVcs,
215 NonLocalValue,
216 OperationValue,
217)]
218#[serde(rename_all = "camelCase")]
219pub struct DevIndicatorsOptions {
220 pub build_activity_position: Option<BuildActivityPositions>,
221 pub position: Option<BuildActivityPositions>,
222}
223
224#[derive(
225 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
226)]
227#[serde(untagged)]
228pub enum DevIndicatorsConfig {
229 WithOptions(DevIndicatorsOptions),
230 Boolean(bool),
231}
232
233#[derive(
234 Clone,
235 Debug,
236 Default,
237 PartialEq,
238 Serialize,
239 Deserialize,
240 TraceRawVcs,
241 NonLocalValue,
242 OperationValue,
243)]
244#[serde(rename_all = "camelCase")]
245struct OnDemandEntriesConfig {
246 max_inactive_age: f64,
247 pages_buffer_length: f64,
248}
249
250#[derive(
251 Clone,
252 Debug,
253 Default,
254 PartialEq,
255 Serialize,
256 Deserialize,
257 TraceRawVcs,
258 NonLocalValue,
259 OperationValue,
260)]
261#[serde(rename_all = "camelCase")]
262struct HttpAgentConfig {
263 keep_alive: bool,
264}
265
266#[derive(
267 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
268)]
269#[serde(rename_all = "camelCase")]
270pub struct DomainLocale {
271 pub default_locale: String,
272 pub domain: String,
273 pub http: Option<bool>,
274 pub locales: Option<Vec<String>>,
275}
276
277#[derive(
278 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
279)]
280#[serde(rename_all = "camelCase")]
281pub struct I18NConfig {
282 pub default_locale: String,
283 pub domains: Option<Vec<DomainLocale>>,
284 pub locale_detection: Option<bool>,
285 pub locales: Vec<String>,
286}
287
288#[derive(
289 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
290)]
291#[serde(rename_all = "kebab-case")]
292pub enum OutputType {
293 Standalone,
294 Export,
295}
296
297#[derive(
298 Debug,
299 Clone,
300 Hash,
301 Eq,
302 PartialEq,
303 Ord,
304 PartialOrd,
305 TaskInput,
306 TraceRawVcs,
307 Serialize,
308 Deserialize,
309 NonLocalValue,
310 OperationValue,
311)]
312#[serde(tag = "type", rename_all = "kebab-case")]
313pub enum RouteHas {
314 Header {
315 key: RcStr,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 value: Option<RcStr>,
318 },
319 Cookie {
320 key: RcStr,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 value: Option<RcStr>,
323 },
324 Query {
325 key: RcStr,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 value: Option<RcStr>,
328 },
329 Host {
330 value: RcStr,
331 },
332}
333
334#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
335#[serde(rename_all = "camelCase")]
336pub struct HeaderValue {
337 pub key: RcStr,
338 pub value: RcStr,
339}
340
341#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
342#[serde(rename_all = "camelCase")]
343pub struct Header {
344 pub source: String,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub base_path: Option<bool>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub locale: Option<bool>,
349 pub headers: Vec<HeaderValue>,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub has: Option<Vec<RouteHas>>,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub missing: Option<Vec<RouteHas>>,
354}
355
356#[derive(
357 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
358)]
359#[serde(rename_all = "camelCase")]
360pub enum RedirectStatus {
361 StatusCode(f64),
362 Permanent(bool),
363}
364
365#[derive(
366 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
367)]
368#[serde(rename_all = "camelCase")]
369pub struct Redirect {
370 pub source: String,
371 pub destination: String,
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub base_path: Option<bool>,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub locale: Option<bool>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub has: Option<Vec<RouteHas>>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub missing: Option<Vec<RouteHas>>,
380
381 #[serde(flatten)]
382 pub status: RedirectStatus,
383}
384
385#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
386#[serde(rename_all = "camelCase")]
387pub struct Rewrite {
388 pub source: String,
389 pub destination: String,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub base_path: Option<bool>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub locale: Option<bool>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub has: Option<Vec<RouteHas>>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub missing: Option<Vec<RouteHas>>,
398}
399
400#[turbo_tasks::value(eq = "manual")]
401#[derive(Clone, Debug, Default, PartialEq)]
402#[serde(rename_all = "camelCase")]
403pub struct Rewrites {
404 pub before_files: Vec<Rewrite>,
405 pub after_files: Vec<Rewrite>,
406 pub fallback: Vec<Rewrite>,
407}
408
409#[derive(
410 Clone,
411 Debug,
412 Default,
413 PartialEq,
414 Serialize,
415 Deserialize,
416 TraceRawVcs,
417 NonLocalValue,
418 OperationValue,
419)]
420#[serde(rename_all = "camelCase")]
421pub struct TypeScriptConfig {
422 pub ignore_build_errors: Option<bool>,
423 pub tsconfig_path: Option<String>,
424}
425
426#[turbo_tasks::value(eq = "manual", operation)]
427#[derive(Clone, Debug, PartialEq)]
428#[serde(rename_all = "camelCase")]
429pub struct ImageConfig {
430 pub device_sizes: Vec<u16>,
431 pub image_sizes: Vec<u16>,
432 pub path: String,
433 pub loader: ImageLoader,
434 #[serde(deserialize_with = "empty_string_is_none")]
435 pub loader_file: Option<String>,
436 pub domains: Vec<String>,
437 pub disable_static_images: bool,
438 #[serde(rename = "minimumCacheTTL")]
439 pub minimum_cache_ttl: u64,
440 pub formats: Vec<ImageFormat>,
441 #[serde(rename = "dangerouslyAllowSVG")]
442 pub dangerously_allow_svg: bool,
443 pub content_security_policy: String,
444 pub remote_patterns: Vec<RemotePattern>,
445 pub unoptimized: bool,
446}
447
448fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
449where
450 D: Deserializer<'de>,
451{
452 let o = Option::<String>::deserialize(deserializer)?;
453 Ok(o.filter(|s| !s.is_empty()))
454}
455
456impl Default for ImageConfig {
457 fn default() -> Self {
458 Self {
460 device_sizes: vec![640, 750, 828, 1080, 1200, 1920, 2048, 3840],
461 image_sizes: vec![16, 32, 48, 64, 96, 128, 256, 384],
462 path: "/_next/image".to_string(),
463 loader: ImageLoader::Default,
464 loader_file: None,
465 domains: vec![],
466 disable_static_images: false,
467 minimum_cache_ttl: 60,
468 formats: vec![ImageFormat::Webp],
469 dangerously_allow_svg: false,
470 content_security_policy: "".to_string(),
471 remote_patterns: vec![],
472 unoptimized: false,
473 }
474 }
475}
476
477#[derive(
478 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
479)]
480#[serde(rename_all = "kebab-case")]
481pub enum ImageLoader {
482 Default,
483 Imgix,
484 Cloudinary,
485 Akamai,
486 Custom,
487}
488
489#[derive(
490 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
491)]
492pub enum ImageFormat {
493 #[serde(rename = "image/webp")]
494 Webp,
495 #[serde(rename = "image/avif")]
496 Avif,
497}
498
499#[derive(
500 Clone,
501 Debug,
502 Default,
503 PartialEq,
504 Serialize,
505 Deserialize,
506 TraceRawVcs,
507 NonLocalValue,
508 OperationValue,
509)]
510#[serde(rename_all = "camelCase")]
511pub struct RemotePattern {
512 pub hostname: String,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub protocol: Option<RemotePatternProtocol>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub port: Option<String>,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub pathname: Option<String>,
519}
520
521#[derive(
522 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
523)]
524#[serde(rename_all = "kebab-case")]
525pub enum RemotePatternProtocol {
526 Http,
527 Https,
528}
529
530#[derive(
531 Clone,
532 Debug,
533 Default,
534 PartialEq,
535 Serialize,
536 Deserialize,
537 TraceRawVcs,
538 NonLocalValue,
539 OperationValue,
540)]
541#[serde(rename_all = "camelCase")]
542pub struct TurbopackConfig {
543 pub loaders: Option<JsonValue>,
545 pub rules: Option<FxIndexMap<RcStr, RuleConfigItemOrShortcut>>,
546 #[turbo_tasks(trace_ignore)]
547 pub conditions: Option<FxIndexMap<RcStr, ConfigConditionItem>>,
548 pub resolve_alias: Option<FxIndexMap<RcStr, JsonValue>>,
549 pub resolve_extensions: Option<Vec<RcStr>>,
550 pub module_ids: Option<ModuleIds>,
551}
552
553#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
554pub struct RegexComponents {
555 source: RcStr,
556 flags: RcStr,
557}
558
559#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
560#[serde(tag = "type", content = "value", rename_all = "camelCase")]
561pub enum ConfigConditionPath {
562 Glob(RcStr),
563 Regex(RegexComponents),
564}
565
566impl TryInto<ConditionPath> for ConfigConditionPath {
567 fn try_into(self) -> Result<ConditionPath> {
568 Ok(match self {
569 ConfigConditionPath::Glob(path) => ConditionPath::Glob(path),
570 ConfigConditionPath::Regex(path) => {
571 ConditionPath::Regex(EsRegex::new(&path.source, &path.flags)?.resolved_cell())
572 }
573 })
574 }
575
576 type Error = anyhow::Error;
577}
578
579#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
580pub struct ConfigConditionItem {
581 pub path: ConfigConditionPath,
582}
583
584impl TryInto<ConditionItem> for ConfigConditionItem {
585 fn try_into(self) -> Result<ConditionItem> {
586 Ok(ConditionItem {
587 path: self.path.try_into()?,
588 })
589 }
590
591 type Error = anyhow::Error;
592}
593
594#[derive(
595 Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
596)]
597#[serde(rename_all = "camelCase")]
598pub struct RuleConfigItemOptions {
599 pub loaders: Vec<LoaderItem>,
600 #[serde(default, alias = "as")]
601 pub rename_as: Option<RcStr>,
602}
603
604#[derive(
605 Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
606)]
607#[serde(rename_all = "camelCase", untagged)]
608pub enum RuleConfigItemOrShortcut {
609 Loaders(Vec<LoaderItem>),
610 Advanced(RuleConfigItem),
611}
612
613#[derive(
614 Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
615)]
616#[serde(rename_all = "camelCase", untagged)]
617pub enum RuleConfigItem {
618 Options(RuleConfigItemOptions),
619 Conditional(FxIndexMap<RcStr, RuleConfigItem>),
620 Boolean(bool),
621}
622
623#[derive(
624 Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
625)]
626#[serde(untagged)]
627pub enum LoaderItem {
628 LoaderName(RcStr),
629 LoaderOptions(WebpackLoaderItem),
630}
631
632#[turbo_tasks::value(operation)]
633#[derive(Copy, Clone, Debug)]
634#[serde(rename_all = "camelCase")]
635pub enum ModuleIds {
636 Named,
637 Deterministic,
638}
639
640#[turbo_tasks::value(transparent)]
641pub struct OptionModuleIds(pub Option<ModuleIds>);
642
643#[derive(
644 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
645)]
646#[serde(untagged)]
647pub enum MdxRsOptions {
648 Boolean(bool),
649 Option(MdxTransformOptions),
650}
651
652#[turbo_tasks::value(shared, operation)]
653#[derive(Clone, Debug)]
654#[serde(rename_all = "camelCase")]
655pub enum ReactCompilerMode {
656 Infer,
657 Annotation,
658 All,
659}
660
661#[turbo_tasks::value(shared, operation)]
663#[derive(Clone, Debug)]
664#[serde(rename_all = "camelCase")]
665pub struct ReactCompilerOptions {
666 #[serde(skip_serializing_if = "Option::is_none")]
667 pub compilation_mode: Option<ReactCompilerMode>,
668 #[serde(skip_serializing_if = "Option::is_none")]
669 pub panic_threshold: Option<RcStr>,
670}
671
672#[derive(
673 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
674)]
675#[serde(untagged)]
676pub enum ReactCompilerOptionsOrBoolean {
677 Boolean(bool),
678 Option(ReactCompilerOptions),
679}
680
681#[turbo_tasks::value(transparent)]
682pub struct OptionalReactCompilerOptions(Option<ResolvedVc<ReactCompilerOptions>>);
683
684#[derive(
685 Clone,
686 Debug,
687 Default,
688 PartialEq,
689 Serialize,
690 Deserialize,
691 TraceRawVcs,
692 ValueDebugFormat,
693 NonLocalValue,
694 OperationValue,
695)]
696#[serde(rename_all = "camelCase")]
697pub struct ExperimentalConfig {
698 allowed_revalidate_header_keys: Option<Vec<RcStr>>,
701 client_router_filter: Option<bool>,
702 client_router_filter_allowed_rate: Option<f64>,
705 client_router_filter_redirects: Option<bool>,
706 fetch_cache_key_prefix: Option<RcStr>,
707 isr_flush_to_disk: Option<bool>,
708 mdx_rs: Option<MdxRsOptions>,
711 strict_next_head: Option<bool>,
712 swc_plugins: Option<Vec<(RcStr, serde_json::Value)>>,
713 external_middleware_rewrites_resolve: Option<bool>,
714 scroll_restoration: Option<bool>,
715 manual_client_base_path: Option<bool>,
716 optimistic_client_cache: Option<bool>,
717 middleware_prefetch: Option<MiddlewarePrefetchType>,
718 optimize_css: Option<serde_json::Value>,
721 next_script_workers: Option<bool>,
722 web_vitals_attribution: Option<Vec<RcStr>>,
723 server_actions: Option<ServerActionsOrLegacyBool>,
724 sri: Option<SubResourceIntegrity>,
725 react_compiler: Option<ReactCompilerOptionsOrBoolean>,
726 #[serde(rename = "dynamicIO")]
727 dynamic_io: Option<bool>,
728 use_cache: Option<bool>,
729 adjust_font_fallbacks: Option<bool>,
733 adjust_font_fallbacks_with_size_adjust: Option<bool>,
734 after: Option<bool>,
735 amp: Option<serde_json::Value>,
736 app_document_preloading: Option<bool>,
737 cache_handlers: Option<FxIndexMap<RcStr, RcStr>>,
738 cache_life: Option<FxIndexMap<String, CacheLifeProfile>>,
739 case_sensitive_routes: Option<bool>,
740 cpus: Option<f64>,
741 cra_compat: Option<bool>,
742 disable_optimized_loading: Option<bool>,
743 disable_postcss_preset_env: Option<bool>,
744 esm_externals: Option<EsmExternals>,
745 extension_alias: Option<serde_json::Value>,
746 external_dir: Option<bool>,
747 fallback_node_polyfills: Option<bool>, force_swc_transforms: Option<bool>,
752 fully_specified: Option<bool>,
753 gzip_size: Option<bool>,
754
755 pub inline_css: Option<bool>,
756 instrumentation_hook: Option<bool>,
757 client_trace_metadata: Option<Vec<String>>,
758 large_page_data_bytes: Option<f64>,
759 logging: Option<serde_json::Value>,
760 memory_based_workers_count: Option<bool>,
761 optimize_server_react: Option<bool>,
763 optimize_package_imports: Option<Vec<RcStr>>,
766 ppr: Option<ExperimentalPartialPrerendering>,
769 taint: Option<bool>,
770 #[serde(rename = "routerBFCache")]
771 router_bfcache: Option<bool>,
772 proxy_timeout: Option<f64>,
773 server_minification: Option<bool>,
775 server_source_maps: Option<bool>,
777 swc_trace_profiling: Option<bool>,
778 trust_host_header: Option<bool>,
780 typed_routes: Option<bool>,
784 url_imports: Option<serde_json::Value>,
785 view_transition: Option<bool>,
786 webpack_build_worker: Option<bool>,
789 worker_threads: Option<bool>,
790
791 turbopack_minify: Option<bool>,
792 turbopack_persistent_caching: Option<bool>,
793 turbopack_source_maps: Option<bool>,
794 turbopack_tree_shaking: Option<bool>,
795 turbopack_scope_hoisting: Option<bool>,
796 global_not_found: Option<bool>,
798 turbopack_remove_unused_exports: Option<bool>,
800 devtool_segment_explorer: Option<bool>,
802}
803
804#[derive(
805 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
806)]
807#[serde(rename_all = "camelCase")]
808pub struct CacheLifeProfile {
809 #[serde(skip_serializing_if = "Option::is_none")]
810 pub stale: Option<u32>,
811 #[serde(skip_serializing_if = "Option::is_none")]
812 pub revalidate: Option<u32>,
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub expire: Option<u32>,
815}
816
817#[test]
818fn test_cache_life_profiles() {
819 let json = serde_json::json!({
820 "cacheLife": {
821 "frequent": {
822 "stale": 19,
823 "revalidate": 100,
824 },
825 }
826 });
827
828 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
829 let mut expected_cache_life = FxIndexMap::default();
830
831 expected_cache_life.insert(
832 "frequent".to_string(),
833 CacheLifeProfile {
834 stale: Some(19),
835 revalidate: Some(100),
836 expire: None,
837 },
838 );
839
840 assert_eq!(config.cache_life, Some(expected_cache_life));
841}
842
843#[test]
844fn test_cache_life_profiles_invalid() {
845 let json = serde_json::json!({
846 "cacheLife": {
847 "invalid": {
848 "stale": "invalid_value",
849 },
850 }
851 });
852
853 let result: Result<ExperimentalConfig, _> = serde_json::from_value(json);
854
855 assert!(
856 result.is_err(),
857 "Deserialization should fail due to invalid 'stale' value type"
858 );
859}
860
861#[derive(
862 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
863)]
864#[serde(rename_all = "lowercase")]
865pub enum ExperimentalPartialPrerenderingIncrementalValue {
866 Incremental,
867}
868
869#[derive(
870 Clone, Debug, PartialEq, Deserialize, Serialize, TraceRawVcs, NonLocalValue, OperationValue,
871)]
872#[serde(untagged)]
873pub enum ExperimentalPartialPrerendering {
874 Boolean(bool),
875 Incremental(ExperimentalPartialPrerenderingIncrementalValue),
876}
877
878#[test]
879fn test_parse_experimental_partial_prerendering() {
880 let json = serde_json::json!({
881 "ppr": "incremental"
882 });
883 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
884 assert_eq!(
885 config.ppr,
886 Some(ExperimentalPartialPrerendering::Incremental(
887 ExperimentalPartialPrerenderingIncrementalValue::Incremental
888 ))
889 );
890
891 let json = serde_json::json!({
892 "ppr": true
893 });
894 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
895 assert_eq!(
896 config.ppr,
897 Some(ExperimentalPartialPrerendering::Boolean(true))
898 );
899
900 let json = serde_json::json!({
902 "ppr": "random"
903 });
904 let config = serde_json::from_value::<ExperimentalConfig>(json);
905 assert!(config.is_err());
906}
907
908#[derive(
909 Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
910)]
911#[serde(rename_all = "camelCase")]
912pub struct SubResourceIntegrity {
913 pub algorithm: Option<RcStr>,
914}
915
916#[derive(
917 Clone, Debug, PartialEq, Deserialize, Serialize, TraceRawVcs, NonLocalValue, OperationValue,
918)]
919#[serde(untagged)]
920pub enum ServerActionsOrLegacyBool {
921 ServerActionsConfig(ServerActions),
923
924 LegacyBool(bool),
927}
928
929#[derive(
930 Clone, Debug, PartialEq, Deserialize, Serialize, TraceRawVcs, NonLocalValue, OperationValue,
931)]
932#[serde(rename_all = "kebab-case")]
933pub enum EsmExternalsValue {
934 Loose,
935}
936
937#[derive(
938 Clone, Debug, PartialEq, Deserialize, Serialize, TraceRawVcs, NonLocalValue, OperationValue,
939)]
940#[serde(untagged)]
941pub enum EsmExternals {
942 Loose(EsmExternalsValue),
943 Bool(bool),
944}
945
946#[test]
948fn test_esm_externals_deserialization() {
949 let json = serde_json::json!({
950 "esmExternals": true
951 });
952 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
953 assert_eq!(config.esm_externals, Some(EsmExternals::Bool(true)));
954
955 let json = serde_json::json!({
956 "esmExternals": "loose"
957 });
958 let config: ExperimentalConfig = serde_json::from_value(json).unwrap();
959 assert_eq!(
960 config.esm_externals,
961 Some(EsmExternals::Loose(EsmExternalsValue::Loose))
962 );
963}
964
965#[derive(
966 Clone,
967 Debug,
968 Default,
969 PartialEq,
970 Eq,
971 Deserialize,
972 Serialize,
973 TraceRawVcs,
974 NonLocalValue,
975 OperationValue,
976)]
977#[serde(rename_all = "camelCase")]
978pub struct ServerActions {
979 pub body_size_limit: Option<SizeLimit>,
981}
982
983#[derive(Clone, Debug, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue)]
984#[serde(untagged)]
985pub enum SizeLimit {
986 Number(f64),
987 WithUnit(String),
988}
989
990impl PartialEq for SizeLimit {
993 fn eq(&self, other: &Self) -> bool {
994 match (self, other) {
995 (SizeLimit::Number(a), SizeLimit::Number(b)) => a.to_bits() == b.to_bits(),
996 (SizeLimit::WithUnit(a), SizeLimit::WithUnit(b)) => a == b,
997 _ => false,
998 }
999 }
1000}
1001
1002impl Eq for SizeLimit {}
1003
1004#[derive(
1005 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
1006)]
1007#[serde(rename_all = "kebab-case")]
1008pub enum MiddlewarePrefetchType {
1009 Strict,
1010 Flexible,
1011}
1012
1013#[derive(
1014 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
1015)]
1016#[serde(untagged)]
1017pub enum EmotionTransformOptionsOrBoolean {
1018 Boolean(bool),
1019 Options(EmotionTransformConfig),
1020}
1021
1022impl EmotionTransformOptionsOrBoolean {
1023 pub fn is_enabled(&self) -> bool {
1024 match self {
1025 Self::Boolean(enabled) => *enabled,
1026 _ => true,
1027 }
1028 }
1029}
1030
1031#[derive(
1032 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
1033)]
1034#[serde(untagged)]
1035pub enum StyledComponentsTransformOptionsOrBoolean {
1036 Boolean(bool),
1037 Options(StyledComponentsTransformConfig),
1038}
1039
1040impl StyledComponentsTransformOptionsOrBoolean {
1041 pub fn is_enabled(&self) -> bool {
1042 match self {
1043 Self::Boolean(enabled) => *enabled,
1044 _ => true,
1045 }
1046 }
1047}
1048
1049#[turbo_tasks::value(eq = "manual")]
1050#[derive(Clone, Debug, PartialEq, Default, OperationValue)]
1051#[serde(rename_all = "camelCase")]
1052pub struct CompilerConfig {
1053 pub react_remove_properties: Option<ReactRemoveProperties>,
1054 pub relay: Option<RelayConfig>,
1055 pub emotion: Option<EmotionTransformOptionsOrBoolean>,
1056 pub remove_console: Option<RemoveConsoleConfig>,
1057 pub styled_components: Option<StyledComponentsTransformOptionsOrBoolean>,
1058}
1059
1060#[derive(
1061 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
1062)]
1063#[serde(untagged, rename_all = "camelCase")]
1064pub enum ReactRemoveProperties {
1065 Boolean(bool),
1066 Config { properties: Option<Vec<String>> },
1067}
1068
1069impl ReactRemoveProperties {
1070 pub fn is_enabled(&self) -> bool {
1071 match self {
1072 Self::Boolean(enabled) => *enabled,
1073 _ => true,
1074 }
1075 }
1076}
1077
1078#[derive(
1079 Clone, Debug, PartialEq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue,
1080)]
1081#[serde(untagged)]
1082pub enum RemoveConsoleConfig {
1083 Boolean(bool),
1084 Config { exclude: Option<Vec<String>> },
1085}
1086
1087impl RemoveConsoleConfig {
1088 pub fn is_enabled(&self) -> bool {
1089 match self {
1090 Self::Boolean(enabled) => *enabled,
1091 _ => true,
1092 }
1093 }
1094}
1095
1096#[turbo_tasks::value(transparent)]
1097pub struct ResolveExtensions(Option<Vec<RcStr>>);
1098
1099#[turbo_tasks::value(transparent)]
1100pub struct SwcPlugins(Vec<(RcStr, serde_json::Value)>);
1101
1102#[turbo_tasks::value(transparent)]
1103pub struct OptionalMdxTransformOptions(Option<ResolvedVc<MdxTransformOptions>>);
1104
1105#[turbo_tasks::value(transparent)]
1106
1107pub struct OptionSubResourceIntegrity(Option<SubResourceIntegrity>);
1108
1109#[turbo_tasks::value(transparent)]
1110pub struct OptionServerActions(Option<ServerActions>);
1111
1112#[turbo_tasks::value(transparent)]
1113pub struct OptionJsonValue(pub Option<serde_json::Value>);
1114
1115#[turbo_tasks::value_impl]
1116impl NextConfig {
1117 #[turbo_tasks::function]
1118 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
1119 let string = string.await?;
1120 let mut jdeserializer = serde_json::Deserializer::from_str(&string);
1121 let config: NextConfig = serde_path_to_error::deserialize(&mut jdeserializer)
1122 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
1123 Ok(config.cell())
1124 }
1125
1126 #[turbo_tasks::function]
1127 pub fn bundle_pages_router_dependencies(&self) -> Vc<bool> {
1128 Vc::cell(self.bundle_pages_router_dependencies.unwrap_or_default())
1129 }
1130
1131 #[turbo_tasks::function]
1132 pub fn enable_react_production_profiling(&self) -> Vc<bool> {
1133 Vc::cell(self.react_production_profiling.unwrap_or_default())
1134 }
1135
1136 #[turbo_tasks::function]
1137 pub fn server_external_packages(&self) -> Vc<Vec<RcStr>> {
1138 Vc::cell(
1139 self.server_external_packages
1140 .as_ref()
1141 .cloned()
1142 .unwrap_or_default(),
1143 )
1144 }
1145
1146 #[turbo_tasks::function]
1147 pub fn is_standalone(&self) -> Vc<bool> {
1148 Vc::cell(self.output == Some(OutputType::Standalone))
1149 }
1150
1151 #[turbo_tasks::function]
1152 pub fn cache_handler(&self) -> Vc<Option<RcStr>> {
1153 Vc::cell(self.cache_handler.clone())
1154 }
1155
1156 #[turbo_tasks::function]
1157 pub fn compiler(&self) -> Vc<CompilerConfig> {
1158 self.compiler.clone().unwrap_or_default().cell()
1159 }
1160
1161 #[turbo_tasks::function]
1162 pub fn env(&self) -> Vc<EnvMap> {
1163 let env = self
1167 .env
1168 .iter()
1169 .map(|(k, v)| {
1170 (
1171 k.as_str().into(),
1172 if let JsonValue::String(s) = v {
1173 s.as_str().into()
1175 } else {
1176 v.to_string().into()
1177 },
1178 )
1179 })
1180 .collect();
1181
1182 Vc::cell(env)
1183 }
1184
1185 #[turbo_tasks::function]
1186 pub fn image_config(&self) -> Vc<ImageConfig> {
1187 self.images.clone().cell()
1188 }
1189
1190 #[turbo_tasks::function]
1191 pub fn page_extensions(&self) -> Vc<Vec<RcStr>> {
1192 Vc::cell(self.page_extensions.clone())
1193 }
1194
1195 #[turbo_tasks::function]
1196 pub fn is_global_not_found_enabled(&self) -> Vc<bool> {
1197 Vc::cell(self.experimental.global_not_found.unwrap_or_default())
1198 }
1199
1200 #[turbo_tasks::function]
1201 pub fn transpile_packages(&self) -> Vc<Vec<RcStr>> {
1202 Vc::cell(self.transpile_packages.clone().unwrap_or_default())
1203 }
1204
1205 #[turbo_tasks::function]
1206 pub fn webpack_rules(&self, active_conditions: Vec<RcStr>) -> Vc<OptionWebpackRules> {
1207 let Some(turbo_rules) = self.turbopack.as_ref().and_then(|t| t.rules.as_ref()) else {
1208 return Vc::cell(None);
1209 };
1210 if turbo_rules.is_empty() {
1211 return Vc::cell(None);
1212 }
1213 let active_conditions = active_conditions.into_iter().collect::<FxHashSet<_>>();
1214 let mut rules = FxIndexMap::default();
1215 for (ext, rule) in turbo_rules.iter() {
1216 fn transform_loaders(loaders: &[LoaderItem]) -> ResolvedVc<WebpackLoaderItems> {
1217 ResolvedVc::cell(
1218 loaders
1219 .iter()
1220 .map(|item| match item {
1221 LoaderItem::LoaderName(name) => WebpackLoaderItem {
1222 loader: name.clone(),
1223 options: Default::default(),
1224 },
1225 LoaderItem::LoaderOptions(options) => options.clone(),
1226 })
1227 .collect(),
1228 )
1229 }
1230 enum FindRuleResult<'a> {
1231 Found(&'a RuleConfigItemOptions),
1232 NotFound,
1233 Break,
1234 }
1235 fn find_rule<'a>(
1236 rule: &'a RuleConfigItem,
1237 active_conditions: &FxHashSet<RcStr>,
1238 ) -> FindRuleResult<'a> {
1239 match rule {
1240 RuleConfigItem::Options(rule) => FindRuleResult::Found(rule),
1241 RuleConfigItem::Conditional(map) => {
1242 for (condition, rule) in map.iter() {
1243 if condition == "default" || active_conditions.contains(condition) {
1244 match find_rule(rule, active_conditions) {
1245 FindRuleResult::Found(rule) => {
1246 return FindRuleResult::Found(rule);
1247 }
1248 FindRuleResult::Break => {
1249 return FindRuleResult::Break;
1250 }
1251 FindRuleResult::NotFound => {}
1252 }
1253 }
1254 }
1255 FindRuleResult::NotFound
1256 }
1257 RuleConfigItem::Boolean(_) => FindRuleResult::Break,
1258 }
1259 }
1260 match rule {
1261 RuleConfigItemOrShortcut::Loaders(loaders) => {
1262 rules.insert(
1263 ext.clone(),
1264 LoaderRuleItem {
1265 loaders: transform_loaders(loaders),
1266 rename_as: None,
1267 },
1268 );
1269 }
1270 RuleConfigItemOrShortcut::Advanced(rule) => {
1271 if let FindRuleResult::Found(RuleConfigItemOptions { loaders, rename_as }) =
1272 find_rule(rule, &active_conditions)
1273 {
1274 rules.insert(
1275 ext.clone(),
1276 LoaderRuleItem {
1277 loaders: transform_loaders(loaders),
1278 rename_as: rename_as.clone(),
1279 },
1280 );
1281 }
1282 }
1283 }
1284 }
1285 Vc::cell(Some(ResolvedVc::cell(rules)))
1286 }
1287
1288 #[turbo_tasks::function]
1289 pub fn webpack_conditions(&self) -> Result<Vc<OptionWebpackConditions>> {
1290 let Some(config_conditions) = self.turbopack.as_ref().and_then(|t| t.conditions.as_ref())
1291 else {
1292 return Ok(Vc::cell(None));
1293 };
1294
1295 let conditions = config_conditions
1296 .iter()
1297 .map(|(k, v)| {
1298 let item: Result<ConditionItem> = TryInto::<ConditionItem>::try_into((*v).clone());
1299 item.map(|item| (k.clone(), item))
1300 })
1301 .collect::<Result<FxIndexMap<RcStr, ConditionItem>>>()?;
1302
1303 Ok(Vc::cell(Some(ResolvedVc::cell(conditions))))
1304 }
1305
1306 #[turbo_tasks::function]
1307 pub fn persistent_caching_enabled(&self) -> Result<Vc<bool>> {
1308 Ok(Vc::cell(
1309 self.experimental
1310 .turbopack_persistent_caching
1311 .unwrap_or_default(),
1312 ))
1313 }
1314
1315 #[turbo_tasks::function]
1316 pub fn resolve_alias_options(&self) -> Result<Vc<ResolveAliasMap>> {
1317 let Some(resolve_alias) = self
1318 .turbopack
1319 .as_ref()
1320 .and_then(|t| t.resolve_alias.as_ref())
1321 else {
1322 return Ok(ResolveAliasMap::cell(ResolveAliasMap::default()));
1323 };
1324 let alias_map: ResolveAliasMap = resolve_alias.try_into()?;
1325 Ok(alias_map.cell())
1326 }
1327
1328 #[turbo_tasks::function]
1329 pub fn resolve_extension(&self) -> Vc<ResolveExtensions> {
1330 let Some(resolve_extensions) = self
1331 .turbopack
1332 .as_ref()
1333 .and_then(|t| t.resolve_extensions.as_ref())
1334 else {
1335 return Vc::cell(None);
1336 };
1337 Vc::cell(Some(resolve_extensions.clone()))
1338 }
1339
1340 #[turbo_tasks::function]
1341 pub fn import_externals(&self) -> Result<Vc<bool>> {
1342 Ok(Vc::cell(match self.experimental.esm_externals {
1343 Some(EsmExternals::Bool(b)) => b,
1344 Some(EsmExternals::Loose(_)) => bail!("esmExternals = \"loose\" is not supported"),
1345 None => true,
1346 }))
1347 }
1348
1349 #[turbo_tasks::function]
1350 pub fn mdx_rs(&self) -> Vc<OptionalMdxTransformOptions> {
1351 let options = &self.experimental.mdx_rs;
1352
1353 let options = match options {
1354 Some(MdxRsOptions::Boolean(true)) => OptionalMdxTransformOptions(Some(
1355 MdxTransformOptions {
1356 provider_import_source: Some(mdx_import_source_file()),
1357 ..Default::default()
1358 }
1359 .resolved_cell(),
1360 )),
1361 Some(MdxRsOptions::Option(options)) => OptionalMdxTransformOptions(Some(
1362 MdxTransformOptions {
1363 provider_import_source: Some(
1364 options
1365 .provider_import_source
1366 .clone()
1367 .unwrap_or(mdx_import_source_file()),
1368 ),
1369 ..options.clone()
1370 }
1371 .resolved_cell(),
1372 )),
1373 _ => OptionalMdxTransformOptions(None),
1374 };
1375
1376 options.cell()
1377 }
1378
1379 #[turbo_tasks::function]
1380 pub fn modularize_imports(&self) -> Vc<ModularizeImports> {
1381 Vc::cell(self.modularize_imports.clone().unwrap_or_default())
1382 }
1383
1384 #[turbo_tasks::function]
1385 pub fn experimental_swc_plugins(&self) -> Vc<SwcPlugins> {
1386 Vc::cell(self.experimental.swc_plugins.clone().unwrap_or_default())
1387 }
1388
1389 #[turbo_tasks::function]
1390 pub fn experimental_sri(&self) -> Vc<OptionSubResourceIntegrity> {
1391 Vc::cell(self.experimental.sri.clone())
1392 }
1393
1394 #[turbo_tasks::function]
1395 pub fn experimental_server_actions(&self) -> Vc<OptionServerActions> {
1396 Vc::cell(match self.experimental.server_actions.as_ref() {
1397 Some(ServerActionsOrLegacyBool::ServerActionsConfig(server_actions)) => {
1398 Some(server_actions.clone())
1399 }
1400 Some(ServerActionsOrLegacyBool::LegacyBool(true)) => Some(ServerActions::default()),
1401 _ => None,
1402 })
1403 }
1404
1405 #[turbo_tasks::function]
1406 pub fn react_compiler(&self) -> Vc<OptionalReactCompilerOptions> {
1407 let options = &self.experimental.react_compiler;
1408
1409 let options = match options {
1410 Some(ReactCompilerOptionsOrBoolean::Boolean(true)) => {
1411 OptionalReactCompilerOptions(Some(
1412 ReactCompilerOptions {
1413 compilation_mode: None,
1414 panic_threshold: None,
1415 }
1416 .resolved_cell(),
1417 ))
1418 }
1419 Some(ReactCompilerOptionsOrBoolean::Option(options)) => OptionalReactCompilerOptions(
1420 Some(ReactCompilerOptions { ..options.clone() }.resolved_cell()),
1421 ),
1422 _ => OptionalReactCompilerOptions(None),
1423 };
1424
1425 options.cell()
1426 }
1427
1428 #[turbo_tasks::function]
1429 pub fn sass_config(&self) -> Vc<JsonValue> {
1430 Vc::cell(self.sass_options.clone().unwrap_or_default())
1431 }
1432
1433 #[turbo_tasks::function]
1434 pub fn skip_middleware_url_normalize(&self) -> Vc<bool> {
1435 Vc::cell(self.skip_middleware_url_normalize.unwrap_or(false))
1436 }
1437
1438 #[turbo_tasks::function]
1439 pub fn skip_trailing_slash_redirect(&self) -> Vc<bool> {
1440 Vc::cell(self.skip_trailing_slash_redirect.unwrap_or(false))
1441 }
1442
1443 #[turbo_tasks::function]
1446 pub async fn computed_asset_prefix(self: Vc<Self>) -> Result<Vc<Option<RcStr>>> {
1447 let this = self.await?;
1448
1449 Ok(Vc::cell(Some(
1450 format!(
1451 "{}/_next/",
1452 if let Some(asset_prefix) = &this.asset_prefix {
1453 asset_prefix
1454 } else {
1455 this.base_path.as_ref().map_or("", |b| b.as_str())
1456 }
1457 .trim_end_matches('/')
1458 )
1459 .into(),
1460 )))
1461 }
1462
1463 #[turbo_tasks::function]
1465 pub async fn chunk_suffix_path(self: Vc<Self>) -> Result<Vc<Option<RcStr>>> {
1466 let this = self.await?;
1467
1468 match &this.deployment_id {
1469 Some(deployment_id) => Ok(Vc::cell(Some(format!("?dpl={deployment_id}").into()))),
1470 None => Ok(Vc::cell(None)),
1471 }
1472 }
1473
1474 #[turbo_tasks::function]
1475 pub fn enable_ppr(&self) -> Vc<bool> {
1476 Vc::cell(
1477 self.experimental
1478 .ppr
1479 .as_ref()
1480 .map(|ppr| match ppr {
1481 ExperimentalPartialPrerendering::Incremental(
1482 ExperimentalPartialPrerenderingIncrementalValue::Incremental,
1483 ) => true,
1484 ExperimentalPartialPrerendering::Boolean(b) => *b,
1485 })
1486 .unwrap_or(false),
1487 )
1488 }
1489
1490 #[turbo_tasks::function]
1491 pub fn enable_taint(&self) -> Vc<bool> {
1492 Vc::cell(self.experimental.taint.unwrap_or(false))
1493 }
1494
1495 #[turbo_tasks::function]
1496 pub fn enable_router_bfcache(&self) -> Vc<bool> {
1497 Vc::cell(self.experimental.router_bfcache.unwrap_or(false))
1498 }
1499
1500 #[turbo_tasks::function]
1501 pub fn enable_view_transition(&self) -> Vc<bool> {
1502 Vc::cell(self.experimental.view_transition.unwrap_or(false))
1503 }
1504
1505 #[turbo_tasks::function]
1506 pub fn enable_dynamic_io(&self) -> Vc<bool> {
1507 Vc::cell(self.experimental.dynamic_io.unwrap_or(false))
1508 }
1509
1510 #[turbo_tasks::function]
1511 pub fn enable_use_cache(&self) -> Vc<bool> {
1512 Vc::cell(
1513 self.experimental
1514 .use_cache
1515 .unwrap_or(self.experimental.dynamic_io.unwrap_or(false)),
1519 )
1520 }
1521
1522 #[turbo_tasks::function]
1523 pub fn cache_kinds(&self) -> Vc<CacheKinds> {
1524 let mut cache_kinds = CacheKinds::default();
1525
1526 if let Some(handlers) = self.experimental.cache_handlers.as_ref() {
1527 cache_kinds.extend(handlers.keys().cloned());
1528 }
1529
1530 cache_kinds.cell()
1531 }
1532
1533 #[turbo_tasks::function]
1534 pub fn optimize_package_imports(&self) -> Vc<Vec<RcStr>> {
1535 Vc::cell(
1536 self.experimental
1537 .optimize_package_imports
1538 .clone()
1539 .unwrap_or_default(),
1540 )
1541 }
1542
1543 #[turbo_tasks::function]
1544 pub fn tree_shaking_mode_for_foreign_code(
1545 &self,
1546 _is_development: bool,
1547 ) -> Vc<OptionTreeShaking> {
1548 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
1549 Some(false) => Some(TreeShakingMode::ReexportsOnly),
1550 Some(true) => Some(TreeShakingMode::ModuleFragments),
1551 None => Some(TreeShakingMode::ReexportsOnly),
1552 })
1553 .cell()
1554 }
1555
1556 #[turbo_tasks::function]
1557 pub fn tree_shaking_mode_for_user_code(&self, _is_development: bool) -> Vc<OptionTreeShaking> {
1558 OptionTreeShaking(match self.experimental.turbopack_tree_shaking {
1559 Some(false) => Some(TreeShakingMode::ReexportsOnly),
1560 Some(true) => Some(TreeShakingMode::ModuleFragments),
1561 None => Some(TreeShakingMode::ReexportsOnly),
1562 })
1563 .cell()
1564 }
1565
1566 #[turbo_tasks::function]
1567 pub fn turbopack_remove_unused_exports(&self, is_development: bool) -> Vc<bool> {
1568 Vc::cell(
1569 self.experimental
1570 .turbopack_remove_unused_exports
1571 .unwrap_or(!is_development),
1572 )
1573 }
1574
1575 #[turbo_tasks::function]
1576 pub async fn module_ids(&self, mode: Vc<NextMode>) -> Result<Vc<ModuleIds>> {
1577 Ok(match *mode.await? {
1578 NextMode::Development => ModuleIds::Named.cell(),
1580 NextMode::Build => self
1581 .turbopack
1582 .as_ref()
1583 .and_then(|t| t.module_ids)
1584 .unwrap_or(ModuleIds::Deterministic)
1585 .cell(),
1586 })
1587 }
1588
1589 #[turbo_tasks::function]
1590 pub async fn turbo_minify(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
1591 let minify = self.experimental.turbopack_minify;
1592 Ok(Vc::cell(
1593 minify.unwrap_or(matches!(*mode.await?, NextMode::Build)),
1594 ))
1595 }
1596
1597 #[turbo_tasks::function]
1598 pub async fn turbo_scope_hoisting(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
1599 Ok(Vc::cell(match *mode.await? {
1600 NextMode::Development => false,
1602 NextMode::Build => self.experimental.turbopack_scope_hoisting.unwrap_or(true),
1603 }))
1604 }
1605
1606 #[turbo_tasks::function]
1607 pub fn client_source_maps(&self, _mode: Vc<NextMode>) -> Result<Vc<bool>> {
1608 let source_maps = self.experimental.turbopack_source_maps;
1612 Ok(Vc::cell(source_maps.unwrap_or(true)))
1613 }
1614
1615 #[turbo_tasks::function]
1616 pub fn server_source_maps(&self) -> Result<Vc<bool>> {
1617 let source_maps = self.experimental.turbopack_source_maps;
1618 Ok(Vc::cell(source_maps.unwrap_or(true)))
1619 }
1620
1621 #[turbo_tasks::function]
1622 pub fn typescript_tsconfig_path(&self) -> Result<Vc<Option<RcStr>>> {
1623 Ok(Vc::cell(
1624 self.typescript
1625 .tsconfig_path
1626 .as_ref()
1627 .map(|path| path.to_owned().into()),
1628 ))
1629 }
1630
1631 #[turbo_tasks::function]
1632 pub fn output_file_tracing_includes(&self) -> Vc<OptionJsonValue> {
1633 Vc::cell(self.output_file_tracing_includes.clone())
1634 }
1635
1636 #[turbo_tasks::function]
1637 pub fn output_file_tracing_excludes(&self) -> Vc<OptionJsonValue> {
1638 Vc::cell(self.output_file_tracing_excludes.clone())
1639 }
1640}
1641
1642#[turbo_tasks::value(serialization = "custom", eq = "manual")]
1645#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1646#[serde(rename_all = "camelCase")]
1647pub struct JsConfig {
1648 compiler_options: Option<serde_json::Value>,
1649}
1650
1651#[turbo_tasks::value_impl]
1652impl JsConfig {
1653 #[turbo_tasks::function]
1654 pub async fn from_string(string: Vc<RcStr>) -> Result<Vc<Self>> {
1655 let string = string.await?;
1656 let config: JsConfig = serde_json::from_str(&string)
1657 .with_context(|| format!("failed to parse next.config.js: {string}"))?;
1658
1659 Ok(config.cell())
1660 }
1661
1662 #[turbo_tasks::function]
1663 pub fn compiler_options(&self) -> Vc<serde_json::Value> {
1664 Vc::cell(self.compiler_options.clone().unwrap_or_default())
1665 }
1666}