From 0152dac87f2965c7f1f5fe5d4e272af4b9298d14 Mon Sep 17 00:00:00 2001 From: Ado Kukic Date: Wed, 22 Jul 2020 18:03:18 -0700 Subject: [PATCH 01/16] MongoDB Example (#15029) * MongoDB Example * Apply suggestions from code review * Add changes based on feedback. * clean up code with more descriptive props * Use MongoDB in ServerSideProps instead of separate API route * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-mongodb/README.md Co-authored-by: Luis Alvarez D. Co-authored-by: Luis Alvarez D Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- examples/with-mongodb/.env.local.example | 2 + examples/with-mongodb/.gitignore | 30 +++ examples/with-mongodb/README.md | 79 ++++++++ examples/with-mongodb/package.json | 16 ++ examples/with-mongodb/pages/index.js | 233 +++++++++++++++++++++++ examples/with-mongodb/public/favicon.ico | Bin 0 -> 15086 bytes examples/with-mongodb/public/vercel.svg | 4 + examples/with-mongodb/util/mongodb.js | 37 ++++ 8 files changed, 401 insertions(+) create mode 100644 examples/with-mongodb/.env.local.example create mode 100644 examples/with-mongodb/.gitignore create mode 100644 examples/with-mongodb/README.md create mode 100644 examples/with-mongodb/package.json create mode 100644 examples/with-mongodb/pages/index.js create mode 100644 examples/with-mongodb/public/favicon.ico create mode 100644 examples/with-mongodb/public/vercel.svg create mode 100644 examples/with-mongodb/util/mongodb.js diff --git a/examples/with-mongodb/.env.local.example b/examples/with-mongodb/.env.local.example new file mode 100644 index 0000000000000..0bc8a2ce22f98 --- /dev/null +++ b/examples/with-mongodb/.env.local.example @@ -0,0 +1,2 @@ +MONGODB_URI= +MONGODB_DB= \ No newline at end of file diff --git a/examples/with-mongodb/.gitignore b/examples/with-mongodb/.gitignore new file mode 100644 index 0000000000000..20fccdd4b84d9 --- /dev/null +++ b/examples/with-mongodb/.gitignore @@ -0,0 +1,30 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local diff --git a/examples/with-mongodb/README.md b/examples/with-mongodb/README.md new file mode 100644 index 0000000000000..2f88e28478dc2 --- /dev/null +++ b/examples/with-mongodb/README.md @@ -0,0 +1,79 @@ +## Example app using MongoDB + +[MongoDB](https://www.mongodb.com/) is a general purpose, document-based, distributed database built for modern application developers and for the cloud era. This example will show you how to connect to and use MongoDB as your backend for your Next.js app. + +If you want to learn more about MongoDB, visit the following pages: + +- [MongoDB Atlas](https://mongodb.com/atlas) +- [MongoDB Documentation](https://docs.mongodb.com/) + +## Deploy your own + +Once you have access to the environment variables you'll need, deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/git?c=1&s=https://github.com/vercel/next.js/tree/canary/examples/with-mongodb&env=MONGODB_URI,MONGODB_DB&envDescription=Required%20to%20connect%20the%20app%20with%20MongoDB) + +## How to use + +### Using `create-next-app` + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example with-mongodb with-mongodb +# or +yarn create next-app --example with-mongodb with-mongodb +``` + +## Configuration + +### Set up a MongoDB database + +Set up a MongoDB database either locally or with [MongoDB Atlas for free](https://mongodb.com/atlas). + +### Set up environment variables + +Copy the `env.local.example` file in this directory to `.env.local` (which will be ignored by Git): + +```bash +cp .env.local.example .env.local +``` + +Set each variable on `.env.local`: + +- `MONGODB_URI` - Your MongoDB connection string. If you are using [MongoDB Atlas](https://mongodb.com/atlas) you can find this by clicking the "Connect" button for your cluster. +- `MONGODB_DB` - The name of the MongoDB database you want to use. + +### Run Next.js in development mode + +```bash +npm install +npm run dev + +# or + +yarn install +yarn dev +``` + +Your app should be up and running on [http://localhost:3000](http://localhost:3000)! If it doesn't work, post on [GitHub discussions](https://github.com/zeit/next.js/discussions). + +You will either see a message stating "You are connected to MongoDB" or "You are NOT connected to MongoDB". Ensure that you have provided the correct `MONGODB_URI` and `MONGODB_DB` environment variables. + +When you are successfully connected, you can refer to the [MongoDB Node.js Driver docs](https://mongodb.github.io/node-mongodb-native/3.4/tutorials/collections/) for further instructions on how to query your database. + +## Deploy on Vercel + +You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). + +#### Deploy Your Local Project + +To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [import to Vercel](https://vercel.com/import/git?utm_source=github&utm_medium=readme&utm_campaign=next-example). + +**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to match your `.env.local` file. + +#### Deploy from Our Template + +Alternatively, you can deploy using our template by clicking on the Deploy button below. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/git?c=1&s=https://github.com/vercel/next.js/tree/canary/examples/with-mongodb&env=MONGODB_URI,MONGODB_DB&envDescription=Required%20to%20connect%20the%20app%20with%20MongoDB) diff --git a/examples/with-mongodb/package.json b/examples/with-mongodb/package.json new file mode 100644 index 0000000000000..284a43b5e5618 --- /dev/null +++ b/examples/with-mongodb/package.json @@ -0,0 +1,16 @@ +{ + "name": "with-mongodb", + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "mongodb": "^3.5.9", + "next": "latest", + "react": "^16.13.1", + "react-dom": "^16.13.1" + }, + "license": "MIT" +} diff --git a/examples/with-mongodb/pages/index.js b/examples/with-mongodb/pages/index.js new file mode 100644 index 0000000000000..d2f47e31f09e7 --- /dev/null +++ b/examples/with-mongodb/pages/index.js @@ -0,0 +1,233 @@ +import Head from 'next/head' +import { connectToDatabase } from '../util/mongodb' + +export default function Home({ isConnected }) { + return ( +
+ + Create Next App + + + +
+

+ Welcome to Next.js with MongoDB! +

+ + {isConnected ? ( +

You are connected to MongoDB

+ ) : ( +

+ You are NOT connected to MongoDB. Check the README.md{' '} + for instructions. +

+ )} + +

+ Get started by editing pages/index.js +

+ + +
+ + + + + + +
+ ) +} + +export async function getServerSideProps(context) { + const { client } = await connectToDatabase() + + const isConnected = await client.isConnected() // Returns true or false + + return { + props: { isConnected }, + } +} diff --git a/examples/with-mongodb/public/favicon.ico b/examples/with-mongodb/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4965832f2c9b0605eaa189b7c7fb11124d24e48a GIT binary patch literal 15086 zcmeHOOH5Q(7(R0cc?bh2AT>N@1PWL!LLfZKyG5c!MTHoP7_p!sBz0k$?pjS;^lmgJ zU6^i~bWuZYHL)9$wuvEKm~qo~(5=Lvx5&Hv;?X#m}i|`yaGY4gX+&b>tew;gcnRQA1kp zBbm04SRuuE{Hn+&1wk%&g;?wja_Is#1gKoFlI7f`Gt}X*-nsMO30b_J@)EFNhzd1QM zdH&qFb9PVqQOx@clvc#KAu}^GrN`q5oP(8>m4UOcp`k&xwzkTio*p?kI4BPtIwX%B zJN69cGsm=x90<;Wmh-bs>43F}ro$}Of@8)4KHndLiR$nW?*{Rl72JPUqRr3ta6e#A z%DTEbi9N}+xPtd1juj8;(CJt3r9NOgb>KTuK|z7!JB_KsFW3(pBN4oh&M&}Nb$Ee2 z$-arA6a)CdsPj`M#1DS>fqj#KF%0q?w50GN4YbmMZIoF{e1yTR=4ablqXHBB2!`wM z1M1ke9+<);|AI;f=2^F1;G6Wfpql?1d5D4rMr?#f(=hkoH)U`6Gb)#xDLjoKjp)1;Js@2Iy5yk zMXUqj+gyk1i0yLjWS|3sM2-1ECc;MAz<4t0P53%7se$$+5Ex`L5TQO_MMXXi04UDIU+3*7Ez&X|mj9cFYBXqM{M;mw_ zpw>azP*qjMyNSD4hh)XZt$gqf8f?eRSFX8VQ4Y+H3jAtvyTrXr`qHAD6`m;aYmH2zOhJC~_*AuT} zvUxC38|JYN94i(05R)dVKgUQF$}#cxV7xZ4FULqFCNX*Forhgp*yr6;DsIk=ub0Hv zpk2L{9Q&|uI^b<6@i(Y+iSxeO_n**4nRLc`P!3ld5jL=nZRw6;DEJ*1z6Pvg+eW|$lnnjO zjd|8>6l{i~UxI244CGn2kK@cJ|#ecwgSyt&HKA2)z zrOO{op^o*- + + \ No newline at end of file diff --git a/examples/with-mongodb/util/mongodb.js b/examples/with-mongodb/util/mongodb.js new file mode 100644 index 0000000000000..f5a50bcfeebe6 --- /dev/null +++ b/examples/with-mongodb/util/mongodb.js @@ -0,0 +1,37 @@ +import { MongoClient } from 'mongodb' + +let uri = process.env.MONGODB_URI +let dbName = process.env.MONGODB_DB + +let cachedClient = null +let cachedDb = null + +if (!uri) { + throw new Error( + 'Please define the MONGODB_URI environment variable inside .env.local' + ) +} + +if (!dbName) { + throw new Error( + 'Please define the MONGODB_DB environment variable inside .env.local' + ) +} + +export async function connectToDatabase() { + if (cachedClient && cachedDb) { + return { client: cachedClient, db: cachedDb } + } + + const client = await MongoClient.connect(uri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) + + const db = await client.db(dbName) + + cachedClient = client + cachedDb = db + + return { client, db } +} From e4e3ad38fd7cfc7dd5f72efd493d75a5b945c300 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 23 Jul 2020 06:06:50 +0200 Subject: [PATCH 02/16] Upgrade actions/cache to v2.1.0 (#15415) --- .github/workflows/build_test_deploy.yml | 32 ++++++++++++------------- .github/workflows/test_react_next.yml | 8 +++---- errors/no-cache.md | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 814cd47f071f2..bd74c76cff084 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -15,20 +15,20 @@ jobs: - uses: actions/checkout@v2 - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - run: yarn install --frozen-lockfile --check-files - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: cache-build with: - path: '.' + path: ./* key: ${{ github.sha }} lint: runs-on: ubuntu-latest needs: build steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: yarn lint @@ -39,10 +39,10 @@ jobs: env: NEXT_TELEMETRY_DISABLED: 1 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: ./check-pre-compiled.sh @@ -59,10 +59,10 @@ jobs: matrix: group: [1, 2, 3, 4, 5, 6] steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} # TODO: remove after we fix watchpack watching too much @@ -104,10 +104,10 @@ jobs: BROWSERNAME: 'firefox' NEXT_TELEMETRY_DISABLED: 1 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: node run-tests.js test/integration/production/test/index.test.js @@ -123,10 +123,10 @@ jobs: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production/test/index.test.js' @@ -143,10 +143,10 @@ jobs: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production-nav/test/index.test.js' @@ -157,10 +157,10 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} steps: - - uses: actions/cache@v1 + - uses: actions/cache@v2 id: restore-build with: - path: '.' + path: ./* key: ${{ github.sha }} - run: ./publish-release.sh diff --git a/.github/workflows/test_react_next.yml b/.github/workflows/test_react_next.yml index 443b0e25af518..9eb76fe8ee434 100644 --- a/.github/workflows/test_react_next.yml +++ b/.github/workflows/test_react_next.yml @@ -17,10 +17,10 @@ jobs: # - run: yarn upgrade react@next react-dom@next -W --dev - # - uses: actions/cache@v1 + # - uses: actions/cache@v2 # id: cache-build # with: - # path: '.' + # path: ./* # key: ${{ github.sha }} testAll: @@ -35,10 +35,10 @@ jobs: matrix: group: [1, 2, 3, 4, 5, 6] steps: - # - uses: actions/cache@v1 + # - uses: actions/cache@v2 # id: restore-build # with: - # path: '.' + # path: ./* # key: ${{ github.sha }} - uses: actions/checkout@v2 diff --git a/errors/no-cache.md b/errors/no-cache.md index 8bf18012bbd60..53a632c4f087f 100644 --- a/errors/no-cache.md +++ b/errors/no-cache.md @@ -78,7 +78,7 @@ cache: Using GitHub's [actions/cache](https://github.com/actions/cache), add the following step in your workflow file: ```yaml -uses: actions/cache@v1 +uses: actions/cache@v2 with: path: ${{ github.workspace }}/.next/cache key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }} From 23ebe3e4aac920b57cf71cf5a530bcfaa7b186de Mon Sep 17 00:00:00 2001 From: Colleen O'Rourke Date: Thu, 23 Jul 2020 14:32:44 -0700 Subject: [PATCH 03/16] Update Sentry example for use with Sentry/Vercel integration (#15349) * Update Sentry example for use with Sentry/Vercel integration * update linting * Update link in readme * Update readme, add comment * Add step about commit SHA * Updated readme * Use if * dont call sentry webpack plugin w/o a commit sha present * Update examples/with-sentry/README.md Co-authored-by: Luis Alvarez D. * update release value * prettier readme * Updated note Co-authored-by: Luis Alvarez Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- examples/with-sentry/README.md | 36 ++++++++++------------------ examples/with-sentry/next.config.js | 11 ++++++++- examples/with-sentry/pages/_app.js | 10 ++++---- examples/with-sentry/pages/_error.js | 6 ++++- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/examples/with-sentry/README.md b/examples/with-sentry/README.md index 4b1200bccea59..cd458d3d713f4 100644 --- a/examples/with-sentry/README.md +++ b/examples/with-sentry/README.md @@ -24,27 +24,6 @@ npx create-next-app --example with-sentry with-sentry yarn create next-app --example with-sentry with-sentry ``` -### Download Manually - -Download the example: - -```bash -curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-sentry -cd with-sentry -``` - -Install it and run: - -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` - -Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). - ## Configuration ### Step 1. Enable error tracking @@ -75,10 +54,19 @@ Your app should be up and running on [http://localhost:3000](http://localhost:30 ### Step 3. Automatic sourcemap upload (optional) +#### Using Vercel + +You will need to install and configure the [Sentry Vercel integration](https://docs.sentry.io/workflow/integrations/vercel). After you've completed the project linking step, all the needed environment variables will be set in your Vercel project. + +> **Note:** A Vercel project connected to a [Git integration](https://vercel.com/docs/v2/platform/deployments#git-integration) is required before adding the Sentry integration. + +#### Without Using Vercel + 1. Set up the `NEXT_PUBLIC_SENTRY_DSN` environment variable as described above. -2. Save your Sentry Organization slug as the `SENTRY_ORG` environment variable and your project slug as the `SENTRY_PROJECT` environment variable in `.env.local`. -3. Create an auth token in Sentry. The recommended way to do this is by creating a new internal integration for your organization. To do so, go into **Settings > Developer Settings > New internal integration**. After the integration is created, copy the Token. -4. Save the token inside the `SENTRY_AUTH_TOKEN` environment variable in `.env.local`. +2. Save your Sentry organization slug as the `SENTRY_ORG` environment variable and your project slug as the `SENTRY_PROJECT` environment variable in `.env.local`. +3. Save your git provider's commit SHA as either `VERCEL_GITHUB_COMMIT_SHA`, `VERCEL_GITLAB_COMMIT_SHA`, or `VERCEL_BITBUCKET_COMMIT_SHA` environment variable in `.env.local`. +4. Create an auth token in Sentry. The recommended way to do this is by creating a new internal integration for your organization. To do so, go into **Settings > Developer Settings > New internal integration**. After the integration is created, copy the Token. +5. Save the token inside the `SENTRY_AUTH_TOKEN` environment variable in `.env.local`. > **Note:** Sourcemap upload is disabled in development mode using the `NODE_ENV` environment variable. To change this behavior, remove the `NODE_ENV === 'production'` check from your `next.config.js` file. diff --git a/examples/with-sentry/next.config.js b/examples/with-sentry/next.config.js index f20aab6c0c627..7b8cb6549d6e6 100644 --- a/examples/with-sentry/next.config.js +++ b/examples/with-sentry/next.config.js @@ -10,8 +10,16 @@ const { SENTRY_PROJECT, SENTRY_AUTH_TOKEN, NODE_ENV, + VERCEL_GITHUB_COMMIT_SHA, + VERCEL_GITLAB_COMMIT_SHA, + VERCEL_BITBUCKET_COMMIT_SHA, } = process.env +const COMMIT_SHA = + VERCEL_GITHUB_COMMIT_SHA || + VERCEL_GITLAB_COMMIT_SHA || + VERCEL_BITBUCKET_COMMIT_SHA + process.env.SENTRY_DSN = SENTRY_DSN module.exports = withSourceMaps({ @@ -44,6 +52,7 @@ module.exports = withSourceMaps({ SENTRY_ORG && SENTRY_PROJECT && SENTRY_AUTH_TOKEN && + COMMIT_SHA && NODE_ENV === 'production' ) { config.plugins.push( @@ -51,7 +60,7 @@ module.exports = withSourceMaps({ include: '.next', ignore: ['node_modules'], urlPrefix: '~/_next', - release: options.buildId, + release: COMMIT_SHA, }) ) } diff --git a/examples/with-sentry/pages/_app.js b/examples/with-sentry/pages/_app.js index 93e2a2a412c5c..4a3037ce39baa 100644 --- a/examples/with-sentry/pages/_app.js +++ b/examples/with-sentry/pages/_app.js @@ -1,9 +1,11 @@ import * as Sentry from '@sentry/node' -Sentry.init({ - enabled: process.env.NODE_ENV === 'production', - dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, -}) +if (process.env.NEXT_PUBLIC_SENTRY_DSN) { + Sentry.init({ + enabled: process.env.NODE_ENV === 'production', + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + }) +} export default function App({ Component, pageProps, err }) { // Workaround for https://github.com/vercel/next.js/issues/8592 diff --git a/examples/with-sentry/pages/_error.js b/examples/with-sentry/pages/_error.js index 76a72693d4672..0b38c8d7b7776 100644 --- a/examples/with-sentry/pages/_error.js +++ b/examples/with-sentry/pages/_error.js @@ -1,12 +1,14 @@ import NextErrorComponent from 'next/error' import * as Sentry from '@sentry/node' -const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => { +const MyError = async ({ statusCode, hasGetInitialPropsRun, err }) => { if (!hasGetInitialPropsRun && err) { // getInitialProps is not called in case of // https://github.com/vercel/next.js/issues/8592. As a workaround, we pass // err via _app.js so it can be captured Sentry.captureException(err) + // flush is needed after calling captureException to send server side errors to Sentry, otherwise the serverless function will exit before it's sent + await Sentry.flush(2000) } return @@ -41,6 +43,7 @@ MyError.getInitialProps = async ({ res, err, asPath }) => { } if (err) { Sentry.captureException(err) + await Sentry.flush(2000) return errorInitialProps } @@ -50,6 +53,7 @@ MyError.getInitialProps = async ({ res, err, asPath }) => { Sentry.captureException( new Error(`_error.js getInitialProps missing data at path: ${asPath}`) ) + await Sentry.flush(2000) return errorInitialProps } From 00ebce55538f99b752408608aaecb85942f7d3dc Mon Sep 17 00:00:00 2001 From: Mehedi Hassan Date: Fri, 24 Jul 2020 02:56:32 +0100 Subject: [PATCH 04/16] More helpful README (#14830) * More helpful README Updated to include more details about Next.js, link to the interactive tutorial, showcase, etc. Content mostly based on the official Next.js site. * create-next-app readme An updated readme with more details on options, benefits, etc. * Apply edits from code review Co-authored-by: Luis Alvarez D. * Remove redundant intro * Update packages/create-next-app/README.md * Remove introduction and list in showcase * Apply suggestions from code review * Update packages/next/README.md Co-authored-by: Luis Alvarez D. Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/create-next-app/README.md | 32 +++++++++++++++++++++++++----- packages/next/README.md | 6 +++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/create-next-app/README.md b/packages/create-next-app/README.md index ad7d9fce6ea76..9edafcecd5bc7 100644 --- a/packages/create-next-app/README.md +++ b/packages/create-next-app/README.md @@ -1,8 +1,30 @@ -# create-next-app +# Create Next App -This package includes the global command for creating [Next.js](https://github.com/vercel/next.js) applications. +The easiest way to get started with Next.js is by using `create-next-app`. This simple CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the [official Next.js examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: -Please refer to its documentation: +```bash +npx create-next-app +``` -- [Setup](https://nextjs.org/docs/getting-started#setup) – How to create a new Next.js application. -- [Documentation](https://nextjs.org/docs) – How to develop Next.js applications. +To create a new app in a specific folder, you can send a name as an argument. For example, the following command will create a new Next.js app called `blog-app` in a folder with the same name: + +```bash +npx create-next-app blog-app +``` + +## Options + +`create-next-app` comes with the following options: + +* **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. +* **--example-path <path-to-example>** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` + +## Why use Create Next App? + +`create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: + +* **Interactive Experience**: Running `npx create-next-app` (with no arguments) launches an interactive experience that guides you through setting up a project. +* **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. +* **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. +* **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). +* **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. diff --git a/packages/next/README.md b/packages/next/README.md index 9a401f10e64b7..560d19627a134 100644 --- a/packages/next/README.md +++ b/packages/next/README.md @@ -21,7 +21,11 @@ Visit https://next ## Documentation -Visit https://nextjs.org/docs to view the documentation. +Visit [https://nextjs.org/docs](https://nextjs.org/docs) to view the full documentation. + +## Who is using Next.js? + +Next.js is used by the world's leading companies. Check out the [Next.js Showcase](https://nextjs.org/showcase) to learn more. ## Contributing From 681ebfa5cbe24b738ee08a4a5964c0b942af49bb Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Thu, 23 Jul 2020 21:10:41 -0500 Subject: [PATCH 05/16] Fix lint (#15449) --- packages/create-next-app/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/create-next-app/README.md b/packages/create-next-app/README.md index 9edafcecd5bc7..98ff887a14504 100644 --- a/packages/create-next-app/README.md +++ b/packages/create-next-app/README.md @@ -16,15 +16,15 @@ npx create-next-app blog-app `create-next-app` comes with the following options: -* **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. -* **--example-path <path-to-example>** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` +- **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. +- **--example-path <path-to-example>** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` ## Why use Create Next App? `create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: -* **Interactive Experience**: Running `npx create-next-app` (with no arguments) launches an interactive experience that guides you through setting up a project. -* **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. -* **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. -* **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). -* **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. +- **Interactive Experience**: Running `npx create-next-app` (with no arguments) launches an interactive experience that guides you through setting up a project. +- **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. +- **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. +- **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). +- **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. From cc541fb25260f7315d3e223d368cfad80c99f25a Mon Sep 17 00:00:00 2001 From: Arsalan Khattak <37709578+eKhattak@users.noreply.github.com> Date: Fri, 24 Jul 2020 21:35:39 +0500 Subject: [PATCH 06/16] Add Sitemap Example (#15047) * Add with-sitemap Example * Update README * Update examples/with-sitemap/scripts/generate-sitemap.js Co-authored-by: Luis Alvarez D. * Update examples/with-sitemap/package.json Co-authored-by: Luis Alvarez D. * Update examples/with-sitemap/public/sitemap.xml Co-authored-by: Luis Alvarez D. * Update README * Add .env Info to README * Update examples/with-sitemap/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-sitemap/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-sitemap/README.md Co-authored-by: Luis Alvarez D. * Update examples/with-sitemap/README.md Co-authored-by: Luis Alvarez D. Co-authored-by: Luis Alvarez D. --- examples/with-sitemap/.env | 2 + examples/with-sitemap/README.md | 51 +++++ examples/with-sitemap/next.config.js | 9 + examples/with-sitemap/package.json | 18 ++ examples/with-sitemap/pages/contact.js | 128 +++++++++++ examples/with-sitemap/pages/index.js | 209 ++++++++++++++++++ examples/with-sitemap/public/favicon.ico | Bin 0 -> 15086 bytes examples/with-sitemap/public/sitemap.xml | 10 + examples/with-sitemap/public/vercel.svg | 4 + .../with-sitemap/scripts/generate-sitemap.js | 28 +++ 10 files changed, 459 insertions(+) create mode 100644 examples/with-sitemap/.env create mode 100644 examples/with-sitemap/README.md create mode 100644 examples/with-sitemap/next.config.js create mode 100644 examples/with-sitemap/package.json create mode 100644 examples/with-sitemap/pages/contact.js create mode 100644 examples/with-sitemap/pages/index.js create mode 100644 examples/with-sitemap/public/favicon.ico create mode 100644 examples/with-sitemap/public/sitemap.xml create mode 100644 examples/with-sitemap/public/vercel.svg create mode 100644 examples/with-sitemap/scripts/generate-sitemap.js diff --git a/examples/with-sitemap/.env b/examples/with-sitemap/.env new file mode 100644 index 0000000000000..8d8ccb6fca0e4 --- /dev/null +++ b/examples/with-sitemap/.env @@ -0,0 +1,2 @@ +# Used to add the domain to sitemap.xml, replace it with a real domain in production +WEBSITE_URL=http://localhost:3000 \ No newline at end of file diff --git a/examples/with-sitemap/README.md b/examples/with-sitemap/README.md new file mode 100644 index 0000000000000..a6d6d7b56b76b --- /dev/null +++ b/examples/with-sitemap/README.md @@ -0,0 +1,51 @@ +# With Sitemap example + +This example shows how to generate a `sitemap.xml` file based on the pages in your [Next.js](https://nextjs.org/) app. The sitemap will be generated and saved in the `public` directory after starting the development server or by making a build. + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/hello-world) + +## How to use + +### Using `create-next-app` + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example with-sitemap with-sitemap-app +# or +yarn create next-app --example with-sitemap with-sitemap-app +``` + +### Download manually + +Download the example: + +```bash +curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-sitemap +cd with-sitemap +``` + +Install it and run: + +```bash +npm install +npm run dev +# or +yarn +yarn dev +``` + +Your app should be up and running on [http://localhost:3000](http://localhost:3000) and the sitemap should now be available in [http://localhost:3000/sitemap.xml](http://localhost:3000/sitemap.xml)! If it doesn't work, post on [GitHub discussions](https://github.com/vercel/next.js/discussions). + +To change the website URL used by `sitemap.xml`, open the file `.env` and change the `WEBSITE_URL` environment variable: + +```bash +# Used to add the domain to sitemap.xml, replace it with a real domain in production +WEBSITE_URL=https://my-domain.com +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/with-sitemap/next.config.js b/examples/with-sitemap/next.config.js new file mode 100644 index 0000000000000..08a972d01c6d6 --- /dev/null +++ b/examples/with-sitemap/next.config.js @@ -0,0 +1,9 @@ +module.exports = { + webpack: (config, { isServer }) => { + if (isServer) { + require('./scripts/generate-sitemap') + } + + return config + }, +} diff --git a/examples/with-sitemap/package.json b/examples/with-sitemap/package.json new file mode 100644 index 0000000000000..beb83580fa2e2 --- /dev/null +++ b/examples/with-sitemap/package.json @@ -0,0 +1,18 @@ +{ + "name": "with-sitemap", + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^16.13.1", + "react-dom": "^16.13.1" + }, + "devDependencies": { + "globby": "^11.0.1" + }, + "license": "MIT" +} diff --git a/examples/with-sitemap/pages/contact.js b/examples/with-sitemap/pages/contact.js new file mode 100644 index 0000000000000..0d718f02d5fc9 --- /dev/null +++ b/examples/with-sitemap/pages/contact.js @@ -0,0 +1,128 @@ +import Head from 'next/head' + +export default function Home() { + return ( +
+ + Create Next App + + + +
+

+ Welcome to Next.js! +

+ +

Contact Page

+
+ + + + + + +
+ ) +} diff --git a/examples/with-sitemap/pages/index.js b/examples/with-sitemap/pages/index.js new file mode 100644 index 0000000000000..1379c5f92dabb --- /dev/null +++ b/examples/with-sitemap/pages/index.js @@ -0,0 +1,209 @@ +import Head from 'next/head' + +export default function Home() { + return ( +
+ + Create Next App + + + +
+

+ Welcome to Next.js! +

+ +

+ Get started by editing pages/index.js +

+ + +
+ + + + + + +
+ ) +} diff --git a/examples/with-sitemap/public/favicon.ico b/examples/with-sitemap/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4965832f2c9b0605eaa189b7c7fb11124d24e48a GIT binary patch literal 15086 zcmeHOOH5Q(7(R0cc?bh2AT>N@1PWL!LLfZKyG5c!MTHoP7_p!sBz0k$?pjS;^lmgJ zU6^i~bWuZYHL)9$wuvEKm~qo~(5=Lvx5&Hv;?X#m}i|`yaGY4gX+&b>tew;gcnRQA1kp zBbm04SRuuE{Hn+&1wk%&g;?wja_Is#1gKoFlI7f`Gt}X*-nsMO30b_J@)EFNhzd1QM zdH&qFb9PVqQOx@clvc#KAu}^GrN`q5oP(8>m4UOcp`k&xwzkTio*p?kI4BPtIwX%B zJN69cGsm=x90<;Wmh-bs>43F}ro$}Of@8)4KHndLiR$nW?*{Rl72JPUqRr3ta6e#A z%DTEbi9N}+xPtd1juj8;(CJt3r9NOgb>KTuK|z7!JB_KsFW3(pBN4oh&M&}Nb$Ee2 z$-arA6a)CdsPj`M#1DS>fqj#KF%0q?w50GN4YbmMZIoF{e1yTR=4ablqXHBB2!`wM z1M1ke9+<);|AI;f=2^F1;G6Wfpql?1d5D4rMr?#f(=hkoH)U`6Gb)#xDLjoKjp)1;Js@2Iy5yk zMXUqj+gyk1i0yLjWS|3sM2-1ECc;MAz<4t0P53%7se$$+5Ex`L5TQO_MMXXi04UDIU+3*7Ez&X|mj9cFYBXqM{M;mw_ zpw>azP*qjMyNSD4hh)XZt$gqf8f?eRSFX8VQ4Y+H3jAtvyTrXr`qHAD6`m;aYmH2zOhJC~_*AuT} zvUxC38|JYN94i(05R)dVKgUQF$}#cxV7xZ4FULqFCNX*Forhgp*yr6;DsIk=ub0Hv zpk2L{9Q&|uI^b<6@i(Y+iSxeO_n**4nRLc`P!3ld5jL=nZRw6;DEJ*1z6Pvg+eW|$lnnjO zjd|8>6l{i~UxI244CGn2kK@cJ|#ecwgSyt&HKA2)z zrOO{op^o*- + + http://localhost:3000/contact + hourly + + + http://localhost:3000 + hourly + + \ No newline at end of file diff --git a/examples/with-sitemap/public/vercel.svg b/examples/with-sitemap/public/vercel.svg new file mode 100644 index 0000000000000..fbf0e25a651c2 --- /dev/null +++ b/examples/with-sitemap/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/examples/with-sitemap/scripts/generate-sitemap.js b/examples/with-sitemap/scripts/generate-sitemap.js new file mode 100644 index 0000000000000..4bd49377b2c50 --- /dev/null +++ b/examples/with-sitemap/scripts/generate-sitemap.js @@ -0,0 +1,28 @@ +const fs = require('fs') +const globby = require('globby') + +function addPage(page) { + const path = page.replace('pages', '').replace('.js', '').replace('.mdx', '') + const route = path === '/index' ? '' : path + + return ` + ${`${process.env.WEBSITE_URL}${route}`} + hourly + ` +} + +async function generateSitemap() { + // Ignore Next.js specific files (e.g., _app.js) and API routes. + const pages = await globby([ + 'pages/**/*{.js,.mdx}', + '!pages/_*.js', + '!pages/api', + ]) + const sitemap = ` +${pages.map(addPage).join('\n')} +` + + fs.writeFileSync('public/sitemap.xml', sitemap) +} + +generateSitemap() From c5f29b76ea125d122d443c975896399f44c428f0 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 24 Jul 2020 21:23:23 +0200 Subject: [PATCH 07/16] Update webpack to land chokidar patch for all Next.js users (#15460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forces all Next.js users to have this patch installed: https://twitter.com/timneutkens/status/1282129714627448832 Huge thanks to @paulmillr and @sokra 🙏 --- packages/next/package.json | 2 +- yarn.lock | 53 +++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/packages/next/package.json b/packages/next/package.json index bada05e57c3a2..3004bc9753eba 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -107,7 +107,7 @@ "use-subscription": "1.4.1", "watchpack": "2.0.0-beta.13", "web-vitals": "0.2.1", - "webpack": "4.43.0", + "webpack": "4.44.0", "webpack-sources": "1.4.3" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index 5ba1dacc9ea3a..b154883c4e116 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4104,7 +4104,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.13.0, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: +browserslist@4.13.0, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: version "4.13.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d" integrity sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ== @@ -4114,6 +4114,14 @@ browserslist@4.13.0, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7 escalade "^3.0.1" node-releases "^1.1.58" +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk= + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + browserstack-local@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.0.tgz#d979cac056f57b9af159b3bcd7fdc09b4354537c" @@ -4412,11 +4420,21 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634: version "1.0.30001023" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001023.tgz#f856f71af16a5a44e81f1fcefc1673912a43da72" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001093: +caniuse-db@^1.0.30000639: + version "1.0.30001105" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001105.tgz#5cc03239a9d4540b3fa9a1dc8b5e3bda50226e96" + integrity sha512-GZytZn8lOiru/Tw+/X5sFxrFt2uPdSvkxVKzRMJyX20JGwfwOuTiRg5IMVF9II8Lao/7C4YeHR8YnZzpTvYXdQ== + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020: version "1.0.30001066" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz#0a8a58a10108f2b9bf38e7b65c237b12fd9c5f04" integrity sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw== +caniuse-lite@^1.0.30001093: + version "1.0.30001105" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001105.tgz#d2cb0b31e5cf2f3ce845033b61c5c01566549abf" + integrity sha512-JupOe6+dGMr7E20siZHIZQwYqrllxotAhiaej96y6x00b/48rPt42o+SzOSCPbrpsDWvRja40Hwrj0g0q6LZJg== + capitalize@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/capitalize/-/capitalize-1.0.0.tgz#dc802c580aee101929020d2ca14b4ca8a0ae44be" @@ -4590,7 +4608,7 @@ chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" -chokidar@^3.4.0: +chokidar@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1" integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g== @@ -6138,6 +6156,11 @@ ejs@^2.6.1: version "2.7.4" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" +electron-to-chromium@^1.2.7: + version "1.3.506" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.506.tgz#74bd3e1fb31285b3247f165d4958da74d70ae4e4" + integrity sha512-k0PHtv4gD6KJu1k6lp8pvQOe12uZriOwS2x66Vnxkq0NOBucsNrItOj/ehomvcZ3S4K1ueqUCv+fsLhXBs6Zyw== + electron-to-chromium@^1.3.488: version "1.3.501" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.501.tgz#faa17a2cb0105ee30d5e1ca87eae7d8e85dd3175" @@ -6192,7 +6215,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: +enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== @@ -15999,15 +16022,15 @@ watchpack@2.0.0-beta.13: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" optionalDependencies: - chokidar "^3.4.0" + chokidar "^3.4.1" watchpack-chokidar2 "^2.0.0" wcwidth@^1.0.0, wcwidth@^1.0.1: @@ -16062,10 +16085,10 @@ webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-s source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.43.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== +webpack@4.44.0: + version "4.44.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.0.tgz#3b08f88a89470175f036f4a9496b8a0428668802" + integrity sha512-wAuJxK123sqAw31SpkPiPW3iKHgFUiKvO7E7UZjtdExcsRe3fgav4mvoMM7vvpjLHVoJ6a0Mtp2fzkoA13e0Zw== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -16075,7 +16098,7 @@ webpack@4.43.0: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -16088,7 +16111,7 @@ webpack@4.43.0: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" + watchpack "^1.7.4" webpack-sources "^1.4.1" websocket-driver@>=0.5.1: From e6e2722b117976d6233f5c72c337a3b03a3906c9 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Fri, 24 Jul 2020 22:04:53 +0200 Subject: [PATCH 08/16] Tweak test retries for invalid-href suite (#15459) - Reduce jest retries to 2 for a total of 3 attempts - Disable retries in `invalid-href` test. I noticed jest retries don't help when this test fails (see log output of https://github.com/vercel/next.js/runs/904147534). --- run-tests.js | 2 +- test/integration/invalid-href/test/index.test.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/run-tests.js b/run-tests.js index df5991493e6f4..5dd3964ab57cd 100644 --- a/run-tests.js +++ b/run-tests.js @@ -136,7 +136,7 @@ const TIMINGS_API = `https://next-timings.jjsweb.site/api/timings` { stdio: 'inherit', env: { - JEST_RETRY_TIMES: 3, + JEST_RETRY_TIMES: 2, ...process.env, ...(isAzure ? { diff --git a/test/integration/invalid-href/test/index.test.js b/test/integration/invalid-href/test/index.test.js index e80edc99d00b6..b612671d88a38 100644 --- a/test/integration/invalid-href/test/index.test.js +++ b/test/integration/invalid-href/test/index.test.js @@ -14,7 +14,7 @@ import { import webdriver from 'next-webdriver' import { join } from 'path' -jest.setTimeout(1000 * 60 * 2) +jest.setTimeout(1000 * 60 * 1) let app let appPort @@ -23,6 +23,10 @@ const appDir = join(__dirname, '..') const firstErrorRegex = /Invalid href passed to router: mailto:idk@idk.com.*invalid-href-passed/ const secondErrorRegex = /Invalid href passed to router: .*google\.com.*invalid-href-passed/ +// This test doesn't seem to benefit from retries, let's disable them until the test gets fixed +// to prevent long running times +jest.retryTimes(0) + const showsError = async (pathname, regex, click = false, isWarn = false) => { const browser = await webdriver(appPort, pathname) try { From 1a34b237b62eeb9424983794dc31820f713e02a0 Mon Sep 17 00:00:00 2001 From: Robin Tom <31811117+robintom@users.noreply.github.com> Date: Sat, 25 Jul 2020 08:08:58 +0530 Subject: [PATCH 09/16] Example for Rewrites (Custom routes) (#15403) This PR adds example for #15073 > - [ ] `rewrites` For [docs/api-reference/next.config.js/rewrites.md](https://github.com/vercel/next.js/blob/canary/docs/api-reference/next.config.js/rewrites.md) --- docs/api-reference/next.config.js/rewrites.md | 7 +++ examples/rewrites/.gitignore | 34 ++++++++++++ examples/rewrites/README.md | 23 ++++++++ examples/rewrites/next.config.js | 29 ++++++++++ examples/rewrites/package.json | 15 ++++++ examples/rewrites/pages/about.js | 32 +++++++++++ examples/rewrites/pages/index.js | 54 +++++++++++++++++++ examples/rewrites/pages/news/[...slug].js | 35 ++++++++++++ examples/rewrites/styles.module.css | 51 ++++++++++++++++++ 9 files changed, 280 insertions(+) create mode 100644 examples/rewrites/.gitignore create mode 100644 examples/rewrites/README.md create mode 100644 examples/rewrites/next.config.js create mode 100644 examples/rewrites/package.json create mode 100644 examples/rewrites/pages/about.js create mode 100644 examples/rewrites/pages/index.js create mode 100644 examples/rewrites/pages/news/[...slug].js create mode 100644 examples/rewrites/styles.module.css diff --git a/docs/api-reference/next.config.js/rewrites.md b/docs/api-reference/next.config.js/rewrites.md index f922daaebf2cd..29d99b460ad98 100644 --- a/docs/api-reference/next.config.js/rewrites.md +++ b/docs/api-reference/next.config.js/rewrites.md @@ -4,6 +4,13 @@ description: Add rewrites to your Next.js app. # Rewrites +
+ Examples + +
+ Rewrites allow you to map an incoming request path to a different destination path. Rewrites are only available on the Node.js environment and do not affect client-side routing. diff --git a/examples/rewrites/.gitignore b/examples/rewrites/.gitignore new file mode 100644 index 0000000000000..1437c53f70bc2 --- /dev/null +++ b/examples/rewrites/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/rewrites/README.md b/examples/rewrites/README.md new file mode 100644 index 0000000000000..4e02089b55fb9 --- /dev/null +++ b/examples/rewrites/README.md @@ -0,0 +1,23 @@ +# Rewrites Example + +This example shows how to use [rewrites in Next.js](https://nextjs.org/docs/api-reference/next.config.js/rewrites) to map an incoming request path to a different destination path. + +The index page ([`pages/index.js`](pages/index.js)) has a list of links that match the rewrites defined in [`next.config.js`](next.config.js). Run or deploy the app to see how it works! + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/rewrites) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example rewrites rewrites-app +# or +yarn create next-app --example rewrites rewrites-app +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/rewrites/next.config.js b/examples/rewrites/next.config.js new file mode 100644 index 0000000000000..be6f24b054da1 --- /dev/null +++ b/examples/rewrites/next.config.js @@ -0,0 +1,29 @@ +module.exports = { + async rewrites() { + return [ + { + source: '/team', + destination: '/about', + }, + { + source: '/about-us', + destination: '/about', + }, + // Path Matching - will match `/post/a` but not `/post/a/b` + { + source: '/post/:slug', + destination: '/news/:slug', + }, + // Wildcard Path Matching - will match `/news/a` and `/news/a/b` + { + source: '/blog/:slug*', + destination: '/news/:slug*', + }, + // Rewriting to an external URL + { + source: '/docs/:slug', + destination: 'http://example.com/docs/:slug', + }, + ] + }, +} diff --git a/examples/rewrites/package.json b/examples/rewrites/package.json new file mode 100644 index 0000000000000..460cf0fcad9ff --- /dev/null +++ b/examples/rewrites/package.json @@ -0,0 +1,15 @@ +{ + "name": "rewrites", + "version": "1.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "9.4.5-canary.43", + "react": "^16.13.1", + "react-dom": "^16.13.1" + }, + "license": "MIT" +} diff --git a/examples/rewrites/pages/about.js b/examples/rewrites/pages/about.js new file mode 100644 index 0000000000000..4e4ce29213362 --- /dev/null +++ b/examples/rewrites/pages/about.js @@ -0,0 +1,32 @@ +import { useState, useEffect } from 'react' +import { useRouter } from 'next/router' +import Link from 'next/link' +import styles from '../styles.module.css' + +const Code = (p) => + +export default function About() { + const { asPath, route } = useRouter() + const [path, setPath] = useState() + + // `asPath` is always `/about` in Node.js (server render), because the page is statically generated + // so we wait for the browser to load, and use the updated `asPath`, which may be a path + // other than `/about` when using a rewrite. This way we can avoid a content mismatch + useEffect(() => setPath(asPath), [asPath]) + + return ( +
+
+

Path: {path}

+
+

+ {' '} + This page was rendered by {`pages${route}.js`}. +

+ + ← Back home + +
+
+ ) +} diff --git a/examples/rewrites/pages/index.js b/examples/rewrites/pages/index.js new file mode 100644 index 0000000000000..6baeb6afce252 --- /dev/null +++ b/examples/rewrites/pages/index.js @@ -0,0 +1,54 @@ +import styles from '../styles.module.css' +import Link from 'next/link' + +const Code = (p) => + +const Index = () => ( +
+
+

Rewrites with Next.js

+
+

+ The links below are{' '} + + custom rewrites + {' '} + that map an incoming request path to a different destination path. +

+ +

+ Open next.config.js to learn more about the rewrites that + match the links above. +

+
+
+
+) + +export default Index diff --git a/examples/rewrites/pages/news/[...slug].js b/examples/rewrites/pages/news/[...slug].js new file mode 100644 index 0000000000000..1b537cf69be27 --- /dev/null +++ b/examples/rewrites/pages/news/[...slug].js @@ -0,0 +1,35 @@ +import { useRouter } from 'next/router' +import Link from 'next/link' +import styles from '../../styles.module.css' + +const Code = (p) => + +export default function News() { + const { asPath, route, query } = useRouter() + + return ( +
+
+

Path: {asPath}

+
+

+ This page was rendered by {`pages${route}.js`}. +

+

+ The query slug for this page is:{' '} + {JSON.stringify(query.slug)} +

+ + ← Back home + +
+
+ ) +} + +// Use SSR for this page as currently rewrites don't work with dynamic pages without SSR +export async function getServerSideProps(context) { + return { + props: {}, + } +} diff --git a/examples/rewrites/styles.module.css b/examples/rewrites/styles.module.css new file mode 100644 index 0000000000000..cd7bec9b86a9b --- /dev/null +++ b/examples/rewrites/styles.module.css @@ -0,0 +1,51 @@ +.container { + padding: 4rem 1rem; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; +} + +.container p { + margin: 1.5rem 0; +} + +.card { + max-width: 50rem; + box-shadow: -10px 10px 80px rgba(0, 0, 0, 0.12); + border: 1px solid #eee; + border-radius: 8px; + padding: 2rem; + margin: 0 auto; +} + +.inlineCode { + color: #be00ff; + font-size: 16px; + white-space: pre-wrap; +} + +.inlineCode::before, +.inlineCode::after { + content: '`'; +} + +.hr { + border: 0; + border-top: 1px solid #eaeaea; + margin: 1.5rem 0; +} + +.list { + padding-left: 1.5rem; + margin: 1.25rem 0; + list-style-type: none; +} + +.list li { + margin-bottom: 0.75rem; +} + +.list li:before { + content: '-'; + color: #999999; + position: absolute; + margin-left: -1rem; +} From 1509465f25169444454276074397466e1f73447e Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Fri, 24 Jul 2020 23:07:42 -0400 Subject: [PATCH 10/16] Update lockfile --- yarn.lock | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/yarn.lock b/yarn.lock index b154883c4e116..fa0589cc1b5e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4104,7 +4104,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.13.0, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: +browserslist@4.13.0, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.13.0, browserslist@^4.3.6, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: version "4.13.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d" integrity sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ== @@ -4114,14 +4114,6 @@ browserslist@4.13.0, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4. escalade "^3.0.1" node-releases "^1.1.58" -browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: - version "1.7.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" - integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk= - dependencies: - caniuse-db "^1.0.30000639" - electron-to-chromium "^1.2.7" - browserstack-local@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.0.tgz#d979cac056f57b9af159b3bcd7fdc09b4354537c" @@ -4420,21 +4412,11 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634: version "1.0.30001023" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001023.tgz#f856f71af16a5a44e81f1fcefc1673912a43da72" -caniuse-db@^1.0.30000639: - version "1.0.30001105" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001105.tgz#5cc03239a9d4540b3fa9a1dc8b5e3bda50226e96" - integrity sha512-GZytZn8lOiru/Tw+/X5sFxrFt2uPdSvkxVKzRMJyX20JGwfwOuTiRg5IMVF9II8Lao/7C4YeHR8YnZzpTvYXdQ== - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001019, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001093: version "1.0.30001066" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz#0a8a58a10108f2b9bf38e7b65c237b12fd9c5f04" integrity sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw== -caniuse-lite@^1.0.30001093: - version "1.0.30001105" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001105.tgz#d2cb0b31e5cf2f3ce845033b61c5c01566549abf" - integrity sha512-JupOe6+dGMr7E20siZHIZQwYqrllxotAhiaej96y6x00b/48rPt42o+SzOSCPbrpsDWvRja40Hwrj0g0q6LZJg== - capitalize@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/capitalize/-/capitalize-1.0.0.tgz#dc802c580aee101929020d2ca14b4ca8a0ae44be" @@ -6156,11 +6138,6 @@ ejs@^2.6.1: version "2.7.4" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" -electron-to-chromium@^1.2.7: - version "1.3.506" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.506.tgz#74bd3e1fb31285b3247f165d4958da74d70ae4e4" - integrity sha512-k0PHtv4gD6KJu1k6lp8pvQOe12uZriOwS2x66Vnxkq0NOBucsNrItOj/ehomvcZ3S4K1ueqUCv+fsLhXBs6Zyw== - electron-to-chromium@^1.3.488: version "1.3.501" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.501.tgz#faa17a2cb0105ee30d5e1ca87eae7d8e85dd3175" From c9836676542e6aed8c833307853489838a8672fe Mon Sep 17 00:00:00 2001 From: Karl Horky Date: Sat, 25 Jul 2020 05:28:18 +0200 Subject: [PATCH 11/16] Link with-polyfills example to updated polyfill docs (#13943) * Link to updated polyfill docs Ref: https://github.com/vercel/next.js/pull/13766 * Update examples/with-polyfills/README.md Co-authored-by: Joe Haddad --- examples/with-polyfills/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/with-polyfills/README.md b/examples/with-polyfills/README.md index 360159375bb82..0ac052ada46a3 100644 --- a/examples/with-polyfills/README.md +++ b/examples/with-polyfills/README.md @@ -1,5 +1,7 @@ # Example app with polyfills +> ❗️ Warning: This example is not the suggested way to add polyfills and is known to cause issues with bundling. See [the browser support docs](https://nextjs.org/docs/basic-features/supported-browsers-features#custom-polyfills) for the correct way to load polyfills. + Next.js supports modern browsers and IE 11. It loads required polyfills automatically. If you need to add custom polyfills, you can follow this example. ## Deploy your own From f22f88fd733518299b9941c2a2e9747b837e47f1 Mon Sep 17 00:00:00 2001 From: James Mosier <2854919+jamesmosier@users.noreply.github.com> Date: Sat, 25 Jul 2020 00:36:43 -0400 Subject: [PATCH 12/16] Always resolve after router.prefetch() (#15448) In development or with an invalid href, `await router.prefetch()` would not resolve the promise. This PR ensures that `await router.prefetch()` always resolves, no matter if it succeeds or not. Fixes: https://github.com/vercel/next.js/issues/15436 Relevant discussion: https://github.com/vercel/next.js/discussions/15431#discussioncomment-41264 --- .../next/next-server/lib/router/router.ts | 29 ++++----- .../build-output/test/index.test.js | 2 +- .../router-prefetch/pages/another-page.js | 3 + .../router-prefetch/pages/index.js | 22 +++++++ .../router-prefetch/test/index.test.js | 65 +++++++++++++++++++ 5 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 test/integration/router-prefetch/pages/another-page.js create mode 100644 test/integration/router-prefetch/pages/index.js create mode 100644 test/integration/router-prefetch/test/index.test.js diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 56e5fa0efda50..1e256d190aac2 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -802,28 +802,27 @@ export default class Router implements BaseRouter { * @param url the href of prefetched page * @param asPath the as path of the prefetched page */ - prefetch( + async prefetch( url: string, asPath: string = url, options: PrefetchOptions = {} ): Promise { - return new Promise((resolve, reject) => { - const parsed = tryParseRelativeUrl(url) + const parsed = tryParseRelativeUrl(url) - if (!parsed) return + if (!parsed) return - const { pathname } = parsed + const { pathname } = parsed - // Prefetch is not supported in development mode because it would trigger on-demand-entries - if (process.env.NODE_ENV !== 'production') { - return - } - const route = removePathTrailingSlash(pathname) - Promise.all([ - this.pageLoader.prefetchData(url, asPath), - this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route), - ]).then(() => resolve(), reject) - }) + // Prefetch is not supported in development mode because it would trigger on-demand-entries + if (process.env.NODE_ENV !== 'production') { + return + } + + const route = removePathTrailingSlash(pathname) + await Promise.all([ + this.pageLoader.prefetchData(url, asPath), + this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route), + ]) } async fetchComponent(route: string): Promise { diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index fc148ee0066de..fb864ecf43991 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -104,7 +104,7 @@ describe('Build Output', () => { expect(parseFloat(err404FirstLoad) - 63).toBeLessThanOrEqual(0) expect(err404FirstLoad.endsWith('kB')).toBe(true) - expect(parseFloat(sharedByAll) - 59.2).toBeLessThanOrEqual(0) + expect(parseFloat(sharedByAll) - 59.3).toBeLessThanOrEqual(0) expect(sharedByAll.endsWith('kB')).toBe(true) if (_appSize.endsWith('kB')) { diff --git a/test/integration/router-prefetch/pages/another-page.js b/test/integration/router-prefetch/pages/another-page.js new file mode 100644 index 0000000000000..00812ffc7c611 --- /dev/null +++ b/test/integration/router-prefetch/pages/another-page.js @@ -0,0 +1,3 @@ +export default function AnotherPage() { + return null +} diff --git a/test/integration/router-prefetch/pages/index.js b/test/integration/router-prefetch/pages/index.js new file mode 100644 index 0000000000000..9bedf8703bc64 --- /dev/null +++ b/test/integration/router-prefetch/pages/index.js @@ -0,0 +1,22 @@ +import { useState } from 'react' +import { useRouter } from 'next/router' + +export default function RouterPrefetch() { + const router = useRouter() + const [visible, setVisible] = useState(false) + const handleClick = async () => { + await router.prefetch( + process.env.NODE_ENV === 'development' ? '/another-page' : 'vercel.com' + ) + setVisible(true) + } + + return ( +
+ + {visible &&
visible
} +
+ ) +} diff --git a/test/integration/router-prefetch/test/index.test.js b/test/integration/router-prefetch/test/index.test.js new file mode 100644 index 0000000000000..ba8335c0141bc --- /dev/null +++ b/test/integration/router-prefetch/test/index.test.js @@ -0,0 +1,65 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { + findPort, + launchApp, + killApp, + nextStart, + nextBuild, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 5) +let app +let appPort +const appDir = join(__dirname, '..') + +const didResolveAfterPrefetch = async () => { + const browser = await webdriver(appPort, '/') + const text = await browser + .elementByCss('#prefetch-button') + .click() + .waitForElementByCss('#hidden-until-click') + .text() + expect(text).toBe('visible') + await browser.close() +} + +describe('Router prefetch', () => { + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + + it('should not prefetch', async () => { + const browser = await webdriver(appPort, '/') + const links = await browser + .elementByCss('#prefetch-button') + .click() + .elementsByCss('link[rel=prefetch]') + + expect(links.length).toBe(0) + await browser.close() + }) + + it('should resolve prefetch promise', async () => { + await didResolveAfterPrefetch() + }) + }) + + describe('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + it('should resolve prefetch promise with invalid href', async () => { + await didResolveAfterPrefetch() + }) + }) +}) From 574fe0b582d5cc1b13663121fd47a3d82deaaa17 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Sat, 25 Jul 2020 07:11:42 +0200 Subject: [PATCH 13/16] Make dynamic routes case-sensitive (#15444) Fixes https://github.com/vercel/next.js/issues/15377 Closes https://github.com/vercel/next.js/pull/15394 --- .../lib/router/utils/route-regex.ts | 4 ++-- .../pages/dynamic/[slug]/route.js | 13 +++++++++++ .../client-navigation/test/index.test.js | 22 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/integration/client-navigation/pages/dynamic/[slug]/route.js diff --git a/packages/next/next-server/lib/router/utils/route-regex.ts b/packages/next/next-server/lib/router/utils/route-regex.ts index b0f74bef7480a..be5c3865e2da7 100644 --- a/packages/next/next-server/lib/router/utils/route-regex.ts +++ b/packages/next/next-server/lib/router/utils/route-regex.ts @@ -107,7 +107,7 @@ export function getRouteRegex( .join('') return { - re: new RegExp(`^${parameterizedRoute}(?:/)?$`, 'i'), + re: new RegExp(`^${parameterizedRoute}(?:/)?$`), groups, routeKeys, namedRegex: `^${namedParameterizedRoute}(?:/)?$`, @@ -115,7 +115,7 @@ export function getRouteRegex( } return { - re: new RegExp(`^${parameterizedRoute}(?:/)?$`, 'i'), + re: new RegExp(`^${parameterizedRoute}(?:/)?$`), groups, } } diff --git a/test/integration/client-navigation/pages/dynamic/[slug]/route.js b/test/integration/client-navigation/pages/dynamic/[slug]/route.js new file mode 100644 index 0000000000000..0f1da367eac78 --- /dev/null +++ b/test/integration/client-navigation/pages/dynamic/[slug]/route.js @@ -0,0 +1,13 @@ +import React from 'react' + +export default class DynamicRoute extends React.Component { + static async getInitialProps({ query = { slug: 'default' } }) { + return { + query, + } + } + + render() { + return

{this.props.query.slug}

+ } +} diff --git a/test/integration/client-navigation/test/index.test.js b/test/integration/client-navigation/test/index.test.js index 482d3d5a9cee5..12d3cf648e93f 100644 --- a/test/integration/client-navigation/test/index.test.js +++ b/test/integration/client-navigation/test/index.test.js @@ -43,6 +43,7 @@ describe('Client Navigation', () => { '/url-prop-override', '/dynamic/ssr', + '/dynamic/[slug]/route', '/nav', '/nav/about', @@ -1002,6 +1003,27 @@ describe('Client Navigation', () => { await browser.close() }) + it('should get url dynamic param', async () => { + const browser = await webdriver( + context.appPort, + '/dynamic/dynamic-part/route' + ) + expect(await browser.elementByCss('p').text()).toBe('dynamic-part') + await browser.close() + }) + + it('should 404 on wrong casing of url dynamic param', async () => { + const browser = await webdriver( + context.appPort, + '/dynamic/dynamic-part/RoUtE' + ) + expect(await browser.elementByCss('h1').text()).toBe('404') + expect(await browser.elementByCss('h2').text()).toBe( + 'This page could not be found.' + ) + await browser.close() + }) + it('should not 404 for /', async () => { const browser = await webdriver(context.appPort, '/nav/about/') const text = await browser.elementByCss('p').text() From ebe4bb1ee4fb79fa78bf7ef5ee23f3c035becf75 Mon Sep 17 00:00:00 2001 From: David Stotijn Date: Sat, 25 Jul 2020 23:16:20 +0200 Subject: [PATCH 14/16] Upgrade Apollo Client to 3.0 in `examples/api-routes-apollo-server-and-client-auth` (#15272) --- .../apollo/client.js | 7 +++---- .../apollo/type-defs.js | 2 +- .../package.json | 9 +-------- .../pages/_app.js | 2 +- .../pages/index.js | 3 +-- .../pages/signin.js | 4 ++-- .../pages/signout.js | 3 +-- .../pages/signup.js | 3 +-- 8 files changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/api-routes-apollo-server-and-client-auth/apollo/client.js b/examples/api-routes-apollo-server-and-client-auth/apollo/client.js index 26ae378500bdc..a25caf6efc987 100644 --- a/examples/api-routes-apollo-server-and-client-auth/apollo/client.js +++ b/examples/api-routes-apollo-server-and-client-auth/apollo/client.js @@ -1,16 +1,15 @@ import { useMemo } from 'react' -import { ApolloClient } from 'apollo-client' -import { InMemoryCache } from 'apollo-cache-inmemory' +import { ApolloClient, InMemoryCache } from '@apollo/client' let apolloClient function createIsomorphLink() { if (typeof window === 'undefined') { - const { SchemaLink } = require('apollo-link-schema') + const { SchemaLink } = require('@apollo/client/link/schema') const { schema } = require('./schema') return new SchemaLink({ schema }) } else { - const { HttpLink } = require('apollo-link-http') + const { HttpLink } = require('@apollo/client/link/http') return new HttpLink({ uri: '/api/graphql', credentials: 'same-origin', diff --git a/examples/api-routes-apollo-server-and-client-auth/apollo/type-defs.js b/examples/api-routes-apollo-server-and-client-auth/apollo/type-defs.js index bea78a284e80b..e9ae131763228 100644 --- a/examples/api-routes-apollo-server-and-client-auth/apollo/type-defs.js +++ b/examples/api-routes-apollo-server-and-client-auth/apollo/type-defs.js @@ -1,4 +1,4 @@ -import gql from 'graphql-tag' +import { gql } from '@apollo/client' export const typeDefs = gql` type User { diff --git a/examples/api-routes-apollo-server-and-client-auth/package.json b/examples/api-routes-apollo-server-and-client-auth/package.json index d0ccec797752a..5eb3bf2467176 100644 --- a/examples/api-routes-apollo-server-and-client-auth/package.json +++ b/examples/api-routes-apollo-server-and-client-auth/package.json @@ -7,18 +7,11 @@ "start": "next start" }, "dependencies": { - "@apollo/react-common": "^3.1.4", - "@apollo/react-hooks": "^3.1.5", + "@apollo/client": "^3.0.2", "@hapi/iron": "6.0.0", - "apollo-cache-inmemory": "^1.6.6", - "apollo-client": "^2.6.10", - "apollo-link-http": "^1.5.17", - "apollo-link-schema": "^1.2.5", "apollo-server-micro": "^2.14.2", - "apollo-utilities": "^1.3.2", "cookie": "^0.4.1", "graphql": "^14.0.2", - "graphql-tag": "^2.10.3", "next": "latest", "prop-types": "^15.6.2", "react": "^16.7.0", diff --git a/examples/api-routes-apollo-server-and-client-auth/pages/_app.js b/examples/api-routes-apollo-server-and-client-auth/pages/_app.js index 0345a86b23ea3..482728ae76e6d 100644 --- a/examples/api-routes-apollo-server-and-client-auth/pages/_app.js +++ b/examples/api-routes-apollo-server-and-client-auth/pages/_app.js @@ -1,4 +1,4 @@ -import { ApolloProvider } from '@apollo/react-hooks' +import { ApolloProvider } from '@apollo/client' import { useApollo } from '../apollo/client' export default function App({ Component, pageProps }) { diff --git a/examples/api-routes-apollo-server-and-client-auth/pages/index.js b/examples/api-routes-apollo-server-and-client-auth/pages/index.js index 11ed53b934dee..34d1186744cfc 100644 --- a/examples/api-routes-apollo-server-and-client-auth/pages/index.js +++ b/examples/api-routes-apollo-server-and-client-auth/pages/index.js @@ -1,8 +1,7 @@ import { useEffect } from 'react' import { useRouter } from 'next/router' import Link from 'next/link' -import gql from 'graphql-tag' -import { useQuery } from '@apollo/react-hooks' +import { gql, useQuery } from '@apollo/client' const ViewerQuery = gql` query ViewerQuery { diff --git a/examples/api-routes-apollo-server-and-client-auth/pages/signin.js b/examples/api-routes-apollo-server-and-client-auth/pages/signin.js index 34bc49d790b26..0e652164cffef 100644 --- a/examples/api-routes-apollo-server-and-client-auth/pages/signin.js +++ b/examples/api-routes-apollo-server-and-client-auth/pages/signin.js @@ -1,8 +1,8 @@ import { useState } from 'react' import { useRouter } from 'next/router' import Link from 'next/link' -import gql from 'graphql-tag' -import { useMutation, useApolloClient } from '@apollo/react-hooks' +import { gql } from '@apollo/client' +import { useMutation, useApolloClient } from '@apollo/client' import { getErrorMessage } from '../lib/form' import Field from '../components/field' diff --git a/examples/api-routes-apollo-server-and-client-auth/pages/signout.js b/examples/api-routes-apollo-server-and-client-auth/pages/signout.js index 11e67f9e99d09..046310db75e77 100644 --- a/examples/api-routes-apollo-server-and-client-auth/pages/signout.js +++ b/examples/api-routes-apollo-server-and-client-auth/pages/signout.js @@ -1,7 +1,6 @@ import { useEffect } from 'react' import { useRouter } from 'next/router' -import { useMutation, useApolloClient } from '@apollo/react-hooks' -import gql from 'graphql-tag' +import { gql, useMutation, useApolloClient } from '@apollo/client' const SignOutMutation = gql` mutation SignOutMutation { diff --git a/examples/api-routes-apollo-server-and-client-auth/pages/signup.js b/examples/api-routes-apollo-server-and-client-auth/pages/signup.js index 70db989347a4f..ce16f159e4c94 100644 --- a/examples/api-routes-apollo-server-and-client-auth/pages/signup.js +++ b/examples/api-routes-apollo-server-and-client-auth/pages/signup.js @@ -1,8 +1,7 @@ import { useState } from 'react' import { useRouter } from 'next/router' import Link from 'next/link' -import gql from 'graphql-tag' -import { useMutation } from '@apollo/react-hooks' +import { gql, useMutation } from '@apollo/client' import { getErrorMessage } from '../lib/form' import Field from '../components/field' From d3955cdf514e7d5032d5929e551796106ede21c7 Mon Sep 17 00:00:00 2001 From: Kaic Bastidas Date: Sat, 25 Jul 2020 22:36:36 -0300 Subject: [PATCH 15/16] TypeScript documentation for _document.tsx (#15386) - Update the [Custom Document page](https://nextjs.org/docs/advanced-features/custom-document) to include an example using `DocumentContext`. --- docs/advanced-features/custom-document.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/advanced-features/custom-document.md b/docs/advanced-features/custom-document.md index a3e02877b2988..1e1605044fcb9 100644 --- a/docs/advanced-features/custom-document.md +++ b/docs/advanced-features/custom-document.md @@ -85,3 +85,21 @@ class MyDocument extends Document { export default MyDocument ``` + +## TypeScript + +You can use the built-in `DocumentContext` type and change the file name to `./pages/_document.tsx` like so: + +```tsx +import Document, { DocumentContext } from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + const initialProps = await Document.getInitialProps(ctx) + + return initialProps + } +} + +export default MyDocument +``` From 2f50f1f8c948b5766a783adfba5e05d129deb624 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Sun, 26 Jul 2020 06:57:06 +0200 Subject: [PATCH 16/16] Stabilize more tests (#15470) jest retries seem to be masking test failures, like the `auto-export` one (and maybe others). I turned it off for now. The `auto-export` test fails when retries are turned off. the output of this test failure was a bit unhelpful so I also improved it. Many tests have anonymous page functions. --- package.json | 4 +- run-tests.js | 2 +- test/integration/amphtml/pages/only-amp.js | 12 ++- .../auto-export/pages/[post]/[cmnt].js | 6 +- .../auto-export/pages/[post]/index.js | 2 +- .../auto-export/test/index.test.js | 5 +- test/integration/build-indicator/pages/a.js | 4 +- test/integration/build-indicator/pages/b.js | 4 +- .../build-indicator/pages/index.js | 22 +++-- .../test/index.test.js | 38 +++---- .../error-in-error/test/index.test.js | 2 +- test/integration/invalid-href/pages/third.js | 2 +- .../invalid-href/test/index.test.js | 23 ++--- .../query-with-encoding/test/index.test.js | 76 +++++--------- .../scroll-back-restoration/pages/another.js | 18 ++-- .../serverless-runtime-configs/pages/_app.js | 14 +-- test/lib/wd-chain.js | 8 ++ yarn.lock | 98 ++++++++++--------- 18 files changed, 167 insertions(+), 173 deletions(-) diff --git a/package.json b/package.json index 661cb0b35b35d..0e6df064ec978 100644 --- a/package.json +++ b/package.json @@ -112,8 +112,8 @@ "release": "6.0.1", "request-promise-core": "1.1.2", "rimraf": "2.6.3", - "selenium-standalone": "6.17.0", - "selenium-webdriver": "4.0.0-alpha.5", + "selenium-standalone": "6.18.0", + "selenium-webdriver": "4.0.0-alpha.7", "shell-quote": "1.7.2", "styled-components": "5.1.0", "styled-jsx-plugin-postcss": "2.0.1", diff --git a/run-tests.js b/run-tests.js index 5dd3964ab57cd..c37091b807d66 100644 --- a/run-tests.js +++ b/run-tests.js @@ -136,7 +136,7 @@ const TIMINGS_API = `https://next-timings.jjsweb.site/api/timings` { stdio: 'inherit', env: { - JEST_RETRY_TIMES: 2, + JEST_RETRY_TIMES: 0, ...process.env, ...(isAzure ? { diff --git a/test/integration/amphtml/pages/only-amp.js b/test/integration/amphtml/pages/only-amp.js index 653dbecd541d5..41866b35c52c3 100644 --- a/test/integration/amphtml/pages/only-amp.js +++ b/test/integration/amphtml/pages/only-amp.js @@ -1,7 +1,9 @@ export const config = { amp: true } -export default () => ( -
-

Only AMP for me...

-
-) +export default function Page() { + return ( +
+

Only AMP for me...

+
+ ) +} diff --git a/test/integration/auto-export/pages/[post]/[cmnt].js b/test/integration/auto-export/pages/[post]/[cmnt].js index 6c7af1c61ca1a..0982164d25db0 100644 --- a/test/integration/auto-export/pages/[post]/[cmnt].js +++ b/test/integration/auto-export/pages/[post]/[cmnt].js @@ -5,17 +5,17 @@ if (typeof window !== 'undefined') { const origWarn = window.console.warn const origError = window.console.error window.console.warn = function (...args) { - window.caughtWarns.push(1) + window.caughtWarns.push(args) origWarn(...args) } window.console.error = function (...args) { - window.caughtWarns.push(1) + window.caughtWarns.push(args) origError(...args) } window.pathnames = [] } -export default () => { +export default function Page() { if (typeof window !== 'undefined') { window.pathnames.push(window.location.pathname) } diff --git a/test/integration/auto-export/pages/[post]/index.js b/test/integration/auto-export/pages/[post]/index.js index 94492b467fa51..f3aee05d5bb12 100644 --- a/test/integration/auto-export/pages/[post]/index.js +++ b/test/integration/auto-export/pages/[post]/index.js @@ -1,6 +1,6 @@ import { useRouter } from 'next/router' -export default () => { +export default function Page() { const { query } = useRouter() return

post: {query.post}

diff --git a/test/integration/auto-export/test/index.test.js b/test/integration/auto-export/test/index.test.js index 6407b174dbc2e..edaff678a19d5 100644 --- a/test/integration/auto-export/test/index.test.js +++ b/test/integration/auto-export/test/index.test.js @@ -81,9 +81,8 @@ describe('Auto Export', () => { it('should not show hydration warning from mismatching asPath', async () => { const browser = await webdriver(appPort, '/zeit/cmnt-1') - - const numCaught = await browser.eval(`window.caughtWarns.length`) - expect(numCaught).toBe(0) + const caughtWarns = await browser.eval(`window.caughtWarns`) + expect(caughtWarns).toEqual([]) }) }) }) diff --git a/test/integration/build-indicator/pages/a.js b/test/integration/build-indicator/pages/a.js index f87a684893418..45361f18d6af3 100644 --- a/test/integration/build-indicator/pages/a.js +++ b/test/integration/build-indicator/pages/a.js @@ -1 +1,3 @@ -export default () =>

Hello from a

+export default function Page() { + return

Hello from a

+} diff --git a/test/integration/build-indicator/pages/b.js b/test/integration/build-indicator/pages/b.js index 6226f2d97f20a..fb38aab0d4496 100644 --- a/test/integration/build-indicator/pages/b.js +++ b/test/integration/build-indicator/pages/b.js @@ -1 +1,3 @@ -export default () =>

Hello from b

+export default function Page() { + return

Hello from b

+} diff --git a/test/integration/build-indicator/pages/index.js b/test/integration/build-indicator/pages/index.js index 9f630e36e417e..b810ac290b56c 100644 --- a/test/integration/build-indicator/pages/index.js +++ b/test/integration/build-indicator/pages/index.js @@ -1,12 +1,14 @@ import Link from 'next/link' -export default () => ( - <> - - Go to a - - - Go to b - - -) +export default function Page() { + return ( + <> + + Go to a + + + Go to b + + + ) +} diff --git a/test/integration/dynamic-optional-routing/test/index.test.js b/test/integration/dynamic-optional-routing/test/index.test.js index 90dcb70c9e138..c17bd9168bf55 100644 --- a/test/integration/dynamic-optional-routing/test/index.test.js +++ b/test/integration/dynamic-optional-routing/test/index.test.js @@ -10,7 +10,7 @@ import { nextBuild, nextStart, renderViaHTTP, - waitFor, + check, } from 'next-test-utils' import { join } from 'path' @@ -18,6 +18,7 @@ jest.setTimeout(1000 * 60 * 2) let app let appPort +let stderr const appDir = join(__dirname, '../') const DUMMY_PAGE = 'export default () => null' @@ -187,9 +188,10 @@ function runInvalidPagesTests(buildFn) { const invalidRoute = appDir + 'pages/index.js' try { await fs.outputFile(invalidRoute, DUMMY_PAGE, 'utf-8') - const { stderr } = await buildFn(appDir) - await expect(stderr).toMatch( - 'You cannot define a route with the same specificity as a optional catch-all route' + await buildFn(appDir) + await check( + () => stderr, + /You cannot define a route with the same specificity as a optional catch-all route/ ) } finally { await fs.unlink(invalidRoute) @@ -200,9 +202,10 @@ function runInvalidPagesTests(buildFn) { const invalidRoute = appDir + 'pages/nested.js' try { await fs.outputFile(invalidRoute, DUMMY_PAGE, 'utf-8') - const { stderr } = await buildFn(appDir) - await expect(stderr).toMatch( - 'You cannot define a route with the same specificity as a optional catch-all route' + await buildFn(appDir) + await check( + () => stderr, + /You cannot define a route with the same specificity as a optional catch-all route/ ) } finally { await fs.unlink(invalidRoute) @@ -213,8 +216,8 @@ function runInvalidPagesTests(buildFn) { const invalidRoute = appDir + 'pages/nested/[...param].js' try { await fs.outputFile(invalidRoute, DUMMY_PAGE, 'utf-8') - const { stderr } = await buildFn(appDir) - await expect(stderr).toMatch(/You cannot use both .+ at the same level/) + await buildFn(appDir) + await check(() => stderr, /You cannot use both .+ at the same level/) } finally { await fs.unlink(invalidRoute) } @@ -224,9 +227,10 @@ function runInvalidPagesTests(buildFn) { const invalidRoute = appDir + 'pages/invalid/[[param]].js' try { await fs.outputFile(invalidRoute, DUMMY_PAGE, 'utf-8') - const { stderr } = await buildFn(appDir) - await expect(stderr).toMatch( - 'Optional route parameters are not yet supported' + await buildFn(appDir) + await check( + () => stderr, + /Optional route parameters are not yet supported/ ) } finally { await fs.unlink(invalidRoute) @@ -245,14 +249,12 @@ describe('Dynamic Optional Routing', () => { runTests() runInvalidPagesTests(async (appDir) => { - let stderr = '' + stderr = '' await launchApp(appDir, await findPort(), { onStderr: (msg) => { stderr += msg }, }) - await waitFor(1000) - return { stderr } }) }) @@ -272,9 +274,9 @@ describe('Dynamic Optional Routing', () => { runTests() - runInvalidPagesTests(async (appDir) => - nextBuild(appDir, [], { stderr: true }) - ) + runInvalidPagesTests(async (appDir) => { + ;({ stderr } = await nextBuild(appDir, [], { stderr: true })) + }) it('should fail to build when param is not explicitly defined', async () => { const invalidRoute = appDir + 'pages/invalid/[[...slug]].js' diff --git a/test/integration/error-in-error/test/index.test.js b/test/integration/error-in-error/test/index.test.js index 2b4bee2991071..4443aec17bc33 100644 --- a/test/integration/error-in-error/test/index.test.js +++ b/test/integration/error-in-error/test/index.test.js @@ -31,7 +31,7 @@ describe('Handles an Error in _error', () => { it('Handles error during client transition', async () => { const browser = await webdriver(port, '/') - await browser.elementByCss('a').click() + await browser.waitForElementByCss('a').click() await waitFor(1000) const html = await browser.eval('document.body.innerHTML') expect(html).toMatch(/internal server error/i) diff --git a/test/integration/invalid-href/pages/third.js b/test/integration/invalid-href/pages/third.js index 12daa43427166..509367eab393d 100644 --- a/test/integration/invalid-href/pages/third.js +++ b/test/integration/invalid-href/pages/third.js @@ -3,7 +3,7 @@ import { useState } from 'react' const invalidLink = 'https://vercel.com/' -export default () => { +export default function Page() { const { query, ...router } = useRouter() const [isDone, setIsDone] = useState(false) const { method = 'push' } = query diff --git a/test/integration/invalid-href/test/index.test.js b/test/integration/invalid-href/test/index.test.js index b612671d88a38..cb95ed64945cd 100644 --- a/test/integration/invalid-href/test/index.test.js +++ b/test/integration/invalid-href/test/index.test.js @@ -30,24 +30,18 @@ jest.retryTimes(0) const showsError = async (pathname, regex, click = false, isWarn = false) => { const browser = await webdriver(appPort, pathname) try { + // wait for page to be built and navigated to + await browser.waitForElementByCss('#click-me') if (isWarn) { await browser.eval(`(function() { window.warnLogs = [] var origWarn = window.console.warn - window.console.warn = function() { - var warnStr = '' - for (var i = 0; i < arguments.length; i++) { - if (i > 0) warnStr += ' '; - warnStr += arguments[i] - } - window.warnLogs.push(warnStr) - origWarn.apply(undefined, arguments) + window.console.warn = (...args) => { + window.warnLogs.push(args.join(' ')) + origWarn.apply(window.console, args) } })()`) } - // wait for page to be built and navigated to - await waitFor(3000) - await browser.waitForElementByCss('#click-me') if (click) { await browser.elementByCss('#click-me').click() await waitFor(500) @@ -70,6 +64,11 @@ const showsError = async (pathname, regex, click = false, isWarn = false) => { const noError = async (pathname, click = false) => { const browser = await webdriver(appPort, '/') try { + await check(async () => { + const appReady = await browser.eval('!!window.next.router') + console.log('app ready: ', appReady) + return appReady ? 'ready' : 'nope' + }, 'ready') await browser.eval(`(function() { window.caughtErrors = [] window.addEventListener('error', function (error) { @@ -80,8 +79,6 @@ const noError = async (pathname, click = false) => { }) window.next.router.replace('${pathname}') })()`) - // wait for page to be built and navigated to - await waitFor(3000) await browser.waitForElementByCss('#click-me') if (click) { await browser.elementByCss('#click-me').click() diff --git a/test/integration/query-with-encoding/test/index.test.js b/test/integration/query-with-encoding/test/index.test.js index 237a054c152a1..3b25cc0e53016 100644 --- a/test/integration/query-with-encoding/test/index.test.js +++ b/test/integration/query-with-encoding/test/index.test.js @@ -1,12 +1,6 @@ /* eslint-env jest */ -import { - nextBuild, - nextServer, - startApp, - stopApp, - waitFor, -} from 'next-test-utils' +import { nextBuild, nextServer, startApp, stopApp } from 'next-test-utils' import webdriver from 'next-webdriver' import { join } from 'path' @@ -46,12 +40,11 @@ describe('Query String with Encoding', () => { it('should have correct query on Router#push', async () => { const browser = await webdriver(appPort, '/') try { - await waitFor(2000) + await browser.waitForCondition('!!window.next.router') await browser.eval( `window.next.router.push({pathname:'/',query:{abc:'def\\n'}})` ) - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"abc":"def\\n"}') } finally { await browser.close() @@ -61,10 +54,8 @@ describe('Query String with Encoding', () => { it('should have correct query on simple client-side ', async () => { const browser = await webdriver(appPort, '/newline') try { - await waitFor(2000) - await browser.elementByCss('#hello-lf').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-lf').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"another":"hello\\n"}') } finally { await browser.close() @@ -74,10 +65,8 @@ describe('Query String with Encoding', () => { it('should have correct query on complex client-side ', async () => { const browser = await webdriver(appPort, '/newline') try { - await waitFor(2000) - await browser.elementByCss('#hello-complex').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"complex":"yes\\n"}') } finally { await browser.close() @@ -99,12 +88,11 @@ describe('Query String with Encoding', () => { it('should have correct query on Router#push', async () => { const browser = await webdriver(appPort, '/') try { - await waitFor(2000) + await browser.waitForCondition('!!window.next.router') await browser.eval( `window.next.router.push({pathname:'/',query:{abc:'def '}})` ) - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"abc":"def "}') } finally { await browser.close() @@ -114,10 +102,8 @@ describe('Query String with Encoding', () => { it('should have correct query on simple client-side ', async () => { const browser = await webdriver(appPort, '/space') try { - await waitFor(2000) - await browser.elementByCss('#hello-space').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-space').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"another":"hello "}') } finally { await browser.close() @@ -127,10 +113,8 @@ describe('Query String with Encoding', () => { it('should have correct query on complex client-side ', async () => { const browser = await webdriver(appPort, '/space') try { - await waitFor(2000) - await browser.elementByCss('#hello-complex').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"complex":"yes "}') } finally { await browser.close() @@ -152,12 +136,11 @@ describe('Query String with Encoding', () => { it('should have correct query on Router#push', async () => { const browser = await webdriver(appPort, '/') try { - await waitFor(2000) + await browser.waitForCondition('!!window.next.router') await browser.eval( `window.next.router.push({pathname:'/',query:{abc:'def%'}})` ) - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"abc":"def%"}') } finally { await browser.close() @@ -167,10 +150,8 @@ describe('Query String with Encoding', () => { it('should have correct query on simple client-side ', async () => { const browser = await webdriver(appPort, '/percent') try { - await waitFor(2000) - await browser.elementByCss('#hello-percent').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-percent').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"another":"hello%"}') } finally { await browser.close() @@ -180,10 +161,8 @@ describe('Query String with Encoding', () => { it('should have correct query on complex client-side ', async () => { const browser = await webdriver(appPort, '/percent') try { - await waitFor(2000) - await browser.elementByCss('#hello-complex').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"complex":"yes%"}') } finally { await browser.close() @@ -205,12 +184,11 @@ describe('Query String with Encoding', () => { it('should have correct query on Router#push', async () => { const browser = await webdriver(appPort, '/') try { - await waitFor(2000) + await browser.waitForCondition('!!window.next.router') await browser.eval( `window.next.router.push({pathname:'/',query:{abc:'def+'}})` ) - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"abc":"def+"}') } finally { await browser.close() @@ -220,10 +198,8 @@ describe('Query String with Encoding', () => { it('should have correct query on simple client-side ', async () => { const browser = await webdriver(appPort, '/plus') try { - await waitFor(2000) - await browser.elementByCss('#hello-plus').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-plus').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"another":"hello+"}') } finally { await browser.close() @@ -233,10 +209,8 @@ describe('Query String with Encoding', () => { it('should have correct query on complex client-side ', async () => { const browser = await webdriver(appPort, '/plus') try { - await waitFor(2000) - await browser.elementByCss('#hello-complex').click() - await waitFor(1000) - const text = await browser.elementByCss('#query-content').text() + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser.waitForElementByCss('#query-content').text() expect(text).toBe('{"complex":"yes+"}') } finally { await browser.close() diff --git a/test/integration/scroll-back-restoration/pages/another.js b/test/integration/scroll-back-restoration/pages/another.js index a37308e7d8779..340b4403e3d80 100644 --- a/test/integration/scroll-back-restoration/pages/another.js +++ b/test/integration/scroll-back-restoration/pages/another.js @@ -1,10 +1,12 @@ import Link from 'next/link' -export default () => ( - <> -

hi from another

- - to index - - -) +export default function Page() { + return ( + <> +

hi from another

+ + to index + + + ) +} diff --git a/test/integration/serverless-runtime-configs/pages/_app.js b/test/integration/serverless-runtime-configs/pages/_app.js index 9d7f9f03f9d3b..de64911781767 100644 --- a/test/integration/serverless-runtime-configs/pages/_app.js +++ b/test/integration/serverless-runtime-configs/pages/_app.js @@ -2,9 +2,11 @@ import getConfig from 'next/config' const config = getConfig() -export default ({ Component, pageProps }) => ( - <> -

{JSON.stringify(config)}

- - -) +export default function App({ Component, pageProps }) { + return ( + <> +

{JSON.stringify(config)}

+ + + ) +} diff --git a/test/lib/wd-chain.js b/test/lib/wd-chain.js index fc807464183a7..af562bf313771 100644 --- a/test/lib/wd-chain.js +++ b/test/lib/wd-chain.js @@ -92,6 +92,14 @@ export default class Chain { ) } + waitForCondition(condition) { + return this.updateChain(() => + this.browser.wait(async (driver) => { + return driver.executeScript('return ' + condition).catch(() => false) + }) + ) + } + eval(snippet) { if (typeof snippet === 'string' && !snippet.startsWith('return')) { snippet = `return ${snippet}` diff --git a/yarn.lock b/yarn.lock index fa0589cc1b5e3..8668aa6810881 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3917,19 +3917,21 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493" - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - bl@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" dependencies: readable-stream "^3.0.1" +bl@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a" + integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -4177,6 +4179,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" @@ -9509,9 +9519,10 @@ jsx-ast-utils@^2.2.3: array-includes "^3.0.3" object.assign "^4.1.0" -jszip@^3.1.5: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.2.tgz#b143816df7e106a9597a94c77493385adca5bd1d" +jszip@^3.2.2: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6" + integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -13267,7 +13278,7 @@ read@1, read@~1.0.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.6.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -13638,9 +13649,9 @@ request@2.85.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@2.88.0, request@^2.86.0, request@^2.87.0, request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" +request@2.88.2, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -13649,7 +13660,7 @@ request@2.88.0, request@^2.86.0, request@^2.87.0, request@^2.88.0: extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.0" + har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -13659,13 +13670,13 @@ request@2.88.0, request@^2.86.0, request@^2.87.0, request@^2.88.0: performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.4.3" + tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" -request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" +request@^2.86.0, request@^2.87.0, request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -13674,7 +13685,7 @@ request@^2.88.2: extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.3" + har-validator "~5.1.0" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -13684,7 +13695,7 @@ request@^2.88.2: performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.5.0" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" uuid "^3.3.2" @@ -14122,7 +14133,7 @@ sass-loader@8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: +sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -14169,9 +14180,10 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" -selenium-standalone@6.17.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.17.0.tgz#0f24b691836205ee9bc3d7a6f207ebcb28170cd9" +selenium-standalone@6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.18.0.tgz#011e0672b1b86893f77244a86ddea1b6baadfb87" + integrity sha512-JfuJZoPPhnRuOXPM60wElwzzf3f92VkHa/W1f0QLBxSMKy6K/CTICfPo75aSro8X5wf6pa2npjD/CQmZjUqsoA== dependencies: async "^2.6.2" commander "^2.19.0" @@ -14181,20 +14193,20 @@ selenium-standalone@6.17.0: minimist "^1.2.0" mkdirp "^0.5.1" progress "2.0.3" - request "2.88.0" - tar-stream "2.0.0" + request "2.88.2" + tar-stream "2.1.3" urijs "^1.19.1" which "^1.3.1" yauzl "^2.10.0" -selenium-webdriver@4.0.0-alpha.5: - version "4.0.0-alpha.5" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.5.tgz#e4683b3dbf827d70df09a7e43bf02ebad20fa7c1" +selenium-webdriver@4.0.0-alpha.7: + version "4.0.0-alpha.7" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.7.tgz#e3879d8457fd7ad8e4424094b7dc0540d99e6797" + integrity sha512-D4qnTsyTr91jT8f7MfN+OwY0IlU5+5FmlO5xlgRUV6hDEV8JyYx2NerdTEqDDkNq7RZDYc4VoPALk8l578RBHw== dependencies: - jszip "^3.1.5" - rimraf "^2.6.3" + jszip "^3.2.2" + rimraf "^2.7.1" tmp "0.0.30" - xml2js "^0.4.19" semver-compare@^1.0.0: version "1.0.0" @@ -15087,11 +15099,12 @@ tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.0.0" -tar-stream@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.0.0.tgz#8829bbf83067bc0288a9089db49c56be395b6aea" +tar-stream@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41" + integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA== dependencies: - bl "^2.2.0" + bl "^4.0.1" end-of-stream "^1.4.1" fs-constants "^1.0.0" inherits "^2.0.3" @@ -16325,17 +16338,6 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" -xml2js@^0.4.19: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"