Skip to content

Commit

Permalink
Merge branch 'canary' into why-you-render-example
Browse files Browse the repository at this point in the history
  • Loading branch information
Moti Ko committed Feb 28, 2020
2 parents 5041fcf + b262401 commit ad5aca1
Show file tree
Hide file tree
Showing 85 changed files with 983 additions and 447 deletions.
4 changes: 2 additions & 2 deletions docs/concepts/_data-fetching.md
Expand Up @@ -46,7 +46,7 @@ export default HomePage

[Read more about how Server-Side Rendering works]().

To opt-in to Server-Side Rendering, making every request render HTML on-demand you use the `getServerProps` method.
To opt-in to Server-Side Rendering, making every request render HTML on-demand you use the `getServerSideProps` method.

It allows you to fetch data before rendering starts when a request comes in.

Expand All @@ -56,7 +56,7 @@ Taking the same example as Static Generation, but opted into rendering the page
import fetch from 'isomorphic-unfetch'

// Called when a request comes in.
export async function getServerProps() {
export async function getServerSideProps() {
const res = await fetch('https://api.github.com/repos/zeit/next.js')
const json = await res.json()

Expand Down
2 changes: 1 addition & 1 deletion errors/404-get-initial-props.md
Expand Up @@ -2,7 +2,7 @@

#### Why This Error Occurred

In your `404.js` page you added `getInitialProps` or `getServerProps` which is not allowed as this prevents the page from being static and `404.js` is meant to provide more flexibility for a static 404 page. Having a static 404 page is the most ideal as this page can be served very often and if not static puts extra strain on your server and more invocations for serverless functions which increase the cost of hosting your site unnecessarily.
In your `404.js` page you added `getInitialProps` or `getServerSideProps` which is not allowed as this prevents the page from being static and `404.js` is meant to provide more flexibility for a static 404 page. Having a static 404 page is the most ideal as this page can be served very often and if not static puts extra strain on your server and more invocations for serverless functions which increase the cost of hosting your site unnecessarily.

#### Possible Ways to Fix It

Expand Down
33 changes: 28 additions & 5 deletions errors/invalid-getstaticpaths-value.md
@@ -1,17 +1,40 @@
# Invalid unstable_getStaticPaths Return Value
# Invalid getStaticPaths Return Value

#### Why This Error Occurred

In one of the page's `unstable_getStaticPaths` the return value had the incorrect shape.
In one of the page's `getStaticPaths` the return value had the incorrect shape.

#### Possible Ways to Fix It

Make sure to return the following shape from `unstable_getStaticPaths`:
Make sure to return the following shape from `getStaticPaths`:

```js
export async function unstable_getStaticPaths() {
export async function getStaticPaths() {
return {
paths: Array<string | { params: { [key: string]: string } }>
paths: Array<string | { params: { [key: string]: string } }>,
fallback: boolean
}
}
```

There are two required properties:

1. `paths`: this property is an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of URLs ("paths") that should be statically generated at build-time. The returned paths must match the dynamic route shape.
- You may return a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) or an [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) that explicitly defines all URL `params`.
```js
// pages/blog/[slug].js
export async function getStaticPaths() {
return {
paths: [
// String variant:
'/blog/first-post',
// Object variant:
{ params: { slug: 'second-post' } },
],
fallback: true,
}
}
```
1. `fallback`: this property is a [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), specifying whether or not a fallback version of this page should be generated.
- Enabling `fallback` (via `true`) allows you to return a subset of all the possible paths that should be statically generated. At runtime, Next.js will statically generate the remaining paths the **first time they are requested**. Consecutive calls to the path will be served as-if it was statically generated at build-time. This reduces build times when dealing with thousands or millions of pages.
- Disabling `fallback` (via `false`) requires you return the full collection of paths you would like to statically generate at build-time. At runtime, any path that was not generated at build-time **will 404**.
8 changes: 4 additions & 4 deletions errors/invalid-getstaticprops-value.md
@@ -1,15 +1,15 @@
# Invalid unstable_getStaticProps Return Value
# Invalid getStaticProps Return Value

#### Why This Error Occurred

In one of the page's `unstable_getStaticProps` the return value had the incorrect shape.
In one of the page's `getStaticProps` the return value had the incorrect shape.

#### Possible Ways to Fix It

Make sure to return the following shape from `unstable_getStaticProps`:
Make sure to return the following shape from `getStaticProps`:

```js
export async function unstable_getStaticProps() {
export async function getStaticProps() {
return {
props: { [key: string]: any }
}
Expand Down
2 changes: 1 addition & 1 deletion examples/with-apollo/lib/apollo.js
Expand Up @@ -11,7 +11,7 @@ let globalApolloClient = null
/**
* Installs the Apollo Client on NextPageContext
* or NextAppContext. Useful if you want to use apolloClient
* inside getStaticProps, getStaticPaths or getServerProps
* inside getStaticProps, getStaticPaths or getServerSideProps
* @param {NextPageContext | NextAppContext} ctx
*/
export const initOnContext = ctx => {
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/ask.js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../components/stories'
import getStories from '../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
export async function getStaticProps() {
const page = 1
const stories = await getStories('askstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/index.js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../components/stories'
import getStories from '../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
export async function getStaticProps() {
const page = 1
const stories = await getStories('topstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/item/[id].js
Expand Up @@ -4,7 +4,7 @@ import Item from '../../components/item'
import getItem from '../../lib/get-item'
import getComments from '../../lib/get-comments'

export async function unstable_getStaticProps({ params }) {
export async function getStaticProps({ params }) {
const story = await getItem(params.id)
return { props: { story } }
}
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/jobs.js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../components/stories'
import getStories from '../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
export async function getStaticProps() {
const page = 1
const stories = await getStories('jobstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/newest.js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../components/stories'
import getStories from '../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
export async function getStaticProps() {
const page = 1
const stories = await getStories('newstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/news/[id].js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../../components/stories'
import getStories from '../../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps({ params }) {
export async function getStaticProps({ params }) {
const page = Number(params.id)
const stories = await getStories('topstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion examples/z-experimental-next-news/pages/show.js
Expand Up @@ -4,7 +4,7 @@ import Stories from '../components/stories'
import getStories from '../lib/get-stories'

// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
export async function getStaticProps() {
const page = 1
const stories = await getStories('showstories', { page })
return { props: { page, stories } }
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Expand Up @@ -12,5 +12,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "9.2.3-canary.13"
"version": "9.2.3-canary.17"
}
98 changes: 82 additions & 16 deletions packages/create-next-app/create-app.ts
Expand Up @@ -5,7 +5,14 @@ import makeDir from 'make-dir'
import os from 'os'
import path from 'path'

import { downloadAndExtractExample, hasExample } from './helpers/examples'
import {
hasExample,
hasRepo,
getRepoInfo,
downloadAndExtractExample,
downloadAndExtractRepo,
RepoInfo,
} from './helpers/examples'
import { tryGitInit } from './helpers/git'
import { install } from './helpers/install'
import { isFolderEmpty } from './helpers/is-folder-empty'
Expand All @@ -16,20 +23,69 @@ export async function createApp({
appPath,
useNpm,
example,
examplePath,
}: {
appPath: string
useNpm: boolean
example?: string
examplePath?: string
}) {
let repoInfo: RepoInfo | undefined

if (example) {
const found = await hasExample(example)
if (!found) {
console.error(
`Could not locate an example named ${chalk.red(
`"${example}"`
)}. Please check your spelling and try again.`
)
process.exit(1)
let repoUrl: URL | undefined

try {
repoUrl = new URL(example)
} catch (error) {
if (error.code !== 'ERR_INVALID_URL') {
console.error(error)
process.exit(1)
}
}

if (repoUrl) {
if (repoUrl.origin !== 'https://github.com') {
console.error(
`Invalid URL: ${chalk.red(
`"${example}"`
)}. Only GitHub repositories are supported. Please use a GitHub URL and try again.`
)
process.exit(1)
}

repoInfo = getRepoInfo(repoUrl, examplePath)

if (!repoInfo) {
console.error(
`Found invalid GitHub URL: ${chalk.red(
`"${example}"`
)}. Please fix the URL and try again.`
)
process.exit(1)
}

const found = await hasRepo(repoInfo)

if (!found) {
console.error(
`Could not locate the repository for ${chalk.red(
`"${example}"`
)}. Please check that the repository exists and try again.`
)
process.exit(1)
}
} else {
const found = await hasExample(example)

if (!found) {
console.error(
`Could not locate an example named ${chalk.red(
`"${example}"`
)}. Please check your spelling and try again.`
)
process.exit(1)
}
}
}

Expand All @@ -53,13 +109,23 @@ export async function createApp({
process.chdir(root)

if (example) {
console.log(
`Downloading files for example ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await downloadAndExtractExample(root, example)
if (repoInfo) {
console.log(
`Downloading files from repo ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await downloadAndExtractRepo(root, repoInfo)
} else {
console.log(
`Downloading files for example ${chalk.cyan(
example
)}. This might take a moment.`
)
console.log()
await downloadAndExtractExample(root, example)
}

// Copy our default `.gitignore` if the application did not provide one
const ignorePath = path.join(root, '.gitignore')
Expand Down
61 changes: 55 additions & 6 deletions packages/create-next-app/helpers/examples.ts
Expand Up @@ -2,20 +2,69 @@ import got from 'got'
import promisePipe from 'promisepipe'
import tar from 'tar'

export async function hasExample(name: string): Promise<boolean> {
const res = await got(
export type RepoInfo = {
username: string
name: string
branch: string
filePath: string
}

export async function isUrlOk(url: string) {
const res = await got(url).catch(e => e)
return res.statusCode === 200
}

export function getRepoInfo(
url: URL,
examplePath?: string
): RepoInfo | undefined {
const [, username, name, t, _branch, ...file] = url.pathname.split('/')
const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/')
// If examplePath is available, the branch name takes the entire path
const branch = examplePath
? `${_branch}/${file.join('/')}`.replace(new RegExp(`/${filePath}|/$`), '')
: _branch

if (username && name && branch && t === 'tree') {
return { username, name, branch, filePath }
}
}

export function hasRepo({ username, name, branch, filePath }: RepoInfo) {
const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents`
const packagePath = `${filePath ? `/${filePath}` : ''}/package.json`

return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`)
}

export function hasExample(name: string): Promise<boolean> {
return isUrlOk(
`https://api.github.com/repos/zeit/next.js/contents/examples/${encodeURIComponent(
name
)}/package.json`
).catch(e => e)
return res.statusCode === 200
)
}

export function downloadAndExtractRepo(
root: string,
{ username, name, branch, filePath }: RepoInfo
): Promise<void> {
return promisePipe(
got.stream(
`https://codeload.github.com/${username}/${name}/tar.gz/${branch}`
),
tar.extract(
{ cwd: root, strip: filePath ? filePath.split('/').length + 1 : 1 },
[`${name}-${branch}${filePath ? `/${filePath}` : ''}`]
)
)
}

export async function downloadAndExtractExample(
export function downloadAndExtractExample(
root: string,
name: string
): Promise<void> {
return await promisePipe(
return promisePipe(
got.stream('https://codeload.github.com/zeit/next.js/tar.gz/canary'),
tar.extract({ cwd: root, strip: 3 }, [`next.js-canary/examples/${name}`])
)
Expand Down

0 comments on commit ad5aca1

Please sign in to comment.