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