diff --git a/lerna.json b/lerna.json index 948802af90977..9f3fcff0827a1 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.2-canary.20" + "version": "9.2.2-canary.21" } diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 3520a03d4bec4..2f6c558f63f55 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -105,6 +105,11 @@ export async function createApp({ case 'gitignore': { return '.'.concat(name) } + // README.md is ignored by webpack-asset-relocator-loader used by ncc: + // https://github.com/zeit/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 + case 'README-template.md': { + return 'README.md' + } default: { return name } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index dd151039101c3..aa4ac2b456040 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "keywords": [ "react", "next", diff --git a/packages/create-next-app/templates/default/README-template.md b/packages/create-next-app/templates/default/README-template.md new file mode 100644 index 0000000000000..06b9f042d76a3 --- /dev/null +++ b/packages/create-next-app/templates/default/README-template.md @@ -0,0 +1,30 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/zeit/next.js/) - your feedback and contributions are welcome! + +## Deploy on ZEIT Now + +The easiest way to deploy your Next.js app is to use the [ZEIT Now Platform](https://zeit.co/) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/packages/create-next-app/templates/default/README.md b/packages/create-next-app/templates/default/README.md deleted file mode 100644 index 007329cc4b829..0000000000000 --- a/packages/create-next-app/templates/default/README.md +++ /dev/null @@ -1,171 +0,0 @@ -This project was bootstrapped with [Create Next App](https://github.com/zeit/next.js). - -Find the most recent version of this guide at [here](https://github.com/zeit/next.js/blob/master/lib/templates/default/README.md). And check out [Next.js repo](https://github.com/zeit/next.js) for the most up-to-date info. - -## Table of Contents - -- [Questions? Feedback?](#questions-feedback) -- [Folder Structure](#folder-structure) -- [Available Scripts](#available-scripts) - - [npm run dev](#npm-run-dev) - - [npm run build](#npm-run-build) - - [npm run start](#npm-run-start) -- [Using CSS](#using-css) -- [Adding Components](#adding-components) -- [Fetching Data](#fetching-data) -- [Syntax Highlighting](#syntax-highlighting) -- [Deploy to Now](#deploy-to-now) -- [Something Missing?](#something-missing) - -## Questions? Feedback? - -Check out [Next.js FAQ & docs](https://github.com/zeit/next.js#faq) or [let us know](https://github.com/zeit/next.js/issues) your feedback. - -## Folder Structure - -After creating an app, it should look something like: - -``` -. -├── README.md -├── node_modules -│ ├── [...] -├── package.json -├── pages -│ └── index.js -├── public -│ └── favicon.ico -└── yarn.lock -``` - -Routing in Next.js is based on the file system, so `./pages/index.js` maps to the `/` route and -`./pages/about.js` would map to `/about`. - -The `./public` directory maps to `/` in the `next` server, so you can put all your -other static resources like images or compiled CSS in there. - -Out of the box, we get: - -- Automatic compilation and bundling (with Babel and webpack) -- Hot code reloading -- Server rendering and indexing of `./pages/` -- Static file serving. `./public/` is mapped to `/` - -Read more about [Next's Routing](https://nextjs.org/docs/routing/introduction) - -## Available Scripts - -In the project directory, you can run: - -### `npm run dev` - -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
-You will also see any errors in the console. - -### `npm run build` - -Builds the app for production to the `.next` folder.
-It correctly bundles React in production mode and optimizes the build for the best performance. - -### `npm run start` - -Starts the application in production mode. -The application should be compiled with \`next build\` first. - -See the section in Next docs about [deployment](https://nextjs.org/docs/deployment) for more information. - -## Using CSS - -[`styled-jsx`](https://github.com/zeit/styled-jsx) is bundled with next to provide support for isolated scoped CSS. -The aim is to support "shadow CSS" resembling of Web Components. - -```jsx -export default () => ( -
- Hello world -

scoped!

