From 6826ddf9d73a942df14b4ddd0c26adda7e4c69bd Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Tue, 17 Nov 2020 06:51:12 -0600 Subject: [PATCH 001/119] Add docs on incrementally adopting Next.js. (#19226) Building off the "Migrating to Next.js" section started, this doc provides information on using `rewrites`, `redirects`, and micro-frontends to incrementally adopt Next.js in your codebase. --- docs/manifest.json | 4 ++ docs/migrating/incremental-adoption.md | 77 ++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 docs/migrating/incremental-adoption.md diff --git a/docs/manifest.json b/docs/manifest.json index bfd47e758f3e4..c5f01e402368a 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -198,6 +198,10 @@ { "title": "Migrating to Next.js", "routes": [ + { + "title": "Incrementally Adopting Next.js", + "path": "/docs/migrating/incremental-adoption.md" + }, { "title": "Migrating from Gatsby", "path": "/docs/migrating/from-gatsby.md" diff --git a/docs/migrating/incremental-adoption.md b/docs/migrating/incremental-adoption.md new file mode 100644 index 0000000000000..f8e07c2149e44 --- /dev/null +++ b/docs/migrating/incremental-adoption.md @@ -0,0 +1,77 @@ +--- +description: Learn different strategies for incrementally adopting Next.js into your development workflow. +--- + +# Incrementally Adopting Next.js + +
+ Examples + +
+ +Next.js has been designed from the start for gradual adoption. You can use as much (or as little) React as you need. By starting small and incrementally adding more pages, you can prevent derailing feature work by avoiding a complete rewrite. + +## Strategies + +### Subpath + +If you need multiple applications on a single domain, you can take over an entire subpath. For example, you might deploy your Next.js e-commerce store at `acme.com/store`. + +Using [`basePath`](/docs/api-reference/next.config.js/basepath.md), you can configure your Next.js application's assets and links to automatically work with your new subpath `/store`. Since each page in Next.js is its own [standalone route](/docs/routing/introduction.md), new files like `pages/products.js` will route to `acme.com/store/products` in your new application. + +```jsx +// next.config.js + +module.exports = { + basePath: '/store', +} +``` + +> This feature was introduced in [Next.js 9.5](https://nextjs.org/blog/next-9-5) and up. If you’re using older versions of Next.js, please upgrade before trying it out. + +### Rewrites + +If you plan on fully migrating your domain to Next.js, you can use [`rewrites`](/docs/api-reference/next.config.js/rewrites.md) inside `next.config.js`. This allows you to check your new routes before falling back to proxying your existing website. + +For example, let's say you took over `/about` with Next.js. When a request for `acme.com/about` hits your Next.js application, it will serve the new page. A request for any other route (e.g. `acme.com/dashboard`) will fall back and proxy the URL you specify. + +```jsx +// next.config.js + +module.exports = { + async rewrites() { + return [ + // we need to define a no-op rewrite to trigger checking + // all pages/static files before we attempt proxying + { + source: '/:path*', + destination: '/:path*', + }, + { + source: '/:path*', + destination: `https://acme-proxy.com/:path*`, + }, + ] + }, +} +``` + +> This feature was introduced in [Next.js 9.5](https://nextjs.org/blog/next-9-5) and up. If you’re using older versions of Next.js, please upgrade before trying it out. + +### Micro-Frontends + +Next.js and [Vercel](https://vercel.com) make it easy to adopt [micro-frontends](https://martinfowler.com/articles/micro-frontends.html) and deploy as a [Monorepo](https://vercel.com/blog/monorepos). This allows you to use [subdomains](https://en.wikipedia.org/wiki/Subdomain) to adopt new applications incrementally. Some benefits of micro-frontends: + +- Smaller, more cohesive and maintainable codebases. +- More scalable organizations with decoupled, autonomous teams. +- The ability to upgrade, update, or even rewrite parts of the frontend in a more incremental fashion. + +Once your monorepo is set up, push changes to your Git repository as usual and you'll see the commits deployed to the Vercel projects you've connected. + +## Conclusion + +To learn more, read about [subpaths](/docs/api-reference/next.config.js/basepath.md) and [rewrites](/docs/api-reference/next.config.js/rewrites.md) or [deploy an example with micro-frontends](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-zones). From c283442ef09364de4891ed8da888544c39ee26ce Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Tue, 17 Nov 2020 14:11:11 +0100 Subject: [PATCH 002/119] Update 1.Bug_report.md --- .github/ISSUE_TEMPLATE/1.Bug_report.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1.Bug_report.md b/.github/ISSUE_TEMPLATE/1.Bug_report.md index abc5f350a3be4..9488cca282ec9 100644 --- a/.github/ISSUE_TEMPLATE/1.Bug_report.md +++ b/.github/ISSUE_TEMPLATE/1.Bug_report.md @@ -33,8 +33,9 @@ If applicable, add screenshots to help explain your problem. - OS: [e.g. macOS, Windows] - Browser (if applies) [e.g. chrome, safari] -- Version of Next.js: [e.g. 6.0.2] -- Version of Node.js: [e.g. 10.10.0] +- Version of Next.js: [e.g. 10.0.1] +- Version of Node.js: [e.g. 12.0.0] +- Deployment: [e.g. next start, next export, Vercel, other platform] ## Additional context From ab0e2744eeb5082ced09330bf27e8f7ff1393519 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 17 Nov 2020 11:53:35 -0500 Subject: [PATCH 003/119] Upgrade `styled-jsx` (#19241) This fixes compatibility with React 17 and `styled-jsx`. Fixes #19242 --- packages/next/package.json | 2 +- test/integration/client-navigation/lib/colored-blue.js | 6 ++++++ .../client-navigation/pages/styled-jsx-external.js | 9 +++++++++ test/integration/client-navigation/test/index.test.js | 1 + test/integration/client-navigation/test/rendering.js | 8 ++++++++ yarn.lock | 8 ++++---- 6 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 test/integration/client-navigation/lib/colored-blue.js create mode 100644 test/integration/client-navigation/pages/styled-jsx-external.js diff --git a/packages/next/package.json b/packages/next/package.json index 6b65ff072ee17..52499fa8ea846 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -98,7 +98,7 @@ "schema-utils": "2.7.1", "stream-browserify": "3.0.0", "style-loader": "1.2.1", - "styled-jsx": "3.3.1", + "styled-jsx": "3.3.2", "use-subscription": "1.5.1", "vm-browserify": "1.1.2", "watchpack": "2.0.0-beta.13", diff --git a/test/integration/client-navigation/lib/colored-blue.js b/test/integration/client-navigation/lib/colored-blue.js new file mode 100644 index 0000000000000..af26b8fe6d9a2 --- /dev/null +++ b/test/integration/client-navigation/lib/colored-blue.js @@ -0,0 +1,6 @@ +import { css } from 'styled-jsx/css' +export const pBlue = css.resolve` + p { + color: blue; + } +` diff --git a/test/integration/client-navigation/pages/styled-jsx-external.js b/test/integration/client-navigation/pages/styled-jsx-external.js new file mode 100644 index 0000000000000..c4182130e5f29 --- /dev/null +++ b/test/integration/client-navigation/pages/styled-jsx-external.js @@ -0,0 +1,9 @@ +import { pBlue } from '../lib/colored-blue' +export default () => ( +
+

+ This is blue +

+ {pBlue.styles} +
+) diff --git a/test/integration/client-navigation/test/index.test.js b/test/integration/client-navigation/test/index.test.js index ec697fa9f4b0d..7d8a3eb1f185e 100644 --- a/test/integration/client-navigation/test/index.test.js +++ b/test/integration/client-navigation/test/index.test.js @@ -39,6 +39,7 @@ describe('Client Navigation', () => { '/fragment-syntax', '/custom-extension', '/styled-jsx', + '/styled-jsx-external', '/with-cdm', '/url-prop', '/url-prop-override', diff --git a/test/integration/client-navigation/test/rendering.js b/test/integration/client-navigation/test/rendering.js index ba5cbef38c420..937763ee1419a 100644 --- a/test/integration/client-navigation/test/rendering.js +++ b/test/integration/client-navigation/test/rendering.js @@ -205,6 +205,14 @@ export default function (render, fetch, ctx) { expect(style.text().includes(`p.${styleId}{color:blue`)).toBeTruthy() }) + test('renders styled jsx external', async () => { + const $ = await get$('/styled-jsx-external') + const styleId = $('#blue-box').attr('class') + const style = $('style') + + expect(style.text().includes(`p.${styleId}{color:blue`)).toBeTruthy() + }) + test('renders properties populated asynchronously', async () => { const html = await render('/async-props') expect(html.includes('Diego Milito')).toBeTruthy() diff --git a/yarn.lock b/yarn.lock index ad625cfbe8bbf..5dfff92352bcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15358,10 +15358,10 @@ styled-jsx-plugin-postcss@3.0.2: postcss "^7.0.2" postcss-load-plugins "^2.3.0" -styled-jsx@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.1.tgz#d79f306c42c99cefbe8e76f35dad8100dc5c9ecc" - integrity sha512-RhW71t3k95E3g7Zq3lEBk+kmf+p4ZME7c5tfsYf9M5mq6CgIvFXkbvhawL2gWriXLRlMyKAYACE89Qa2JnTqUw== +styled-jsx@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018" + integrity sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g== dependencies: "@babel/types" "7.8.3" babel-plugin-syntax-jsx "6.18.0" From 99e10a07fb9dd992c039cb0d9d4761935b91e547 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 17 Nov 2020 12:04:07 -0600 Subject: [PATCH 004/119] Ensure data prefetch for default locale is correct (#19085) This makes sure SSG data is correctly prefetched for the default locale and other locales on the same page. Tests for this behavior have been added for catch-all and normal pages. Closes: https://github.com/vercel/next.js/issues/19048 --- packages/next/client/link.tsx | 15 +++++-- .../i18n-support-base-path/pages/mixed.js | 33 ++++++++++++++++ .../pages/[[...slug]].js | 20 ++++++++++ .../i18n-support-catchall/test/index.test.js | 39 ++++++++++++++++++- test/integration/i18n-support/pages/mixed.js | 33 ++++++++++++++++ test/integration/i18n-support/test/shared.js | 25 ++++++++++++ 6 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 test/integration/i18n-support-base-path/pages/mixed.js create mode 100644 test/integration/i18n-support/pages/mixed.js diff --git a/packages/next/client/link.tsx b/packages/next/client/link.tsx index 1f53da32c45b2..6b25e42b232d4 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -52,8 +52,13 @@ function prefetch( throw err } }) + const curLocale = + options && typeof options.locale !== 'undefined' + ? options.locale + : router && router.locale + // Join on an invalid URI character - prefetched[href + '%' + as] = true + prefetched[href + '%' + as + (curLocale ? '%' + curLocale : '')] = true } function isModifiedEvent(event: React.MouseEvent) { @@ -251,11 +256,13 @@ function Link(props: React.PropsWithChildren) { ) useEffect(() => { const shouldPrefetch = isVisible && p && isLocalURL(href) - const isPrefetched = prefetched[href + '%' + as] + const curLocale = + typeof locale !== 'undefined' ? locale : router && router.locale + const isPrefetched = + prefetched[href + '%' + as + (curLocale ? '%' + curLocale : '')] if (shouldPrefetch && !isPrefetched) { prefetch(router, href, as, { - locale: - typeof locale !== 'undefined' ? locale : router && router.locale, + locale: curLocale, }) } }, [as, href, isVisible, locale, p, router]) diff --git a/test/integration/i18n-support-base-path/pages/mixed.js b/test/integration/i18n-support-base-path/pages/mixed.js new file mode 100644 index 0000000000000..5b830abe6b937 --- /dev/null +++ b/test/integration/i18n-support-base-path/pages/mixed.js @@ -0,0 +1,33 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> + +

{JSON.stringify(props)}

+

{router.locale}

+

{router.defaultLocale}

+

{JSON.stringify(router.locales)}

+

{JSON.stringify(router.query)}

+

{router.pathname}

+

{router.asPath}

+ + + to /gsp + +
+ + + to /gsp + +
+ + + to /gsp + + + ) +} diff --git a/test/integration/i18n-support-catchall/pages/[[...slug]].js b/test/integration/i18n-support-catchall/pages/[[...slug]].js index 242ec4549708a..be4f2f3fb350e 100644 --- a/test/integration/i18n-support-catchall/pages/[[...slug]].js +++ b/test/integration/i18n-support-catchall/pages/[[...slug]].js @@ -15,6 +15,16 @@ export default function Page(props) {

{router.pathname}

{router.asPath}

+ + to / + +
+ + + to /another + +
+ to /nl-nl @@ -24,6 +34,16 @@ export default function Page(props) { to /nl-nl/another
+ + + to /fr + +
+ + + to /fr/another + +
) } diff --git a/test/integration/i18n-support-catchall/test/index.test.js b/test/integration/i18n-support-catchall/test/index.test.js index f5b66f2630dd5..42b74921c32d1 100644 --- a/test/integration/i18n-support-catchall/test/index.test.js +++ b/test/integration/i18n-support-catchall/test/index.test.js @@ -1,5 +1,5 @@ /* eslint-env jest */ - +import assert from 'assert' import http from 'http' import qs from 'querystring' import fs from 'fs-extra' @@ -188,6 +188,43 @@ function runTests(isDev) { defaultLocale: 'en-US', }) }) + + if (!isDev) { + it('should preload data correctly', async () => { + const browser = await webdriver(appPort, '/') + + await browser.eval(`(function() { + document.querySelector('#to-def-locale-index').scrollIntoView() + document.querySelector('#to-def-locale-another').scrollIntoView() + document.querySelector('#to-locale-index').scrollIntoView() + document.querySelector('#to-locale-another').scrollIntoView() + document.querySelector('#to-fr-locale-another').scrollIntoView() + document.querySelector('#to-fr-locale-index').scrollIntoView() + })()`) + + await check(async () => { + const hrefs = await browser.eval(`Object.keys(window.next.router.sdc)`) + hrefs.sort() + + console.log({ hrefs }) + + assert.deepEqual( + hrefs.map((href) => + new URL(href).pathname.replace(/^\/_next\/data\/[^/]+/, '') + ), + [ + '/en-US.json', + '/en-US/another.json', + '/fr.json', + '/fr/another.json', + '/nl-NL.json', + '/nl-NL/another.json', + ] + ) + return 'yes' + }, 'yes') + }) + } } describe('i18n Support Root Catch-all', () => { diff --git a/test/integration/i18n-support/pages/mixed.js b/test/integration/i18n-support/pages/mixed.js new file mode 100644 index 0000000000000..5b830abe6b937 --- /dev/null +++ b/test/integration/i18n-support/pages/mixed.js @@ -0,0 +1,33 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> + +

{JSON.stringify(props)}

+

{router.locale}

+

{router.defaultLocale}

+

{JSON.stringify(router.locales)}

+

{JSON.stringify(router.query)}

+

{router.pathname}

+

{router.asPath}

+ + + to /gsp + +
+ + + to /gsp + +
+ + + to /gsp + + + ) +} diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index e4822fcf0f66d..0133bf52863eb 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -38,6 +38,31 @@ export function runTests(ctx) { '`redirect` and `notFound` can not both be returned from getStaticProps at the same time. Page: /gsp/fallback/[slug]' ) }) + } else { + it('should preload all locales data correctly', async () => { + const browser = await webdriver(ctx.appPort, `${ctx.basePath}/mixed`) + + await browser.eval(`(function() { + document.querySelector('#to-gsp-en-us').scrollIntoView() + document.querySelector('#to-gsp-nl-nl').scrollIntoView() + document.querySelector('#to-gsp-fr').scrollIntoView() + })()`) + + await check(async () => { + const hrefs = await browser.eval(`Object.keys(window.next.router.sdc)`) + hrefs.sort() + + assert.deepEqual( + hrefs.map((href) => + new URL(href).pathname + .replace(ctx.basePath, '') + .replace(/^\/_next\/data\/[^/]+/, '') + ), + ['/en-US/gsp.json', '/fr/gsp.json', '/nl-NL/gsp.json'] + ) + return 'yes' + }, 'yes') + }) } it('should have correct values for non-prefixed path', async () => { From 2750880546dd27eb59756de9a5683f7fecaa33ee Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 17 Nov 2020 12:54:56 -0600 Subject: [PATCH 005/119] v10.0.2-canary.19 --- 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-env/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-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index b65707c5edcf9..947351976a3de 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.0.2-canary.18" + "version": "10.0.2-canary.19" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 26b0c6a708f3e..e7a0d1e964a33 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 4ec2eed027003..a22bee9d836a8 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": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 3b5a689b85416..b9987995b89d5 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 2257660ac6637..8cdd46c311b43 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 7fc7e3af24bdb..b5547b17ec2a2 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 5f266a649448f..4d493ef5fcd3f 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 c37a26f05ee6e..c24c367fc3c0c 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": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 ed592bbd55305..854764fd95fb1 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 146d53801d156..86e057d46357e 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index eb6a60c447a9a..a25beb84fdfbd 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 922ab25ef3d3c..8e8c698386a4f 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 52499fa8ea846..0f1fa5db5c3d9 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -63,10 +63,10 @@ "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.0.2-canary.18", - "@next/polyfill-module": "10.0.2-canary.18", - "@next/react-dev-overlay": "10.0.2-canary.18", - "@next/react-refresh-utils": "10.0.2-canary.18", + "@next/env": "10.0.2-canary.19", + "@next/polyfill-module": "10.0.2-canary.19", + "@next/react-dev-overlay": "10.0.2-canary.19", + "@next/react-refresh-utils": "10.0.2-canary.19", "ast-types": "0.13.2", "babel-plugin-transform-define": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.24", @@ -128,7 +128,7 @@ "@babel/preset-react": "7.12.5", "@babel/preset-typescript": "7.12.1", "@babel/types": "7.12.6", - "@next/polyfill-nomodule": "10.0.2-canary.18", + "@next/polyfill-nomodule": "10.0.2-canary.19", "@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 841270b1676f3..6c00ffc12c22d 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": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "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 3a54ad0ecc30d..6a6725e1370ae 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": "10.0.2-canary.18", + "version": "10.0.2-canary.19", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From ca590c4cb91cef4fb164c6e1211386145038eded Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 17 Nov 2020 15:46:46 -0600 Subject: [PATCH 006/119] Ensure i18n + trailingSlash: true handles correctly (#19149) This ensures redirects are handled properly with i18n + `trailingSlash: true`, additional tests have also been added to ensure this is covered Fixes: https://github.com/vercel/next.js/issues/19069 --- .../next/next-server/server/next-server.ts | 30 +++-- packages/next/next-server/server/router.ts | 7 ++ test/integration/i18n-support/next.config.js | 1 + .../i18n-support/test/index.test.js | 111 ++++++++++++++++++ 4 files changed, 139 insertions(+), 10 deletions(-) diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 89ee282ab1ddd..4a6d00c38df49 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -342,6 +342,11 @@ export default class Server { detectedLocale = detectedLocale || acceptPreferredLocale let localeDomainRedirect: string | undefined + ;(req as any).__nextHadTrailingSlash = pathname!.endsWith('/') + + if (pathname === '/') { + ;(req as any).__nextHadTrailingSlash = this.nextConfig.trailingSlash + } const localePathResult = normalizeLocalePath(pathname!, i18n.locales) if (localePathResult.detectedLocale) { @@ -351,7 +356,12 @@ export default class Server { pathname: localePathResult.pathname, }) ;(req as any).__nextStrippedLocale = true - parsedUrl.pathname = `${basePath || ''}${localePathResult.pathname}` + parsedUrl.pathname = `${basePath || ''}${localePathResult.pathname}${ + (req as any).__nextHadTrailingSlash && + localePathResult.pathname !== '/' + ? '/' + : '' + }` } // If a detected locale is a domain specific locale and we aren't already @@ -426,15 +436,15 @@ export default class Server { res.setHeader( 'Location', - formatUrl({ - // make sure to include any query values when redirecting - ...parsed, - pathname: localeDomainRedirect - ? localeDomainRedirect - : shouldStripDefaultLocale - ? basePath || `/` - : `${basePath || ''}/${detectedLocale}`, - }) + localeDomainRedirect + ? localeDomainRedirect + : formatUrl({ + // make sure to include any query values when redirecting + ...parsed, + pathname: shouldStripDefaultLocale + ? basePath || `/` + : `${basePath || ''}/${detectedLocale}`, + }) ) res.statusCode = 307 res.end() diff --git a/packages/next/next-server/server/router.ts b/packages/next/next-server/server/router.ts index cd1c2bd6d846c..5360806a09f2a 100644 --- a/packages/next/next-server/server/router.ts +++ b/packages/next/next-server/server/router.ts @@ -194,6 +194,13 @@ export default class Router { currentPathname === '/' ? '' : currentPathname }` + if ( + (req as any).__nextHadTrailingSlash && + !currentPathname.endsWith('/') + ) { + currentPathname += '/' + } + if (keepBasePath) { currentPathname = `${this.basePath}${currentPathname}` } diff --git a/test/integration/i18n-support/next.config.js b/test/integration/i18n-support/next.config.js index 18f254b1aef1d..cbb272c278545 100644 --- a/test/integration/i18n-support/next.config.js +++ b/test/integration/i18n-support/next.config.js @@ -1,6 +1,7 @@ module.exports = { // target: 'experimental-serverless-trace', // basePath: '/docs', + // trailingSlash: true, i18n: { // localeDetection: false, locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'], diff --git a/test/integration/i18n-support/test/index.test.js b/test/integration/i18n-support/test/index.test.js index 5a3107f155e21..3002246a1acf3 100644 --- a/test/integration/i18n-support/test/index.test.js +++ b/test/integration/i18n-support/test/index.test.js @@ -1,8 +1,10 @@ +import url from 'url' import http from 'http' import fs from 'fs-extra' import { join } from 'path' import cheerio from 'cheerio' import { runTests, locales } from './shared' +import webdriver from 'next-webdriver' import { nextBuild, nextStart, @@ -275,4 +277,113 @@ describe('i18n Support', () => { } }) }) + + describe('with trailingSlash: true', () => { + const curCtx = { + ...ctx, + isDev: true, + } + beforeAll(async () => { + await fs.remove(join(appDir, '.next')) + nextConfig.replace('// trailingSlash', 'trailingSlash') + + curCtx.appPort = await findPort() + curCtx.app = await launchApp(appDir, curCtx.appPort) + }) + afterAll(async () => { + nextConfig.restore() + await killApp(curCtx.app) + }) + + it('should redirect correctly', async () => { + for (const locale of locales) { + const res = await fetchViaHTTP(curCtx.appPort, '/', undefined, { + redirect: 'manual', + headers: { + 'accept-language': locale, + }, + }) + + if (locale === 'en-US') { + expect(res.status).toBe(200) + } else { + expect(res.status).toBe(307) + + const parsed = url.parse(res.headers.get('location'), true) + expect(parsed.pathname).toBe(`/${locale}`) + expect(parsed.query).toEqual({}) + } + } + }) + + it('should serve pages correctly with locale prefix', async () => { + for (const locale of locales) { + const res = await fetchViaHTTP( + curCtx.appPort, + `/${locale}/`, + undefined, + { + redirect: 'manual', + } + ) + expect(res.status).toBe(200) + + const $ = cheerio.load(await res.text()) + + expect($('#router-pathname').text()).toBe('/') + expect($('#router-as-path').text()).toBe('/') + expect($('#router-locale').text()).toBe(locale) + expect(JSON.parse($('#router-locales').text())).toEqual(locales) + expect($('#router-default-locale').text()).toBe('en-US') + } + }) + + it('should navigate between pages correctly', async () => { + for (const locale of locales) { + const localePath = `/${locale !== 'en-US' ? `${locale}/` : ''}` + const browser = await webdriver(curCtx.appPort, localePath) + + await browser.eval('window.beforeNav = 1') + await browser.elementByCss('#to-gsp').click() + await browser.waitForElementByCss('#gsp') + + expect(await browser.elementByCss('#router-pathname').text()).toBe( + '/gsp' + ) + expect(await browser.elementByCss('#router-as-path').text()).toBe( + '/gsp/' + ) + expect(await browser.elementByCss('#router-locale').text()).toBe(locale) + expect(await browser.eval('window.beforeNav')).toBe(1) + expect(await browser.eval('window.location.pathname')).toBe( + `${localePath}gsp/` + ) + + await browser.back().waitForElementByCss('#index') + + expect(await browser.elementByCss('#router-pathname').text()).toBe('/') + expect(await browser.elementByCss('#router-as-path').text()).toBe('/') + expect(await browser.elementByCss('#router-locale').text()).toBe(locale) + expect(await browser.eval('window.beforeNav')).toBe(1) + expect(await browser.eval('window.location.pathname')).toBe( + `${localePath}` + ) + + await browser.elementByCss('#to-gssp-slug').click() + await browser.waitForElementByCss('#gssp') + + expect(await browser.elementByCss('#router-pathname').text()).toBe( + '/gssp/[slug]' + ) + expect(await browser.elementByCss('#router-as-path').text()).toBe( + '/gssp/first/' + ) + expect(await browser.elementByCss('#router-locale').text()).toBe(locale) + expect(await browser.eval('window.beforeNav')).toBe(1) + expect(await browser.eval('window.location.pathname')).toBe( + `${localePath}gssp/first/` + ) + } + }) + }) }) From 5e5f206bcf5d8eaf1aa99f7d5be6e8bb7cf47bc8 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 18 Nov 2020 00:49:44 -0500 Subject: [PATCH 007/119] v10.0.2-canary.20 --- 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-env/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-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index 947351976a3de..37db07cb766f7 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.0.2-canary.19" + "version": "10.0.2-canary.20" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index e7a0d1e964a33..e2baa4b477ebc 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index a22bee9d836a8..ecfe442d32e24 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": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 b9987995b89d5..56741f9419b67 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 8cdd46c311b43..6e7b27f6568fd 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index b5547b17ec2a2..68d9f6cfd9b86 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 4d493ef5fcd3f..ee608872a749a 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 c24c367fc3c0c..62744181eb6d4 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": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 854764fd95fb1..7e709454a76dc 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 86e057d46357e..b6e90f74491a1 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index a25beb84fdfbd..7a91bd0466a06 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 8e8c698386a4f..bcf8573a0c01a 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 0f1fa5db5c3d9..0efa11548f702 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -63,10 +63,10 @@ "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.0.2-canary.19", - "@next/polyfill-module": "10.0.2-canary.19", - "@next/react-dev-overlay": "10.0.2-canary.19", - "@next/react-refresh-utils": "10.0.2-canary.19", + "@next/env": "10.0.2-canary.20", + "@next/polyfill-module": "10.0.2-canary.20", + "@next/react-dev-overlay": "10.0.2-canary.20", + "@next/react-refresh-utils": "10.0.2-canary.20", "ast-types": "0.13.2", "babel-plugin-transform-define": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.24", @@ -128,7 +128,7 @@ "@babel/preset-react": "7.12.5", "@babel/preset-typescript": "7.12.1", "@babel/types": "7.12.6", - "@next/polyfill-nomodule": "10.0.2-canary.19", + "@next/polyfill-nomodule": "10.0.2-canary.20", "@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 6c00ffc12c22d..78a3b7f939ff2 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": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "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 6a6725e1370ae..f4b3f20b5d6cc 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": "10.0.2-canary.19", + "version": "10.0.2-canary.20", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From 0f5db2a583213f71333fc85695213c8e7d107039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Chrastina?= <9218736+Simply007@users.noreply.github.com> Date: Wed, 18 Nov 2020 12:07:56 +0100 Subject: [PATCH 008/119] Fix link to developer plan in Kontent example (#19265) --- examples/cms-kontent/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cms-kontent/README.md b/examples/cms-kontent/README.md index 696ccc5a63598..cdb721f0c8fee 100644 --- a/examples/cms-kontent/README.md +++ b/examples/cms-kontent/README.md @@ -44,7 +44,7 @@ yarn create next-app --example cms-kontent cms-kontent-app First, [create an account on Kontent.ai](https://app.kontent.ai/sign-up?utm_source=nextjs_docs_example&utm_medium=devrel&utm_campaign=extended_trial). -> The link above will provide you with the 90-days trial. Once you finish the trial, or even during the trial period, you could switch to the [**developer plan**](https://kontent.ai/blog/a-new-developer-plan) which is **free of charge** and offers all the features you'll need to test out the example capabilities. +> The link above will provide you with the 90-days trial. Once you finish the trial, or even during the trial period, you could switch to the [**developer plan**](https://kontent.ai/developer-plan) which is **free of charge** and offers all the features you'll need to test out the example capabilities. After signing up, [create an empty project](https://docs.kontent.ai/tutorials/set-up-kontent/projects/manage-projects#a-creating-projects). From b955268dbb911bd8e8504ce847a434ca344908ca Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Wed, 18 Nov 2020 07:39:35 -0500 Subject: [PATCH 009/119] Update next/link docs to not include example without `href` (#19247) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- docs/api-reference/next/link.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/api-reference/next/link.md b/docs/api-reference/next/link.md index b519347341f40..4447ee0331279 100644 --- a/docs/api-reference/next/link.md +++ b/docs/api-reference/next/link.md @@ -197,27 +197,6 @@ The default behavior of the `Link` component is to `push` a new URL into the `hi ``` -## Using a component that supports `onClick` - -`Link` supports any component that supports the `onClick` event, in the case you don't provide an `` tag, consider the following example: - -```jsx -import Link from 'next/link' -import Image from 'next/image' - -function Avatar() { - return ( - - me - - ) -} - -export default Avatar -``` - -The child of `Link` is [`Image`](/docs/api-reference/next/image.md) instead of ``. `Link` will send the `onClick` property to `Image` but won't pass the `href` property. - ## Disable scrolling to the top of the page The default behavior of `Link` is to scroll to the top of the page. When there is a hash defined it will scroll to the specific id, like a normal `` tag. To prevent scrolling to the top / hash `scroll={false}` can be added to `Link`: From c3293b730e78c2eef590bc1a01b1b8d9d782a195 Mon Sep 17 00:00:00 2001 From: Vitor Dino Date: Wed, 18 Nov 2020 10:06:18 -0300 Subject: [PATCH 010/119] Fix docs about the return of getStaticProps on basic-features/pages (#19267) --- docs/basic-features/pages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic-features/pages.md b/docs/basic-features/pages.md index 2fefaa05b9069..1770c555e1a98 100644 --- a/docs/basic-features/pages.md +++ b/docs/basic-features/pages.md @@ -124,7 +124,7 @@ export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() - // By returning { props: posts }, the Blog component + // By returning { props: { posts } }, the Blog component // will receive `posts` as a prop at build time return { props: { From ce5d9c858b70f647b532ec2f62ddde506af32b6a Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 18 Nov 2020 08:49:17 -0500 Subject: [PATCH 011/119] v10.0.2 --- 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-env/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-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index 37db07cb766f7..cd0cc788136de 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.0.2-canary.20" + "version": "10.0.2" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index e2baa4b477ebc..fda353784a451 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.0.2-canary.20", + "version": "10.0.2", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index ecfe442d32e24..9a65004272502 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": "10.0.2-canary.20", + "version": "10.0.2", "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 56741f9419b67..6a2f9cc05b891 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.0.2-canary.20", + "version": "10.0.2", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 6e7b27f6568fd..e01e032177b2d 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.0.2-canary.20", + "version": "10.0.2", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 68d9f6cfd9b86..ef0eb6c38c7ba 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.0.2-canary.20", + "version": "10.0.2", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index ee608872a749a..7a8f4a6a11999 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.0.2-canary.20", + "version": "10.0.2", "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 62744181eb6d4..c9b0cd44ff6db 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": "10.0.2-canary.20", + "version": "10.0.2", "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 7e709454a76dc..ac01fc96635c0 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "10.0.2-canary.20", + "version": "10.0.2", "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 b6e90f74491a1..54ac8e74eae8d 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.0.2-canary.20", + "version": "10.0.2", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 7a91bd0466a06..bce77f93cdca4 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.0.2-canary.20", + "version": "10.0.2", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index bcf8573a0c01a..6884fdf09140e 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.0.2-canary.20", + "version": "10.0.2", "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 0efa11548f702..5e5270ed07e2f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.0.2-canary.20", + "version": "10.0.2", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -63,10 +63,10 @@ "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.0.2-canary.20", - "@next/polyfill-module": "10.0.2-canary.20", - "@next/react-dev-overlay": "10.0.2-canary.20", - "@next/react-refresh-utils": "10.0.2-canary.20", + "@next/env": "10.0.2", + "@next/polyfill-module": "10.0.2", + "@next/react-dev-overlay": "10.0.2", + "@next/react-refresh-utils": "10.0.2", "ast-types": "0.13.2", "babel-plugin-transform-define": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.24", @@ -128,7 +128,7 @@ "@babel/preset-react": "7.12.5", "@babel/preset-typescript": "7.12.1", "@babel/types": "7.12.6", - "@next/polyfill-nomodule": "10.0.2-canary.20", + "@next/polyfill-nomodule": "10.0.2", "@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 78a3b7f939ff2..ad4b85c65462d 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": "10.0.2-canary.20", + "version": "10.0.2", "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 f4b3f20b5d6cc..095eb9dd155f9 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": "10.0.2-canary.20", + "version": "10.0.2", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From 30c2dfdc47603ba68bc54ba5f716435385907361 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 18 Nov 2020 13:30:00 -0500 Subject: [PATCH 012/119] Remove old modern mode experiment (#19275) This PR removes the modern mode experiment because: - It does not yield meaningful bundle size wins when compared to other initiatives we've taken - It's not compatible with webpack 5 (which we're upgrading to) - It's currently broken and causes most apps to malfunction - There's no champion currently owning the experiment We can re-introduce this in the future when we'd like to make it a default for all Next.js apps. Note: **Next.js still supports Differential Loading (`nomodule`) and does it by default.** This PR strictly removes the experimental modern _syntax_, and does not disable our existing modern/legacy polyfilling. --- Fixes #19200 Fixes #18960 Fixes #14707 Fixes #14465 --- packages/next/build/babel/preset.ts | 7 +- packages/next/build/index.ts | 28 +- packages/next/build/utils.ts | 32 +- packages/next/build/webpack-config.ts | 44 +- .../webpack/loaders/next-babel-loader.js | 58 +-- .../webpack/plugins/build-manifest-plugin.ts | 40 +- .../build/webpack/plugins/next-esm-plugin.ts | 390 ------------------ packages/next/export/index.ts | 1 - packages/next/next-server/server/config.ts | 1 - packages/next/pages/_document.tsx | 45 +- test/.stats-app/stats-config.js | 63 +-- test/integration/amphtml/test/index.test.js | 54 +-- .../css-client-nav/test/index.test.js | 159 ------- .../multi-module-modern/next.config.js | 9 - .../multi-module-modern/pages/blue.js | 20 - .../multi-module-modern/pages/blue.module.css | 3 - .../multi-module-modern/pages/none.js | 19 - .../multi-module-modern/pages/red.js | 20 - .../multi-module-modern/pages/red.module.css | 3 - .../css-fixtures/multi-module/next.config.js | 8 +- .../dynamic-routing/next.config.js | 5 - .../dynamic-routing/test/index.test.js | 54 +-- test/integration/modern-mode/next.config.js | 3 - test/integration/modern-mode/pages/index.js | 1 - .../modern-mode/test/index.test.js | 66 --- test/integration/size-limit/next.config.js | 3 - .../integration/size-limit/test/index.test.js | 29 +- .../src-dir-support-double-dir/next.config.js | 7 +- .../test/index.test.js | 20 +- .../src-dir-support/next.config.js | 6 +- test/unit/next-babel-loader.unit.test.js | 8 +- 31 files changed, 54 insertions(+), 1152 deletions(-) delete mode 100644 packages/next/build/webpack/plugins/next-esm-plugin.ts delete mode 100644 test/integration/css-fixtures/multi-module-modern/next.config.js delete mode 100644 test/integration/css-fixtures/multi-module-modern/pages/blue.js delete mode 100644 test/integration/css-fixtures/multi-module-modern/pages/blue.module.css delete mode 100644 test/integration/css-fixtures/multi-module-modern/pages/none.js delete mode 100644 test/integration/css-fixtures/multi-module-modern/pages/red.js delete mode 100644 test/integration/css-fixtures/multi-module-modern/pages/red.module.css delete mode 100644 test/integration/dynamic-routing/next.config.js delete mode 100644 test/integration/modern-mode/next.config.js delete mode 100644 test/integration/modern-mode/pages/index.js delete mode 100644 test/integration/modern-mode/test/index.test.js diff --git a/packages/next/build/babel/preset.ts b/packages/next/build/babel/preset.ts index a0f56e79bcc81..206ad2d753d3c 100644 --- a/packages/next/build/babel/preset.ts +++ b/packages/next/build/babel/preset.ts @@ -66,16 +66,15 @@ module.exports = ( ): BabelPreset => { const supportsESM = api.caller(supportsStaticESM) const isServer = api.caller((caller: any) => !!caller && caller.isServer) - const isModern = api.caller((caller: any) => !!caller && caller.isModern) + const useJsxRuntime = options['preset-react']?.runtime === 'automatic' || (Boolean(api.caller((caller: any) => !!caller && caller.hasJsxRuntime)) && options['preset-react']?.runtime !== 'classic') const isLaxModern = - isModern || - (options['preset-env']?.targets && - options['preset-env'].targets.esmodules === true) + options['preset-env']?.targets && + options['preset-env'].targets.esmodules === true const presetEnvConfig = { // In the test environment `modules` is often needed to be set to true, babel figures that out by itself using the `'auto'` option diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 497e383d635e5..5e51c99d35b14 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -540,8 +540,7 @@ export default async function build( const [selfSize, allSize] = await getJsPageSizeInKb( actualPage, distDir, - buildManifest, - config.experimental.modern + buildManifest ) let isSsg = false @@ -1114,7 +1113,6 @@ export default async function build( await generateClientSsgManifest(prerenderManifest, { distDir, buildId, - isModern: !!config.experimental.modern, }) } else { const prerenderManifest: PrerenderManifest = { @@ -1177,7 +1175,6 @@ export default async function build( useStatic404, pageExtensions: config.pageExtensions, buildManifest, - isModern: config.experimental.modern, } ) @@ -1259,11 +1256,7 @@ export type ClientSsgManifest = Set function generateClientSsgManifest( prerenderManifest: PrerenderManifest, - { - buildId, - distDir, - isModern, - }: { buildId: string; distDir: string; isModern: boolean } + { buildId, distDir }: { buildId: string; distDir: string } ) { const ssgPages: ClientSsgManifest = new Set([ ...Object.entries(prerenderManifest.routes) @@ -1273,21 +1266,12 @@ function generateClientSsgManifest( ...Object.keys(prerenderManifest.dynamicRoutes), ]) - const clientSsgManifestPaths = [ - '_ssgManifest.js', - isModern && '_ssgManifest.module.js', - ] - .filter(Boolean) - .map((f) => - path.join(`${CLIENT_STATIC_FILES_PATH}/${buildId}`, f as string) - ) const clientSsgManifestContent = `self.__SSG_MANIFEST=${devalue( ssgPages )};self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()` - clientSsgManifestPaths.forEach((clientSsgManifestPath) => - writeFileSync( - path.join(distDir, clientSsgManifestPath), - clientSsgManifestContent - ) + + writeFileSync( + path.join(distDir, CLIENT_STATIC_FILES_PATH, buildId, '_ssgManifest.js'), + clientSsgManifestContent ) } diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 80e51a7e5ec8a..d0e17714c6beb 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -66,7 +66,6 @@ export async function printTreeView( pagesDir, pageExtensions, buildManifest, - isModern, useStatic404, }: { distPath: string @@ -74,7 +73,6 @@ export async function printTreeView( pagesDir: string pageExtensions: string[] buildManifest: BuildManifest - isModern: boolean useStatic404: boolean } ) { @@ -114,12 +112,7 @@ export async function printTreeView( list = [...list, '/404'] } - const sizeData = await computeFromManifest( - buildManifest, - distPath, - isModern, - pageInfos - ) + const sizeData = await computeFromManifest(buildManifest, distPath, pageInfos) const pageList = list .slice() @@ -374,18 +367,15 @@ type ComputeManifestShape = { let cachedBuildManifest: BuildManifest | undefined let lastCompute: ComputeManifestShape | undefined -let lastComputeModern: boolean | undefined let lastComputePageInfo: boolean | undefined async function computeFromManifest( manifest: BuildManifest, distPath: string, - isModern: boolean, pageInfos?: Map ): Promise { if ( Object.is(cachedBuildManifest, manifest) && - lastComputeModern === isModern && lastComputePageInfo === !!pageInfos ) { return lastCompute! @@ -405,13 +395,6 @@ async function computeFromManifest( ++expected manifest.pages[key].forEach((file) => { - if ( - // Select Modern or Legacy scripts - file.endsWith('.module.js') !== isModern - ) { - return - } - if (key === '/_app') { files.set(file, Infinity) } else if (files.has(file)) { @@ -471,7 +454,6 @@ async function computeFromManifest( } cachedBuildManifest = manifest - lastComputeModern = isModern lastComputePageInfo = !!pageInfos return lastCompute! } @@ -495,18 +477,16 @@ function sum(a: number[]): number { export async function getJsPageSizeInKb( page: string, distPath: string, - buildManifest: BuildManifest, - isModern: boolean + buildManifest: BuildManifest ): Promise<[number, number]> { - const data = await computeFromManifest(buildManifest, distPath, isModern) + const data = await computeFromManifest(buildManifest, distPath) - const fnFilterModern = (entry: string) => - entry.endsWith('.js') && entry.endsWith('.module.js') === isModern + const fnFilterJs = (entry: string) => entry.endsWith('.js') const pageFiles = ( buildManifest.pages[denormalizePagePath(page)] || [] - ).filter(fnFilterModern) - const appFiles = (buildManifest.pages['/_app'] || []).filter(fnFilterModern) + ).filter(fnFilterJs) + const appFiles = (buildManifest.pages['/_app'] || []).filter(fnFilterJs) const fnMapRealPath = (dep: string) => `${distPath}/${dep}` diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index b192b18ab01d3..71568573431b9 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -62,12 +62,6 @@ type ExcludesFalse = (x: T | false) => x is T const isWebpack5 = parseInt(webpack.version!) === 5 -const escapePathVariables = (value: any) => { - return typeof value === 'string' - ? value.replace(/\[(\\*[\w:]+\\*)\]/gi, '[\\$1\\]') - : value -} - const devtoolRevertWarning = execOnce((devtool: Configuration['devtool']) => { console.warn( chalk.yellow.bold('Warning: ') + @@ -249,7 +243,6 @@ export default async function getBaseWebpackConfig( // Webpack 5 has a built-in loader cache cache: !isWebpack5, babelPresetPlugins, - hasModern: !!config.experimental.modern, development: dev, hasReactRefresh, hasJsxRuntime, @@ -536,10 +529,7 @@ export default async function getBaseWebpackConfig( splitChunksConfig = splitChunksConfigs.prodGranular } - const crossOrigin = - !config.crossOrigin && config.experimental.modern - ? 'anonymous' - : config.crossOrigin + const crossOrigin = config.crossOrigin let customAppFile: string | null = await findPageFile( pagesDir, @@ -888,9 +878,7 @@ export default async function getBaseWebpackConfig( options: { cacheContext: dir, cacheDirectory: path.join(dir, '.next', 'cache', 'webpack'), - cacheIdentifier: `webpack${isServer ? '-server' : ''}${ - config.experimental.modern ? '-hasmodern' : '' - }`, + cacheIdentifier: `webpack${isServer ? '-server' : ''}`, }, }, { @@ -963,9 +951,6 @@ export default async function getBaseWebpackConfig( 'process.env.__NEXT_TRAILING_SLASH': JSON.stringify( config.trailingSlash ), - 'process.env.__NEXT_MODERN_BUILD': JSON.stringify( - config.experimental.modern && !dev - ), 'process.env.__NEXT_BUILD_INDICATOR': JSON.stringify( config.devIndicators.buildActivity ), @@ -1077,35 +1062,11 @@ export default async function getBaseWebpackConfig( new BuildManifestPlugin({ buildId, rewrites, - modern: config.experimental.modern, }), tracer && new ProfilingPlugin({ tracer, }), - !isWebpack5 && - config.experimental.modern && - !isServer && - !dev && - (() => { - const { NextEsmPlugin } = require('./webpack/plugins/next-esm-plugin') - return new NextEsmPlugin({ - filename: (getFileName: Function | string) => (...args: any[]) => { - const name = - typeof getFileName === 'function' - ? getFileName(...args) - : getFileName - - return name.includes('.js') - ? name.replace(/\.js$/, '.module.js') - : escapePathVariables( - args[0].chunk.name.replace(/\.js$/, '.module.js') - ) - }, - chunkFilename: (inputChunkName: string) => - inputChunkName.replace(/\.js$/, '.module.js'), - }) - })(), config.experimental.optimizeFonts && !dev && isServer && @@ -1191,7 +1152,6 @@ export default async function getBaseWebpackConfig( crossOrigin: config.crossOrigin, pageExtensions: config.pageExtensions, trailingSlash: config.trailingSlash, - modern: config.experimental.modern, buildActivity: config.devIndicators.buildActivity, plugins: config.experimental.plugins, reactStrictMode: config.reactStrictMode, diff --git a/packages/next/build/webpack/loaders/next-babel-loader.js b/packages/next/build/webpack/loaders/next-babel-loader.js index 92b9f90831437..6ac59abf699ef 100644 --- a/packages/next/build/webpack/loaders/next-babel-loader.js +++ b/packages/next/build/webpack/loaders/next-babel-loader.js @@ -8,34 +8,6 @@ import * as Log from '../../output/log' const cacheKey = 'babel-cache-' + 'n' + '-' const nextBabelPreset = require('../../babel/preset') -const getModernOptions = (babelOptions = {}) => { - const presetEnvOptions = Object.assign({}, babelOptions['preset-env']) - const transformRuntimeOptions = Object.assign( - {}, - babelOptions['transform-runtime'], - { regenerator: false } - ) - - presetEnvOptions.targets = { - esmodules: true, - } - presetEnvOptions.exclude = [ - ...(presetEnvOptions.exclude || []), - // Block accidental inclusions - 'transform-regenerator', - 'transform-async-to-generator', - ] - - return { - ...babelOptions, - 'preset-env': presetEnvOptions, - 'transform-runtime': transformRuntimeOptions, - } -} - -const nextBabelPresetModern = (presetOptions) => (context) => - nextBabelPreset(context, getModernOptions(presetOptions)) - module.exports = babelLoader.custom((babel) => { const presetItem = babel.createConfigItem(nextBabelPreset, { type: 'preset', @@ -55,9 +27,7 @@ module.exports = babelLoader.custom((babel) => { customOptions(opts) { const custom = { isServer: opts.isServer, - isModern: opts.isModern, pagesDir: opts.pagesDir, - hasModern: opts.hasModern, babelPresetPlugins: opts.babelPresetPlugins, development: opts.development, hasReactRefresh: opts.hasReactRefresh, @@ -72,8 +42,6 @@ module.exports = babelLoader.custom((babel) => { cacheIdentifier: cacheKey + (opts.isServer ? '-server' : '') + - (opts.isModern ? '-modern' : '') + - (opts.hasModern ? '-has-modern' : '') + '-new-polyfills' + (opts.development ? '-development' : '-production') + (opts.hasReactRefresh ? '-react-refresh' : '') + @@ -95,8 +63,6 @@ module.exports = babelLoader.custom((babel) => { delete loader.isServer delete loader.cache delete loader.distDir - delete loader.isModern - delete loader.hasModern delete loader.pagesDir delete loader.babelPresetPlugins delete loader.development @@ -110,8 +76,6 @@ module.exports = babelLoader.custom((babel) => { source, customOptions: { isServer, - isModern, - hasModern, pagesDir, babelPresetPlugins, development, @@ -138,7 +102,6 @@ module.exports = babelLoader.custom((babel) => { } options.caller.isServer = isServer - options.caller.isModern = isModern options.caller.isDev = development options.caller.hasJsxRuntime = hasJsxRuntime @@ -198,28 +161,9 @@ module.exports = babelLoader.custom((babel) => { options.plugins.push(nextDataPlugin) } - if (isModern) { - const nextPreset = options.presets.find( - (preset) => preset && preset.value === nextBabelPreset - ) || { options: {} } - - const additionalPresets = options.presets.filter( - (preset) => preset !== nextPreset - ) - - const presetItemModern = babel.createConfigItem( - nextBabelPresetModern(nextPreset.options), - { - type: 'preset', - } - ) - - options.presets = [...additionalPresets, presetItemModern] - } - // If the file has `module.exports` we have to transpile commonjs because Babel adds `import` statements // That break webpack, since webpack doesn't support combining commonjs and esmodules - if (!hasModern && source.indexOf('module.exports') !== -1) { + if (source.indexOf('module.exports') !== -1) { options.plugins.push(applyCommonJs) } diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index 01cacf64ad57d..1b62d4799d858 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -28,7 +28,6 @@ export type ClientBuildManifest = Record // reduced version to send to the client. function generateClientManifest( assetMap: BuildManifest, - isModern: boolean, rewrites: Rewrite[] ): string { const clientManifest: ClientBuildManifest = { @@ -44,11 +43,7 @@ function generateClientManifest( if (page === '/_app') return // Filter out dependencies in the _app entry, because those will have already // been loaded by the client prior to a navigation event - const filteredDeps = dependencies.filter( - (dep) => - !appDependencies.has(dep) && - (!dep.endsWith('.js') || dep.endsWith('.module.js') === isModern) - ) + const filteredDeps = dependencies.filter((dep) => !appDependencies.has(dep)) // The manifest can omit the page if it has no requirements if (filteredDeps.length) { @@ -82,16 +77,11 @@ function getFilesArray(files: any) { // It has a mapping of "entry" filename to real filename. Because the real filename can be hashed in production export default class BuildManifestPlugin { private buildId: string - private modern: boolean private rewrites: Rewrite[] - constructor(options: { - buildId: string - modern: boolean - rewrites: Rewrite[] - }) { + constructor(options: { buildId: string; rewrites: Rewrite[] }) { this.buildId = options.buildId - this.modern = options.modern + this.rewrites = options.rewrites.map((r) => { const rewrite = { ...r } @@ -185,11 +175,6 @@ export default class BuildManifestPlugin { assetMap.lowPriorityFiles.push( `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.js` ) - if (this.modern) { - assetMap.lowPriorityFiles.push( - `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.module.js` - ) - } // Add the runtime ssg manifest file as a lazy-loaded file dependency. // We also stub this file out for development mode (when it is not @@ -200,12 +185,6 @@ export default class BuildManifestPlugin { assetMap.lowPriorityFiles.push(ssgManifestPath) assets[ssgManifestPath] = new RawSource(srcEmptySsgManifest) - if (this.modern) { - const ssgManifestPathModern = `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_ssgManifest.module.js` - assetMap.lowPriorityFiles.push(ssgManifestPathModern) - assets[ssgManifestPathModern] = new RawSource(srcEmptySsgManifest) - } - assetMap.pages = Object.keys(assetMap.pages) .sort() // eslint-disable-next-line @@ -218,23 +197,10 @@ export default class BuildManifestPlugin { assets[clientManifestPath] = new RawSource( `self.__BUILD_MANIFEST = ${generateClientManifest( assetMap, - false, this.rewrites )};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()` ) - if (this.modern) { - const modernClientManifestPath = `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.module.js` - - assets[modernClientManifestPath] = new RawSource( - `self.__BUILD_MANIFEST = ${generateClientManifest( - assetMap, - true, - this.rewrites - )};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()` - ) - } - return assets } diff --git a/packages/next/build/webpack/plugins/next-esm-plugin.ts b/packages/next/build/webpack/plugins/next-esm-plugin.ts deleted file mode 100644 index b70db0aa98693..0000000000000 --- a/packages/next/build/webpack/plugins/next-esm-plugin.ts +++ /dev/null @@ -1,390 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2018 Prateek Bhatnagar - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * Webpack plugin for NextJs that runs a child compiler to generate a second (modern) JS bundle - * - * @author: Janicklas Ralph (https://github.com/janickals-ralph) - * - * Original source from which this was built upon - https://github.com/prateekbh/babel-esm-plugin - */ -import { - Compiler, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - compilation as CompilationType, - Plugin, - RuleSetRule, - RuleSetLoader, - Output, -} from 'webpack' - -const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin') -const MultiEntryPlugin = require('webpack/lib/MultiEntryPlugin') -const JsonpTemplatePlugin = require('webpack/lib/web/JsonpTemplatePlugin') -const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin') -const RuntimeChunkPlugin = require('webpack/lib/optimize/RuntimeChunkPlugin') - -type BabelConfigItem = string | [string] | [string, any] - -const PLUGIN_NAME = 'NextEsmPlugin' - -// Matches all variations of preset-env as a filepath. -// Example: /project/foo/node_modules/babel-preset-react-app/dependencies.js -const IS_PRESET_ENV = /(^|[\\/])(babel-preset-react-app\/dependencies(\.js)?|@babel\/(preset-)?env)([\\/]|$)/ - -// Matches Babel preset paths that support the useBuiltIns option -const PRESETS_WITH_USEBUILTINS = /(^|[\\/])(@babel\/(preset-)?react)([\\/]|$)/ - -// Matches Babel plugin paths that support the useBuiltIns option -const PLUGINS_WITH_USEBUILTINS = /(^|[\\/])(@babel\/(plugin-)?transform-react-jsx)([\\/]|$)/ - -export class NextEsmPlugin implements Plugin { - options: { - filename: any - chunkFilename: any - excludedPlugins: string[] - additionalPlugins: Plugin[] - } - - constructor(options: { - filename: any - chunkFilename: any - excludedPlugins?: string[] - additionalPlugins?: any - }) { - this.options = Object.assign( - { excludedPlugins: [], additionalPlugins: [] }, - options - ) - } - - apply(compiler: Compiler) { - compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => { - this.runBuild(compiler, compilation).then(callback) - }) - } - - getLoaders(rules: RuleSetRule[], predicate: (loader: string) => boolean) { - const results = [] - for (let rule of rules) { - if (Array.isArray(rule.use)) { - const matches = (rule.use as RuleSetLoader[]).filter( - (r) => r.loader && predicate(r.loader) - ) - if (matches.length > 0) { - results.push(...matches) - } - } - - const ruleUse = rule.use as RuleSetLoader - let ruleLoader = rule.loader - if (typeof ruleLoader === 'object' && 'loader' in ruleLoader) { - ruleLoader = ruleLoader.loader - } - if ( - (ruleUse?.loader && predicate(ruleUse.loader)) || - (ruleLoader && predicate(ruleLoader as string)) - ) { - results.push(ruleUse || rule) - } - } - return results - } - - updateOptions(childCompiler: Compiler) { - if (!childCompiler.options.module) { - throw new Error('Webpack.options.module not found!') - } - - let babelLoader = this.getLoaders( - childCompiler.options.module.rules, - (loader) => loader.includes('next-babel-loader') - )[0] - - if (!babelLoader) { - throw new Error('Babel-loader config not found!') - } - - babelLoader.options = Object.assign({}, babelLoader.options, { - isModern: true, - }) - if (typeof babelLoader.options !== 'string') { - this.ensureModernBabelOptions(babelLoader.options) - } - - const additionalBabelLoaders = this.getLoaders( - childCompiler.options.module.rules, - (loader) => /(^|[\\/])babel-loader([\\/]|$)/.test(loader) - ) - for (const loader of additionalBabelLoaders) { - // @TODO support string options? - if (!loader.options || typeof loader.options === 'string') continue - this.ensureModernBabelOptions(loader.options) - } - } - - ensureModernBabelOptions(options: { - presets?: BabelConfigItem[] - plugins?: BabelConfigItem[] - }) { - // find and remove known ES2017-to-ES5 transforms - if (options.presets) { - options.presets = options.presets.reduce( - (presets: BabelConfigItem[], preset) => { - const name = Array.isArray(preset) ? preset[0] : preset - const opts = Object.assign( - {}, - (Array.isArray(preset) && preset[1]) || {} - ) - - if (PRESETS_WITH_USEBUILTINS.test(name)) { - opts.useBuiltIns = true - } - - if (IS_PRESET_ENV.test(name)) { - presets.push([ - require('next/dist/compiled/babel/preset-env'), - { - bugfixes: true, - loose: true, - targets: { esmodules: true }, - }, - ]) - } else { - presets.push([name, opts]) - } - return presets - }, - [] - ) - } - - if (options.plugins) { - options.plugins = options.plugins.map((plugin) => { - const name = Array.isArray(plugin) ? plugin[0] : plugin - const opts = Object.assign( - {}, - (Array.isArray(plugin) && plugin[1]) || {} - ) - - if (PLUGINS_WITH_USEBUILTINS.test(name)) { - opts.useBuiltIns = true - } - - return [name, opts] - }) - } - } - - updateAssets( - compilation: CompilationType.Compilation, - childCompilation: CompilationType.Compilation - ) { - compilation.assets = Object.assign( - childCompilation.assets, - compilation.assets - ) - - compilation.namedChunkGroups = Object.assign( - childCompilation.namedChunkGroups, - compilation.namedChunkGroups - ) - - const unnamedChunks: CompilationType.Chunk[] = [] - const childChunkFileMap = childCompilation.chunks.reduce( - ( - chunkMap: { [key: string]: CompilationType.Chunk }, - chunk: CompilationType.Chunk - ) => { - // Dynamic chunks may not have a name. It'll be null in such cases - if (chunk.name === null) { - unnamedChunks.push(chunk) - } else { - chunkMap[chunk.name] = chunk - } - - return chunkMap - }, - {} - ) - - // Merge chunks - merge the files of chunks with the same name - compilation.chunks.forEach((chunk: CompilationType.Chunk) => { - const childChunk = childChunkFileMap[chunk.name] - - // Do not merge null named chunks since they are different - if (chunk.name !== null && childChunk?.files) { - delete childChunkFileMap[chunk.name] - chunk.files.push( - ...childChunk.files.filter((v: any) => !chunk.files.includes(v)) - ) - } - }) - - // Add modern only chunks into the main compilation - compilation.chunks.push( - ...Object.values(childChunkFileMap), - ...unnamedChunks - ) - - // Place modern only (unmerged) chunks inside the right entry point - compilation.entrypoints.forEach((entryPoint, entryPointName) => { - const childEntryPoint = childCompilation.entrypoints.get(entryPointName) - - childEntryPoint.chunks.forEach((chunk: CompilationType.Chunk) => { - if ( - // Add null named dynamic chunks since they weren't merged - chunk.name === null || - childChunkFileMap.hasOwnProperty(chunk.name) - ) { - entryPoint.chunks.push(chunk) - } - }) - }) - } - - async runBuild(compiler: Compiler, compilation: CompilationType.Compilation) { - const outputOptions: Output = { ...compiler.options.output } - - if (typeof this.options.filename === 'function') { - outputOptions.filename = this.options.filename(outputOptions.filename) - } else { - outputOptions.filename = this.options.filename - } - - if (typeof this.options.chunkFilename === 'function') { - outputOptions.chunkFilename = this.options.chunkFilename( - outputOptions.chunkFilename - ) - } else { - outputOptions.chunkFilename = this.options.chunkFilename - } - - let plugins = (compiler.options.plugins || []).filter( - (c) => - !( - this.options.excludedPlugins.includes(c.constructor.name) || - c.constructor.name === PLUGIN_NAME - ) - ) - - // Add the additionalPlugins - plugins = plugins.concat(this.options.additionalPlugins) - - /** - * We are deliberately not passing plugins in createChildCompiler. - * All webpack does with plugins is to call `apply` method on them - * with the childCompiler. - * But by then we haven't given childCompiler a fileSystem or other options - * which a few plugins might expect while execution the apply method. - * We do call the `apply` method of all plugins by ourselves later in the code - */ - const childCompiler = compilation.createChildCompiler( - PLUGIN_NAME, - outputOptions - ) - - childCompiler.context = compiler.context - childCompiler.inputFileSystem = compiler.inputFileSystem - childCompiler.outputFileSystem = compiler.outputFileSystem - - // Call the `apply` method of all plugins by ourselves. - if (Array.isArray(plugins)) { - for (const plugin of plugins) { - plugin.apply(childCompiler) - } - } - - let compilerEntries: any = compiler.options.entry - if (typeof compilerEntries === 'function') { - compilerEntries = await compilerEntries() - } - if (typeof compilerEntries === 'string') { - compilerEntries = { index: compilerEntries } - } - - Object.keys(compilerEntries).forEach((entry) => { - const entryFiles = compilerEntries[entry] - if (Array.isArray(entryFiles)) { - new MultiEntryPlugin(compiler.context, entryFiles, entry).apply( - childCompiler - ) - } else { - new SingleEntryPlugin(compiler.context, entryFiles, entry).apply( - childCompiler - ) - } - }) - - // Convert entry chunk to entry file - new JsonpTemplatePlugin().apply(childCompiler) - - const optimization = compiler.options.optimization - if (optimization) { - if (optimization.splitChunks) { - new SplitChunksPlugin( - Object.assign({}, optimization.splitChunks) - ).apply(childCompiler) - } - - if (optimization.runtimeChunk) { - new RuntimeChunkPlugin( - Object.assign({}, optimization.runtimeChunk) - ).apply(childCompiler) - } - } - - // Hold back the main compilation until our Child Compiler has completed so its assets get optimized - const child = new Promise((resolve, reject) => { - // Defer the child compiler until known main thread "dead time" (while Terser is doing minification in the background) - let started = false - compilation.hooks.optimizeChunkAssets.intercept({ - call: () => { - // only run the first time optimizeChunkAssets is called - if (started) return - started = true - - // Delay the Child Compiler until optimizeChunkAssets has had time to send work to the Terser pool - setTimeout(() => { - this.updateOptions(childCompiler) - - childCompiler.runAsChild((err, _entries, childCompilation) => { - if (err) { - return reject(err) - } - - if (childCompilation.errors.length > 0) { - return reject(childCompilation.errors[0]) - } - - this.updateAssets(compilation, childCompilation) - resolve() - }) - }, 500) - }, - }) - }) - compilation.hooks.optimizeAssets.tapPromise(PLUGIN_NAME, () => child) - } -} diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index f5395b9702f45..cbee7d5c3c1cb 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -327,7 +327,6 @@ Read more: https://err.sh/next.js/export-image-api` hotReloader: null, basePath: nextConfig.basePath, canonicalBase: nextConfig.amp?.canonicalBase || '', - isModern: nextConfig.experimental.modern, ampValidatorPath: nextConfig.experimental.amp?.validator || undefined, ampSkipValidation: nextConfig.experimental.amp?.skipValidation || false, ampOptimizerConfig: nextConfig.experimental.amp?.optimizer || undefined, diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 9ff522442d2f8..65240c95279b9 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -46,7 +46,6 @@ const defaultConfig: { [key: string]: any } = { (Number(process.env.CIRCLE_NODE_TOTAL) || (os.cpus() || { length: 1 }).length) - 1 ), - modern: false, plugins: false, profiling: false, sprFlushToDisk: true, diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 04f36af36f2d6..4bc920f05a4a2 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -37,13 +37,6 @@ function dedupe(bundles: T[]): T[] { return kept } -function getOptionalModernScriptVariant(path: string): string { - if (process.env.__NEXT_MODERN_BUILD) { - return path.replace(/\.js$/, '.module.js') - } - return path -} - type DocumentFiles = { sharedFiles: readonly string[] pageFiles: readonly string[] @@ -225,10 +218,7 @@ export class Head extends Component< return ( dedupe(dynamicImports) .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. - if (!bundle.file.endsWith(getOptionalModernScriptVariant('.js'))) { + if (!bundle.file.endsWith('.js')) { return null } @@ -255,10 +245,7 @@ export class Head extends Component< getPreloadMainLinks(files: DocumentFiles): JSX.Element[] | null { const { assetPrefix, devOnlyCacheBusterQueryString } = this.context const preloadFiles = files.allFiles.filter((file: string) => { - // `dynamicImports` will contain both `.js` and `.module.js` when - // the feature is enabled. This clause will filter down to the - // modern variants only. - return file.endsWith(getOptionalModernScriptVariant('.js')) + return file.endsWith('.js') }) return !preloadFiles.length @@ -560,13 +547,6 @@ export class NextScript extends Component { } = this.context return dedupe(dynamicImports).map((bundle) => { - let modernProps = {} - if (process.env.__NEXT_MODERN_BUILD) { - modernProps = bundle.file.endsWith('.module.js') - ? { type: 'module' } - : { noModule: true } - } - if (!bundle.file.endsWith('.js') || files.allFiles.includes(bundle.file)) return null @@ -581,7 +561,6 @@ export class NextScript extends Component { crossOrigin={ this.props.crossOrigin || process.env.__NEXT_CROSS_ORIGIN } - {...modernProps} /> ) }) @@ -601,13 +580,6 @@ export class NextScript extends Component { ) return [...normalScripts, ...lowPriorityScripts].map((file) => { - let modernProps = {} - if (process.env.__NEXT_MODERN_BUILD) { - modernProps = file.endsWith('.module.js') - ? { type: 'module' } - : { noModule: true } - } - return ( +
index
+ + ) +} + +export default Page diff --git a/test/integration/script-loader/pages/page1.js b/test/integration/script-loader/pages/page1.js new file mode 100644 index 0000000000000..9fea586b4a736 --- /dev/null +++ b/test/integration/script-loader/pages/page1.js @@ -0,0 +1,16 @@ +import Script from 'next/experimental-script' + +const Page = () => { + return ( +
+ +
page1
+
+ ) +} + +export default Page diff --git a/test/integration/script-loader/pages/page2.js b/test/integration/script-loader/pages/page2.js new file mode 100644 index 0000000000000..cb779f0a90f3d --- /dev/null +++ b/test/integration/script-loader/pages/page2.js @@ -0,0 +1,16 @@ +import Script from 'next/experimental-script' + +const Page = () => { + return ( +
+ +
page2
+
+ ) +} + +export default Page diff --git a/test/integration/script-loader/pages/page3.js b/test/integration/script-loader/pages/page3.js new file mode 100644 index 0000000000000..5ee81d1a30dba --- /dev/null +++ b/test/integration/script-loader/pages/page3.js @@ -0,0 +1,23 @@ +import Script from 'next/experimental-script' + +const Page = () => { + return ( +
+ + +
page3
+
+ ) +} + +export default Page diff --git a/test/integration/script-loader/pages/page4.js b/test/integration/script-loader/pages/page4.js new file mode 100644 index 0000000000000..5e24bdfb784f9 --- /dev/null +++ b/test/integration/script-loader/pages/page4.js @@ -0,0 +1,38 @@ +import Script from 'next/experimental-script' + +const url = + 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js' + +const Page = () => { + return ( +
+ + + +
+
+ ) +} + +export default Page diff --git a/test/integration/script-loader/styles/styles.css b/test/integration/script-loader/styles/styles.css new file mode 100644 index 0000000000000..301c8e48e6e18 --- /dev/null +++ b/test/integration/script-loader/styles/styles.css @@ -0,0 +1,6 @@ +body { + font-family: 'Arial', sans-serif; + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} diff --git a/test/integration/script-loader/test/index.test.js b/test/integration/script-loader/test/index.test.js new file mode 100644 index 0000000000000..a2a764baad748 --- /dev/null +++ b/test/integration/script-loader/test/index.test.js @@ -0,0 +1,149 @@ +/* eslint-env jest */ + +import { join } from 'path' +import { + renderViaHTTP, + nextServer, + startApp, + stopApp, + nextBuild, + waitFor, +} from 'next-test-utils' +import webdriver from 'next-webdriver' +import cheerio from 'cheerio' + +jest.setTimeout(1000 * 60 * 5) + +let appDir = join(__dirname, '..') +let server +let appPort + +describe('Script Loader', () => { + beforeAll(async () => { + await nextBuild(appDir) + const app = nextServer({ + dir: appDir, + dev: false, + quiet: true, + }) + + server = await startApp(app) + appPort = server.address().port + }) + afterAll(() => { + stopApp(server) + }) + + it('priority defer', async () => { + let browser + try { + browser = await webdriver(appPort, '/') + await waitFor(1000) + + const script = await browser.elementById('script') + const src = await script.getAttribute('src') + const scriptPreload = await browser.elementsByCss( + `link[rel=preload][href="${src}"]` + ) + const endScripts = await browser.elementsByCss( + '#script ~ script[src^="/_next/static/"]' + ) + const endPreloads = await browser.elementsByCss( + `link[rel=preload][href="${src}"] ~ link[rel=preload][href^="/_next/static/"]` + ) + + // Renders script tag + expect(script).toBeDefined() + // Renders preload + expect(scriptPreload.length).toBeGreaterThan(0) + // Script is inserted at the end + expect(endScripts.length).toBe(0) + //Preload is defined at the end + expect(endPreloads.length).toBe(0) + } finally { + if (browser) await browser.close() + } + }) + + it('priority lazy', async () => { + let browser + try { + browser = await webdriver(appPort, '/page3') + + await browser.waitForElementByCss('#onload-div') + await waitFor(1000) + + const script = await browser.elementById('script') + const endScripts = await browser.elementsByCss( + '#script ~ script[src^="/_next/static/"]' + ) + + // Renders script tag + expect(script).toBeDefined() + // Script is inserted at the end + expect(endScripts.length).toBe(0) + } finally { + if (browser) await browser.close() + } + }) + + it('priority eager', async () => { + const html = await renderViaHTTP(appPort, '/page1') + const $ = cheerio.load(html) + + const script = $('#script') + const src = script.attr('src') + + // Renders script tag + expect(script).toBeDefined() + // Preload is inserted at the beginning + expect( + $( + `link[rel=preload][href="${src}"] ~ link[rel=preload][href^="/_next/static/"]` + ).length && + !$( + `link[rel=preload][href^="/_next/static/chunks/main"] ~ link[rel=preload][href="${src}"]` + ).length + ).toBeTruthy() + + // Preload is inserted after fonts and CSS + expect( + $( + `link[rel=stylesheet][href^="/_next/static/css"] ~ link[rel=preload][href="${src}"]` + ).length + ).toBeGreaterThan(0) + expect( + $( + `link[rel=stylesheet][href="https://fonts.googleapis.com/css?family=Voces"] ~ link[rel=preload][href="${src}"]` + ).length + ).toBeGreaterThan(0) + + // Script is inserted before NextScripts + expect( + $('#__NEXT_DATA__ ~ #script ~ script[src^="/_next/static/chunks/main"]') + .length + ).toBeGreaterThan(0) + }) + + it('priority dangerouslyBlockRendering', async () => { + const html = await renderViaHTTP(appPort, '/page2') + const $ = cheerio.load(html) + + // Script is inserted in place + expect($('.container #script').length).toBeGreaterThan(0) + }) + + it('onloads fire correctly', async () => { + let browser + try { + browser = await webdriver(appPort, '/page4') + await waitFor(3000) + + const text = await browser.elementById('text').text() + + expect(text).toBe('aaabbbccc') + } finally { + if (browser) await browser.close() + } + }) +}) From d016eadeef14a439fad2a0cf4d638f6618e20749 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 1 Dec 2020 14:53:27 -0600 Subject: [PATCH 067/119] v10.0.4-canary.0 --- 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-env/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-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index f3d3e61fbad93..f9d6467a8a94b 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.0.3" + "version": "10.0.4-canary.0" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index c04e2f203de6d..5bb2c94ccc805 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.0.3", + "version": "10.0.4-canary.0", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 83b0732cb35d4..d047007437c29 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": "10.0.3", + "version": "10.0.4-canary.0", "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 d001fa64a19bb..f274d5d82f92e 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.0.3", + "version": "10.0.4-canary.0", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 5eea71d782c21..89dbe5f279988 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.0.3", + "version": "10.0.4-canary.0", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 630500755f805..2904e56fd6ec4 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.0.3", + "version": "10.0.4-canary.0", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 453fcbd11c81b..df18432b987a1 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.0.3", + "version": "10.0.4-canary.0", "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 7abaa13841918..629347db7593d 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": "10.0.3", + "version": "10.0.4-canary.0", "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 afa24d889f742..afb9b993dab7c 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "10.0.3", + "version": "10.0.4-canary.0", "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 3f402572444c1..2884561461b8f 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.0.3", + "version": "10.0.4-canary.0", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index d45a2d19fb579..345fc962e67db 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.0.3", + "version": "10.0.4-canary.0", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index c3bb7cad6e9a3..79abb76bf0591 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.0.3", + "version": "10.0.4-canary.0", "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 27cee11e7e807..a1fd45b4df073 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.0.3", + "version": "10.0.4-canary.0", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -63,10 +63,10 @@ "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.0.3", - "@next/polyfill-module": "10.0.3", - "@next/react-dev-overlay": "10.0.3", - "@next/react-refresh-utils": "10.0.3", + "@next/env": "10.0.4-canary.0", + "@next/polyfill-module": "10.0.4-canary.0", + "@next/react-dev-overlay": "10.0.4-canary.0", + "@next/react-refresh-utils": "10.0.4-canary.0", "ast-types": "0.13.2", "babel-plugin-transform-define": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.24", @@ -128,7 +128,7 @@ "@babel/preset-react": "7.12.5", "@babel/preset-typescript": "7.12.1", "@babel/types": "7.12.6", - "@next/polyfill-nomodule": "10.0.3", + "@next/polyfill-nomodule": "10.0.4-canary.0", "@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 ed3cd1ac1da68..0b492f8f1b9ad 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": "10.0.3", + "version": "10.0.4-canary.0", "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 f1e93c40f6645..257daf0dcedb8 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": "10.0.3", + "version": "10.0.4-canary.0", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From b86a1731e10d3752f8c3d440eaf30b3ada9e219f Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Tue, 1 Dec 2020 16:58:52 -0500 Subject: [PATCH 068/119] Error Message Clarity (#16052) * make the error message more clear if webpack config comes back undefined * Update check and add test * bump * Update build-output test Co-authored-by: JJ Kasper --- errors/undefined-webpack-config.md | 28 +++++++++++++++ packages/next/build/webpack-config.ts | 7 ++++ .../build-output/test/index.test.js | 4 +-- .../undefined-webpack-config/next.config.js | 3 ++ .../undefined-webpack-config/pages/index.js | 3 ++ .../test/index.test.js | 34 +++++++++++++++++++ 6 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 errors/undefined-webpack-config.md create mode 100644 test/integration/undefined-webpack-config/next.config.js create mode 100644 test/integration/undefined-webpack-config/pages/index.js create mode 100644 test/integration/undefined-webpack-config/test/index.test.js diff --git a/errors/undefined-webpack-config.md b/errors/undefined-webpack-config.md new file mode 100644 index 0000000000000..fb15fdb731ba9 --- /dev/null +++ b/errors/undefined-webpack-config.md @@ -0,0 +1,28 @@ +# Missing webpack config + +#### Why This Error Occurred + +The value returned from the custom `webpack` function in your `next.config.js` was undefined. This can occur from the initial config value not being returned. + +#### Possible Ways to Fix It + +Make sure to return the `webpack` config from your custom `webpack` function in your `next.config.js` + +```js +// next.config.js + +module.exports = { + webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { + // Note: we provide webpack above so you should not `require` it + // Perform customizations to webpack config + config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)) + + // Important: return the modified config + return config + }, +} +``` + +### Useful Links + +- [Custom webpack config Documentation](https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index e34c745b170fc..7d32a355b366c 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1224,6 +1224,13 @@ export default async function getBaseWebpackConfig( webpack, }) + if (!webpackConfig) { + throw new Error( + 'Webpack config is undefined. You may have forgot to return properly from within the "webpack" method of your next.config.js.\n' + + 'See more info here https://err.sh/next.js/undefined-webpack-config' + ) + } + if (dev && originalDevtool !== webpackConfig.devtool) { webpackConfig.devtool = originalDevtool devtoolRevertWarning(originalDevtool) diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index 5beb126dc409a..3512b72b523c9 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -101,10 +101,10 @@ describe('Build Output', () => { expect(parseFloat(err404Size) - 3.6).toBeLessThanOrEqual(0) expect(err404Size.endsWith('kB')).toBe(true) - expect(parseFloat(err404FirstLoad) - 65.1).toBeLessThanOrEqual(0) + expect(parseFloat(err404FirstLoad) - 65.2).toBeLessThanOrEqual(0) expect(err404FirstLoad.endsWith('kB')).toBe(true) - expect(parseFloat(sharedByAll) - 61.7).toBeLessThanOrEqual(0) + expect(parseFloat(sharedByAll) - 61.8).toBeLessThanOrEqual(0) expect(sharedByAll.endsWith('kB')).toBe(true) if (_appSize.endsWith('kB')) { diff --git a/test/integration/undefined-webpack-config/next.config.js b/test/integration/undefined-webpack-config/next.config.js new file mode 100644 index 0000000000000..bade10b3d5504 --- /dev/null +++ b/test/integration/undefined-webpack-config/next.config.js @@ -0,0 +1,3 @@ +module.exports = { + webpack() {}, +} diff --git a/test/integration/undefined-webpack-config/pages/index.js b/test/integration/undefined-webpack-config/pages/index.js new file mode 100644 index 0000000000000..1b2698e72394f --- /dev/null +++ b/test/integration/undefined-webpack-config/pages/index.js @@ -0,0 +1,3 @@ +export default function Page() { + return 'Index page' +} diff --git a/test/integration/undefined-webpack-config/test/index.test.js b/test/integration/undefined-webpack-config/test/index.test.js new file mode 100644 index 0000000000000..6bfece853f10c --- /dev/null +++ b/test/integration/undefined-webpack-config/test/index.test.js @@ -0,0 +1,34 @@ +/* eslint-env jest */ + +import { join } from 'path' +import { launchApp, nextBuild } from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 2) + +const appDir = join(__dirname, '../') +const expectedErr = /Webpack config is undefined. You may have forgot to return properly from within the "webpack" method of your next.config.js/ + +describe('undefined webpack config error', () => { + it('should show in production mode', async () => { + const result = await nextBuild(appDir, [], { + stdout: true, + stderr: true, + }) + expect(result.stderr || '' + result.stdout || '').toMatch(expectedErr) + }) + + it('should show in dev mode', async () => { + let output = '' + + await launchApp(appDir, [], { + onStderr(msg) { + output += msg || '' + }, + ontStdout(msg) { + output += msg || '' + }, + }) + + expect(output).toMatch(expectedErr) + }) +}) From 7ae76f4326840fb92cc542d867c5b9c27333a80c Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 1 Dec 2020 18:00:06 -0600 Subject: [PATCH 069/119] v10.0.4-canary.1 --- 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-env/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-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index f9d6467a8a94b..bbd5abe3beff3 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.0.4-canary.0" + "version": "10.0.4-canary.1" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 5bb2c94ccc805..721635311bdc7 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "keywords": [ "react", "next", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index d047007437c29..5a32e340cf3aa 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": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 f274d5d82f92e..93cd123841db6 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 89dbe5f279988..a0a7acdc4da50 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 2904e56fd6ec4..567383380ddc4 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index df18432b987a1..47cbc02d0be7a 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 629347db7593d..17f62447a9837 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": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 afb9b993dab7c..e4921dfb6952e 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 2884561461b8f..ca31ed833b1e8 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 345fc962e67db..0b82c9f320e88 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 79abb76bf0591..ecc14e2a4d17d 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 a1fd45b4df073..40747950224f5 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -63,10 +63,10 @@ "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.0.4-canary.0", - "@next/polyfill-module": "10.0.4-canary.0", - "@next/react-dev-overlay": "10.0.4-canary.0", - "@next/react-refresh-utils": "10.0.4-canary.0", + "@next/env": "10.0.4-canary.1", + "@next/polyfill-module": "10.0.4-canary.1", + "@next/react-dev-overlay": "10.0.4-canary.1", + "@next/react-refresh-utils": "10.0.4-canary.1", "ast-types": "0.13.2", "babel-plugin-transform-define": "2.0.0", "babel-plugin-transform-react-remove-prop-types": "0.4.24", @@ -128,7 +128,7 @@ "@babel/preset-react": "7.12.5", "@babel/preset-typescript": "7.12.1", "@babel/types": "7.12.6", - "@next/polyfill-nomodule": "10.0.4-canary.0", + "@next/polyfill-nomodule": "10.0.4-canary.1", "@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 0b492f8f1b9ad..6a29986396b4e 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": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "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 257daf0dcedb8..c4117a4398d85 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": "10.0.4-canary.0", + "version": "10.0.4-canary.1", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From bb1bdbb10fb7f8b4f5d6b05c5e63ba589151cb50 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 2 Dec 2020 06:55:43 -0600 Subject: [PATCH 070/119] Update @ampproject/toolbox-optimizer to latest version (#19722) This updates to the latest version of the `@ampproject/toolbox-optimizer` which contains updates for dependencies to remove the warnings on install from `npm` x-ref: https://github.com/vercel/next.js/pull/18994 Closes: https://github.com/vercel/next.js/issues/17416 --- packages/next/package.json | 2 +- yarn.lock | 153 ++++++++++++++++++------------------- 2 files changed, 74 insertions(+), 81 deletions(-) diff --git a/packages/next/package.json b/packages/next/package.json index 40747950224f5..e4a794a7e610f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -60,7 +60,7 @@ ] }, "dependencies": { - "@ampproject/toolbox-optimizer": "2.7.0-alpha.1", + "@ampproject/toolbox-optimizer": "2.7.1-alpha.0", "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", "@next/env": "10.0.4-canary.1", diff --git a/yarn.lock b/yarn.lock index 30cf1378d2da8..fb09cad7ec295 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,56 +2,56 @@ # yarn lockfile v1 -"@ampproject/toolbox-core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.6.0.tgz#9824d5f133d82106a9bf0774920843c69fa5c869" - integrity sha512-sDMnHj8WaX3tqJS5VsIHkeW98nq5WQ0C9RoFc1PPS3rmYIlS0vhAfHbrjJw6wtuxBTQFxccje+Ew+2OJ2D15kA== +"@ampproject/toolbox-core@^2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.7.1-alpha.0.tgz#6ac10a832ecbb2b8151bbad964f29770d91560d6" + integrity sha512-Vutu9Bzo43Hp6Oxem2f14+d1WT107TJiVgpMGdyhdOfd7s+K/D7iI2EKuEsqLRGfJ/NWyXhv8xkQnGZXjIIXjQ== dependencies: - cross-fetch "3.0.5" + cross-fetch "3.0.6" lru-cache "6.0.0" -"@ampproject/toolbox-optimizer@2.7.0-alpha.1": - version "2.7.0-alpha.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.7.0-alpha.1.tgz#ab4c386645f991e5da5a9d2967ed2bb734a9f6c4" - integrity sha512-2wTvOyM6GP6FrYQzxSQCg43STo1jMRGeDKa6YUkYXYH9fm9Wbt2wTRx+ajjb48JQ6WwUnGwga1MhQhVFzRQ+wQ== +"@ampproject/toolbox-optimizer@2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.7.1-alpha.0.tgz#1571dcd02608223ff68f6b7223102a123e381197" + integrity sha512-WGPZKVQvHgNYJk1XVJCCmY+NVGTGJtvn0OALDyiegN4FJWOcilQUhCIcjMkZN59u1flz/u+sEKccM5qsROqVyg== dependencies: - "@ampproject/toolbox-core" "^2.6.0" - "@ampproject/toolbox-runtime-version" "^2.7.0-alpha.1" + "@ampproject/toolbox-core" "^2.7.1-alpha.0" + "@ampproject/toolbox-runtime-version" "^2.7.1-alpha.0" "@ampproject/toolbox-script-csp" "^2.5.4" - "@ampproject/toolbox-validator-rules" "^2.5.4" + "@ampproject/toolbox-validator-rules" "^2.7.1-alpha.0" abort-controller "3.0.0" - cross-fetch "3.0.5" - cssnano-simple "1.2.0" - dom-serializer "1.0.1" - domhandler "3.0.0" - domutils "2.1.0" - htmlparser2 "4.1.0" + cross-fetch "3.0.6" + cssnano-simple "1.2.1" + dom-serializer "1.1.0" + domhandler "3.3.0" + domutils "2.4.2" + htmlparser2 "5.0.1" https-proxy-agent "5.0.0" lru-cache "6.0.0" - node-fetch "2.6.0" + node-fetch "2.6.1" normalize-html-whitespace "1.0.0" postcss "7.0.32" postcss-safe-parser "4.0.2" - terser "5.1.0" + terser "5.5.1" -"@ampproject/toolbox-runtime-version@^2.7.0-alpha.1": - version "2.7.0-alpha.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.7.0-alpha.1.tgz#2ecd603e1fc986f21048947639e99b5706e01ec3" - integrity sha512-JruvO4RfaC/piKOY/2w6vuasNjdrHnb+xvmQTl4zBBdMsDooohZKsN9jv9YiKIdpny4MzLt1ce497840vJJq+g== +"@ampproject/toolbox-runtime-version@^2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.7.1-alpha.0.tgz#1b1fac873811696294011f9ff798fd405de8ffc3" + integrity sha512-CB3JicgGp/Hp5Tey5/S1hgR+E5kNhS9V2GUeMW7qDYDqaJ6dLNlQ5tYgqaebrpeRxufZrK5MRd7bnM4o5tAyuA== dependencies: - "@ampproject/toolbox-core" "^2.6.0" + "@ampproject/toolbox-core" "^2.7.1-alpha.0" "@ampproject/toolbox-script-csp@^2.5.4": version "2.5.4" resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-2.5.4.tgz#d8b7b91a678ae8f263cb36d9b74e441b7d633aad" integrity sha512-+knTYetI5nWllRZ9wFcj7mYxelkiiFVRAAW/hl0ad8EnKHMH82tRlk40CapEnUHhp6Er5sCYkumQ8dngs3Q4zQ== -"@ampproject/toolbox-validator-rules@^2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.5.4.tgz#7dee3a3edceefea459d060571db8cc6e7bbf0dd6" - integrity sha512-bS7uF+h0s5aiklc/iRaujiSsiladOsZBLrJ6QImJDXvubCAQtvE7om7ShlGSXixkMAO0OVMDWyuwLlEy8V1Ing== +"@ampproject/toolbox-validator-rules@^2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.7.1-alpha.0.tgz#ff00958bc2015d9d4697143da606e34fc773577a" + integrity sha512-1+nrZDkHIk8Go4Qh9DUfjTrbsW0OTvLYdwcI716LdgyB3WsmLfsAx/fJU/9Tc14a1YS73ptx2Xb0Tdh+ewwEIw== dependencies: - cross-fetch "3.0.5" + cross-fetch "^3.0.6" "@babel/code-frame@7.10.4", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": version "7.10.4" @@ -5376,12 +5376,12 @@ cross-env@6.0.3: dependencies: cross-spawn "^7.0.0" -cross-fetch@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.5.tgz#2739d2981892e7ab488a7ad03b92df2816e03f4c" - integrity sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew== +cross-fetch@3.0.6, cross-fetch@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== dependencies: - node-fetch "2.6.0" + node-fetch "2.6.1" cross-spawn-async@^2.1.1: version "2.2.5" @@ -5664,14 +5664,6 @@ cssnano-preset-default@^4.0.7: postcss-svgo "^4.0.2" postcss-unique-selectors "^4.0.1" -cssnano-preset-simple@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-1.2.0.tgz#afcf13eb076e8ebd91c4f311cd449781c14c7371" - integrity sha512-zojGlY+KasFeQT/SnD/WqYXHcKddz2XHRDtIwxrWpGqGHp5IyLWsWFS3UW7pOf3AWvfkpYSRdxOSlYuJPz8j8g== - dependencies: - caniuse-lite "^1.0.30001093" - postcss "^7.0.32" - cssnano-preset-simple@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-1.2.1.tgz#8976013114b1fc4718253d30f21aaed1780fb80e" @@ -5680,14 +5672,6 @@ cssnano-preset-simple@1.2.1: caniuse-lite "^1.0.30001093" postcss "^7.0.32" -cssnano-simple@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-1.2.0.tgz#b8cc5f52c2a52e6513b4636d0da165ec9d48d327" - integrity sha512-pton9cZ70/wOCWMAbEGHO1ACsW1KggTB6Ikj7k71uOEsz6SfByH++86+WAmXjRSc9q/g9gxkpFP9bDX9vRotdA== - dependencies: - cssnano-preset-simple "1.2.0" - postcss "^7.0.32" - cssnano-simple@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-1.2.1.tgz#6de5d9dd75774bc8f31767573410a952c7dd8a12" @@ -6115,17 +6099,17 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@0, dom-serializer@^0.2.1: +dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" dependencies: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.0.1.tgz#79695eb49af3cd8abc8d93a73da382deb1ca0795" - integrity sha512-1Aj1Qy3YLbdslkI75QEOfdp9TkQ3o8LRISAzxOibjBs/xWwr1WxZFOQphFkZuepHFGo+kB8e5FVJSS0faAJ4Rw== +dom-serializer@1.1.0, dom-serializer@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.1.0.tgz#5f7c828f1bfc44887dc2a315ab5c45691d544b58" + integrity sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ== dependencies: domelementtype "^2.0.1" domhandler "^3.0.0" @@ -6167,9 +6151,10 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@3.0.0, domhandler@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" +domhandler@3.3.0, domhandler@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== dependencies: domelementtype "^2.0.1" @@ -6179,6 +6164,12 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + dependencies: + domelementtype "^2.0.1" + domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -6186,14 +6177,14 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" -domutils@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.1.0.tgz#7ade3201af43703fde154952e3a868eb4b635f16" - integrity sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg== +domutils@2.4.2, domutils@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.2.tgz#7ee5be261944e1ad487d9aa0616720010123922b" + integrity sha512-NKbgaM8ZJOecTZsIzW5gSuplsX2IWW2mIK7xVr8hTQF2v1CJWTmLZ1HOCh5sH+IzVPAGE5IucooOkvwBRAdowA== dependencies: - dom-serializer "^0.2.1" + dom-serializer "^1.0.1" domelementtype "^2.0.1" - domhandler "^3.0.0" + domhandler "^3.3.0" domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" @@ -6202,14 +6193,6 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" - dependencies: - dom-serializer "^0.2.1" - domelementtype "^2.0.1" - domhandler "^3.0.0" - dot-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" @@ -8001,13 +7984,14 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -htmlparser2@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" +htmlparser2@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" + integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== dependencies: domelementtype "^2.0.1" - domhandler "^3.0.0" - domutils "^2.0.0" + domhandler "^3.3.0" + domutils "^2.4.2" entities "^2.0.0" htmlparser2@^3.9.1: @@ -14968,7 +14952,7 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@^0.5.16: +source-map-support@^0.5.16, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -14991,7 +14975,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" -source-map@0.7.3: +source-map@0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -15719,6 +15703,15 @@ terser@5.1.0, terser@^5.0.0: source-map "~0.6.1" source-map-support "~0.5.12" +terser@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + terser@^3.8.2: version "3.17.0" resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" From 8c2cb4640a4279b13416a38846786526874d6bea Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Wed, 2 Dec 2020 10:23:42 -0600 Subject: [PATCH 071/119] Clarify data fetching documentation on calling API routes. (#19748) We've received some feedback that the current note about calling API routes inside gSP/gSSP is confusing. This updates the wording to make it clear you can still use `fetch` in your application, and also to not say you "import" an API route. You import the _logic_ inside the route. --- docs/basic-features/data-fetching.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/basic-features/data-fetching.md b/docs/basic-features/data-fetching.md index b3402cb23e149..0a7c05aa96ce2 100644 --- a/docs/basic-features/data-fetching.md +++ b/docs/basic-features/data-fetching.md @@ -114,8 +114,8 @@ The `context` parameter is an object containing the following keys: > This includes reading from the filesystem or a database. > **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to -> call an API route in your application. -> Instead, directly import the API route and call its function yourself. +> call an API route in `getStaticProps`. +> Instead, directly import the logic used inside your API route. > You may need to slightly refactor your code for this approach. > > Fetching from an external API is fine! @@ -661,8 +661,8 @@ The `context` parameter is an object containing the following keys: > This includes reading from the filesystem or a database. > **Note**: You should not use [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to -> call an API route in your application. -> Instead, directly import the API route and call its function yourself. +> call an API route in `getServerSideProps`. +> Instead, directly import the logic used inside your API route. > You may need to slightly refactor your code for this approach. > > Fetching from an external API is fine! From 3924b0047865f7a1c11694e5940d2491252d1003 Mon Sep 17 00:00:00 2001 From: Michael McQuade Date: Wed, 2 Dec 2020 12:09:54 -0600 Subject: [PATCH 072/119] docs/Remove extra word (#19754) Remove extra word "the" --- docs/deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deployment.md b/docs/deployment.md index fc1a806e9ccf8..b6464f4e46fbf 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -40,7 +40,7 @@ By using the DPS workflow, in addition to doing _code reviews_, you can do _depl For example, the [hybrid pages](/docs/basic-features/pages.md) approach is fully supported out of the box. - Every page can either use [Static Generation](/docs/basic-features/pages.md#static-generation) or [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering). -- Pages that use [Static Generation](/docs/basic-features/pages.md#static-generation) and assets (JS, CSS, images, fonts, etc) will automatically be served from the [Vercel's Edge Network](https://vercel.com/docs/v2/edge-network/overview), which is blazingly fast. +- Pages that use [Static Generation](/docs/basic-features/pages.md#static-generation) and assets (JS, CSS, images, fonts, etc) will automatically be served from [Vercel's Edge Network](https://vercel.com/docs/v2/edge-network/overview), which is blazingly fast. - Pages that use [Server-Side Rendering](/docs/basic-features/pages.md#server-side-rendering) and [API routes](/docs/api-routes/introduction.md) will automatically become isolated Serverless Functions. This allows page rendering and API requests to scale infinitely. ### Custom Domains, Environment Variables, Automatic HTTPS, and more From 20949612dfb56e44a623498eb3237a69177229e5 Mon Sep 17 00:00:00 2001 From: Carmelo Scandaliato <8927157+cascandaliato@users.noreply.github.com> Date: Wed, 2 Dec 2020 20:45:31 +0100 Subject: [PATCH 073/119] Replace Graphcool with a working GraphQL endpoint in with-apollo-and-redux example (#19248) The example stopped working when the GraphQL service [Graphcool](https://www.graph.cool/) shut down in July. I have replaced it with an [Hasura](https://hasura.io/) endpoint. Fixes #19093 --- examples/with-apollo-and-redux/README.md | 2 ++ .../components/PostList.js | 2 +- .../components/PostUpvoter.js | 11 ++++---- .../components/Submit.js | 28 ++++++++++--------- examples/with-apollo-and-redux/lib/apollo.js | 13 +++++++-- examples/with-apollo-and-redux/package.json | 1 + 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/examples/with-apollo-and-redux/README.md b/examples/with-apollo-and-redux/README.md index 622fd8c1528d8..267ec45b04a83 100644 --- a/examples/with-apollo-and-redux/README.md +++ b/examples/with-apollo-and-redux/README.md @@ -4,6 +4,8 @@ This example serves as a conduit if you were using Apollo 1.X with Redux, and ar In 3.0.0, Apollo serves out-of-the-box support for redux in favor of Apollo's state management. This example aims to be an amalgamation of the [`with-apollo`](https://github.com/vercel/next.js/tree/master/examples/with-apollo) and [`with-redux`](https://github.com/vercel/next.js/tree/master/examples/with-redux) examples. +To inspect the GraphQL API, use its [web IDE](https://nextjs-graphql-with-prisma-simple.vercel.app/api). + ## Deploy your own Deploy the example using [Vercel](https://vercel.com): diff --git a/examples/with-apollo-and-redux/components/PostList.js b/examples/with-apollo-and-redux/components/PostList.js index a2a264488f01e..d9b01d3ef1e6a 100644 --- a/examples/with-apollo-and-redux/components/PostList.js +++ b/examples/with-apollo-and-redux/components/PostList.js @@ -4,7 +4,7 @@ import PostUpvoter from './PostUpvoter' export const ALL_POSTS_QUERY = gql` query allPosts($first: Int!, $skip: Int!) { - allPosts(orderBy: createdAt_DESC, first: $first, skip: $skip) { + allPosts(orderBy: { createdAt: desc }, first: $first, skip: $skip) { id title votes diff --git a/examples/with-apollo-and-redux/components/PostUpvoter.js b/examples/with-apollo-and-redux/components/PostUpvoter.js index 122ad86bfe2a5..a34d697113798 100644 --- a/examples/with-apollo-and-redux/components/PostUpvoter.js +++ b/examples/with-apollo-and-redux/components/PostUpvoter.js @@ -1,9 +1,9 @@ import { gql, useMutation } from '@apollo/client' import PropTypes from 'prop-types' -const UPDATE_POST_MUTATION = gql` - mutation updatePost($id: ID!, $votes: Int) { - updatePost(id: $id, votes: $votes) { +const VOTE_POST = gql` + mutation votePost($id: String!) { + votePost(id: $id) { __typename id votes @@ -12,13 +12,12 @@ const UPDATE_POST_MUTATION = gql` ` const PostUpvoter = ({ votes, id }) => { - const [updatePost] = useMutation(UPDATE_POST_MUTATION) + const [votePost] = useMutation(VOTE_POST) const upvotePost = () => { - updatePost({ + votePost({ variables: { id, - votes: votes + 1, }, optimisticResponse: { __typename: 'Mutation', diff --git a/examples/with-apollo-and-redux/components/Submit.js b/examples/with-apollo-and-redux/components/Submit.js index f3a44607f1d37..5b0cd4b17b2b3 100644 --- a/examples/with-apollo-and-redux/components/Submit.js +++ b/examples/with-apollo-and-redux/components/Submit.js @@ -1,5 +1,4 @@ import { gql, useMutation } from '@apollo/client' -import { ALL_POSTS_QUERY, allPostsQueryVars } from './PostList' const CREATE_POST_MUTATION = gql` mutation createPost($title: String!, $url: String!) { @@ -26,19 +25,22 @@ const Submit = () => { createPost({ variables: { title, url }, - update: (proxy, { data: { createPost } }) => { - const data = proxy.readQuery({ - query: ALL_POSTS_QUERY, - variables: allPostsQueryVars, - }) - // Update the cache with the new post at the top of the - proxy.writeQuery({ - query: ALL_POSTS_QUERY, - data: { - ...data, - allPosts: [createPost, ...data.allPosts], + update: (cache, { data: { createPost } }) => { + cache.modify({ + fields: { + allPosts(existingPosts = []) { + const newPostRef = cache.writeFragment({ + data: createPost, + fragment: gql` + fragment NewPost on allPosts { + id + type + } + `, + }) + return [newPostRef, ...existingPosts] + }, }, - variables: allPostsQueryVars, }) }, }) diff --git a/examples/with-apollo-and-redux/lib/apollo.js b/examples/with-apollo-and-redux/lib/apollo.js index 7c66dc9bb8655..b91ca34b1c7df 100644 --- a/examples/with-apollo-and-redux/lib/apollo.js +++ b/examples/with-apollo-and-redux/lib/apollo.js @@ -2,6 +2,7 @@ import { useMemo } from 'react' import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' import { concatPagination } from '@apollo/client/utilities' import merge from 'deepmerge' +import isEqual from 'lodash/isEqual' let apolloClient @@ -9,7 +10,7 @@ function createApolloClient() { return new ApolloClient({ ssrMode: typeof window === 'undefined', link: new HttpLink({ - uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute) + uri: 'https://nextjs-graphql-with-prisma-simple.vercel.app/api', // Server URL (must be absolute) credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` }), cache: new InMemoryCache({ @@ -34,7 +35,15 @@ export function initializeApollo(initialState = null) { const existingCache = _apolloClient.extract() // Merge the existing cache into data passed from getStaticProps/getServerSideProps - const data = merge(initialState, existingCache) + const data = merge(initialState, existingCache, { + // combine arrays using object equality (like in sets) + arrayMerge: (destinationArray, sourceArray) => [ + ...sourceArray, + ...destinationArray.filter((d) => + sourceArray.every((s) => !isEqual(d, s)) + ), + ], + }) // Restore the cache with the merged data _apolloClient.cache.restore(data) diff --git a/examples/with-apollo-and-redux/package.json b/examples/with-apollo-and-redux/package.json index 79f629c1c5892..8e557367eae84 100644 --- a/examples/with-apollo-and-redux/package.json +++ b/examples/with-apollo-and-redux/package.json @@ -10,6 +10,7 @@ "@apollo/client": "^3.0.0", "deepmerge": "^4.2.2", "graphql": "14.5.8", + "lodash": "4.17.20", "next": "latest", "prop-types": "^15.6.0", "react": "^16.11.0", From dc4beade676b6c0310320c1a15de9213105c13cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Figueroa?= Date: Thu, 3 Dec 2020 20:14:39 -0300 Subject: [PATCH 074/119] with-facebook-pixel (#19762) ## Problems with the other implementation - pixel not working first time load page (this generate fake information to facebook analytics data) - package react-facebook-pixel error when try use events in code blocks or other pages with the current implementation - sometimes pixel mark twice pageview (this generate warning in facebook panel) - standar or custom events not working ## Solutions - Initialize pixel when entering each page (_document) - Now, we can use custom and standar events (utils/fpixel.js) - correct way to implement pixel according to facebook and guide facebook to implement in SPA - this solution is complemented with example "with-google-analytics" In my opinion, the other development has problems, but I preferred created a new example because the way to implement the base code is different. It seems that the other example is based on set the events from the Facebook control panel then this method limits an advanced implementation. --- .../components/FacebookPixel.js | 32 +++++++-------- examples/with-facebook-pixel/lib/fpixel.js | 10 +++++ .../with-facebook-pixel/pages/_document.js | 41 +++++++++++++++++++ examples/with-facebook-pixel/pages/index.js | 18 ++++++-- 4 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 examples/with-facebook-pixel/lib/fpixel.js create mode 100644 examples/with-facebook-pixel/pages/_document.js diff --git a/examples/with-facebook-pixel/components/FacebookPixel.js b/examples/with-facebook-pixel/components/FacebookPixel.js index 6d0a6038124cf..17df4ec3e5b11 100644 --- a/examples/with-facebook-pixel/components/FacebookPixel.js +++ b/examples/with-facebook-pixel/components/FacebookPixel.js @@ -1,27 +1,25 @@ import { useEffect } from 'react' import { useRouter } from 'next/router' +import * as fbq from '../lib/fpixel' -const pixelId = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID +const handleRouteChange = () => { + fbq.pageview() +} -export default function FacebookPixel({ children }) { +const FacebookPixel = ({ children }) => { const router = useRouter() + useEffect(() => { - if (!pixelId) return - let fb - function onRouteChange() { - fb.pageView() + // This pageview only trigger first time (it is important for Pixel to have real information) + fbq.pageview() + + router.events.on('routeChangeComplete', handleRouteChange) + return () => { + router.events.off('routeChangeComplete', handleRouteChange) } - import('react-facebook-pixel') - .then((module) => (fb = module.default)) - .then(() => { - fb.init(pixelId, { - autoConfig: true, - debug: true, - }) - fb.pageView() - }) - router.events.on('routeChangeComplete', onRouteChange) - return () => router.events.off('routeChangeComplete', onRouteChange) }, [router.events]) + return children } + +export default FacebookPixel diff --git a/examples/with-facebook-pixel/lib/fpixel.js b/examples/with-facebook-pixel/lib/fpixel.js new file mode 100644 index 0000000000000..145f000bfc024 --- /dev/null +++ b/examples/with-facebook-pixel/lib/fpixel.js @@ -0,0 +1,10 @@ +export const FB_PIXEL_ID = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID + +export const pageview = () => { + window.fbq('track', 'PageView') +} + +// https://developers.facebook.com/docs/facebook-pixel/advanced/ +export const event = (name, options = {}) => { + window.fbq('track', name, options) +} diff --git a/examples/with-facebook-pixel/pages/_document.js b/examples/with-facebook-pixel/pages/_document.js new file mode 100644 index 0000000000000..ccfc02007802c --- /dev/null +++ b/examples/with-facebook-pixel/pages/_document.js @@ -0,0 +1,41 @@ +import Document, { Html, Head, Main, NextScript } from 'next/document' +import { FB_PIXEL_ID } from '../lib/fpixel' + +export default class MyDocument extends Document { + render() { + return ( + + + {/* Global Site Code Pixel - Facebook Pixel */} +