Skip to content

Commit

Permalink
Merge branch 'canary' into update-css
Browse files Browse the repository at this point in the history
  • Loading branch information
Timer authored Jan 27, 2020
2 parents 3f36f70 + c9dc17b commit c7bcf14
Show file tree
Hide file tree
Showing 27 changed files with 197 additions and 103 deletions.
18 changes: 3 additions & 15 deletions docs/basic-features/built-in-css-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,22 +158,10 @@ export default HelloWorld

Please see the [styled-jsx documentation](https://github.com/zeit/styled-jsx) for more examples.

## Sass Support
## Sass, Less and Stylus Support

Next.js allows you to import Sass using both the `.scss` and `.sass` extensions.
You can use component-level Sass via CSS Modules and the `.module.scss` or `.module.sass` extension.

Before you can use Next.js' built-in Sass support, be sure to install [`sass`](https://github.com/sass/sass):

```bash
npm install sass
```

Sass support has the same benefits and restrictions as the built-in CSS support detailed above.

## Less and Stylus Support

To support importing `.less` or `.styl` files you can use the following plugins:
To support importing `.scss`, `.sass`, `.less`, or `.styl` files you can use the following plugins:

- [@zeit/next-sass](https://github.com/zeit/next-plugins/tree/master/packages/next-sass)
- [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less)
- [@zeit/next-stylus](https://github.com/zeit/next-plugins/tree/master/packages/next-stylus)
4 changes: 2 additions & 2 deletions docs/basic-features/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The following is an example of how to use the built-in types for API routes:
import { NextApiRequest, NextApiResponse } from 'next'

export default (req: NextApiRequest, res: NextApiResponse) => {
res.status(200).json({ name: 'Jhon Doe' })
res.status(200).json({ name: 'John Doe' })
}
```

Expand All @@ -115,6 +115,6 @@ type Data = {
}

export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
res.status(200).json({ name: 'Jhon Doe' })
res.status(200).json({ name: 'John Doe' })
}
```
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ These scripts refer to the different stages of developing an application:
- `build` - Runs `next build` which builds the application for production usage
- `start` - Runs `next start` which starts a Next.js production server

Next.js is built around the concept of pages. A page is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.ts`, or `.tsx` file in the `pages` directory.
Next.js is built around the concept of pages. A page is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory.

Pages are associated with a route based on their file name. For example `pages/about.js` is mapped to `/about`. You can even add dynamic route parameters with the filename.

Expand Down
2 changes: 1 addition & 1 deletion docs/routing/dynamic-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Client-side navigations to a dynamic route can be handled with [`next/link`](/do

Dynamic routes can be extended to catch all paths by adding three dots (`...`) inside the brackets. For example:

- `pages/post/[...slug]` matches `/post/a`, but also `post/a/b`, `post/a/b/c` and so on.
- `pages/post/[...slug].js` matches `/post/a`, but also `post/a/b`, `post/a/b/c` and so on.

Matched parameters will be sent as a query parameter (`slug` in the example) to the page, and it will always be an array, so, the path `/post/a` will have the following `query` object:

Expand Down
2 changes: 1 addition & 1 deletion docs/routing/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ To match a dynamic segment you can use the bracket syntax. This allows you to ma

- `pages/blog/[slug].js``/blog/:slug` (`/blog/hello-world`)
- `pages/[username]/settings.js``/:username/settings` (`/foo/settings`)
- `pages/post/[...all]``/post/*` (`/post/2020/id/title`)
- `pages/post/[...all].js``/post/*` (`/post/2020/id/title`)

## Linking between pages

Expand Down
4 changes: 2 additions & 2 deletions examples/with-graphql-faunadb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ By importing a `.gql` or `.graphql` schema into FaunaDB ([see our sample schema

You can start with this template [using `create-next-app`](#using-create-next-app) or by [downloading the repository manually](#download-manually).

To use a live FaunaDB database, create a database at [dashboard.fauna.com](https://dashboard.fauna.com/) and generate a server token by going to the **Security** tab on the left and then click **New Key**. Give the new key a name and select the 'Server' Role. Copy the token since the setup script will ask for it. Do not use it in the frontend, it has superpowers which you don't want to give to your users.
To use a live FaunaDB database, create a database at [dashboard.fauna.com](https://dashboard.fauna.com/) and generate an admin token by going to the **Security** tab on the left and then click **New Key**. Give the new key a name and select the 'Admin' Role. Copy the token since the setup script will ask for it. Do not use it in the frontend, it has superpowers which you don't want to give to your users.

The database can then be set up with the delivered setup by running:

```
yarn setup
```

This script will ask for the server token. Once you provide it with a valid token, this is what the script automatically does for you:
This script will ask for the admin token. Once you provide it with a valid token, this is what the script automatically does for you:

- **Import the GraphQL schema**, by importing a GraphQL schema in FaunaDB, FaunaDB automatically sets up collections and indexes to support your queries. This is now done for you with this script but can also be done from the [dashboard.fauna.com](https://dashboard.fauna.com/) UI by going to the GraphQL tab
- **Create a role suitable for the Client**, FaunaDB has a security system that allows you to define which resources can be accessed for a specific token. That's how we limit our clients powers, feel free to look at the scripts/setup.js script to see how we make roles and tokens.
Expand Down
10 changes: 5 additions & 5 deletions examples/with-graphql-faunadb/scripts/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ const readline = require('readline').createInterface({
output: process.stdout,
})

// In order to set up a database, we need a server key, so let's ask the user for a key.
readline.question(`Please provide the FaunaDB server key\n`, serverKey => {
// In order to set up a database, we need an admin key, so let's ask the user for a key.
readline.question(`Please provide the FaunaDB admin key\n`, adminKey => {
// A graphql schema can be imported in override or merge mode: 'https://docs.fauna.com/fauna/current/api/graphql/endpoints#import'
const options = {
model: 'merge',
uri: 'https://graphql.fauna.com/import',
headers: { Authorization: `Bearer ${serverKey}` },
headers: { Authorization: `Bearer ${adminKey}` },
}
const stream = fs.createReadStream('./schema.gql').pipe(request.post(options))

Expand All @@ -44,7 +44,7 @@ readline.question(`Please provide the FaunaDB server key\n`, serverKey => {
.then(res => {
// The GraphQL schema is important, this means that we now have a GuestbookEntry Colleciton and an entries index.
// Then we create a token that can only read and write to that index and collection
var client = new faunadb.Client({ secret: serverKey })
var client = new faunadb.Client({ secret: adminKey })
return client
.query(
q.CreateRole({
Expand Down Expand Up @@ -81,7 +81,7 @@ readline.question(`Please provide the FaunaDB server key\n`, serverKey => {
.then(res => {
// The GraphQL schema is important, this means that we now have a GuestbookEntry Colleciton and an entries index.
// Then we create a token that can only read and write to that index and collection
var client = new faunadb.Client({ secret: serverKey })
var client = new faunadb.Client({ secret: adminKey })
return client
.query(
q.CreateKey({
Expand Down
6 changes: 3 additions & 3 deletions examples/with-typescript-graphql/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# GraphQL and TypeScript Example
# TypeScript and GraphQL Example

One of the strengths of GraphQL is [enforcing data types on runtime](https://graphql.github.io/graphql-spec/June2018/#sec-Value-Completion). Further, TypeScript and [GraphQL Code Generator](https://graphql-code-generator.com/) (graphql-codegen) make it safer by typing data statically, so you can write truly type-protected code with rich IDE assists.

This template extends [Apollo Server and Client Example](https://github.com/zeit/next.js/tree/canary/examples/api-routes-apollo-server-and-client#readme) by rewriting in TypeScript and integrating [graphql-let](https://github.com/piglovesyou/graphql-let#readme), which runs [TypeScript React Apollo](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo) in [graphql-codegen](https://github.com/dotansimha/graphql-code-generator#readme) under the hood. It enhances the typed GraphQL use as below.

```typescript jsx
import { useNewsQuery } from './news.grpahql'
import { useNewsQuery } from './news.graphql'

const News: React.FC = () => {
// Typed already️⚡️
Expand Down Expand Up @@ -59,7 +59,7 @@ now

## Notes

By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yml` point to the same files.
By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yml` are consistent.

Note: Do not be alarmed that you see two renders being executed. Apollo recursively traverses the React render tree looking for Apollo query components. When it has done that, it fetches all these queries and then passes the result to a cache. This cache is then used to render the data on the server side (another React render).
https://www.apollographql.com/docs/react/api/react-ssr/#getdatafromtree
4 changes: 3 additions & 1 deletion examples/with-typescript-graphql/lib/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const resolvers = {
import { IResolvers } from 'apollo-server-micro'

const resolvers: IResolvers = {
Query: {
viewer(_parent, _args, _context, _info) {
return { id: 1, name: 'John Smith', status: 'cached' }
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/babel/plugins/next-page-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NodePath, PluginObj } from '@babel/core'
import * as BabelTypes from '@babel/types'
import { PageConfig } from '../../../types'
import { PageConfig } from 'next/types'

const configKeys = new Set(['amp'])
const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__'
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/babel/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ module.exports = (
],
require('@babel/plugin-proposal-optional-chaining'),
require('@babel/plugin-proposal-nullish-coalescing-operator'),
isServer && require('@babel/plugin-syntax-bigint'),
].filter(Boolean),
}
}
10 changes: 6 additions & 4 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { generateBuildId } from './generate-build-id'
import { isWriteable } from './is-writeable'
import createSpinner from './spinner'
import {
isPageStatic,
collectPages,
getPageSizeInKb,
hasCustomAppGetInitialProps,
Expand Down Expand Up @@ -416,7 +417,8 @@ export default async function build(dir: string, conf = null): Promise<void> {
const staticCheckWorkers = new Worker(staticCheckWorker, {
numWorkers: config.experimental.cpus,
enableWorkerThreads: config.experimental.workerThreads,
})
}) as Worker & { isPageStatic: typeof isPageStatic }

staticCheckWorkers.getStdout().pipe(process.stdout)
staticCheckWorkers.getStderr().pipe(process.stderr)

Expand Down Expand Up @@ -481,7 +483,7 @@ export default async function build(dir: string, conf = null): Promise<void> {

if (nonReservedPage) {
try {
let result: any = await (staticCheckWorkers as any).isPageStatic(
let result = await staticCheckWorkers.isPageStatic(
page,
serverBundle,
runtimeEnvConfig
Expand All @@ -492,15 +494,15 @@ export default async function build(dir: string, conf = null): Promise<void> {
hybridAmpPages.add(page)
}

if (result.prerender) {
if (result.hasStaticProps) {
ssgPages.add(page)
isSsg = true

if (result.prerenderRoutes) {
additionalSsgPaths.set(page, result.prerenderRoutes)
ssgPageRoutes = result.prerenderRoutes
}
} else if (result.static && customAppGetInitialProps === false) {
} else if (result.isStatic && customAppGetInitialProps === false) {
staticPages.add(page)
isStatic = true
}
Expand Down
8 changes: 4 additions & 4 deletions packages/next/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,9 @@ export async function isPageStatic(
serverBundle: string,
runtimeEnvConfig: any
): Promise<{
static?: boolean
prerender?: boolean
isStatic?: boolean
isHybridAmp?: boolean
hasStaticProps?: boolean
prerenderRoutes?: string[] | undefined
}> {
try {
Expand Down Expand Up @@ -593,10 +593,10 @@ export async function isPageStatic(

const config = mod.config || {}
return {
static: !hasStaticProps && !hasGetInitialProps,
isStatic: !hasStaticProps && !hasGetInitialProps,
isHybridAmp: config.amp === 'hybrid',
prerenderRoutes: prerenderPaths,
prerender: hasStaticProps,
hasStaticProps,
}
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') return {}
Expand Down
10 changes: 6 additions & 4 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '../../../next-server/lib/constants'
import { isDynamicRoute } from '../../../next-server/lib/router/utils'
import { API_ROUTE } from '../../../lib/constants'
import escapeRegexp from 'escape-string-regexp'

export type ServerlessLoaderQuery = {
page: string
Expand Down Expand Up @@ -46,7 +47,7 @@ const nextServerlessLoader: loader.Loader = function() {
)
const routesManifest = join(distDir, ROUTES_MANIFEST).replace(/\\/g, '/')

const escapedBuildId = buildId.replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&')
const escapedBuildId = escapeRegexp(buildId)
const pageIsDynamicRoute = isDynamicRoute(page)

const dynamicRouteImports = pageIsDynamicRoute
Expand Down Expand Up @@ -215,13 +216,14 @@ const nextServerlessLoader: loader.Loader = function() {
}
let _nextData = false
if (req.url.match(/_next\\/data/)) {
const parsedUrl = handleRewrites(parse(req.url, true))
if (parsedUrl.pathname.match(/_next\\/data/)) {
_nextData = true
req.url = req.url
parsedUrl.pathname = parsedUrl.pathname
.replace(new RegExp('/_next/data/${escapedBuildId}/'), '/')
.replace(/\\.json$/, '')
}
const parsedUrl = handleRewrites(parse(req.url, true))
const renderOpts = Object.assign(
{
Expand Down
2 changes: 1 addition & 1 deletion packages/next/next-server/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ParsedUrlQuery } from 'querystring'
import { ComponentType } from 'react'
import { format, URLFormatOptions, UrlObject } from 'url'

import { ManifestItem } from '../server/render'
import { ManifestItem } from '../server/load-components'
import { NextRouter } from './router/router'

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/next/next-server/server/api-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Stream } from 'stream'
import getRawBody from 'raw-body'
import { parse } from 'content-type'
import { Params } from './router'
import { PageConfig } from '../../types'
import { PageConfig } from 'next/types'
import { interopDefault } from './load-components'
import { isResSent } from '../lib/utils'

Expand Down Expand Up @@ -59,7 +59,7 @@ export async function apiResolver(

if (process.env.NODE_ENV !== 'production' && !isResSent(res)) {
console.warn(
`API resolved without sending a response for ${req.url}, this may result in a stalled requests.`
`API resolved without sending a response for ${req.url}, this may result in stalled requests.`
)
}
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/next-server/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function assignDefaults(userConfig: { [key: string]: any }) {
`The 'public' directory is reserved in Next.js and can not be set as the 'distDir'. https://err.sh/zeit/next.js/can-not-output-to-public`
)
}
// make sure distDir isn't an empty string which can result the provided
// make sure distDir isn't an empty string as it can result in the provided
// directory being deleted in development mode
if (userDistDir.length === 0) {
throw new Error(
Expand Down
48 changes: 32 additions & 16 deletions packages/next/next-server/server/load-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,44 @@ import {
SERVER_DIRECTORY,
} from '../lib/constants'
import { join } from 'path'
import { PageConfig } from '../../types'
import { requirePage } from './require'
import { ParsedUrlQuery } from 'querystring'
import { BuildManifest } from './get-page-files'
import { AppType, DocumentType } from '../lib/utils'
import { PageConfig, NextPageContext } from 'next/types'

export function interopDefault(mod: any) {
return mod.default || mod
}

export type ManifestItem = {
id: number | string
name: string
file: string
publicPath: string
}

type ReactLoadableManifest = { [moduleId: string]: ManifestItem[] }

type Unstable_getStaticProps = (params: {
params: ParsedUrlQuery | undefined
}) => Promise<{
props: { [key: string]: any }
revalidate?: number | boolean
}>

type Unstable_getStaticPaths = () => Promise<Array<string | ParsedUrlQuery>>

export type LoadComponentsReturnType = {
Component: any
pageConfig: PageConfig
unstable_getStaticProps?: (params: {
params: any
}) => {
props: any
revalidate?: number | boolean
}
unstable_getStaticPaths?: () => void
buildManifest?: any
reactLoadableManifest?: any
Document?: any
DocumentMiddleware?: any
App?: any
Component: React.ComponentType
pageConfig?: PageConfig
buildManifest: BuildManifest
reactLoadableManifest: ReactLoadableManifest
Document: DocumentType
DocumentMiddleware?: (ctx: NextPageContext) => void
App: AppType
unstable_getStaticProps?: Unstable_getStaticProps
unstable_getStaticPaths?: Unstable_getStaticPaths
}

export async function loadComponents(
Expand All @@ -42,7 +58,7 @@ export async function loadComponents(
pageConfig: Component.config || {},
unstable_getStaticProps: Component.unstable_getStaticProps,
unstable_getStaticPaths: Component.unstable_getStaticPaths,
}
} as LoadComponentsReturnType
}
const documentPath = join(
distDir,
Expand Down
Loading

0 comments on commit c7bcf14

Please sign in to comment.