- -
-) -``` - -Read more about [Next's CSS features](https://nextjs.org/docs/basic-features/built-in-css-support). - -## Adding Components - -We recommend keeping React components in `./components/` and they should look like: - -### `./components/hello.js` - -```jsx -import { useState } from 'react' - -export function Hello() { - const [text, setText] = useState('World') - return
Hello {text}
-} -``` - -## Fetching Data - -You can fetch data in `./pages/` components using `getInitialProps` like this: - -### `./pages/stars.js` - -```jsx -const Page = props =>
Next stars: {props.stars}
- -Page.getInitialProps = async ({ req }) => { - const res = await fetch('https://api.github.com/repos/zeit/next.js') - const json = await res.json() - const stars = json.stargazers_count - return { stars } -} - -export default Page -``` - -For the initial page load, `getInitialProps` will execute on the server only. `getInitialProps` will only be executed on the client when navigating to a different route via the `` component or using the routing APIs. - -_Note: `getInitialProps` can **not** be used in children components. Only in `./pages/`._ - -Read more about [fetching data and the component lifecycle](https://nextjs.org/docs/basic-features/data-fetching) - -## Syntax Highlighting - -To configure the syntax highlighting in your favorite text editor, head to the [relevant Babel documentation page](https://babeljs.io/docs/editors) and follow the instructions. Some of the most popular editors are covered. - -## Deploy to Now - -[ZEIT Now](https://zeit.co/home?utm_source=create-next-app&utm_medium=readme&utm_campaign=create-next-app) offers a zero-configuration single-command deployment. - -1. Install the `now` command-line tool either via npm `npm install -g now` or Yarn `yarn global add now`. - -2. Run `now` from your project directory. You will see a **now.sh** URL in your output like this: - - ``` - > Ready! https://your-project-dirname-tpspyhtdtk.now.sh (copied to clipboard) - ``` - - Paste that URL into your browser when the build is complete, and you will see your deployed app. - -You can find more details about [`ZEIT Now` here](https://zeit.co/home?utm_source=create-next-app&utm_medium=readme&utm_campaign=create-next-app). - -## Something Missing? - -If you have ideas for how we could improve this readme or the project in general, [let us know](https://github.com/zeit/next.js/issues) or [contribute some!](https://github.com/zeit/next.js/edit/master/lib/templates/default/README.md) diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 6a9bba9043a26..497cf7817fcdf 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index ef089212d8589..ca83431ccbd5a 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index 98a1b37779357..62d33fae768ee 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index 47f75e8dea01c..6097ea3b917a2 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 1abe2b37c17b3..68aef38e8941e 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 00d3804ec3d4d..8f0bf5fb6ac75 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/client/index.js b/packages/next/client/index.js index ffe66c9715340..c69fedcff8c84 100644 --- a/packages/next/client/index.js +++ b/packages/next/client/index.js @@ -206,6 +206,7 @@ export default async ({ webpackHMR: passedWebpackHMR } = {}) => { Component, wrapApp, err: initialErr, + isFallback, subscription: ({ Component, props, err }, App) => { render({ App, Component, props, err }) }, diff --git a/packages/next/client/router.ts b/packages/next/client/router.ts index 41d4dad46af9f..6de604eada93a 100644 --- a/packages/next/client/router.ts +++ b/packages/next/client/router.ts @@ -29,7 +29,14 @@ const singletonRouter: SingletonRouterBase = { } // Create public properties and methods of the router in the singletonRouter -const urlPropertyFields = ['pathname', 'route', 'query', 'asPath', 'components'] +const urlPropertyFields = [ + 'pathname', + 'route', + 'query', + 'asPath', + 'components', + 'isFallback', +] const routerEvents = [ 'routeChangeStart', 'beforeHistoryChange', diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 51090ffd9b905..6bd26cddd8c1c 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -49,6 +49,7 @@ export type NextRouter = BaseRouter & | 'prefetch' | 'beforePopState' | 'events' + | 'isFallback' > type RouteInfo = { @@ -122,6 +123,7 @@ export default class Router implements BaseRouter { events: MittEmitter _wrapApp: (App: ComponentType) => any isSsr: boolean + isFallback: boolean static events: MittEmitter = mitt() @@ -137,6 +139,7 @@ export default class Router implements BaseRouter { Component, err, subscription, + isFallback, }: { subscription: Subscription initialProps: any @@ -145,6 +148,7 @@ export default class Router implements BaseRouter { App: ComponentType wrapApp: (App: ComponentType) => any err?: Error + isFallback: boolean } ) { // represents the current component key @@ -180,6 +184,8 @@ export default class Router implements BaseRouter { // back from external site this.isSsr = true + this.isFallback = isFallback + if (typeof window !== 'undefined') { // in order for `e.state` to work on the `onpopstate` event // we have to register the initial route upon initialization @@ -580,6 +586,8 @@ export default class Router implements BaseRouter { as: string, data: RouteInfo ): void { + this.isFallback = false + this.route = route this.pathname = pathname this.query = query diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index afaeff62cf4de..946d6c0a5a0de 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -46,14 +46,22 @@ class ServerRouter implements NextRouter { query: ParsedUrlQuery asPath: string events: any + isFallback: boolean // TODO: Remove in the next major version, as this would mean the user is adding event listeners in server-side `render` method static events: MittEmitter = mitt() - constructor(pathname: string, query: ParsedUrlQuery, as: string) { + constructor( + pathname: string, + query: ParsedUrlQuery, + as: string, + { isFallback }: { isFallback: boolean } + ) { this.route = pathname.replace(/\/$/, '') || '/' this.pathname = pathname this.query = query this.asPath = as + + this.isFallback = isFallback } push(): any { noRouter() @@ -394,7 +402,9 @@ export async function renderToHTML( // @ts-ignore url will always be set const asPath: string = req.url - const router = new ServerRouter(pathname, query, asPath) + const router = new ServerRouter(pathname, query, asPath, { + isFallback: isFallback, + }) const ctx = { err, req: isAutoExport ? undefined : req, diff --git a/packages/next/package.json b/packages/next/package.json index 8e49e66c676b9..d4df3a7090913 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.2-canary.20", + "version": "9.2.2-canary.21", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/runtime-corejs2": "7.7.2", - "@next/polyfill-nomodule": "9.2.2-canary.20", + "@next/polyfill-nomodule": "9.2.2-canary.21", "amphtml-validator": "1.0.23", "async-retry": "1.2.3", "async-sema": "3.0.0", diff --git a/test/integration/prerender/pages/catchall/[...slug].js b/test/integration/prerender/pages/catchall/[...slug].js index f1dd8038a6b93..c2ff67739caf0 100644 --- a/test/integration/prerender/pages/catchall/[...slug].js +++ b/test/integration/prerender/pages/catchall/[...slug].js @@ -1,4 +1,10 @@ +import { useRouter } from 'next/router' + export async function unstable_getStaticProps({ params: { slug } }) { + if (slug[0] === 'delayby3s') { + await new Promise(resolve => setTimeout(resolve, 3000)) + } + return { props: { slug, @@ -18,4 +24,10 @@ export async function unstable_getStaticPaths() { } } -export default ({ slug }) =>

Hi {slug?.join('/')}

+export default ({ slug }) => { + const { isFallback } = useRouter() + if (isFallback) { + return

fallback

+ } + return

Hi {slug.join('/')}

+} diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 04e7df065665b..15cc1629d39d2 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -386,9 +386,20 @@ const runTests = (dev = false) => { } // Production will render fallback for a "lazy" route else { - const browser = await webdriver(appPort, '/catchall/notreturnedinpaths') - const text = await browser.elementByCss('#catchall').text() - expect(text).toMatch(/Hi.*?notreturnedinpaths/) + const html = await renderViaHTTP(appPort, '/catchall/notreturnedinpaths') + const $ = cheerio.load(html) + expect($('#catchall').text()).toBe('fallback') + + // hydration + const browser = await webdriver(appPort, '/catchall/delayby3s') + + const text1 = await browser.elementByCss('#catchall').text() + expect(text1).toBe('fallback') + + await new Promise(resolve => setTimeout(resolve, 4000)) + + const text2 = await browser.elementByCss('#catchall').text() + expect(text2).toMatch(/Hi.*?delayby3s/) } })