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/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 +``` 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/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') }} 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' 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; +} 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 0000000000000..4965832f2c9b0 Binary files /dev/null and b/examples/with-mongodb/public/favicon.ico differ diff --git a/examples/with-mongodb/public/vercel.svg b/examples/with-mongodb/public/vercel.svg new file mode 100644 index 0000000000000..fbf0e25a651c2 --- /dev/null +++ b/examples/with-mongodb/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ 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 } +} 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 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 } 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 0000000000000..4965832f2c9b0 Binary files /dev/null and b/examples/with-sitemap/public/favicon.ico differ diff --git a/examples/with-sitemap/public/sitemap.xml b/examples/with-sitemap/public/sitemap.xml new file mode 100644 index 0000000000000..73774003a77ab --- /dev/null +++ b/examples/with-sitemap/public/sitemap.xml @@ -0,0 +1,10 @@ + + + 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() 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/packages/create-next-app/README.md b/packages/create-next-app/README.md index ad7d9fce6ea76..98ff887a14504 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 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/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/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/run-tests.js b/run-tests.js index df5991493e6f4..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: 3, + 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/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/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() 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 e80edc99d00b6..cb95ed64945cd 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,27 +23,25 @@ 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 { + // 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) @@ -66,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) { @@ -76,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/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() + }) + }) +}) 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 5ba1dacc9ea3a..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" @@ -4590,7 +4600,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== @@ -6192,7 +6202,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== @@ -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" @@ -15999,15 +16012,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 +16075,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 +16088,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 +16101,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: @@ -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"