From 4e0b40145d289736a7c67bbf26d8c8e10fa6fe99 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 23 Aug 2020 07:35:30 -0500 Subject: [PATCH 01/11] Correct initial fallback route param values (#16485) This fixes invalid initial route params in development mode and serverless production mode. It also adds tests to ensure these values are correct in development, production, and serverless mode. x-ref: https://github.com/vercel/next.js/pull/16084 Fixes: https://github.com/vercel/next.js/issues/16481 Fixes: https://github.com/vercel/next.js/issues/16482 --- .../webpack/loaders/next-serverless-loader.ts | 4 +- packages/next/next-server/server/render.tsx | 2 +- .../fallback-route-params/pages/[slug].js | 34 +++++++ .../fallback-route-params/test/index.test.js | 89 +++++++++++++++++++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 test/integration/fallback-route-params/pages/[slug].js create mode 100644 test/integration/fallback-route-params/test/index.test.js diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 2b0ffc7301e00..8c18f4ec7f488 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -399,9 +399,7 @@ const nextServerlessLoader: loader.Loader = function () { pageIsDynamicRoute ? ` const params = ( - fromExport && - !getStaticProps && - !getServerSideProps + fromExport ) ? {} : normalizeDynamicRouteParams( trustQuery diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index c473467d6b875..661d5a9d03acb 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -402,7 +402,7 @@ export async function renderToHTML( ) } - if (isAutoExport) { + if (isAutoExport || isFallback) { // remove query values except ones that will be set during export query = { ...(query.amp diff --git a/test/integration/fallback-route-params/pages/[slug].js b/test/integration/fallback-route-params/pages/[slug].js new file mode 100644 index 0000000000000..8259ea56dc20c --- /dev/null +++ b/test/integration/fallback-route-params/pages/[slug].js @@ -0,0 +1,34 @@ +import { useRouter } from 'next/router' + +export const getStaticProps = () => { + return { + props: { + world: 'world', + }, + } +} + +export const getStaticPaths = () => { + return { + paths: [], + fallback: true, + } +} + +export default function Page({ world }) { + const router = useRouter() + + if (typeof window !== 'undefined' && !window.setInitialSlug) { + window.setInitialSlug = true + window.initialSlug = router.query.slug + } + + if (router.isFallback) return 'Loading...' + + return ( + <> +

hello {world}

+

{JSON.stringify(router.query)}

+ + ) +} diff --git a/test/integration/fallback-route-params/test/index.test.js b/test/integration/fallback-route-params/test/index.test.js new file mode 100644 index 0000000000000..877f0b0c1f120 --- /dev/null +++ b/test/integration/fallback-route-params/test/index.test.js @@ -0,0 +1,89 @@ +/* eslint-env jest */ + +import fs from 'fs-extra' +import { join } from 'path' +import cheerio from 'cheerio' +import webdriver from 'next-webdriver' +import { + killApp, + findPort, + nextBuild, + nextStart, + renderViaHTTP, + launchApp, + File, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 2) + +const appDir = join(__dirname, '../') +const nextConfig = new File(join(appDir, 'next.config.js')) +let appPort +let app + +const runTests = () => { + it('should have correct fallback query (skeleton)', async () => { + const html = await renderViaHTTP(appPort, '/first') + const $ = cheerio.load(html) + const { query } = JSON.parse($('#__NEXT_DATA__').text()) + expect(query).toEqual({}) + }) + + it('should have correct fallback query (hydration)', async () => { + const browser = await webdriver(appPort, '/second') + const initialSlug = await browser.eval(() => window.initialSlug) + expect(initialSlug).toBe(null) + + await browser.waitForElementByCss('#query') + + const hydratedQuery = JSON.parse( + await browser.elementByCss('#query').text() + ) + expect(hydratedQuery).toEqual({ slug: 'second' }) + }) +} + +describe('Fallback Dynamic Route Params', () => { + describe('dev mode', () => { + beforeAll(async () => { + await fs.remove(join(appDir, '.next')) + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests() + }) + + describe('production mode', () => { + beforeAll(async () => { + await fs.remove(join(appDir, '.next')) + await nextBuild(appDir, []) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests() + }) + + describe('serverless mode', () => { + beforeAll(async () => { + nextConfig.write(` + module.exports = { + target: 'experimental-serverless-trace' + } + `) + await fs.remove(join(appDir, '.next')) + await nextBuild(appDir, [], { stdout: true }) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + nextConfig.delete() + }) + + runTests() + }) +}) From ec281df70b4d314d41b7d3bc7d0a8ddd42444d7a Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Sun, 23 Aug 2020 13:33:34 -0500 Subject: [PATCH 02/11] Remove experimental example (#16497) @Timer This example seems to not be needed anymore --- examples/z-experimental-refresh/.gitignore | 34 ----------- examples/z-experimental-refresh/README.md | 14 ----- .../components/ClickCount.js | 15 ----- .../components/ClickCount.module.css | 32 ---------- examples/z-experimental-refresh/global.css | 41 ------------- examples/z-experimental-refresh/package.json | 15 ----- examples/z-experimental-refresh/pages/_app.js | 5 -- .../z-experimental-refresh/pages/index.js | 60 ------------------- 8 files changed, 216 deletions(-) delete mode 100644 examples/z-experimental-refresh/.gitignore delete mode 100644 examples/z-experimental-refresh/README.md delete mode 100644 examples/z-experimental-refresh/components/ClickCount.js delete mode 100644 examples/z-experimental-refresh/components/ClickCount.module.css delete mode 100644 examples/z-experimental-refresh/global.css delete mode 100644 examples/z-experimental-refresh/package.json delete mode 100644 examples/z-experimental-refresh/pages/_app.js delete mode 100644 examples/z-experimental-refresh/pages/index.js diff --git a/examples/z-experimental-refresh/.gitignore b/examples/z-experimental-refresh/.gitignore deleted file mode 100644 index 1437c53f70bc2..0000000000000 --- a/examples/z-experimental-refresh/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel diff --git a/examples/z-experimental-refresh/README.md b/examples/z-experimental-refresh/README.md deleted file mode 100644 index 078c5d65e90b5..0000000000000 --- a/examples/z-experimental-refresh/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# z-experimental-refresh - -This is an **experimental** demo of React Fast Refresh. -Please do not use these features in your application or project (yet). - -## Usage - -Run the following command to get started: - -```bash -yarn dev -# or -npm run dev -``` diff --git a/examples/z-experimental-refresh/components/ClickCount.js b/examples/z-experimental-refresh/components/ClickCount.js deleted file mode 100644 index 283266612179e..0000000000000 --- a/examples/z-experimental-refresh/components/ClickCount.js +++ /dev/null @@ -1,15 +0,0 @@ -import { useCallback, useState } from 'react' -import styles from './ClickCount.module.css' - -export default function ClickCount() { - const [count, setCount] = useState(0) - const increment = useCallback(() => { - setCount((v) => v + 1) - }, [setCount]) - - return ( - - ) -} diff --git a/examples/z-experimental-refresh/components/ClickCount.module.css b/examples/z-experimental-refresh/components/ClickCount.module.css deleted file mode 100644 index 3cba6f351a456..0000000000000 --- a/examples/z-experimental-refresh/components/ClickCount.module.css +++ /dev/null @@ -1,32 +0,0 @@ -button.btn { - margin: 0; - border: 1px solid #d1d1d1; - border-radius: 5px; - padding: 0.5em; - vertical-align: middle; - white-space: normal; - background: none; - line-height: 1; - font-size: 1rem; - font-family: inherit; - transition: all 0.2s ease; -} - -button.btn { - padding: 0.65em 1em; - background: #0076ff; - color: #fff; - border: none; - cursor: pointer; - transition: all 0.2s ease; -} -button.btn:focus { - outline: 0; - border-color: #0076ff; -} -button.btn:hover { - background: rgba(0, 118, 255, 0.8); -} -button.btn:focus { - box-shadow: 0 0 0 2px rgba(0, 118, 255, 0.5); -} diff --git a/examples/z-experimental-refresh/global.css b/examples/z-experimental-refresh/global.css deleted file mode 100644 index e28fd3dfe7a37..0000000000000 --- a/examples/z-experimental-refresh/global.css +++ /dev/null @@ -1,41 +0,0 @@ -body { - font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', - 'Arial', sans-serif; - padding: 20px 20px 60px; - max-width: 680px; - margin: 0 auto; - font-size: 16px; - line-height: 1.65; - word-break: break-word; - font-kerning: auto; - font-variant: normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - hyphens: auto; -} - -h2, -h3, -h4 { - margin-top: 1.5em; -} - -code, -pre { - font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, - Bitstream Vera Sans Mono, Courier New, monospace, serif; - font-size: 0.92em; - color: #d400ff; -} - -code:before, -code:after { - content: '`'; -} - -hr { - border: none; - border-bottom: 1px solid #efefef; - margin: 5em auto; -} diff --git a/examples/z-experimental-refresh/package.json b/examples/z-experimental-refresh/package.json deleted file mode 100644 index 97e0becd5a630..0000000000000 --- a/examples/z-experimental-refresh/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "z-experimental-refresh", - "version": "1.0.0", - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "next": "canary", - "react": "^16.9.0", - "react-dom": "^16.9.0" - }, - "license": "MIT" -} diff --git a/examples/z-experimental-refresh/pages/_app.js b/examples/z-experimental-refresh/pages/_app.js deleted file mode 100644 index d305f97bab7f1..0000000000000 --- a/examples/z-experimental-refresh/pages/_app.js +++ /dev/null @@ -1,5 +0,0 @@ -import '../global.css' - -export default function MyApp({ Component, pageProps }) { - return -} diff --git a/examples/z-experimental-refresh/pages/index.js b/examples/z-experimental-refresh/pages/index.js deleted file mode 100644 index 6346c5e117737..0000000000000 --- a/examples/z-experimental-refresh/pages/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' -import ClickCount from '../components/ClickCount' -import styles from '../components/ClickCount.module.css' - -function a() { - console.log( - // hello - document.body() - ) -} - -function foo() { - a() -} - -function Home() { - const [count, setCount] = useState(0) - const increment = useCallback(() => { - setCount((v) => v + 1) - }, [setCount]) - - useEffect(() => { - const r = setInterval(() => { - increment() - }, 250) - return () => { - clearInterval(r) - } - }, [increment]) - - return ( -
-

Home

-
-

Auto Incrementing Value

-

Current value: {count}

-
-
-
-

Component with State

- -
-
-
- -
-
- ) -} - -export default Home From f2ba97d866863b46b3dcb68c9652a78d9fa0c4a9 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 24 Aug 2020 03:37:48 +0200 Subject: [PATCH 03/11] Add webpack 5 cache invalidation (#16494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handles: - Next.js version - next.config.js `env` key - `NEXT_PUBLIC_` prefixed environment variables - next.config.js keys that affect performance Ideally we don't invalidate the whole cache when `NEXT_PUBLIC_` / `env` key variables change, but this is just to initially make the caching reliable, this behavior is similar to the current webpack 4 behavior, so it can only be improved 👍 --- packages/next/build/webpack-config.ts | 60 +++++++++++++++++++++- packages/next/next-server/server/config.ts | 6 ++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index acfece465c8b5..18df99e10f9af 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1121,10 +1121,68 @@ export default async function getBaseWebpackConfig( // Enable webpack 5 caching if (config.experimental.unstable_webpack5cache) { - webpackConfig.cache = { + const nextPublicVariables = Object.keys(process.env).reduce( + (prev: string, key: string) => { + if (key.startsWith('NEXT_PUBLIC_')) { + return `${prev}|${key}=${process.env[key]}` + } + return prev + }, + '' + ) + const nextEnvVariables = Object.keys(config.env).reduce( + (prev: string, key: string) => { + return `${prev}|${key}=${config.env[key]}` + }, + '' + ) + + const configVars = JSON.stringify({ + crossOrigin: config.crossOrigin, + pageExtensions: config.pageExtensions, + trailingSlash: config.trailingSlash, + modern: config.experimental.modern, + buildActivity: config.devIndicators.buildActivity, + autoPrerender: config.devIndicators.autoPrerender, + plugins: config.experimental.plugins, + reactStrictMode: config.reactStrictMode, + reactMode: config.experimental.reactMode, + optimizeFonts: config.experimental.optimizeFonts, + optimizeImages: config.experimental.optimizeImages, + scrollRestoration: config.experimental.scrollRestoration, + basePath: config.basePath, + pageEnv: config.experimental.pageEnv, + excludeDefaultMomentLocales: config.future.excludeDefaultMomentLocales, + assetPrefix: config.assetPrefix, + target, + reactProductionProfiling, + }) + + const cache: any = { type: 'filesystem', + // Includes: + // - Next.js version + // - NEXT_PUBLIC_ variable values (they affect caching) TODO: make this module usage only + // - next.config.js `env` key + // - next.config.js keys that affect compilation + version: `${process.env.__NEXT_VERSION}|${nextPublicVariables}|${nextEnvVariables}|${configVars}`, + buildDependencies: { + config: [], + }, cacheDirectory: path.join(dir, '.next', 'cache', 'webpack'), } + + // Adds `next.config.js` as a buildDependency when custom webpack config is provided + if (config.webpack && config.configFile) { + cache.buildDependencies = { + config: [config.configFile], + } + } + + webpackConfig.cache = cache + + // @ts-ignore TODO: remove ignore when webpack 5 is stable + webpackConfig.optimization.realContentHash = false } } diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index c9eefdca419a6..ac652280fb9f2 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -277,7 +277,11 @@ export default function loadConfig( ) } - return assignDefaults({ configOrigin: CONFIG_FILE, ...userConfig }) + return assignDefaults({ + configOrigin: CONFIG_FILE, + configFile: path, + ...userConfig, + }) } else { const configBaseName = basename(CONFIG_FILE, extname(CONFIG_FILE)) const nonJsPath = findUp.sync( From c97e53efe1a9741bb8762659aaac339961318b73 Mon Sep 17 00:00:00 2001 From: WeichienHung Date: Mon, 24 Aug 2020 10:00:43 +0800 Subject: [PATCH 04/11] basePath should also append in urlPrefix (#16376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/vercel/next.js/issues/16197 See my screenshot. Testing url is changed to http://localhost:3000/test and can find the source map. 圖片 --- examples/with-sentry/next.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/with-sentry/next.config.js b/examples/with-sentry/next.config.js index 272fef9c62884..7d5c90f380c5a 100644 --- a/examples/with-sentry/next.config.js +++ b/examples/with-sentry/next.config.js @@ -21,6 +21,7 @@ const COMMIT_SHA = VERCEL_BITBUCKET_COMMIT_SHA process.env.SENTRY_DSN = SENTRY_DSN +const basePath = '' module.exports = withSourceMaps({ serverRuntimeConfig: { @@ -63,12 +64,12 @@ module.exports = withSourceMaps({ include: '.next', ignore: ['node_modules'], stripPrefix: ['webpack://_N_E/'], - urlPrefix: '~/_next', + urlPrefix: `~${basePath}/_next`, release: COMMIT_SHA, }) ) } - return config }, + basePath, }) From 333a9ea8ab1ad45d77eaf825059b51d23dc8b7af Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Sun, 23 Aug 2020 21:23:12 -0500 Subject: [PATCH 05/11] Documentation updates (#16503) Fixes https://github.com/vercel/next.js/issues/16502 Check the issue for more details. --- docs/advanced-features/custom-error-page.md | 2 ++ docs/advanced-features/static-html-export.md | 13 +++++----- docs/api-reference/cli.md | 2 +- .../data-fetching/getInitialProps.md | 12 ++------- .../next.config.js/environment-variables.md | 2 +- docs/api-routes/dynamic-api-routes.md | 5 ++-- docs/api-routes/introduction.md | 7 +++--- .../supported-browsers-features.md | 2 +- docs/deployment.md | 4 +-- docs/faq.md | 4 +-- examples/data-fetch/README.md | 25 ++----------------- examples/data-fetch/package.json | 7 +++--- examples/data-fetch/pages/index.js | 1 - examples/data-fetch/pages/preact-stars.js | 1 - 14 files changed, 28 insertions(+), 59 deletions(-) diff --git a/docs/advanced-features/custom-error-page.md b/docs/advanced-features/custom-error-page.md index 9b2971d0765e2..243a76ce5164b 100644 --- a/docs/advanced-features/custom-error-page.md +++ b/docs/advanced-features/custom-error-page.md @@ -2,6 +2,8 @@ description: Override and extend the built-in Error page to handle custom errors. --- +# Custom Error Page + ## 404 Page A 404 page may be accessed very often. Server-rendering an error page for every visit increases the load of the Next.js server. This can result in increased costs and slow experiences. diff --git a/docs/advanced-features/static-html-export.md b/docs/advanced-features/static-html-export.md index becfd255d69ed..969a2d20a7c25 100644 --- a/docs/advanced-features/static-html-export.md +++ b/docs/advanced-features/static-html-export.md @@ -21,7 +21,7 @@ The exported app supports almost every feature of Next.js, including dynamic rou > > If you're looking to make a hybrid site where only _some_ pages are prerendered to static HTML, Next.js already does that automatically for you! Read up on [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) for details. > -> `next export` also causes features like Incremental Static Generation and Regeneration to be disabled, as they require `next start` or a serverless deployment to function. +> `next export` also causes features like [Incremental Static Generation](/docs/basic-features/data-fetching.md#fallback-true) and [Regeneration](/docs/basic-features/data-fetching.md#incremental-static-regeneration) to be disabled, as they require [`next start`](/docs/api-reference/cli.md#production) or a serverless deployment to function. ## How to use it @@ -53,7 +53,9 @@ For more advanced scenarios, you can define a parameter called [`exportPathMap`] ## Deployment -You can read about deploying your Next.js application in the [deployment section](/docs/deployment.md). +By default, `next export` will generate an `out` directory, which can be served by any static hosting service or CDN. + +> We strongly recommend using [Vercel](https://vercel.com/) even if your Next.js app is fully static. [Vercel](https://vercel.com/) is optimized to make static Next.js apps blazingly fast. `next export` works with Zero Config deployments on Vercel. ## Caveats @@ -62,11 +64,10 @@ You can read about deploying your Next.js application in the [deployment section - `getInitialProps` cannot be used alongside `getStaticProps` or `getStaticPaths` on any given page. If you have dynamic routes, instead of using `getStaticPaths` you'll need to configure the [`exportPathMap`](/docs/api-reference/next.config.js/exportPathMap.md) parameter in your [`next.config.js`](/docs/api-reference/next.config.js/introduction.md) file to let the exporter know which HTML files it should output. - When `getInitialProps` is called during export, the `req` and `res` fields of its [`context`](/docs/api-reference/data-fetching/getInitialProps.md#context-object) parameter will be empty objects, since during export there is no server running. - `getInitialProps` **will be called on every client-side navigation**, if you'd like to only fetch data at build-time, switch to `getStaticProps`. - - `getInitialProps` cannot use Node.js-specific libraries or the file system like `getStaticProps` can. `getInitialProps` must fetch from an API. + - `getInitialProps` should fetch from an API and cannot use Node.js-specific libraries or the file system like `getStaticProps` can. - For static export, the `getStaticProps` API is always preferred over `getInitialProps`: it's recommended you convert your pages to use the `getStaticProps` if possible. + It's recommended to use and migrate towards `getStaticProps` over `getInitialProps` whenever possible. -- The `fallback: true` mode of `getStaticPaths` is not supported when using `next export`. -- You won't be able to render HTML dynamically when static exporting, as the HTML files are pre-build. Your application can be a hybrid of [Static Generation](/docs/basic-features/pages.md#static-generation) and [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering) when you don't use `next export`. You can learn more about it in the [pages section](/docs/basic-features/pages.md). +- The [`fallback: true`](/docs/basic-features/data-fetching.md#fallback-true) mode of `getStaticPaths` is not supported when using `next export`. - [API Routes](/docs/api-routes/introduction.md) are not supported by this method because they can't be prerendered to HTML. - [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) cannot be used within pages because the method requires a server. Consider using [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) instead. diff --git a/docs/api-reference/cli.md b/docs/api-reference/cli.md index 5b63772e85a2d..bd9959252349e 100644 --- a/docs/api-reference/cli.md +++ b/docs/api-reference/cli.md @@ -48,7 +48,7 @@ NODE_OPTIONS='--inspect' next The first load is colored green, yellow, or red. Aim for green for performant applications. -You can enable production profiling for React with the `--profile` flag in `next build`. This requires Next.js 9.5: +You can enable production profiling for React with the `--profile` flag in `next build`. This requires [Next.js 9.5](https://nextjs.org/blog/next-9-5): ```bash next build --profile diff --git a/docs/api-reference/data-fetching/getInitialProps.md b/docs/api-reference/data-fetching/getInitialProps.md index 6e428820ecd96..cc0d690119a27 100644 --- a/docs/api-reference/data-fetching/getInitialProps.md +++ b/docs/api-reference/data-fetching/getInitialProps.md @@ -4,19 +4,11 @@ description: Enable Server-Side Rendering in a page and do initial data populati # getInitialProps -> **Recommended: [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering)** +> **Recommended: [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation) or [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering)**. > > If you're using Next.js 9.3 or newer, we recommend that you use `getStaticProps` or `getServerSideProps` instead of `getInitialProps`. > -> These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. -> Learn more on the documentation for [Pages](/docs/basic-features/pages.md) and [Data fetching](/docs/basic-features/data-fetching.md): - -
- Examples - -
+> These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. Learn more on the documentation for [Pages](/docs/basic-features/pages.md) and [Data fetching](/docs/basic-features/data-fetching.md). `getInitialProps` enables [server-side rendering](/docs/basic-features/pages.md#server-side-rendering) in a page and allows you to do **initial data population**, it means sending the [page](/docs/basic-features/pages.md) with the data already populated from the server. This is especially useful for [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). diff --git a/docs/api-reference/next.config.js/environment-variables.md b/docs/api-reference/next.config.js/environment-variables.md index 9a3536fa0c9b3..a0766ef442dbc 100644 --- a/docs/api-reference/next.config.js/environment-variables.md +++ b/docs/api-reference/next.config.js/environment-variables.md @@ -4,7 +4,7 @@ description: Learn to add and access environment variables in your Next.js appli # Environment Variables -> Since the release of Next.js 9.4 we now have a more intuitive and ergonomic experience for [adding environment variables](/docs/basic-features/environment-variables.md). Give it a try! +> Since the release of [Next.js 9.4](https://nextjs.org/blog/next-9-4) we now have a more intuitive and ergonomic experience for [adding environment variables](/docs/basic-features/environment-variables.md). Give it a try!
Examples diff --git a/docs/api-routes/dynamic-api-routes.md b/docs/api-routes/dynamic-api-routes.md index 8c49755693a6b..272f68025f7e2 100644 --- a/docs/api-routes/dynamic-api-routes.md +++ b/docs/api-routes/dynamic-api-routes.md @@ -31,7 +31,7 @@ Now, a request to `/api/post/abc` will respond with the text: `Post: abc`. A very common RESTful pattern is to set up routes like this: -- `GET api/posts/` - gets a list of posts, probably paginated +- `GET api/posts` - gets a list of posts, probably paginated - `GET api/posts/12345` - gets post id 12345 We can model this in two ways: @@ -40,11 +40,10 @@ We can model this in two ways: - `/api/posts.js` - `/api/posts/[postId].js` - Option 2: - - `/api/posts/index.js` - `/api/posts/[postId].js` -Both are equivalent. A third option of only using `/api/posts/[postId].js` is not valid because Dynamic Routes (including Catch-all routes - see below) do not have an `undefined` state and `GET api/posts/` will not match `/api/posts/[postId].js` under any circumstances. +Both are equivalent. A third option of only using `/api/posts/[postId].js` is not valid because Dynamic Routes (including Catch-all routes - see below) do not have an `undefined` state and `GET api/posts` will not match `/api/posts/[postId].js` under any circumstances. ### Catch all API routes diff --git a/docs/api-routes/introduction.md b/docs/api-routes/introduction.md index eac86c6ab3db0..9d71dc4aca031 100644 --- a/docs/api-routes/introduction.md +++ b/docs/api-routes/introduction.md @@ -17,7 +17,7 @@ description: Next.js supports API Routes, which allow you to build your API with API routes provide a straightforward solution to build your **API** with Next.js. -Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a `page`. +Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a `page`. They are server-side only bundles and won't increase your client-side bundle size. For example, the following API route `pages/api/user.js` handles a `json` response: @@ -48,9 +48,10 @@ export default (req, res) => { To fetch API endpoints, take a look into any of the examples at the start of this section. -> API Routes [do not specify CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), meaning they are **same-origin only** by default. You can customize such behavior by wrapping the request handler with the [cors middleware](/docs/api-routes/api-middlewares.md#connectexpress-middleware-support). +## Caveats -> API Routes do not increase your client-side bundle size. They are server-side only bundles. +- API Routes [do not specify CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), meaning they are **same-origin only** by default. You can customize such behavior by wrapping the request handler with the [cors middleware](/docs/api-routes/api-middlewares.md#connectexpress-middleware-support). +- API Routes can't be used with [`next export`](/docs/advanced-features/static-html-export.md) ## Related diff --git a/docs/basic-features/supported-browsers-features.md b/docs/basic-features/supported-browsers-features.md index e073c9433608f..9da03b356a6b1 100644 --- a/docs/basic-features/supported-browsers-features.md +++ b/docs/basic-features/supported-browsers-features.md @@ -26,7 +26,7 @@ In addition to `fetch()` on the client side, Next.js polyfills `fetch()` in the If your own code or any external npm dependencies require features not supported by your target browsers, you need to add polyfills yourself. -In this case, you should add a top-level import for the **specific polyfill** you need in your [Custom ``](docs/advanced-features/custom-app.md) or the individual component. +In this case, you should add a top-level import for the **specific polyfill** you need in your [Custom ``](/docs/advanced-features/custom-app.md) or the individual component. ## JavaScript Language Features diff --git a/docs/deployment.md b/docs/deployment.md index eddcc9acd5949..fc1a806e9ccf8 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -72,6 +72,4 @@ Make sure your `package.json` has the `"build"` and `"start"` scripts: ### Static HTML Export -If you’d like to do a static HTML export of your Next.js app, follow the directions on [our documentation](/docs/advanced-features/static-html-export.md). By default, `next export` will generate an `out` directory, which can be served by any static hosting service or CDN. - -> We strongly recommend using [Vercel](https://vercel.com/) even if your Next.js app is fully static. [Vercel](https://vercel.com/) is optimized to make static Next.js apps blazingly fast. `next export` works with Zero Config deployments on Vercel. +If you’d like to do a static HTML export of your Next.js app, follow the directions on [our documentation](/docs/advanced-features/static-html-export.md). diff --git a/docs/faq.md b/docs/faq.md index d13c94b2ff984..8593eed265d7f 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -74,6 +74,6 @@ description: Get to know more about Next.js with the frequently asked questions.
- Can I make a Next.js Progressive Web App? -

Yes! Here's an example.

+ Can I make a Next.js Progressive Web App (PWA)? +

Yes! Check out our PWA Example to see how it works.

diff --git a/examples/data-fetch/README.md b/examples/data-fetch/README.md index d7146d911d652..6dc5c58e7ed8c 100644 --- a/examples/data-fetch/README.md +++ b/examples/data-fetch/README.md @@ -1,9 +1,9 @@ # Data fetch example Next.js was conceived to make it easy to create universal apps. That's why fetching data -on the server and the client when necessary is so easy with Next. +on the server and the client when necessary is so easy with Next.js. -Using `getStaticProps` fetches data at build time from a page, Next.js will pre-render this page at build time. +By using `getStaticProps` Next.js will fetch data at build time from a page, and pre-render the page to static assets. ## Deploy your own @@ -13,8 +13,6 @@ Deploy the example using [Vercel](https://vercel.com): ## How to use -### Using `create-next-app` - Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: ```bash @@ -23,23 +21,4 @@ npx create-next-app --example data-fetch data-fetch-app yarn create next-app --example data-fetch data-fetch-app ``` -### Download manually - -Download the example: - -```bash -curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/data-fetch -cd data-fetch -``` - -Install it and run: - -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` - Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/data-fetch/package.json b/examples/data-fetch/package.json index 55d94b79a3ea6..17aacdb9fe651 100644 --- a/examples/data-fetch/package.json +++ b/examples/data-fetch/package.json @@ -8,9 +8,8 @@ }, "dependencies": { "next": "latest", - "node-fetch": "^2.6.0", - "react": "^16.8.4", - "react-dom": "^16.8.4" + "react": "^16.13.1", + "react-dom": "^16.13.1" }, - "license": "ISC" + "license": "MIT" } diff --git a/examples/data-fetch/pages/index.js b/examples/data-fetch/pages/index.js index 90dc176afd3cd..d668c07e8fd6c 100644 --- a/examples/data-fetch/pages/index.js +++ b/examples/data-fetch/pages/index.js @@ -1,5 +1,4 @@ import Link from 'next/link' -import fetch from 'node-fetch' function Index({ stars }) { return ( diff --git a/examples/data-fetch/pages/preact-stars.js b/examples/data-fetch/pages/preact-stars.js index a8435ed70c3ff..0db33607dde37 100644 --- a/examples/data-fetch/pages/preact-stars.js +++ b/examples/data-fetch/pages/preact-stars.js @@ -1,5 +1,4 @@ import Link from 'next/link' -import fetch from 'node-fetch' function PreactStars({ stars }) { return ( From a7550bf1d7b2a2c4075b4ba958a03e21612e93ad Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 23 Aug 2020 21:42:51 -0500 Subject: [PATCH 06/11] Add error when document component isn't rendered (#16459) If a custom `_document` is added but not all of the expected document components are rendered it can cause unintended errors as noticed in https://github.com/vercel/next.js/issues/10219 so this adds detecting when one of the expected document components isn't rendered and shows an error. Closes: https://github.com/vercel/next.js/issues/10219 --- errors/missing-document-component.md | 13 ++ packages/next/next-server/lib/utils.ts | 6 + packages/next/next-server/server/render.tsx | 30 ++++ packages/next/pages/_document.tsx | 19 ++- .../pages/index.js | 3 + .../test/index.test.js | 159 ++++++++++++++++++ 6 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 errors/missing-document-component.md create mode 100644 test/integration/missing-document-component-error/pages/index.js create mode 100644 test/integration/missing-document-component-error/test/index.test.js diff --git a/errors/missing-document-component.md b/errors/missing-document-component.md new file mode 100644 index 0000000000000..89fcdb1081eb0 --- /dev/null +++ b/errors/missing-document-component.md @@ -0,0 +1,13 @@ +# Missing Document Components + +#### Why This Error Occurred + +In your custom `pages/_document` an expected sub-component was not rendered. + +#### Possible Ways to Fix It + +Make sure to import and render all of the expected `Document` components. + +### Useful Links + +- [Custom Document Docs](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/packages/next/next-server/lib/utils.ts b/packages/next/next-server/lib/utils.ts index ca0f1e0d28312..93b53fe693c1c 100644 --- a/packages/next/next-server/lib/utils.ts +++ b/packages/next/next-server/lib/utils.ts @@ -166,6 +166,12 @@ export type DocumentInitialProps = RenderPageResult & { export type DocumentProps = DocumentInitialProps & { __NEXT_DATA__: NEXT_DATA dangerousAsPath: string + docComponentsRendered: { + Html?: boolean + Main?: boolean + Head?: boolean + NextScript?: boolean + } buildManifest: BuildManifest ampPath: string inAmpMode: boolean diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 661d5a9d03acb..5172d948a07a2 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -41,6 +41,7 @@ import { loadGetInitialProps, NextComponentType, RenderPage, + DocumentProps, } from '../lib/utils' import { tryGetPreviewData, __ApiPreviewProps } from './api-utils' import { denormalizePagePath } from './denormalize-page-path' @@ -158,6 +159,7 @@ function renderDocument( Document: DocumentType, { buildManifest, + docComponentsRendered, props, docProps, pathname, @@ -188,6 +190,7 @@ function renderDocument( devOnlyCacheBusterQueryString, }: RenderOpts & { props: any + docComponentsRendered: DocumentProps['docComponentsRendered'] docProps: DocumentInitialProps pathname: string query: ParsedUrlQuery @@ -233,6 +236,7 @@ function renderDocument( appGip, // whether the _app has getInitialProps }, buildManifest, + docComponentsRendered, dangerousAsPath, canonicalBase, ampPath, @@ -759,8 +763,11 @@ export async function renderToHTML( renderOpts.inAmpMode = inAmpMode renderOpts.hybridAmp = hybridAmp + const docComponentsRendered: DocumentProps['docComponentsRendered'] = {} + let html = renderDocument(Document, { ...renderOpts, + docComponentsRendered, buildManifest: filteredBuildManifest, // Only enabled in production as development mode has features relying on HMR (style injection for example) unstable_runtimeJS: @@ -787,6 +794,29 @@ export async function renderToHTML( devOnlyCacheBusterQueryString, }) + if (process.env.NODE_ENV !== 'production') { + const nonRenderedComponents = [] + const expectedDocComponents = ['Main', 'Head', 'NextScript', 'Html'] + + for (const comp of expectedDocComponents) { + if (!(docComponentsRendered as any)[comp]) { + nonRenderedComponents.push(comp) + } + } + const plural = nonRenderedComponents.length !== 1 ? 's' : '' + + if (nonRenderedComponents.length) { + console.warn( + `Expected Document Component${plural} ${nonRenderedComponents.join( + ', ' + )} ${ + plural ? 'were' : 'was' + } not rendered. Make sure you render them in your custom \`_document\`\n` + + `See more info here https://err.sh/next.js/missing-document-component` + ) + } + } + if (inAmpMode && html) { // inject HTML to AMP_RENDER_TARGET to allow rendering // directly to body in AMP mode diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 96b16f7385b12..6a3102f839bb1 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -123,7 +123,12 @@ export function Html( HTMLHtmlElement > ) { - const { inAmpMode } = useContext(DocumentComponentContext) + const { inAmpMode, docComponentsRendered } = useContext( + DocumentComponentContext + ) + + docComponentsRendered.Html = true + return ( (only in development) @@ -501,7 +508,12 @@ export class Head extends Component< } export function Main() { - const { inAmpMode, html } = useContext(DocumentComponentContext) + const { inAmpMode, html, docComponentsRendered } = useContext( + DocumentComponentContext + ) + + docComponentsRendered.Main = true + if (inAmpMode) return <>{AMP_RENDER_TARGET} return
} @@ -642,10 +654,13 @@ export class NextScript extends Component { inAmpMode, buildManifest, unstable_runtimeJS, + docComponentsRendered, devOnlyCacheBusterQueryString, } = this.context const disableRuntimeJS = unstable_runtimeJS === false + docComponentsRendered.NextScript = true + if (inAmpMode) { if (process.env.NODE_ENV === 'production') { return null diff --git a/test/integration/missing-document-component-error/pages/index.js b/test/integration/missing-document-component-error/pages/index.js new file mode 100644 index 0000000000000..71d6a609df67c --- /dev/null +++ b/test/integration/missing-document-component-error/pages/index.js @@ -0,0 +1,3 @@ +export default function Index() { + return

Index page

+} diff --git a/test/integration/missing-document-component-error/test/index.test.js b/test/integration/missing-document-component-error/test/index.test.js new file mode 100644 index 0000000000000..5502a0f628d6d --- /dev/null +++ b/test/integration/missing-document-component-error/test/index.test.js @@ -0,0 +1,159 @@ +/* eslint-env jest */ + +import fs from 'fs-extra' +import { join } from 'path' +import { + findPort, + killApp, + launchApp, + check, + renderViaHTTP, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 2) + +const appDir = join(__dirname, '..') +const docPath = join(appDir, 'pages/_document.js') +let appPort +let app + +const checkMissing = async (missing = [], docContent) => { + await fs.writeFile(docPath, docContent) + let stderr = '' + + appPort = await findPort() + app = await launchApp(appDir, appPort, { + onStderr(msg) { + stderr += msg || '' + }, + }) + + await renderViaHTTP(appPort, '/') + + await check(() => stderr, new RegExp(`missing-document-component`)) + await check(() => stderr, new RegExp(`${missing.join(', ')}`)) + + await killApp(app) + await fs.remove(docPath) +} + +describe('Missing _document components error', () => { + it('should detect missing Html component', async () => { + await checkMissing( + ['Html'], + ` + import Document, { Head, Main, NextScript } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + + + +
+ + + + ) + } + } + + export default MyDocument + ` + ) + }) + + it('should detect missing Head component', async () => { + await checkMissing( + ['Head'], + ` + import Document, { Html, Main, NextScript } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + + +
+ + + + ) + } + } + + export default MyDocument + ` + ) + }) + + it('should detect missing Main component', async () => { + await checkMissing( + ['Main'], + ` + import Document, { Html, Head, NextScript } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + + + + + + + ) + } + } + + export default MyDocument + ` + ) + }) + + it('should detect missing NextScript component', async () => { + await checkMissing( + ['NextScript'], + ` + import Document, { Html, Head, Main } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + + +
+ + + ) + } + } + + export default MyDocument + ` + ) + }) + + it('should detect multiple missing document components', async () => { + await checkMissing( + ['Head', 'NextScript'], + ` + import Document, { Html, Main } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + + +
+ + + ) + } + } + + export default MyDocument + ` + ) + }) +}) From 735aab6f03a9b70dd8c2cb70c8702bd86e6c9005 Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Sun, 23 Aug 2020 22:04:25 -0500 Subject: [PATCH 07/11] Update Fast Refresh warning (#16496) Fixes https://github.com/vercel/next.js/issues/16495 Terminal: ![image](https://user-images.githubusercontent.com/4278345/90984442-96421b00-e53a-11ea-9939-44b00cba87cf.png) Browser: ![image](https://user-images.githubusercontent.com/4278345/90984456-a9ed8180-e53a-11ea-86da-d9f28e9c2fdd.png) The link https://nextjs.link/codemod-ndc used for the warning links to a currently 404 docs page, that will be available in the next stable release. Tests not added as the warning has tests already. --- .../build/babel/plugins/no-anonymous-default-export.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/next/build/babel/plugins/no-anonymous-default-export.ts b/packages/next/build/babel/plugins/no-anonymous-default-export.ts index 32aa89b6815bb..1d21b759fa030 100644 --- a/packages/next/build/babel/plugins/no-anonymous-default-export.ts +++ b/packages/next/build/babel/plugins/no-anonymous-default-export.ts @@ -48,6 +48,10 @@ export default function NoAnonymousDefaultExport({ chalk.bold('After'), chalk.cyan('const Named = () =>
;'), chalk.cyan('export default Named;'), + '', + `A codemod is available to fix the most common cases: ${chalk.cyan( + 'https://nextjs.link/codemod-ndc' + )}`, ].join('\n') ) break @@ -67,6 +71,10 @@ export default function NoAnonymousDefaultExport({ '', chalk.bold('After'), chalk.cyan('export default function Named() { /* ... */ }'), + '', + `A codemod is available to fix the most common cases: ${chalk.cyan( + 'https://nextjs.link/codemod-ndc' + )}`, ].join('\n') ) } From 1a7f3e519979b186c4dbbb2bd218f74801406509 Mon Sep 17 00:00:00 2001 From: khades Date: Mon, 24 Aug 2020 08:20:11 +0300 Subject: [PATCH 08/11] Store css file dependencies info for dynamic imports and apply it at SSR (#12843) To prevent FOUC, discussed in #10557 i need to store information about css file dependencies for chunk. Right now current implementation just throws away everything but js. Can there be more than one css file in chunk? If no - code will be simplified. closes #10557 --- .../webpack/plugins/react-loadable-plugin.ts | 14 ++++--- packages/next/pages/_document.tsx | 28 ++++++++++---- .../dynamic-css/many-imports/with-css-1.js | 3 ++ .../many-imports/with-css-1.module.css | 3 ++ .../dynamic-css/many-imports/with-css-2.js | 3 ++ .../many-imports/with-css-2.module.css | 3 ++ .../dynamic-css/many-imports/with-css-3.js | 3 ++ .../many-imports/with-css-3.module.css | 3 ++ .../many-modules/with-css-2.module.css | 3 ++ .../dynamic-css/many-modules/with-css.js | 8 ++++ .../many-modules/with-css.module.css | 3 ++ .../components/dynamic-css/nested/Nested.jsx | 3 ++ .../dynamic-css/nested/with-css-2.module.css | 3 ++ .../components/dynamic-css/nested/with-css.js | 8 ++++ .../dynamic-css/nested/with-css.module.css | 3 ++ .../components/dynamic-css/no-css.js | 1 + .../shared-css-module/with-css-2.js | 8 ++++ .../shared-css-module/with-css-2.module.css | 3 ++ .../with-css-shared.module.css | 3 ++ .../dynamic-css/shared-css-module/with-css.js | 8 ++++ .../shared-css-module/with-css.module.css | 3 ++ .../components/dynamic-css/with-css.js | 3 ++ .../dynamic-css/with-css.module.css | 3 ++ .../production/pages/dynamic/css.js | 5 +++ .../pages/dynamic/many-css-modules.js | 7 ++++ .../pages/dynamic/many-dynamic-css.js | 21 +++++++++++ .../production/pages/dynamic/nested-css.js | 7 ++++ .../production/pages/dynamic/no-css.js | 5 +++ .../pages/dynamic/shared-css-module.js | 17 +++++++++ test/integration/production/test/dynamic.js | 37 +++++++++++++++++++ 30 files changed, 209 insertions(+), 13 deletions(-) create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-1.js create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-1.module.css create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-2.js create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-2.module.css create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-3.js create mode 100644 test/integration/production/components/dynamic-css/many-imports/with-css-3.module.css create mode 100644 test/integration/production/components/dynamic-css/many-modules/with-css-2.module.css create mode 100644 test/integration/production/components/dynamic-css/many-modules/with-css.js create mode 100644 test/integration/production/components/dynamic-css/many-modules/with-css.module.css create mode 100644 test/integration/production/components/dynamic-css/nested/Nested.jsx create mode 100644 test/integration/production/components/dynamic-css/nested/with-css-2.module.css create mode 100644 test/integration/production/components/dynamic-css/nested/with-css.js create mode 100644 test/integration/production/components/dynamic-css/nested/with-css.module.css create mode 100644 test/integration/production/components/dynamic-css/no-css.js create mode 100644 test/integration/production/components/dynamic-css/shared-css-module/with-css-2.js create mode 100644 test/integration/production/components/dynamic-css/shared-css-module/with-css-2.module.css create mode 100644 test/integration/production/components/dynamic-css/shared-css-module/with-css-shared.module.css create mode 100644 test/integration/production/components/dynamic-css/shared-css-module/with-css.js create mode 100644 test/integration/production/components/dynamic-css/shared-css-module/with-css.module.css create mode 100644 test/integration/production/components/dynamic-css/with-css.js create mode 100644 test/integration/production/components/dynamic-css/with-css.module.css create mode 100644 test/integration/production/pages/dynamic/css.js create mode 100644 test/integration/production/pages/dynamic/many-css-modules.js create mode 100644 test/integration/production/pages/dynamic/many-dynamic-css.js create mode 100644 test/integration/production/pages/dynamic/nested-css.js create mode 100644 test/integration/production/pages/dynamic/no-css.js create mode 100644 test/integration/production/pages/dynamic/shared-css-module.js diff --git a/packages/next/build/webpack/plugins/react-loadable-plugin.ts b/packages/next/build/webpack/plugins/react-loadable-plugin.ts index bb6810c094dfe..c2f3400b4fa56 100644 --- a/packages/next/build/webpack/plugins/react-loadable-plugin.ts +++ b/packages/next/build/webpack/plugins/react-loadable-plugin.ts @@ -22,9 +22,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR // Modified to strip out unneeded results for Next's specific use case import webpack, { - Compiler, // eslint-disable-next-line @typescript-eslint/no-unused-vars compilation as CompilationType, + Compiler, } from 'webpack' import sources from 'webpack-sources' @@ -65,7 +65,12 @@ function buildManifest( chunkGroup.chunks.forEach((chunk: any) => { chunk.files.forEach((file: string) => { - if (!file.match(/\.js$/) || !file.match(/^static\/chunks\//)) { + if ( + !( + (file.endsWith('.js') || file.endsWith('.css')) && + file.match(/^static\/(chunks|css)\//) + ) + ) { return } @@ -85,10 +90,7 @@ function buildManifest( continue } - manifest[request].push({ - id, - file, - }) + manifest[request].push({ id, file }) } }) }) diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 6a3102f839bb1..8b90a4846ef44 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -25,9 +25,9 @@ export type OriginProps = { crossOrigin?: string } -function dedupe(bundles: any[]): any[] { - const files = new Set() - const kept = [] +function dedupe(bundles: T[]): T[] { + const files = new Set() + const kept: T[] = [] for (const bundle of bundles) { if (files.has(bundle.file)) continue @@ -157,10 +157,25 @@ export class Head extends Component< context!: React.ContextType getCssLinks(files: DocumentFiles): JSX.Element[] | null { - const { assetPrefix, devOnlyCacheBusterQueryString } = this.context + const { + assetPrefix, + devOnlyCacheBusterQueryString, + dynamicImports, + } = this.context const cssFiles = files.allFiles.filter((f) => f.endsWith('.css')) const sharedFiles = new Set(files.sharedFiles) + let dynamicCssFiles = dedupe( + dynamicImports.filter((f) => f.file.endsWith('.css')) + ).map((f) => f.file) + if (dynamicCssFiles.length) { + const existing = new Set(cssFiles) + dynamicCssFiles = dynamicCssFiles.filter( + (f) => !(existing.has(f) || sharedFiles.has(f)) + ) + cssFiles.push(...dynamicCssFiles) + } + const cssLinkElements: JSX.Element[] = [] cssFiles.forEach((file) => { const isSharedFile = sharedFiles.has(file) @@ -193,7 +208,6 @@ export class Head extends Component< /> ) }) - return cssLinkElements.length === 0 ? null : cssLinkElements } @@ -206,7 +220,7 @@ export class Head extends Component< return ( dedupe(dynamicImports) - .map((bundle: any) => { + .map((bundle) => { // `dynamicImports` will contain both `.js` and `.module.js` when the // feature is enabled. This clause will filter down to the modern // variants only. @@ -540,7 +554,7 @@ export class NextScript extends Component { devOnlyCacheBusterQueryString, } = this.context - return dedupe(dynamicImports).map((bundle: any) => { + return dedupe(dynamicImports).map((bundle) => { let modernProps = {} if (process.env.__NEXT_MODERN_BUILD) { modernProps = bundle.file.endsWith('.module.js') diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-1.js b/test/integration/production/components/dynamic-css/many-imports/with-css-1.js new file mode 100644 index 0000000000000..1df8d47c21e5f --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-1.js @@ -0,0 +1,3 @@ +import styles from './with-css-1.module.css' + +export default () =>

With CSS 1

diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-1.module.css b/test/integration/production/components/dynamic-css/many-imports/with-css-1.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-1.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-2.js b/test/integration/production/components/dynamic-css/many-imports/with-css-2.js new file mode 100644 index 0000000000000..1112809cb0b39 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-2.js @@ -0,0 +1,3 @@ +import styles from './with-css-2.module.css' + +export default () =>

With CSS 2

diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-2.module.css b/test/integration/production/components/dynamic-css/many-imports/with-css-2.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-2.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-3.js b/test/integration/production/components/dynamic-css/many-imports/with-css-3.js new file mode 100644 index 0000000000000..fa4f705cbac5e --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-3.js @@ -0,0 +1,3 @@ +import styles from './with-css-3.module.css' + +export default () =>

With CSS 3

diff --git a/test/integration/production/components/dynamic-css/many-imports/with-css-3.module.css b/test/integration/production/components/dynamic-css/many-imports/with-css-3.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-imports/with-css-3.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/many-modules/with-css-2.module.css b/test/integration/production/components/dynamic-css/many-modules/with-css-2.module.css new file mode 100644 index 0000000000000..3efa99e6fb171 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-modules/with-css-2.module.css @@ -0,0 +1,3 @@ +.text { + color: red; +} diff --git a/test/integration/production/components/dynamic-css/many-modules/with-css.js b/test/integration/production/components/dynamic-css/many-modules/with-css.js new file mode 100644 index 0000000000000..fc11238a63deb --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-modules/with-css.js @@ -0,0 +1,8 @@ +import styles from './with-css.module.css' +import styles2 from './with-css-2.module.css' + +export default () => ( +
+

With CSS

+
+) diff --git a/test/integration/production/components/dynamic-css/many-modules/with-css.module.css b/test/integration/production/components/dynamic-css/many-modules/with-css.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/many-modules/with-css.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/nested/Nested.jsx b/test/integration/production/components/dynamic-css/nested/Nested.jsx new file mode 100644 index 0000000000000..7cc93b468ad9d --- /dev/null +++ b/test/integration/production/components/dynamic-css/nested/Nested.jsx @@ -0,0 +1,3 @@ +import styles2 from './with-css-2.module.css' + +export default () =>

With CSS

diff --git a/test/integration/production/components/dynamic-css/nested/with-css-2.module.css b/test/integration/production/components/dynamic-css/nested/with-css-2.module.css new file mode 100644 index 0000000000000..3efa99e6fb171 --- /dev/null +++ b/test/integration/production/components/dynamic-css/nested/with-css-2.module.css @@ -0,0 +1,3 @@ +.text { + color: red; +} diff --git a/test/integration/production/components/dynamic-css/nested/with-css.js b/test/integration/production/components/dynamic-css/nested/with-css.js new file mode 100644 index 0000000000000..a513d6e9d10b3 --- /dev/null +++ b/test/integration/production/components/dynamic-css/nested/with-css.js @@ -0,0 +1,8 @@ +import styles from './with-css.module.css' +import Nested from './Nested' + +export default () => ( +
+ +
+) diff --git a/test/integration/production/components/dynamic-css/nested/with-css.module.css b/test/integration/production/components/dynamic-css/nested/with-css.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/nested/with-css.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/no-css.js b/test/integration/production/components/dynamic-css/no-css.js new file mode 100644 index 0000000000000..380145b1902c9 --- /dev/null +++ b/test/integration/production/components/dynamic-css/no-css.js @@ -0,0 +1 @@ +export default () =>

Without CSS

diff --git a/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.js b/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.js new file mode 100644 index 0000000000000..f8514fa12614b --- /dev/null +++ b/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.js @@ -0,0 +1,8 @@ +import styles from './with-css-2.module.css' +import stylesShared from './with-css-shared.module.css' + +export default () => ( +
+

With CSS

+
+) diff --git a/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.module.css b/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/shared-css-module/with-css-2.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/shared-css-module/with-css-shared.module.css b/test/integration/production/components/dynamic-css/shared-css-module/with-css-shared.module.css new file mode 100644 index 0000000000000..3efa99e6fb171 --- /dev/null +++ b/test/integration/production/components/dynamic-css/shared-css-module/with-css-shared.module.css @@ -0,0 +1,3 @@ +.text { + color: red; +} diff --git a/test/integration/production/components/dynamic-css/shared-css-module/with-css.js b/test/integration/production/components/dynamic-css/shared-css-module/with-css.js new file mode 100644 index 0000000000000..d0397a8c33d5b --- /dev/null +++ b/test/integration/production/components/dynamic-css/shared-css-module/with-css.js @@ -0,0 +1,8 @@ +import styles from './with-css.module.css' +import stylesShared from './with-css-shared.module.css' + +export default () => ( +
+

With CSS

+
+) diff --git a/test/integration/production/components/dynamic-css/shared-css-module/with-css.module.css b/test/integration/production/components/dynamic-css/shared-css-module/with-css.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/shared-css-module/with-css.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/components/dynamic-css/with-css.js b/test/integration/production/components/dynamic-css/with-css.js new file mode 100644 index 0000000000000..d988353142973 --- /dev/null +++ b/test/integration/production/components/dynamic-css/with-css.js @@ -0,0 +1,3 @@ +import styles from './with-css.module.css' + +export default () =>

With CSS

diff --git a/test/integration/production/components/dynamic-css/with-css.module.css b/test/integration/production/components/dynamic-css/with-css.module.css new file mode 100644 index 0000000000000..69fda78f79397 --- /dev/null +++ b/test/integration/production/components/dynamic-css/with-css.module.css @@ -0,0 +1,3 @@ +.content { + color: inherit; +} diff --git a/test/integration/production/pages/dynamic/css.js b/test/integration/production/pages/dynamic/css.js new file mode 100644 index 0000000000000..116e142e80af7 --- /dev/null +++ b/test/integration/production/pages/dynamic/css.js @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic' + +const Hello = dynamic(import('../../components/dynamic-css/with-css')) + +export default Hello diff --git a/test/integration/production/pages/dynamic/many-css-modules.js b/test/integration/production/pages/dynamic/many-css-modules.js new file mode 100644 index 0000000000000..4f77b5b90b668 --- /dev/null +++ b/test/integration/production/pages/dynamic/many-css-modules.js @@ -0,0 +1,7 @@ +import dynamic from 'next/dynamic' + +const Component = dynamic( + import('../../components/dynamic-css/many-modules/with-css') +) + +export default Component diff --git a/test/integration/production/pages/dynamic/many-dynamic-css.js b/test/integration/production/pages/dynamic/many-dynamic-css.js new file mode 100644 index 0000000000000..817164b8090b8 --- /dev/null +++ b/test/integration/production/pages/dynamic/many-dynamic-css.js @@ -0,0 +1,21 @@ +import dynamic from 'next/dynamic' + +const First = dynamic( + import('../../components/dynamic-css/many-imports/with-css-1') +) +const Second = dynamic( + import('../../components/dynamic-css/many-imports/with-css-2') +) +const Third = dynamic( + import('../../components/dynamic-css/many-imports/with-css-3') +) + +export default function Page() { + return ( +
+ + + +
+ ) +} diff --git a/test/integration/production/pages/dynamic/nested-css.js b/test/integration/production/pages/dynamic/nested-css.js new file mode 100644 index 0000000000000..9efdb6d0dcb10 --- /dev/null +++ b/test/integration/production/pages/dynamic/nested-css.js @@ -0,0 +1,7 @@ +import dynamic from 'next/dynamic' + +const Component = dynamic( + import('../../components/dynamic-css/nested/with-css') +) + +export default Component diff --git a/test/integration/production/pages/dynamic/no-css.js b/test/integration/production/pages/dynamic/no-css.js new file mode 100644 index 0000000000000..bb67592264e20 --- /dev/null +++ b/test/integration/production/pages/dynamic/no-css.js @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic' + +const Hello = dynamic(import('../../components/dynamic-css/no-css')) + +export default Hello diff --git a/test/integration/production/pages/dynamic/shared-css-module.js b/test/integration/production/pages/dynamic/shared-css-module.js new file mode 100644 index 0000000000000..e90b1dd138ae2 --- /dev/null +++ b/test/integration/production/pages/dynamic/shared-css-module.js @@ -0,0 +1,17 @@ +import dynamic from 'next/dynamic' + +const First = dynamic( + import('../../components/dynamic-css/shared-css-module/with-css') +) +const Second = dynamic( + import('../../components/dynamic-css/shared-css-module/with-css-2') +) + +export default function Page() { + return ( +
+ + +
+ ) +} diff --git a/test/integration/production/test/dynamic.js b/test/integration/production/test/dynamic.js index 510e7f9dde30f..61e4cfaa56f86 100644 --- a/test/integration/production/test/dynamic.js +++ b/test/integration/production/test/dynamic.js @@ -16,6 +16,43 @@ export default (context, render) => { expect($('body').text()).toMatch(/Hello World 1/) }) + it('should render one dynamically imported component and load its css files', async () => { + const $ = await get$('/dynamic/css') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(1) + }) + + it('should render three dynamically imported components and load their css files', async () => { + const $ = await get$('/dynamic/many-dynamic-css') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(3) + }) + + it('should bundle two css modules for one dynamically imported component into one css file', async () => { + const $ = await get$('/dynamic/many-css-modules') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(1) + }) + + it('should bundle two css modules for nested components into one css file', async () => { + const $ = await get$('/dynamic/nested-css') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(1) + }) + + // It seem to be abnormal, dynamic CSS modules are completely self-sufficient, so shared styles are copied across files + it('should output two css files even in case of three css module files while one is shared across files', async () => { + const $ = await get$('/dynamic/shared-css-module') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(2) + }) + + it('should render one dynamically imported component without any css files', async () => { + const $ = await get$('/dynamic/no-css') + const cssFiles = $('link[rel=stylesheet]') + expect(cssFiles.length).toBe(0) + }) + it('should render even there are no physical chunk exists', async () => { let browser try { From 885d145ff72aae21e8166070c78fe1321c307a7c Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Mon, 24 Aug 2020 01:23:01 -0400 Subject: [PATCH 09/11] v9.5.3-canary.21 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-plugin-next/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 8 ++++---- packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lerna.json b/lerna.json index e88cb15d77dfa..7b24d1e9cddbb 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.5.3-canary.20" + "version": "9.5.3-canary.21" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index a79c458755614..ee505f6569a9e 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index b2b7f7c76dcff..5b6b487205796 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "description": "ESLint plugin for NextJS.", "main": "lib/index.js", "license": "MIT", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 62d53ada27dc3..928d386bd50f8 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 5c7df344fbd14..829625db0d60e 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 875e4ae23e593..b3c71a342fe13 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index c35ad8a73d85c..a1cd6adcbef2c 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-google-analytics" diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 63c9b9e29de71..537b3d93b754b 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-sentry" diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index f9f9921d9f126..b40824e7a8998 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 4b05c023bb0a2..4afafe1384f51 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 6bcf76fc4617c..f43c2483e78c8 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -77,8 +77,8 @@ "@babel/preset-typescript": "7.9.0", "@babel/runtime": "7.9.6", "@babel/types": "7.9.6", - "@next/react-dev-overlay": "9.5.3-canary.20", - "@next/react-refresh-utils": "9.5.3-canary.20", + "@next/react-dev-overlay": "9.5.3-canary.21", + "@next/react-refresh-utils": "9.5.3-canary.21", "ast-types": "0.13.2", "babel-plugin-syntax-jsx": "6.18.0", "babel-plugin-transform-define": "2.0.0", @@ -123,7 +123,7 @@ "react-dom": "^16.6.0" }, "devDependencies": { - "@next/polyfill-nomodule": "9.5.3-canary.20", + "@next/polyfill-nomodule": "9.5.3-canary.21", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", "@taskr/watch": "1.1.0", diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 87a32842f60c7..e602a80e40e7c 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index ae163a90c7fdd..01668d624eab0 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "9.5.3-canary.20", + "version": "9.5.3-canary.21", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From c9ad33bd5917f43cd6fa199c8cfb25fa301c652a Mon Sep 17 00:00:00 2001 From: Christian Pena Valerio Date: Mon, 24 Aug 2020 10:58:13 -0400 Subject: [PATCH 10/11] Update fast-refresh.md to fix 404 link (#16505) --- docs/basic-features/fast-refresh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic-features/fast-refresh.md b/docs/basic-features/fast-refresh.md index 0d6040eb9050b..5df2d35968a98 100644 --- a/docs/basic-features/fast-refresh.md +++ b/docs/basic-features/fast-refresh.md @@ -108,5 +108,5 @@ with an empty array of dependencies would still re-run once during Fast Refresh. However, writing code resilient to occasional re-running of `useEffect` is a good practice even without Fast Refresh. It will make it easier for you to introduce new dependencies to it later on -and it's enforced by [React Strict Mode](/docs/api-reference/next.config.js/react-strict-mode), +and it's enforced by [React Strict Mode](/docs/api-reference/next.config.js/react-strict-mode.md), which we highly recommend enabling. From ed0820f763e74d0071625030aed70b3b21184aef Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Tue, 25 Aug 2020 00:37:52 +0200 Subject: [PATCH 11/11] Enable webpack 5 caching by default (#16531) When the webpack 5 beta is used disk caching will be automatically enabled to improve incremental build performance. --- packages/next/build/webpack-config.ts | 118 ++++++++++----------- packages/next/next-server/server/config.ts | 1 - 2 files changed, 56 insertions(+), 63 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 18df99e10f9af..31ee7da60ec97 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -238,7 +238,7 @@ export default async function getBaseWebpackConfig( pagesDir, cwd: dir, // Webpack 5 has a built-in loader cache - cache: !config.experimental.unstable_webpack5cache, + cache: !isWebpack5, babelPresetPlugins, hasModern: !!config.experimental.modern, development: dev, @@ -1107,9 +1107,9 @@ export default async function getBaseWebpackConfig( } if (isWebpack5) { - // On by default: + // futureEmitAssets is on by default in webpack 5 delete webpackConfig.output?.futureEmitAssets - // No longer polyfills Node.js modules: + // webpack 5 no longer polyfills Node.js modules: if (webpackConfig.node) delete webpackConfig.node.setImmediate if (dev) { @@ -1119,71 +1119,65 @@ export default async function getBaseWebpackConfig( webpackConfig.optimization.usedExports = false } - // Enable webpack 5 caching - if (config.experimental.unstable_webpack5cache) { - const nextPublicVariables = Object.keys(process.env).reduce( - (prev: string, key: string) => { - if (key.startsWith('NEXT_PUBLIC_')) { - return `${prev}|${key}=${process.env[key]}` - } - return prev - }, - '' - ) - const nextEnvVariables = Object.keys(config.env).reduce( - (prev: string, key: string) => { - return `${prev}|${key}=${config.env[key]}` - }, - '' - ) + const nextPublicVariables = Object.keys(process.env).reduce( + (prev: string, key: string) => { + if (key.startsWith('NEXT_PUBLIC_')) { + return `${prev}|${key}=${process.env[key]}` + } + return prev + }, + '' + ) + const nextEnvVariables = Object.keys(config.env).reduce( + (prev: string, key: string) => { + return `${prev}|${key}=${config.env[key]}` + }, + '' + ) - const configVars = JSON.stringify({ - crossOrigin: config.crossOrigin, - pageExtensions: config.pageExtensions, - trailingSlash: config.trailingSlash, - modern: config.experimental.modern, - buildActivity: config.devIndicators.buildActivity, - autoPrerender: config.devIndicators.autoPrerender, - plugins: config.experimental.plugins, - reactStrictMode: config.reactStrictMode, - reactMode: config.experimental.reactMode, - optimizeFonts: config.experimental.optimizeFonts, - optimizeImages: config.experimental.optimizeImages, - scrollRestoration: config.experimental.scrollRestoration, - basePath: config.basePath, - pageEnv: config.experimental.pageEnv, - excludeDefaultMomentLocales: config.future.excludeDefaultMomentLocales, - assetPrefix: config.assetPrefix, - target, - reactProductionProfiling, - }) + const configVars = JSON.stringify({ + crossOrigin: config.crossOrigin, + pageExtensions: config.pageExtensions, + trailingSlash: config.trailingSlash, + modern: config.experimental.modern, + buildActivity: config.devIndicators.buildActivity, + autoPrerender: config.devIndicators.autoPrerender, + plugins: config.experimental.plugins, + reactStrictMode: config.reactStrictMode, + reactMode: config.experimental.reactMode, + optimizeFonts: config.experimental.optimizeFonts, + optimizeImages: config.experimental.optimizeImages, + scrollRestoration: config.experimental.scrollRestoration, + basePath: config.basePath, + pageEnv: config.experimental.pageEnv, + excludeDefaultMomentLocales: config.future.excludeDefaultMomentLocales, + assetPrefix: config.assetPrefix, + target, + reactProductionProfiling, + }) - const cache: any = { - type: 'filesystem', - // Includes: - // - Next.js version - // - NEXT_PUBLIC_ variable values (they affect caching) TODO: make this module usage only - // - next.config.js `env` key - // - next.config.js keys that affect compilation - version: `${process.env.__NEXT_VERSION}|${nextPublicVariables}|${nextEnvVariables}|${configVars}`, - buildDependencies: { - config: [], - }, - cacheDirectory: path.join(dir, '.next', 'cache', 'webpack'), - } + const cache: any = { + type: 'filesystem', + // Includes: + // - Next.js version + // - NEXT_PUBLIC_ variable values (they affect caching) TODO: make this module usage only + // - next.config.js `env` key + // - next.config.js keys that affect compilation + version: `${process.env.__NEXT_VERSION}|${nextPublicVariables}|${nextEnvVariables}|${configVars}`, + cacheDirectory: path.join(dir, '.next', 'cache', 'webpack'), + } - // Adds `next.config.js` as a buildDependency when custom webpack config is provided - if (config.webpack && config.configFile) { - cache.buildDependencies = { - config: [config.configFile], - } + // Adds `next.config.js` as a buildDependency when custom webpack config is provided + if (config.webpack && config.configFile) { + cache.buildDependencies = { + config: [config.configFile], } + } - webpackConfig.cache = cache + webpackConfig.cache = cache - // @ts-ignore TODO: remove ignore when webpack 5 is stable - webpackConfig.optimization.realContentHash = false - } + // @ts-ignore TODO: remove ignore when webpack 5 is stable + webpackConfig.optimization.realContentHash = false } webpackConfig = await buildConfiguration(webpackConfig, { diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index ac652280fb9f2..3de166a548902 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -54,7 +54,6 @@ const defaultConfig: { [key: string]: any } = { optimizeFonts: false, optimizeImages: false, scrollRestoration: false, - unstable_webpack5cache: false, }, future: { excludeDefaultMomentLocales: false,