From bf294ee4942f79d02d7d9403a43ab080d4827422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Huvar?= Date: Thu, 23 May 2019 21:31:22 +0200 Subject: [PATCH] Types description (#7217) * App, Document, Page, Error types description * Router description * Added head changed router * Additions * Fix examples * Push and replace details * Update packages/next-server/lib/head.tsx Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next-server/lib/utils.ts Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next/types/index.d.ts Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next/pages/_document.tsx Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next/pages/_document.tsx Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next/pages/_app.tsx Co-Authored-By: Luis Fernando Alvarez D. * Update packages/next/pages/_document.tsx Co-Authored-By: Luis Fernando Alvarez D. --- .../pages/_document.tsx | 4 +- packages/next-server/lib/head.tsx | 4 + packages/next-server/lib/router/router.ts | 27 +++++ packages/next-server/lib/utils.ts | 109 +++++++++++++++--- packages/next/pages/_app.tsx | 10 +- packages/next/pages/_document.tsx | 8 ++ packages/next/pages/_error.tsx | 3 + packages/next/types/index.d.ts | 19 ++- 8 files changed, 156 insertions(+), 28 deletions(-) diff --git a/examples/with-typescript-styled-components/pages/_document.tsx b/examples/with-typescript-styled-components/pages/_document.tsx index fb1ebb667b..e41f157c66 100644 --- a/examples/with-typescript-styled-components/pages/_document.tsx +++ b/examples/with-typescript-styled-components/pages/_document.tsx @@ -9,7 +9,7 @@ export default class MyDocument extends Document { try { ctx.renderPage = () => originalRenderPage({ - enhanceApp: App => props => sheet.collectStyles() + enhanceApp: (App) => (props) => sheet.collectStyles(), }); const initialProps = await Document.getInitialProps(ctx); @@ -20,7 +20,7 @@ export default class MyDocument extends Document { {initialProps.styles} {sheet.getStyleElement()} - ) + ), }; } finally { sheet.seal(); diff --git a/packages/next-server/lib/head.tsx b/packages/next-server/lib/head.tsx index e170c62ed5..0fb3546858 100644 --- a/packages/next-server/lib/head.tsx +++ b/packages/next-server/lib/head.tsx @@ -131,6 +131,10 @@ function reduceComponents(headElements: Array>, props: W const Effect = withSideEffect(); +/** + * This component injects elements to `` of your page. + * To avoid duplicated `tags` in `` you can use the `key` property, which will make sure every tag is only rendered once. + */ function Head({ children }: { children: React.ReactNode }) { return ( diff --git a/packages/next-server/lib/router/router.ts b/packages/next-server/lib/router/router.ts index 319ea3f54f..817806d683 100644 --- a/packages/next-server/lib/router/router.ts +++ b/packages/next-server/lib/router/router.ts @@ -34,6 +34,9 @@ export default class Router implements BaseRouter { pathname: string query: ParsedUrlQuery asPath: string + /** + * Map of all components loaded in `Router` + */ components: {[pathname: string]: RouteInfo} subscriptions: Set componentLoadCancel: (() => void) | null @@ -151,14 +154,29 @@ export default class Router implements BaseRouter { window.location.reload() } + /** + * Go back in history + */ back() { window.history.back() } + /** + * Performs a `pushState` with arguments + * @param url of the route + * @param as masks `url` for the browser + * @param options object you can define `shallow` and other options + */ push(url: string, as: string = url, options = {}) { return this.change('pushState', url, as, options) } + /** + * Performs a `replaceState` with arguments + * @param url of the route + * @param as masks `url` for the browser + * @param options object you can define `shallow` and other options + */ replace(url: string, as: string = url, options = {}) { return this.change('replaceState', url, as, options) } @@ -339,6 +357,10 @@ export default class Router implements BaseRouter { this.notify(data) } + /** + * Callback to execute before replacing router state + * @param cb callback to be executed + */ beforePopState(cb: BeforePopStateCallback) { this._bps = cb } @@ -391,6 +413,11 @@ export default class Router implements BaseRouter { return this.asPath !== asPath } + /** + * Prefetch `page` code, you may wait for the data during `page` rendering. + * This feature only works in production! + * @param url of prefetched `page` + */ prefetch(url: string): Promise { return new Promise((resolve, reject) => { // Prefetch is not supported in development mode because it would trigger on-demand-entries diff --git a/packages/next-server/lib/utils.ts b/packages/next-server/lib/utils.ts index f0cfb6a93a..c28f803604 100644 --- a/packages/next-server/lib/utils.ts +++ b/packages/next-server/lib/utils.ts @@ -1,5 +1,5 @@ import { format, UrlObject, URLFormatOptions } from 'url' -import { ServerResponse, IncomingMessage } from 'http'; +import { ServerResponse, IncomingMessage } from 'http' import { ComponentType } from 'react' import { ParsedUrlQuery } from 'querystring' import { ManifestItem } from '../server/get-dynamic-import-bundles' @@ -8,23 +8,44 @@ import { BaseRouter } from './router/router' /** * Types used by both next and next-server */ -export type NextComponentType = ComponentType

& { +export type NextComponentType< + C extends BaseContext = NextPageContext, + IP = {}, + P = {} +> = ComponentType

& { getInitialProps?(context: C): Promise, } -export type DocumentType = NextComponentType +export type DocumentType = NextComponentType< + DocumentContext, + DocumentInitialProps, + DocumentProps +> -export type AppType = NextComponentType +export type AppType = NextComponentType< + AppContextType, + AppInitialProps, + AppPropsType +> export type Enhancer = (Component: C) => C export type ComponentsEnhancer = - | { enhanceApp?: Enhancer; enhanceComponent?: Enhancer } + | { + enhanceApp?: Enhancer + enhanceComponent?: Enhancer, + } | Enhancer -export type RenderPageResult = { html: string, head?: Array, dataOnly?: true } +export type RenderPageResult = { + html: string + head?: Array + dataOnly?: true, +} -export type RenderPage = (options?: ComponentsEnhancer) => RenderPageResult | Promise +export type RenderPage = ( + options?: ComponentsEnhancer, +) => RenderPageResult | Promise export type BaseContext = { res?: ServerResponse @@ -45,13 +66,34 @@ export type NEXT_DATA = { err?: Error & { statusCode?: number }, } +/** + * `Next` context + */ // tslint:disable-next-line interface-name export interface NextPageContext { + /** + * Error object if encountered during rendering + */ err?: Error & { statusCode?: number } | null + /** + * `HTTP` request object. + */ req?: IncomingMessage + /** + * `HTTP` response object. + */ res?: ServerResponse + /** + * Path section of `URL`. + */ pathname: string + /** + * Query string section of `URL` parsed as an object. + */ query: ParsedUrlQuery + /** + * `String` of the actual path including query. + */ asPath?: string } @@ -65,7 +107,10 @@ export type AppInitialProps = { pageProps: any, } -export type AppPropsType = AppInitialProps & { +export type AppPropsType< + R extends BaseRouter = BaseRouter, + P = {} +> = AppInitialProps & { Component: NextComponentType router: R, } @@ -117,17 +162,25 @@ export function getURL() { } export function getDisplayName(Component: ComponentType) { - return typeof Component === 'string' ? Component : (Component.displayName || Component.name || 'Unknown') + return typeof Component === 'string' + ? Component + : Component.displayName || Component.name || 'Unknown' } export function isResSent(res: ServerResponse) { return res.finished || res.headersSent } -export async function loadGetInitialProps(Component: NextComponentType, ctx: C): Promise { +export async function loadGetInitialProps< + C extends BaseContext, + IP = {}, + P = {} +>(Component: NextComponentType, ctx: C): Promise { if (process.env.NODE_ENV !== 'production') { if (Component.prototype && Component.prototype.getInitialProps) { - const message = `"${getDisplayName(Component)}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.` + const message = `"${getDisplayName( + Component, + )}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.` throw new Error(message) } } @@ -148,28 +201,46 @@ export async function loadGetInitialProps { if (urlObjectKeys.indexOf(key) === -1) { - console.warn(`Unknown key passed via urlObject into url.format: ${key}`) + console.warn( + `Unknown key passed via urlObject into url.format: ${key}`, + ) } }) } diff --git a/packages/next/pages/_app.tsx b/packages/next/pages/_app.tsx index 8c308e071c..a84cf56bed 100644 --- a/packages/next/pages/_app.tsx +++ b/packages/next/pages/_app.tsx @@ -1,4 +1,4 @@ -import React, {ErrorInfo} from 'react' +import React, { ErrorInfo } from 'react' import PropTypes from 'prop-types' import { execOnce, loadGetInitialProps, AppContextType, AppInitialProps, AppPropsType } from 'next-server/dist/lib/utils' import { Router, makePublicRouterInstance } from '../client/router' @@ -9,6 +9,10 @@ export type AppContext = AppContextType export type AppProps

= AppPropsType +/** + * `App` component is used for initialize of pages. It allows for overwriting and full control of the `page` initialization. + * This allows for keeping state between navigation, custom error handling, injecting additional data. + */ async function appGetInitialProps({ Component, ctx }: AppContext): Promise { const pageProps = await loadGetInitialProps(Component, ctx) return { pageProps } @@ -74,7 +78,9 @@ export class Container extends React.Component { const warnUrl = execOnce(() => { if (process.env.NODE_ENV !== 'production') { - console.error(`Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`) + console.error( + `Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`, + ) } }) diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index dd35e6c795..c979652b64 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -22,12 +22,20 @@ export type DocumentComponentContext = { readonly _devOnlyInvalidateCacheQueryString: string, } +/** + * `Document` component handles the initial `document` markup and renders only on the server side. + * Commonly used for implementing server side rendering for `css-in-js` libraries. + */ export default class Document

extends Component { static childContextTypes = { _documentProps: PropTypes.any, _devOnlyInvalidateCacheQueryString: PropTypes.string, } + /** + * `getInitialProps` hook returns the context object with the addition of `renderPage`. ` + * `renderPage` callback executes `React` rendering logic synchronously to support server-rendering wrappers + */ static async getInitialProps({ renderPage }: DocumentContext): Promise { const { html, head, dataOnly } = await renderPage() const styles = flush() diff --git a/packages/next/pages/_error.tsx b/packages/next/pages/_error.tsx index 534f063f10..fa8b7da127 100644 --- a/packages/next/pages/_error.tsx +++ b/packages/next/pages/_error.tsx @@ -13,6 +13,9 @@ export type ErrorProps = { statusCode: number, } +/** + * `Error` component used for handling errors. + */ export default class Error

extends React.Component

{ static displayName = 'ErrorPage' diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index 021ba46374..e52c030291 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -9,7 +9,7 @@ import { NextPageContext, NextComponentType } from 'next-server/dist/lib/utils'; declare module 'react' { // support interface HtmlHTMLAttributes extends React.HTMLAttributes { - amp?: string; + amp?: string } // support @@ -19,14 +19,23 @@ declare module 'react' { //