diff --git a/packages/next/src/server/config.ts b/packages/next/src/server/config.ts index 53d63bad6a17a4b..647724221c33682 100644 --- a/packages/next/src/server/config.ts +++ b/packages/next/src/server/config.ts @@ -126,6 +126,32 @@ function setFontLoaderDefaults(config: NextConfigComplete) { } catch {} } +export function warnOptionHasBeenMovedOutOfExperimental( + config: NextConfig, + oldKey: string, + newKey: string, + configFileName: string +) { + if (config.experimental && oldKey in config.experimental) { + Log.warn( + `\`${oldKey}\` has been moved out of \`experimental\`` + + (newKey.includes('.') ? ` and into \`${newKey}\`` : '') + + `. Please update your ${configFileName} file accordingly.` + ) + + let current = config + const newKeys = newKey.split('.') + while (newKeys.length > 1) { + const key = newKeys.shift()! + current[key] = current[key] || {} + current = current[key] + } + current[newKeys.shift()!] = (config.experimental as any)[oldKey] + } + + return config +} + function assignDefaults(dir: string, userConfig: { [key: string]: any }) { const configFileName = userConfig.configFileName if (typeof userConfig.exportTrailingSlash !== 'undefined') { @@ -542,55 +568,36 @@ function assignDefaults(dir: string, userConfig: { [key: string]: any }) { } } - if (result.experimental && 'relay' in (result.experimental as any)) { - Log.warn( - `\`relay\` has been moved out of \`experimental\` and into \`compiler\`. Please update your ${configFileName} file accordingly.` - ) - result.compiler = result.compiler || {} - result.compiler.relay = (result.experimental as any).relay - } - - if ( - result.experimental && - 'styledComponents' in (result.experimental as any) - ) { - Log.warn( - `\`styledComponents\` has been moved out of \`experimental\` and into \`compiler\`. Please update your ${configFileName} file accordingly.` - ) - result.compiler = result.compiler || {} - result.compiler.styledComponents = ( - result.experimental as any - ).styledComponents - } - - if (result.experimental && 'emotion' in (result.experimental as any)) { - Log.warn( - `\`emotion\` has been moved out of \`experimental\` and into \`compiler\`. Please update your ${configFileName} file accordingly.` - ) - result.compiler = result.compiler || {} - result.compiler.emotion = (result.experimental as any).emotion - } - - if ( - result.experimental && - 'reactRemoveProperties' in (result.experimental as any) - ) { - Log.warn( - `\`reactRemoveProperties\` has been moved out of \`experimental\` and into \`compiler\`. Please update your ${configFileName} file accordingly.` - ) - result.compiler = result.compiler || {} - result.compiler.reactRemoveProperties = ( - result.experimental as any - ).reactRemoveProperties - } - - if (result.experimental && 'removeConsole' in (result.experimental as any)) { - Log.warn( - `\`removeConsole\` has been moved out of \`experimental\` and into \`compiler\`. Please update your ${configFileName} file accordingly.` - ) - result.compiler = result.compiler || {} - result.compiler.removeConsole = (result.experimental as any).removeConsole - } + warnOptionHasBeenMovedOutOfExperimental( + result, + 'relay', + 'compiler.relay', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'styledComponents', + 'compiler.styledComponents', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'emotion', + 'compiler.emotion', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'reactRemoveProperties', + 'compiler.reactRemoveProperties', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'removeConsole', + 'compiler.removeConsole', + configFileName + ) if (result.experimental?.swcMinifyDebugOptions) { Log.warn( @@ -605,39 +612,24 @@ function assignDefaults(dir: string, userConfig: { [key: string]: any }) { result.output = 'standalone' } - if ( - result.experimental && - 'transpilePackages' in (result.experimental as any) - ) { - Log.warn( - `\`transpilePackages\` has been moved out of \`experimental\`. Please update your ${configFileName} file accordingly.` - ) - result.transpilePackages = (result.experimental as any).transpilePackages - } - - if ( - result.experimental && - 'skipMiddlewareUrlNormalize' in (result.experimental as any) - ) { - Log.warn( - `\`skipMiddlewareUrlNormalize\` has been moved out of \`experimental\`. Please update your ${configFileName} file accordingly.` - ) - result.skipMiddlewareUrlNormalize = ( - result.experimental as any - ).skipMiddlewareUrlNormalize - } - - if ( - result.experimental && - 'skipTrailingSlashRedirect' in (result.experimental as any) - ) { - Log.warn( - `\`skipTrailingSlashRedirect\` has been moved out of \`experimental\`. Please update your ${configFileName} file accordingly.` - ) - result.skipTrailingSlashRedirect = ( - result.experimental as any - ).skipTrailingSlashRedirect - } + warnOptionHasBeenMovedOutOfExperimental( + result, + 'transpilePackages', + 'transpilePackages', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'skipMiddlewareUrlNormalize', + 'skipMiddlewareUrlNormalize', + configFileName + ) + warnOptionHasBeenMovedOutOfExperimental( + result, + 'skipTrailingSlashRedirect', + 'skipTrailingSlashRedirect', + configFileName + ) if ( result.experimental?.outputFileTracingRoot && diff --git a/test/unit/warn-removed-experimental-config.test.ts b/test/unit/warn-removed-experimental-config.test.ts new file mode 100644 index 000000000000000..479a457ee6094ad --- /dev/null +++ b/test/unit/warn-removed-experimental-config.test.ts @@ -0,0 +1,98 @@ +import { warnOptionHasBeenMovedOutOfExperimental } from 'next/dist/server/config' + +describe('warnOptionHasBeenMovedOutOfExperimental', () => { + let spy: jest.SpyInstance + beforeAll(() => { + spy = jest.spyOn(console, 'warn').mockImplementation(() => {}) + }) + + it('should not log warning message without experimental config', () => { + warnOptionHasBeenMovedOutOfExperimental( + {}, + 'skipTrailingSlashRedirect', + 'skipTrailingSlashRedirect', + 'next.config.js' + ) + + warnOptionHasBeenMovedOutOfExperimental( + { + experimental: {}, + }, + 'skipTrailingSlashRedirect', + 'skipTrailingSlashRedirect', + 'next.config.js' + ) + + expect(spy).not.toBeCalled() + }) + + it('should log warning message with removed experimental config', () => { + warnOptionHasBeenMovedOutOfExperimental( + { + experimental: { + skipTrailingSlashRedirect: true, + }, + } as any, + 'skipTrailingSlashRedirect', + 'skipTrailingSlashRedirect', + 'next.config.js' + ) + + expect(spy).toHaveBeenCalledWith( + expect.stringContaining('warn'), + '`skipTrailingSlashRedirect` has been moved out of `experimental`. Please update your next.config.js file accordingly.' + ) + }) + + it('should log warning message with removed experimental config - complex key', () => { + warnOptionHasBeenMovedOutOfExperimental( + { + experimental: { + relay: true, + }, + } as any, + 'relay', + 'compiler.relay', + 'next.config.js' + ) + + expect(spy).toHaveBeenCalledWith( + expect.stringContaining('warn'), + '`relay` has been moved out of `experimental` and into `compiler.relay`. Please update your next.config.js file accordingly.' + ) + }) + + it('should update removed experimental config into new config', () => { + const config = { + experimental: { + skipTrailingSlashRedirect: true, + }, + } as any + warnOptionHasBeenMovedOutOfExperimental( + config, + 'skipTrailingSlashRedirect', + 'skipTrailingSlashRedirect', + 'next.config.js' + ) + + expect(config.experimental.skipTrailingSlashRedirect).toBe(true) + expect(config.skipTrailingSlashRedirect).toBe(true) + }) + + it('should update removed experimental config into new config - complex key', () => { + const config = { + experimental: { + foo: 'bar', + }, + } as any + warnOptionHasBeenMovedOutOfExperimental( + config, + 'foo', + 'deep.prop.baz', + 'next.config.js' + ) + + expect(config.experimental.foo).toBe('bar') + expect(config.deep.prop.baz).toBe('bar') + }) +})