Skip to content

Commit 1fffbca

Browse files
authored
Flag excess properties in Next.js config with TypeScript (#84069)
1 parent 7f29f17 commit 1fffbca

File tree

7 files changed

+54
-32
lines changed

7 files changed

+54
-32
lines changed

packages/next/src/build/webpack-config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,7 +2459,7 @@ export default async function getBaseWebpackConfig(
24592459
assetPrefix: config.assetPrefix || '',
24602460
sassOptions: config.sassOptions,
24612461
productionBrowserSourceMaps: config.productionBrowserSourceMaps,
2462-
future: config.future,
2462+
future: (config as any).future,
24632463
experimental: config.experimental,
24642464
disableStaticImages: config.images.disableStaticImages,
24652465
transpilePackages: config.transpilePackages,
@@ -2636,8 +2636,8 @@ export default async function getBaseWebpackConfig(
26362636
}
26372637

26382638
// Backwards compat with webpack-dev-middleware options object
2639-
if (typeof config.webpackDevMiddleware === 'function') {
2640-
const options = config.webpackDevMiddleware({
2639+
if (typeof (config as any).webpackDevMiddleware === 'function') {
2640+
const options = (config as any).webpackDevMiddleware({
26412641
watchOptions: webpackConfig.watchOptions,
26422642
})
26432643
if (options.watchOptions) {

packages/next/src/build/webpack/config/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ export async function buildConfiguration(
3939
sassOptions: any
4040
productionBrowserSourceMaps: boolean
4141
transpilePackages: NextConfigComplete['transpilePackages']
42+
// @ts-expect-error TODO: remove any
4243
future: NextConfigComplete['future']
4344
experimental: NextConfigComplete['experimental']
44-
disableStaticImages: NextConfigComplete['disableStaticImages']
45+
disableStaticImages: NextConfigComplete['images']['disableStaticImages']
4546
serverSourceMaps: NextConfigComplete['experimental']['serverSourceMaps']
4647
}
4748
): Promise<webpack.Configuration> {

packages/next/src/build/webpack/config/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type ConfigurationContext = {
2626

2727
transpilePackages: NextConfigComplete['transpilePackages']
2828

29+
// @ts-expect-error TODO: remove any
2930
future: NextConfigComplete['future']
3031
experimental: NextConfigComplete['experimental']
3132
}

packages/next/src/export/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ async function exportAppImpl(
127127
)
128128
}
129129

130-
const customRoutes = ['rewrites', 'redirects', 'headers'].filter(
130+
const customRoutes = (['rewrites', 'redirects', 'headers'] as const).filter(
131131
(config) => typeof nextConfig[config] === 'function'
132132
)
133133

packages/next/src/server/config-shared.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ import { INFINITE_CACHE } from '../lib/constants'
1616
import { isStableBuild } from '../shared/lib/canary-only'
1717
import type { FallbackRouteParam } from '../build/static-paths/types'
1818

19-
export type NextConfigComplete = Required<NextConfig> & {
19+
export type NextConfigComplete = Required<Omit<NextConfig, 'configFile'>> & {
2020
images: Required<ImageConfigComplete>
2121
typescript: TypeScriptConfig
22-
configOrigin?: string
23-
configFile?: string
22+
configFile: string | undefined
2423
configFileName: string
2524
// override NextConfigComplete.experimental.htmlLimitedBots to string
2625
// because it's not defined in NextConfigComplete.experimental
@@ -876,7 +875,7 @@ export type ExportPathMap = {
876875
*
877876
* Read more: [Next.js Docs: `next.config.js`](https://nextjs.org/docs/app/api-reference/config/next-config-js)
878877
*/
879-
export interface NextConfig extends Record<string, any> {
878+
export interface NextConfig {
880879
allowedDevOrigins?: string[]
881880

882881
exportPathMap?: (
@@ -1291,6 +1290,26 @@ export interface NextConfig extends Record<string, any> {
12911290
* /Mediapartners-Google|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview/i
12921291
*/
12931292
htmlLimitedBots?: RegExp
1293+
1294+
/**
1295+
* @internal
1296+
*/
1297+
configFile?: string | undefined
1298+
1299+
/**
1300+
* @internal
1301+
*/
1302+
configOrigin?: string | undefined
1303+
1304+
/**
1305+
* @internal
1306+
*/
1307+
_originalRedirects?: any
1308+
1309+
/**
1310+
* @internal
1311+
*/
1312+
_originalRewrites?: any
12941313
}
12951314

12961315
export const defaultConfig = Object.freeze({

packages/next/src/server/config.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export function warnOptionHasBeenDeprecated(
7676
let found = true
7777
const nestedPropertyKeys = nestedPropertyKey.split('.')
7878
for (const key of nestedPropertyKeys) {
79-
if (current[key] !== undefined) {
80-
current = current[key]
79+
if ((current as any)[key] !== undefined) {
80+
current = (current as any)[key]
8181
} else {
8282
found = false
8383
break
@@ -167,10 +167,12 @@ export function warnOptionHasBeenMovedOutOfExperimental(
167167
const newKeys = newKey.split('.')
168168
while (newKeys.length > 1) {
169169
const key = newKeys.shift()!
170-
current[key] = current[key] || {}
171-
current = current[key]
170+
;(current as any)[key] = (current as any)[key] || {}
171+
current = (current as any)[key]
172172
}
173-
current[newKeys.shift()!] = (config.experimental as any)[oldExperimentalKey]
173+
;(current as any)[newKeys.shift()!] = (config.experimental as any)[
174+
oldExperimentalKey
175+
]
174176
}
175177

176178
return config
@@ -192,7 +194,7 @@ function warnCustomizedOption(
192194
if (!(seg in current)) {
193195
return
194196
}
195-
current = current[seg]
197+
current = (current as any)[seg]
196198
}
197199

198200
if (!silent && current !== defaultValue) {
@@ -218,16 +220,16 @@ function assignDefaultsAndValidate(
218220
phase: PHASE_TYPE
219221
): NextConfigComplete {
220222
const configFileName = userConfig.configFileName
221-
if (typeof userConfig.exportTrailingSlash !== 'undefined') {
223+
if (typeof (userConfig as any).exportTrailingSlash !== 'undefined') {
222224
if (!silent) {
223225
Log.warn(
224226
`The "exportTrailingSlash" option has been renamed to "trailingSlash". Please update your ${configFileName}.`
225227
)
226228
}
227229
if (typeof userConfig.trailingSlash === 'undefined') {
228-
userConfig.trailingSlash = userConfig.exportTrailingSlash
230+
userConfig.trailingSlash = (userConfig as any).exportTrailingSlash
229231
}
230-
delete userConfig.exportTrailingSlash
232+
delete (userConfig as any).exportTrailingSlash
231233
}
232234

233235
// Handle migration of experimental.dynamicIO to experimental.cacheComponents
@@ -245,7 +247,7 @@ function assignDefaultsAndValidate(
245247

246248
const config = Object.keys(userConfig).reduce<{ [key: string]: any }>(
247249
(currentConfig, key) => {
248-
const value = userConfig[key]
250+
const value = (userConfig as any)[key]
249251

250252
if (value === undefined || value === null) {
251253
return currentConfig
@@ -1266,7 +1268,6 @@ function getCacheKey(
12661268

12671269
return djb2Hash(keyData).toString(36)
12681270
}
1269-
12701271
export default async function loadConfig(
12711272
phase: PHASE_TYPE,
12721273
dir: string,
@@ -1371,7 +1372,7 @@ export default async function loadConfig(
13711372
silent,
13721373
configuredExperimentalFeatures,
13731374
phase
1374-
) as NextConfigComplete,
1375+
),
13751376
phase,
13761377
silent
13771378
)
@@ -1481,7 +1482,7 @@ export default async function loadConfig(
14811482
validateConfigSchema(userConfig, configFileName, curLog.warn)
14821483
}
14831484

1484-
if (userConfig.target && userConfig.target !== 'server') {
1485+
if ((userConfig as any).target && (userConfig as any).target !== 'server') {
14851486
throw new Error(
14861487
`The "target" property is no longer supported in ${configFileName}.\n` +
14871488
'See more info here https://nextjs.org/docs/messages/deprecated-target-config'
@@ -1538,7 +1539,7 @@ export default async function loadConfig(
15381539
silent,
15391540
configuredExperimentalFeatures,
15401541
phase
1541-
) as NextConfigComplete
1542+
)
15421543

15431544
const finalConfig = await applyModifyConfig(completeConfig, phase, silent)
15441545

test/unit/isolated/config.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ describe('config', () => {
2323
})
2424
it('Should get the configuration', async () => {
2525
const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, pathToConfig)
26-
expect(config.customConfig).toBe(true)
26+
expect((config as any).customConfig).toBe(true)
2727
})
2828

2929
it('Should pass the phase correctly', async () => {
3030
const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, pathToConfigFn)
31-
expect(config.phase).toBe(PHASE_DEVELOPMENT_SERVER)
31+
expect((config as any).phase).toBe(PHASE_DEVELOPMENT_SERVER)
3232
})
3333

3434
it('Should pass the defaultConfig correctly', async () => {
3535
const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, pathToConfigFn)
36-
expect(config.defaultConfig).toBeDefined()
36+
expect((config as any).defaultConfig).toBeDefined()
3737
})
3838

3939
it('Should assign object defaults deeply to user config', async () => {
@@ -48,7 +48,7 @@ describe('config', () => {
4848
customConfigKey: 'customConfigValue',
4949
},
5050
})
51-
expect(config.customConfigKey).toBe('customConfigValue')
51+
expect((config as any).customConfigKey).toBe('customConfigValue')
5252
})
5353

5454
it('Should assign object defaults deeply to customConfig', async () => {
@@ -58,7 +58,7 @@ describe('config', () => {
5858
onDemandEntries: { custom: true },
5959
},
6060
})
61-
expect(config.customConfig).toBe(true)
61+
expect((config as any).customConfig).toBe(true)
6262
expect(config.onDemandEntries.maxInactiveAge).toBeDefined()
6363
})
6464

@@ -68,8 +68,8 @@ describe('config', () => {
6868
bogusSetting: { custom: true },
6969
},
7070
})
71-
expect(config.bogusSetting).toBeDefined()
72-
expect(config.bogusSetting.custom).toBe(true)
71+
expect((config as any).bogusSetting).toBeDefined()
72+
expect((config as any).bogusSetting.custom).toBe(true)
7373
})
7474

7575
it('Should override defaults for arrays from user arrays', async () => {
@@ -107,15 +107,15 @@ describe('config', () => {
107107
PHASE_DEVELOPMENT_SERVER,
108108
join(__dirname, '_resolvedata', 'js-ts-config')
109109
)
110-
expect(config.__test__ext).toBe('js')
110+
expect((config as any).__test__ext).toBe('js')
111111
})
112112

113113
it('Should not throw an error when next.config.ts is present', async () => {
114114
const config = await loadConfig(
115115
PHASE_DEVELOPMENT_SERVER,
116116
join(__dirname, '_resolvedata', 'typescript-config')
117117
)
118-
expect(config.__test__ext).toBe('ts')
118+
expect((config as any).__test__ext).toBe('ts')
119119
})
120120

121121
describe('outputFileTracingRoot and turbopack.root consistency', () => {

0 commit comments

Comments
 (0)