@@ -1227,6 +1227,7 @@ export default async function loadConfig(
12271227 }
12281228
12291229 const path = await findUp ( CONFIG_FILES , { cwd : dir } )
1230+ const configuredExperimentalFeatures : ConfiguredExperimentalFeature [ ] = [ ]
12301231
12311232 // If config file was found
12321233 if ( path ?. length ) {
@@ -1280,8 +1281,6 @@ export default async function loadConfig(
12801281 ) ) as NextConfig
12811282 )
12821283
1283- const configuredExperimentalFeatures : ConfiguredExperimentalFeature [ ] = [ ]
1284-
12851284 if ( reportExperimentalFeatures && loadedConfig . experimental ) {
12861285 for ( const name of Object . keys (
12871286 loadedConfig . experimental
@@ -1420,58 +1419,13 @@ export default async function loadConfig(
14201419 userConfig . htmlLimitedBots = userConfig . htmlLimitedBots . source
14211420 }
14221421
1423- if (
1424- userConfig . experimental &&
1425- userConfig . experimental . enablePrerenderSourceMaps === undefined &&
1426- userConfig . experimental . dynamicIO === true
1427- ) {
1428- userConfig . experimental . enablePrerenderSourceMaps = true
1429- addConfiguredExperimentalFeature (
1430- configuredExperimentalFeatures ,
1431- 'enablePrerenderSourceMaps' ,
1432- true ,
1433- 'enabled by `experimental.dynamicIO`'
1434- )
1435- }
1436-
1437- if (
1438- debugPrerender &&
1439- ( phase === PHASE_PRODUCTION_BUILD || phase === PHASE_EXPORT )
1440- ) {
1441- userConfig . experimental ??= { }
1442-
1443- setExperimentalFeatureForDebugPrerender (
1444- userConfig . experimental ,
1445- 'serverSourceMaps' ,
1446- true ,
1447- reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1448- )
1449-
1450- setExperimentalFeatureForDebugPrerender (
1451- userConfig . experimental ,
1452- process . env . TURBOPACK ? 'turbopackMinify' : 'serverMinification' ,
1453- false ,
1454- reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1455- )
1456-
1457- setExperimentalFeatureForDebugPrerender (
1458- userConfig . experimental ,
1459- 'enablePrerenderSourceMaps' ,
1460- true ,
1461- reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1462- )
1463-
1464- setExperimentalFeatureForDebugPrerender (
1465- userConfig . experimental ,
1466- 'prerenderEarlyExit' ,
1467- false ,
1468- reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1469- )
1470- }
1471-
1472- if ( reportExperimentalFeatures ) {
1473- reportExperimentalFeatures ( configuredExperimentalFeatures )
1474- }
1422+ enforceExperimentalFeatures ( userConfig , {
1423+ configuredExperimentalFeatures : reportExperimentalFeatures
1424+ ? configuredExperimentalFeatures
1425+ : undefined ,
1426+ debugPrerender,
1427+ phase,
1428+ } )
14751429
14761430 const completeConfig = assignDefaults (
14771431 dir ,
@@ -1483,6 +1437,11 @@ export default async function loadConfig(
14831437 } ,
14841438 silent
14851439 ) as NextConfigComplete
1440+
1441+ if ( reportExperimentalFeatures ) {
1442+ reportExperimentalFeatures ( configuredExperimentalFeatures )
1443+ }
1444+
14861445 return await applyModifyConfig ( completeConfig , phase , silent )
14871446 } else {
14881447 const configBaseName = basename ( CONFIG_FILES [ 0 ] , extname ( CONFIG_FILES [ 0 ] ) )
@@ -1506,14 +1465,30 @@ export default async function loadConfig(
15061465 }
15071466 }
15081467
1468+ const clonedDefaultConfig = cloneObject ( defaultConfig ) as NextConfig
1469+
1470+ enforceExperimentalFeatures ( clonedDefaultConfig , {
1471+ configuredExperimentalFeatures : reportExperimentalFeatures
1472+ ? configuredExperimentalFeatures
1473+ : undefined ,
1474+ debugPrerender,
1475+ phase,
1476+ } )
1477+
15091478 // always call assignDefaults to ensure settings like
15101479 // reactRoot can be updated correctly even with no next.config.js
15111480 const completeConfig = assignDefaults (
15121481 dir ,
1513- { ...defaultConfig , configFileName } ,
1482+ { ...clonedDefaultConfig , configFileName } ,
15141483 silent
15151484 ) as NextConfigComplete
1485+
15161486 setHttpClientAndAgentOptions ( completeConfig )
1487+
1488+ if ( reportExperimentalFeatures ) {
1489+ reportExperimentalFeatures ( configuredExperimentalFeatures )
1490+ }
1491+
15171492 return await applyModifyConfig ( completeConfig , phase , silent )
15181493}
15191494
@@ -1523,7 +1498,70 @@ export type ConfiguredExperimentalFeature = {
15231498 reason ?: string
15241499}
15251500
1526- export function addConfiguredExperimentalFeature <
1501+ function enforceExperimentalFeatures (
1502+ config : NextConfig ,
1503+ options : {
1504+ configuredExperimentalFeatures : ConfiguredExperimentalFeature [ ] | undefined
1505+ debugPrerender : boolean | undefined
1506+ phase : string
1507+ }
1508+ ) {
1509+ const { configuredExperimentalFeatures, debugPrerender, phase } = options
1510+
1511+ if (
1512+ config . experimental &&
1513+ config . experimental . enablePrerenderSourceMaps === undefined &&
1514+ config . experimental . dynamicIO === true
1515+ ) {
1516+ config . experimental . enablePrerenderSourceMaps = true
1517+
1518+ if ( configuredExperimentalFeatures ) {
1519+ addConfiguredExperimentalFeature (
1520+ configuredExperimentalFeatures ,
1521+ 'enablePrerenderSourceMaps' ,
1522+ true ,
1523+ 'enabled by `experimental.dynamicIO`'
1524+ )
1525+ }
1526+ }
1527+
1528+ config . experimental ??= { }
1529+
1530+ if (
1531+ debugPrerender &&
1532+ ( phase === PHASE_PRODUCTION_BUILD || phase === PHASE_EXPORT )
1533+ ) {
1534+ setExperimentalFeatureForDebugPrerender (
1535+ config . experimental ,
1536+ 'serverSourceMaps' ,
1537+ true ,
1538+ configuredExperimentalFeatures
1539+ )
1540+
1541+ setExperimentalFeatureForDebugPrerender (
1542+ config . experimental ,
1543+ process . env . TURBOPACK ? 'turbopackMinify' : 'serverMinification' ,
1544+ false ,
1545+ configuredExperimentalFeatures
1546+ )
1547+
1548+ setExperimentalFeatureForDebugPrerender (
1549+ config . experimental ,
1550+ 'enablePrerenderSourceMaps' ,
1551+ true ,
1552+ configuredExperimentalFeatures
1553+ )
1554+
1555+ setExperimentalFeatureForDebugPrerender (
1556+ config . experimental ,
1557+ 'prerenderEarlyExit' ,
1558+ false ,
1559+ configuredExperimentalFeatures
1560+ )
1561+ }
1562+ }
1563+
1564+ function addConfiguredExperimentalFeature <
15271565 KeyType extends keyof ExperimentalConfig ,
15281566> (
15291567 configuredExperimentalFeatures : ConfiguredExperimentalFeature [ ] ,
@@ -1564,20 +1602,50 @@ function setExperimentalFeatureForDebugPrerender<
15641602}
15651603
15661604function cloneObject ( obj : any ) : any {
1605+ // Primitives & null
15671606 if ( obj === null || typeof obj !== 'object' ) {
15681607 return obj
15691608 }
15701609
1610+ // RegExp → clone via constructor
1611+ if ( obj instanceof RegExp ) {
1612+ return new RegExp ( obj . source , obj . flags )
1613+ }
1614+
1615+ // Function → just reuse the function reference
1616+ if ( typeof obj === 'function' ) {
1617+ return obj
1618+ }
1619+
1620+ // Arrays → map each element
15711621 if ( Array . isArray ( obj ) ) {
15721622 return obj . map ( cloneObject )
15731623 }
1574- const keys = Object . keys ( obj )
1575- if ( keys . length === 0 ) {
1624+
1625+ // Detect non‑plain objects (class instances)
1626+ const proto = Object . getPrototypeOf ( obj )
1627+ const isPlainObject = proto === Object . prototype || proto === null
1628+
1629+ // If it's not a plain object, just return the original
1630+ if ( ! isPlainObject ) {
15761631 return obj
15771632 }
15781633
1579- return keys . reduce ( ( acc , key ) => {
1580- ; ( acc as any ) [ key ] = cloneObject ( obj [ key ] )
1581- return acc
1582- } , { } )
1634+ // Plain object → create a new object with the same prototype
1635+ // and copy all properties, cloning data properties and keeping
1636+ // accessor properties (getters/setters) as‑is.
1637+ const result = Object . create ( proto )
1638+ for ( const key of Reflect . ownKeys ( obj ) ) {
1639+ const descriptor = Object . getOwnPropertyDescriptor ( obj , key )
1640+
1641+ if ( descriptor && ( descriptor . get || descriptor . set ) ) {
1642+ // Accessor property → copy descriptor as‑is (get/set functions)
1643+ Object . defineProperty ( result , key , descriptor )
1644+ } else {
1645+ // Data property → clone the value
1646+ result [ key ] = cloneObject ( obj [ key ] )
1647+ }
1648+ }
1649+
1650+ return result
15831651}
0 commit comments