diff --git a/docs/advanced-features/compiler.md b/docs/advanced-features/compiler.md index 9e1549972178c..439474fa11f33 100644 --- a/docs/advanced-features/compiler.md +++ b/docs/advanced-features/compiler.md @@ -7,9 +7,10 @@ description: Learn about the Next.js Compiler, written in Rust, which transforms
Version History -| Version | Changes | -| --------- | --------------------------------------------------------------- | -| `v12.0.0` | Next.js Compiler [introduced](https://nextjs.org/blog/next-12). | +| Version | Changes | +| --------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `v12.1.0` | Added support for Styled Components, Jest, Relay, Remove React Properties, Legacy Decorators, Remove Console, and jsxImportSource. | +| `v12.0.0` | Next.js Compiler [introduced](https://nextjs.org/blog/next-12). |
@@ -30,21 +31,7 @@ We chose to build on SWC for a few reasons: - **WebAssembly:** Rust's support for WASM is essential for supporting all possible platforms and taking Next.js development everywhere. - **Community:** The Rust community and ecosystem are amazing and still growing. -## Experimental Features - -### Minification - -You can opt-in to using the Next.js compiler for minification. This is 7x faster than Terser. - -```js -// next.config.js - -module.exports = { - swcMinify: true, -} -``` - -If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237). +## Supported Features ### Styled Components @@ -56,7 +43,7 @@ First, update to the latest version of Next.js: `npm install next@latest`. Then, // next.config.js module.exports = { - experimental: { + compiler: { // ssr and displayName are configured by default styledComponents: true, }, @@ -101,7 +88,7 @@ To enable [Relay](https://relay.dev/) support: ```js // next.config.js module.exports = { - experimental: { + compiler: { relay: { // This should match relay.config.js src: './', @@ -123,7 +110,7 @@ To remove properties matching the default regex `^data-test`: ```js // next.config.js module.exports = { - experimental: { + compiler: { reactRemoveProperties: true, }, } @@ -134,7 +121,7 @@ To remove custom properties: ```js // next.config.js module.exports = { - experimental: { + compiler: { // The regexes defined here are processed in Rust so the syntax is different from // JavaScript `RegExp`s. See https://docs.rs/regex. reactRemoveProperties: { properties: ['^data-custom$'] }, @@ -142,22 +129,6 @@ module.exports = { } ``` -### Legacy Decorators - -Next.js will automatically detect `experimentalDecorators` in `jsconfig.json` or `tsconfig.json` and apply that. This is commonly used with older versions of libraries like `mobx`. - -This flag is only supported for compatibility with existing applications. We do not recommend using legacy decorators in new applications. - -First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `jsconfig.json` or `tsconfig.json` file: - -```js -{ - "compilerOptions": { - "experimentalDecorators": true - } -} -``` - ### Remove Console This transform allows for removing all `console.*` calls in application code (not `node_modules`). Similar to `babel-plugin-transform-remove-console`. @@ -167,7 +138,7 @@ Remove all `console.*` calls: ```js // next.config.js module.exports = { - experimental: { + compiler: { removeConsole: true, }, } @@ -178,7 +149,7 @@ Remove `console.*` output except `console.error`: ```js // next.config.js module.exports = { - experimental: { + compiler: { removeConsole: { exclude: ['error'], }, @@ -186,6 +157,22 @@ module.exports = { } ``` +### Legacy Decorators + +Next.js will automatically detect `experimentalDecorators` in `jsconfig.json` or `tsconfig.json`. Legacy decorators are commonly used with older versions of libraries like `mobx`. + +This flag is only supported for compatibility with existing applications. We do not recommend using legacy decorators in new applications. + +First, update to the latest version of Next.js: `npm install next@latest`. Then, update your `jsconfig.json` or `tsconfig.json` file: + +```js +{ + "compilerOptions": { + "experimentalDecorators": true + } +} +``` + ### importSource Next.js will automatically detect `jsxImportSource` in `jsconfig.json` or `tsconfig.json` and apply that. This is commonly used with libraries like Theme UI. @@ -195,11 +182,27 @@ First, update to the latest version of Next.js: `npm install next@latest`. Then, ```js { "compilerOptions": { - "jsxImportSource": true + "jsxImportSource": 'preact' } } ``` +## Experimental Features + +### Minification + +You can opt-in to using the Next.js compiler for minification. This is 7x faster than Terser. + +```js +// next.config.js + +module.exports = { + swcMinify: true, +} +``` + +If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237). + ## Unsupported Features When your application has a `.babelrc` file, Next.js will automatically fall back to using Babel for transforming individual files. This ensures backwards compatibility with existing applications that leverage custom Babel plugins. diff --git a/errors/ignored-compiler-options.md b/errors/ignored-compiler-options.md new file mode 100644 index 0000000000000..edab33451d6ec --- /dev/null +++ b/errors/ignored-compiler-options.md @@ -0,0 +1,9 @@ +# ignored-compiler-options + +#### Why This Error Occurred + +Options under the `compiler` key in `next.config.js` only apply to the new Rust based compiler and will be ignored if Babel is used instead. This message will appear if Next.js detects a Babel configuration file while `compiler` options are configured in `next.config.js` + +### Useful Links + +- [Next.js Compiler](https://nextjs.org/docs/advanced-features/compiler) diff --git a/errors/manifest.json b/errors/manifest.json index 568ca2fffdafb..a7e0bc694424a 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -32,7 +32,10 @@ "title": "404-get-initial-props", "path": "/errors/404-get-initial-props.md" }, - { "title": "amp-bind-jsx-alt", "path": "/errors/amp-bind-jsx-alt.md" }, + { + "title": "amp-bind-jsx-alt", + "path": "/errors/amp-bind-jsx-alt.md" + }, { "title": "amp-export-validation", "path": "/errors/amp-export-validation.md" @@ -89,9 +92,18 @@ "title": "conflicting-ssg-paths", "path": "/errors/conflicting-ssg-paths.md" }, - { "title": "css-global", "path": "/errors/css-global.md" }, - { "title": "css-modules-npm", "path": "/errors/css-modules-npm.md" }, - { "title": "css-npm", "path": "/errors/css-npm.md" }, + { + "title": "css-global", + "path": "/errors/css-global.md" + }, + { + "title": "css-modules-npm", + "path": "/errors/css-modules-npm.md" + }, + { + "title": "css-npm", + "path": "/errors/css-npm.md" + }, { "title": "custom-error-no-custom-404", "path": "/errors/custom-error-no-custom-404.md" @@ -100,7 +112,10 @@ "title": "doc-crossorigin-deprecated", "path": "/errors/doc-crossorigin-deprecated.md" }, - { "title": "duplicate-sass", "path": "/errors/duplicate-sass.md" }, + { + "title": "duplicate-sass", + "path": "/errors/duplicate-sass.md" + }, { "title": "empty-configuration", "path": "/errors/empty-configuration.md" @@ -121,7 +136,10 @@ "title": "export-all-in-page", "path": "/errors/export-all-in-page.md" }, - { "title": "export-image-api", "path": "/errors/export-image-api.md" }, + { + "title": "export-image-api", + "path": "/errors/export-image-api.md" + }, { "title": "export-no-custom-routes", "path": "/errors/export-no-custom-routes.md" @@ -154,7 +172,10 @@ "title": "gssp-component-member", "path": "/errors/gssp-component-member.md" }, - { "title": "gssp-export", "path": "/errors/gssp-export.md" }, + { + "title": "gssp-export", + "path": "/errors/gssp-export.md" + }, { "title": "gssp-mixed-not-found-redirect", "path": "/errors/gssp-mixed-not-found-redirect.md" @@ -163,12 +184,18 @@ "title": "gssp-no-mutating-res", "path": "/errors/gssp-no-mutating-res.md" }, - { "title": "head-build-id", "path": "/errors/head-build-id.md" }, + { + "title": "head-build-id", + "path": "/errors/head-build-id.md" + }, { "title": "href-interpolation-failed", "path": "/errors/href-interpolation-failed.md" }, - { "title": "improper-devtool", "path": "/errors/improper-devtool.md" }, + { + "title": "improper-devtool", + "path": "/errors/improper-devtool.md" + }, { "title": "incompatible-href-as", "path": "/errors/incompatible-href-as.md" @@ -177,8 +204,14 @@ "title": "inline-script-id", "path": "/errors/inline-script-id.md" }, - { "title": "install-sass", "path": "/errors/install-sass.md" }, - { "title": "install-sharp", "path": "/errors/install-sharp.md" }, + { + "title": "install-sass", + "path": "/errors/install-sass.md" + }, + { + "title": "install-sharp", + "path": "/errors/install-sharp.md" + }, { "title": "invalid-assetprefix", "path": "/errors/invalid-assetprefix.md" @@ -251,7 +284,10 @@ "title": "link-passhref", "path": "/errors/link-passhref.md" }, - { "title": "manifest.json", "path": "/errors/manifest.json" }, + { + "title": "manifest.json", + "path": "/errors/manifest.json" + }, { "title": "minification-disabled", "path": "/errors/minification-disabled.md" @@ -264,7 +300,10 @@ "title": "missing-env-value", "path": "/errors/missing-env-value.md" }, - { "title": "multi-tabs", "path": "/errors/multi-tabs.md" }, + { + "title": "multi-tabs", + "path": "/errors/multi-tabs.md" + }, { "title": "nested-reserved-page", "path": "/errors/nested-reserved-page.md" @@ -305,8 +344,14 @@ "title": "next-start-serverless", "path": "/errors/next-start-serverless.md" }, - { "title": "no-cache", "path": "/errors/no-cache.md" }, - { "title": "no-css-tags", "path": "/errors/no-css-tags.md" }, + { + "title": "no-cache", + "path": "/errors/no-cache.md" + }, + { + "title": "no-css-tags", + "path": "/errors/no-css-tags.md" + }, { "title": "no-document-import-in-page", "path": "/errors/no-document-import-in-page.md" @@ -379,17 +424,26 @@ "title": "popstate-state-empty", "path": "/errors/popstate-state-empty.md" }, - { "title": "postcss-function", "path": "/errors/postcss-function.md" }, + { + "title": "postcss-function", + "path": "/errors/postcss-function.md" + }, { "title": "postcss-ignored-plugin", "path": "/errors/postcss-ignored-plugin.md" }, - { "title": "postcss-shape", "path": "/errors/postcss-shape.md" }, + { + "title": "postcss-shape", + "path": "/errors/postcss-shape.md" + }, { "title": "prefetch-true-deprecated", "path": "/errors/prefetch-true-deprecated.md" }, - { "title": "prerender-error", "path": "/errors/prerender-error.md" }, + { + "title": "prerender-error", + "path": "/errors/prerender-error.md" + }, { "title": "production-start-no-build-id", "path": "/errors/production-start-no-build-id.md" @@ -402,7 +456,10 @@ "title": "public-next-folder-conflict", "path": "/errors/public-next-folder-conflict.md" }, - { "title": "react-version", "path": "/errors/react-version.md" }, + { + "title": "react-version", + "path": "/errors/react-version.md" + }, { "title": "render-no-starting-slash", "path": "/errors/render-no-starting-slash.md" @@ -427,13 +484,22 @@ "title": "static-dir-deprecated", "path": "/errors/static-dir-deprecated.md" }, - { "title": "threw-undefined", "path": "/errors/threw-undefined.md" }, + { + "title": "threw-undefined", + "path": "/errors/threw-undefined.md" + }, { "title": "undefined-webpack-config", "path": "/errors/undefined-webpack-config.md" }, - { "title": "url-deprecated", "path": "/errors/url-deprecated.md" }, - { "title": "webpack5", "path": "/errors/webpack5.md" }, + { + "title": "url-deprecated", + "path": "/errors/url-deprecated.md" + }, + { + "title": "webpack5", + "path": "/errors/webpack5.md" + }, { "title": "client-side-exception-occurred", "path": "/errors/client-side-exception-occurred.md" @@ -446,8 +512,14 @@ "title": "link-multiple-children", "path": "/errors/link-multiple-children.md" }, - { "title": "no-img-element", "path": "/errors/no-img-element.md" }, - { "title": "no-head-element", "path": "/errors/no-head-element.md" }, + { + "title": "no-img-element", + "path": "/errors/no-img-element.md" + }, + { + "title": "no-head-element", + "path": "/errors/no-head-element.md" + }, { "title": "non-dynamic-getstaticpaths-usage", "path": "/errors/non-dynamic-getstaticpaths-usage.md" @@ -543,6 +615,10 @@ { "title": "deleting-query-params-in-middlewares", "path": "/errors/deleting-query-params-in-middlewares.md" + }, + { + "title": "ignored-compiler-options", + "path": "/errors/ignored-compiler-options.md" } ] } diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index 71504105236b4..2d2cbee22fa8b 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -81,14 +81,14 @@ export function getBaseSWCOptions({ }, }, sourceMaps: jest ? 'inline' : undefined, - styledComponents: nextConfig?.experimental?.styledComponents + styledComponents: nextConfig?.compiler?.styledComponents ? { displayName: Boolean(development), } : null, - removeConsole: nextConfig?.experimental?.removeConsole, - reactRemoveProperties: nextConfig?.experimental?.reactRemoveProperties, - relay: nextConfig?.experimental?.relay, + removeConsole: nextConfig?.compiler?.removeConsole, + reactRemoveProperties: nextConfig?.compiler?.reactRemoveProperties, + relay: nextConfig?.compiler?.relay, } } diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index c710bd5c1712b..20c86444faa5a 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -86,6 +86,7 @@ const devtoolRevertWarning = execOnce( ) let loggedSwcDisabled = false +let loggedIgnoredCompilerOptions = false function getOptimizedAliases(): { [pkg: string]: string } { const stubWindowFetch = path.join(__dirname, 'polyfills', 'fetch', 'index.js') @@ -436,6 +437,13 @@ export default async function getBaseWebpackConfig( loggedSwcDisabled = true } + if (!loggedIgnoredCompilerOptions && !useSWCLoader && config.compiler) { + Log.info( + '`compiler` options in `next.config.js` will be ignored while using Babel https://next.js.org/docs/messages/ignored-compiler-options' + ) + loggedIgnoredCompilerOptions = true + } + const getBabelOrSwcLoader = (isMiddleware: boolean) => { return useSWCLoader ? { @@ -1517,6 +1525,18 @@ export default async function getBaseWebpackConfig( new Map([ ['swcLoader', useSWCLoader], ['swcMinify', config.swcMinify], + ['swcRelay', !!config.compiler?.relay], + ['swcStyledComponents', !!config.compiler?.styledComponents], + [ + 'swcReactRemoveProperties', + !!config.compiler?.reactRemoveProperties, + ], + [ + 'swcExperimentalDecorators', + !!jsConfig?.compilerOptions?.experimentalDecorators, + ], + ['swcRemoveConsole', !!config.compiler?.removeConsole], + ['swcImportSource', !!jsConfig?.compilerOptions?.jsxImportSource], ]) ), ].filter(Boolean as any as ExcludesFalse), @@ -1627,10 +1647,10 @@ export default async function getBaseWebpackConfig( runtime, swcMinify: config.swcMinify, swcLoader: useSWCLoader, - removeConsole: config.experimental.removeConsole, - reactRemoveProperties: config.experimental.reactRemoveProperties, - styledComponents: config.experimental.styledComponents, - relay: config.experimental.relay, + removeConsole: config.compiler?.removeConsole, + reactRemoveProperties: config.compiler?.reactRemoveProperties, + styledComponents: config.compiler?.styledComponents, + relay: config.compiler?.relay, }) const cache: any = { diff --git a/packages/next/build/webpack/plugins/telemetry-plugin.ts b/packages/next/build/webpack/plugins/telemetry-plugin.ts index e961aacb05e6f..fe1f370a283bb 100644 --- a/packages/next/build/webpack/plugins/telemetry-plugin.ts +++ b/packages/next/build/webpack/plugins/telemetry-plugin.ts @@ -6,6 +6,12 @@ type Feature = | 'next/dynamic' | 'swcLoader' | 'swcMinify' + | 'swcRelay' + | 'swcStyledComponents' + | 'swcReactRemoveProperties' + | 'swcExperimentalDecorators' + | 'swcRemoveConsole' + | 'swcImportSource' interface FeatureUsage { featureName: Feature @@ -35,7 +41,16 @@ const FEATURE_MODULE_MAP: ReadonlyMap = new Map([ ]) // List of build features used in webpack configuration -const BUILD_FEATURES: Array = ['swcLoader', 'swcMinify'] +const BUILD_FEATURES: Array = [ + 'swcLoader', + 'swcMinify', + 'swcRelay', + 'swcStyledComponents', + 'swcReactRemoveProperties', + 'swcExperimentalDecorators', + 'swcRemoveConsole', + 'swcImportSource', +] /** * Plugin that queries the ModuleGraph to look for modules that correspond to diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index c7f4037e6a808..e7ad981516692 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -75,17 +75,6 @@ export interface NextJsWebpackConfig { export interface ExperimentalConfig { disablePostcssPresetEnv?: boolean - removeConsole?: - | boolean - | { - exclude?: string[] - } - reactRemoveProperties?: - | boolean - | { - properties?: string[] - } - styledComponents?: boolean swcMinify?: boolean swcFileReading?: boolean cpus?: number @@ -118,11 +107,6 @@ export interface ExperimentalConfig { urlImports?: NonNullable['buildHttp'] outputFileTracingRoot?: string outputStandalone?: boolean - relay?: { - src: string - artifactDirectory?: string - language?: 'typescript' | 'flow' - } } /** @@ -376,6 +360,30 @@ export interface NextConfig extends Record { */ swcMinify?: boolean + /** + * Optionally enable compiler transforms + * + * @see [Supported Compiler Options](https://nextjs.org/docs/advanced-features/compiler#supported-features) + */ + compiler?: { + reactRemoveProperties?: + | boolean + | { + properties?: string[] + } + relay?: { + src: string + artifactDirectory?: string + language?: 'typescript' | 'flow' + } + removeConsole?: + | boolean + | { + exclude?: string[] + } + styledComponents?: boolean + } + /** * Enable experimental features. Note that all experimental features are subject to breaking changes in the future. */ diff --git a/packages/next/server/config.ts b/packages/next/server/config.ts index 6a64992688521..04a1727eddf2d 100644 --- a/packages/next/server/config.ts +++ b/packages/next/server/config.ts @@ -365,6 +365,48 @@ function assignDefaults(userConfig: { [key: string]: any }) { result.swcMinify = (result.experimental as any).swcMinify } + 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 && + '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 + } + if (result.swcMinify) { Log.warn( 'SWC minify release candidate enabled. https://nextjs.org/docs/messages/swc-minify-enabled' diff --git a/packages/next/telemetry/events/build.ts b/packages/next/telemetry/events/build.ts index 0be4c5d4e79a9..e68bbda1dd6d6 100644 --- a/packages/next/telemetry/events/build.ts +++ b/packages/next/telemetry/events/build.ts @@ -134,6 +134,12 @@ export type EventBuildFeatureUsage = { | 'optimizeFonts' | 'swcLoader' | 'swcMinify' + | 'swcRelay' + | 'swcStyledComponents' + | 'swcReactRemoveProperties' + | 'swcExperimentalDecorators' + | 'swcRemoveConsole' + | 'swcImportSource' | 'build-lint' invocationCount: number } diff --git a/test/development/basic/styled-components/next.config.js b/test/development/basic/styled-components/next.config.js index b125c0d794776..91f693fda5258 100644 --- a/test/development/basic/styled-components/next.config.js +++ b/test/development/basic/styled-components/next.config.js @@ -1,5 +1,5 @@ module.exports = { - experimental: { + compiler: { styledComponents: true, }, } diff --git a/test/integration/relay-graphql-swc-multi-project/project-a/next.config.js b/test/integration/relay-graphql-swc-multi-project/project-a/next.config.js index 79ef26c30f3a1..49b503b7fde0a 100644 --- a/test/integration/relay-graphql-swc-multi-project/project-a/next.config.js +++ b/test/integration/relay-graphql-swc-multi-project/project-a/next.config.js @@ -1,7 +1,7 @@ const relay = require('../relay.config') module.exports = { - experimental: { + compiler: { relay: { src: './pages', artifactDirectory: './__generated__', diff --git a/test/integration/relay-graphql-swc-multi-project/project-b/next.config.js b/test/integration/relay-graphql-swc-multi-project/project-b/next.config.js index 79ef26c30f3a1..49b503b7fde0a 100644 --- a/test/integration/relay-graphql-swc-multi-project/project-b/next.config.js +++ b/test/integration/relay-graphql-swc-multi-project/project-b/next.config.js @@ -1,7 +1,7 @@ const relay = require('../relay.config') module.exports = { - experimental: { + compiler: { relay: { src: './pages', artifactDirectory: './__generated__', diff --git a/test/integration/relay-graphql-swc-single-project/next.config.js b/test/integration/relay-graphql-swc-single-project/next.config.js index ef214a05ace7a..89574d57d1a6b 100644 --- a/test/integration/relay-graphql-swc-single-project/next.config.js +++ b/test/integration/relay-graphql-swc-single-project/next.config.js @@ -1,7 +1,7 @@ const relay = require('./relay.config') module.exports = { - experimental: { + compiler: { relay, }, } diff --git a/test/integration/telemetry/jsconfig.swc b/test/integration/telemetry/jsconfig.swc new file mode 100644 index 0000000000000..a178f9dcd04a7 --- /dev/null +++ b/test/integration/telemetry/jsconfig.swc @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "experimentalDecorators": true + } +} \ No newline at end of file diff --git a/test/integration/telemetry/next.config.swc b/test/integration/telemetry/next.config.swc new file mode 100644 index 0000000000000..6394ffee943d8 --- /dev/null +++ b/test/integration/telemetry/next.config.swc @@ -0,0 +1,14 @@ +module.exports = { + swcMinify: true, + compiler: { + relay: { + // This should match relay.config.js + src: './', + artifactDirectory: './__generated__', + language: 'typescript', + }, + styledComponents: true, + removeConsole: true, + reactRemoveProperties: true, + }, +} diff --git a/test/integration/telemetry/test/index.test.js b/test/integration/telemetry/test/index.test.js index add5b0d9665bc..ba86d775b31f3 100644 --- a/test/integration/telemetry/test/index.test.js +++ b/test/integration/telemetry/test/index.test.js @@ -619,12 +619,14 @@ describe('Telemetry CLI', () => { const optimizeFonts = regex.exec(stderr).pop() expect(optimizeFonts).toContain(`"featureName": "optimizeFonts"`) expect(optimizeFonts).toContain(`"invocationCount": 1`) - const swcLoader = regex.exec(stderr).pop() - expect(swcLoader).toContain(`"featureName": "swcLoader"`) - expect(swcLoader).toContain(`"invocationCount": 1`) - const swcMinify = regex.exec(stderr).pop() - expect(swcMinify).toContain(`"featureName": "swcMinify"`) - expect(swcMinify).toContain(`"invocationCount": 0`) + regex.exec(stderr).pop() // swcLoader + regex.exec(stderr).pop() // swcMinify + regex.exec(stderr).pop() // swcRelay + regex.exec(stderr).pop() // swcStyledComponents + regex.exec(stderr).pop() // swcExperimentalDecorators + regex.exec(stderr).pop() // swcReactRemoveProperties + regex.exec(stderr).pop() // swcRemoveConsole + regex.exec(stderr).pop() // swcImportSource const image = regex.exec(stderr).pop() expect(image).toContain(`"featureName": "next/image"`) expect(image).toContain(`"invocationCount": 1`) @@ -636,6 +638,60 @@ describe('Telemetry CLI', () => { expect(dynamic).toContain(`"invocationCount": 1`) }) + it('emits telemetry for usage of swc', async () => { + await fs.remove(path.join(appDir, 'next.config.js')) + await fs.remove(path.join(appDir, 'jsconfig.json')) + await fs.rename( + path.join(appDir, 'next.config.swc'), + path.join(appDir, 'next.config.js') + ) + await fs.rename( + path.join(appDir, 'jsconfig.swc'), + path.join(appDir, 'jsconfig.json') + ) + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + env: { NEXT_TELEMETRY_DEBUG: 1 }, + }) + await fs.remove(path.join(appDir, 'next.config.js')) + await fs.remove(path.join(appDir, 'jsconfig.json')) + + const regex = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/g + regex.exec(stderr).pop() // optimizeCss + regex.exec(stderr).pop() // build-lint + regex.exec(stderr).pop() // optimizeFonts + const swcLoader = regex.exec(stderr).pop() + expect(swcLoader).toContain(`"featureName": "swcLoader"`) + expect(swcLoader).toContain(`"invocationCount": 1`) + const swcMinify = regex.exec(stderr).pop() + expect(swcMinify).toContain(`"featureName": "swcMinify"`) + expect(swcMinify).toContain(`"invocationCount": 1`) + const swcRelay = regex.exec(stderr).pop() + expect(swcRelay).toContain(`"featureName": "swcRelay"`) + expect(swcRelay).toContain(`"invocationCount": 1`) + const swcStyledComponents = regex.exec(stderr).pop() + expect(swcStyledComponents).toContain( + `"featureName": "swcStyledComponents"` + ) + expect(swcStyledComponents).toContain(`"invocationCount": 1`) + const swcReactRemoveProperties = regex.exec(stderr).pop() + expect(swcReactRemoveProperties).toContain( + `"featureName": "swcReactRemoveProperties"` + ) + expect(swcReactRemoveProperties).toContain(`"invocationCount": 1`) + const swcExperimentalDecorators = regex.exec(stderr).pop() + expect(swcExperimentalDecorators).toContain( + `"featureName": "swcExperimentalDecorators"` + ) + expect(swcExperimentalDecorators).toContain(`"invocationCount": 1`) + const swcRemoveConsole = regex.exec(stderr).pop() + expect(swcRemoveConsole).toContain(`"featureName": "swcRemoveConsole"`) + expect(swcRemoveConsole).toContain(`"invocationCount": 1`) + const swcImportSource = regex.exec(stderr).pop() + expect(swcImportSource).toContain(`"featureName": "swcImportSource"`) + expect(swcImportSource).toContain(`"invocationCount": 0`) + }) + it('emits telemetry for usage of `optimizeCss`', async () => { await fs.rename( path.join(appDir, 'next.config.optimize-css'),