rsnext/packages/next/shared/lib/utils.ts

458 lines
11 KiB
TypeScript
Raw Normal View History

import type { HtmlProps } from './html-context'
import type { ComponentType } from 'react'
import type { DomainLocale } from '../../server/config'
import type { Env } from '@next/env'
import type { IncomingMessage, ServerResponse } from 'http'
import type { NextRouter } from './router/router'
import type { ParsedUrlQuery } from 'querystring'
import type { PreviewData } from 'next/types'
import { COMPILER_NAMES } from './constants'
export type NextComponentType<
C extends BaseContext = NextPageContext,
IP = {},
P = {}
> = ComponentType<P> & {
/**
* Used for initial page load data population. Data returned from `getInitialProps` is serialized when server rendered.
* Make sure to return plain `Object` without using `Date`, `Map`, `Set`.
* @param ctx Context of `page`
*/
getInitialProps?(context: C): IP | Promise<IP>
}
export type DocumentType = NextComponentType<
DocumentContext,
DocumentInitialProps,
DocumentProps
>
export type AppType<P = {}> = NextComponentType<
AppContextType,
P,
AppPropsType<any, P>
>
export type AppTreeType = ComponentType<
AppInitialProps & { [name: string]: any }
>
/**
* Web vitals provided to _app.reportWebVitals by Core Web Vitals plugin developed by Google Chrome team.
* https://nextjs.org/blog/next-9-4#integrated-web-vitals-reporting
*/
add attribution to web vitals (#39368) This commit implements the main proposal presented in https://github.com/vercel/next.js/issues/39241 to add attribution to web vitals. Attribution adds more specific debugging info to web vitals, for example in the case of Cumulative Layout Shift (CLS), we might want to know > What's the first element that shifted when the single largest layout shift occurred? on in the case of Largest Contentful Paint (LCP), > What's the element corresponding to the LCP for the page? > If it is an image, what's the URL of the image resource? Attribution is *disabled* by default because it could potentially generate a lot data and overwhelm the RUM backend. It is enabled *per metric* (LCP, FCP, CLS, etc) As part of this change, `web-vitals` has been upgraded to v3.0.0 This version contains minor bug fixes, please see changelog at https://github.com/GoogleChrome/web-vitals/commit/9fe3cc02c875cb70ac0f1803f5e11b428e7a4014 Fixes #39241 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
2022-10-04 02:17:30 +02:00
export const WEB_VITALS = ['CLS', 'FCP', 'FID', 'INP', 'LCP', 'TTFB'] as const
export type NextWebVitalsMetric = {
id: string
startTime: number
value: number
add attribution to web vitals (#39368) This commit implements the main proposal presented in https://github.com/vercel/next.js/issues/39241 to add attribution to web vitals. Attribution adds more specific debugging info to web vitals, for example in the case of Cumulative Layout Shift (CLS), we might want to know > What's the first element that shifted when the single largest layout shift occurred? on in the case of Largest Contentful Paint (LCP), > What's the element corresponding to the LCP for the page? > If it is an image, what's the URL of the image resource? Attribution is *disabled* by default because it could potentially generate a lot data and overwhelm the RUM backend. It is enabled *per metric* (LCP, FCP, CLS, etc) As part of this change, `web-vitals` has been upgraded to v3.0.0 This version contains minor bug fixes, please see changelog at https://github.com/GoogleChrome/web-vitals/commit/9fe3cc02c875cb70ac0f1803f5e11b428e7a4014 Fixes #39241 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
2022-10-04 02:17:30 +02:00
attribution?: { [key: string]: unknown }
} & (
| {
label: 'web-vital'
add attribution to web vitals (#39368) This commit implements the main proposal presented in https://github.com/vercel/next.js/issues/39241 to add attribution to web vitals. Attribution adds more specific debugging info to web vitals, for example in the case of Cumulative Layout Shift (CLS), we might want to know > What's the first element that shifted when the single largest layout shift occurred? on in the case of Largest Contentful Paint (LCP), > What's the element corresponding to the LCP for the page? > If it is an image, what's the URL of the image resource? Attribution is *disabled* by default because it could potentially generate a lot data and overwhelm the RUM backend. It is enabled *per metric* (LCP, FCP, CLS, etc) As part of this change, `web-vitals` has been upgraded to v3.0.0 This version contains minor bug fixes, please see changelog at https://github.com/GoogleChrome/web-vitals/commit/9fe3cc02c875cb70ac0f1803f5e11b428e7a4014 Fixes #39241 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
2022-10-04 02:17:30 +02:00
name: typeof WEB_VITALS[number]
}
| {
label: 'custom'
name:
| 'Next.js-hydration'
| 'Next.js-route-change-to-render'
| 'Next.js-render'
}
)
export type Enhancer<C> = (Component: C) => C
export type ComponentsEnhancer =
| {
enhanceApp?: Enhancer<AppType>
enhanceComponent?: Enhancer<NextComponentType>
}
| Enhancer<NextComponentType>
export type RenderPageResult = {
html: string
head?: Array<JSX.Element | null>
}
export type RenderPage = (
options?: ComponentsEnhancer
) => DocumentInitialProps | Promise<DocumentInitialProps>
2019-04-26 09:37:57 +02:00
export type BaseContext = {
res?: ServerResponse
[k: string]: any
}
export type NEXT_DATA = {
props: Record<string, any>
page: string
query: ParsedUrlQuery
buildId: string
assetPrefix?: string
runtimeConfig?: { [key: string]: any }
nextExport?: boolean
autoExport?: boolean
isFallback?: boolean
dynamicIds?: (string | number)[]
err?: Error & {
statusCode?: number
source?: typeof COMPILER_NAMES.server | typeof COMPILER_NAMES.edgeServer
}
gsp?: boolean
gssp?: boolean
customServer?: boolean
gip?: boolean
appGip?: boolean
locale?: string
locales?: string[]
defaultLocale?: string
domainLocales?: DomainLocale[]
scriptLoader?: any[]
isPreview?: boolean
notFoundSrcPage?: string
}
/**
* `Next` context
*/
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
/**
* The currently active locale
*/
locale?: string
/**
* All configured locales
*/
locales?: string[]
/**
* The configured default locale
*/
defaultLocale?: string
/**
* `Component` the tree of the App to use if needing to render separately
*/
AppTree: AppTreeType
}
export type AppContextType<R extends NextRouter = NextRouter> = {
Component: NextComponentType<NextPageContext>
AppTree: AppTreeType
ctx: NextPageContext
router: R
}
export type AppInitialProps<P = any> = {
pageProps: P
}
export type AppPropsType<
R extends NextRouter = NextRouter,
P = {}
> = AppInitialProps<P> & {
Component: NextComponentType<NextPageContext, any, any>
router: R
__N_SSG?: boolean
__N_SSP?: boolean
}
export type DocumentContext = NextPageContext & {
renderPage: RenderPage
defaultGetInitialProps(
ctx: DocumentContext,
options?: { nonce?: string }
): Promise<DocumentInitialProps>
}
2019-04-26 09:37:57 +02:00
export type DocumentInitialProps = RenderPageResult & {
styles?: React.ReactElement[] | React.ReactFragment | JSX.Element
}
export type DocumentProps = DocumentInitialProps & HtmlProps
/**
* Next `API` route request
*/
export interface NextApiRequest extends IncomingMessage {
/**
* Object of `query` values from url
*/
fix NextApiRequestCookies and NextApiRequestQuery types (#25532) Hello! Thanks for making next.js so great. ## Bug Right now, these types give false confidence. These `key`s are treated as though [a value is defined for _every_ string](https://dev.to/sarioglu/avoiding-unintended-undefined-values-while-using-typescript-record-4igo). However, given an arbitrary request, a particular cookie or query param could be `undefined`. For example, when building an `/api` endpoint, the code might look like this: ```ts import type { NextApiRequest, NextApiResponse } from "next" export default function handler(req: NextApiRequest, res: NextApiResponse) { // According to the old types, `value` is a string const value = req.cookies.value // Type-checking passes but leads to a runtime error when no `value` cookie is provided in the request // Uncaught TypeError: Cannot read property 'toLowerCase' of undefined value.toLowerCause() // ... } ``` By using `Partial`, TypeScript now knows that these objects don't have values defined for every `key` and accessing a given `key` might resolve to `undefined`. --- The only obvious error this caused within this repo was on line 333 of the same file. For better or worse, I ended up casting that cookie value to a `string`. There's a series of `if` statements before it that, I guess, are guaranteeing that it's truly a string. Potentially, that stretch could be refactored such that TypeScript _knows_ it's a string. Also, I tried to follow the contributing guidelines. However, running `yarn types` kicked out a bunch of errors about overwriting files: ``` $ yarn types yarn run v1.22.10 $ lerna run types --stream lerna notice cli v4.0.0 lerna info Executing command in 2 packages: "yarn run types" @next/env: $ tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop next: $ tsc --declaration --emitDeclarationOnly --declarationDir dist next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/index.d.ts' because it would overwrite input file. next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts' because it would overwrite input file. ... ... ... ``` Let me know if there's anything I can improve here! Thanks again.
2022-05-23 02:48:26 +02:00
query: Partial<{
[key: string]: string | string[]
fix NextApiRequestCookies and NextApiRequestQuery types (#25532) Hello! Thanks for making next.js so great. ## Bug Right now, these types give false confidence. These `key`s are treated as though [a value is defined for _every_ string](https://dev.to/sarioglu/avoiding-unintended-undefined-values-while-using-typescript-record-4igo). However, given an arbitrary request, a particular cookie or query param could be `undefined`. For example, when building an `/api` endpoint, the code might look like this: ```ts import type { NextApiRequest, NextApiResponse } from "next" export default function handler(req: NextApiRequest, res: NextApiResponse) { // According to the old types, `value` is a string const value = req.cookies.value // Type-checking passes but leads to a runtime error when no `value` cookie is provided in the request // Uncaught TypeError: Cannot read property 'toLowerCase' of undefined value.toLowerCause() // ... } ``` By using `Partial`, TypeScript now knows that these objects don't have values defined for every `key` and accessing a given `key` might resolve to `undefined`. --- The only obvious error this caused within this repo was on line 333 of the same file. For better or worse, I ended up casting that cookie value to a `string`. There's a series of `if` statements before it that, I guess, are guaranteeing that it's truly a string. Potentially, that stretch could be refactored such that TypeScript _knows_ it's a string. Also, I tried to follow the contributing guidelines. However, running `yarn types` kicked out a bunch of errors about overwriting files: ``` $ yarn types yarn run v1.22.10 $ lerna run types --stream lerna notice cli v4.0.0 lerna info Executing command in 2 packages: "yarn run types" @next/env: $ tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop next: $ tsc --declaration --emitDeclarationOnly --declarationDir dist next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/index.d.ts' because it would overwrite input file. next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts' because it would overwrite input file. ... ... ... ``` Let me know if there's anything I can improve here! Thanks again.
2022-05-23 02:48:26 +02:00
}>
/**
* Object of `cookies` from header
*/
fix NextApiRequestCookies and NextApiRequestQuery types (#25532) Hello! Thanks for making next.js so great. ## Bug Right now, these types give false confidence. These `key`s are treated as though [a value is defined for _every_ string](https://dev.to/sarioglu/avoiding-unintended-undefined-values-while-using-typescript-record-4igo). However, given an arbitrary request, a particular cookie or query param could be `undefined`. For example, when building an `/api` endpoint, the code might look like this: ```ts import type { NextApiRequest, NextApiResponse } from "next" export default function handler(req: NextApiRequest, res: NextApiResponse) { // According to the old types, `value` is a string const value = req.cookies.value // Type-checking passes but leads to a runtime error when no `value` cookie is provided in the request // Uncaught TypeError: Cannot read property 'toLowerCase' of undefined value.toLowerCause() // ... } ``` By using `Partial`, TypeScript now knows that these objects don't have values defined for every `key` and accessing a given `key` might resolve to `undefined`. --- The only obvious error this caused within this repo was on line 333 of the same file. For better or worse, I ended up casting that cookie value to a `string`. There's a series of `if` statements before it that, I guess, are guaranteeing that it's truly a string. Potentially, that stretch could be refactored such that TypeScript _knows_ it's a string. Also, I tried to follow the contributing guidelines. However, running `yarn types` kicked out a bunch of errors about overwriting files: ``` $ yarn types yarn run v1.22.10 $ lerna run types --stream lerna notice cli v4.0.0 lerna info Executing command in 2 packages: "yarn run types" @next/env: $ tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop next: $ tsc --declaration --emitDeclarationOnly --declarationDir dist next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/index.d.ts' because it would overwrite input file. next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts' because it would overwrite input file. ... ... ... ``` Let me know if there's anything I can improve here! Thanks again.
2022-05-23 02:48:26 +02:00
cookies: Partial<{
[key: string]: string
fix NextApiRequestCookies and NextApiRequestQuery types (#25532) Hello! Thanks for making next.js so great. ## Bug Right now, these types give false confidence. These `key`s are treated as though [a value is defined for _every_ string](https://dev.to/sarioglu/avoiding-unintended-undefined-values-while-using-typescript-record-4igo). However, given an arbitrary request, a particular cookie or query param could be `undefined`. For example, when building an `/api` endpoint, the code might look like this: ```ts import type { NextApiRequest, NextApiResponse } from "next" export default function handler(req: NextApiRequest, res: NextApiResponse) { // According to the old types, `value` is a string const value = req.cookies.value // Type-checking passes but leads to a runtime error when no `value` cookie is provided in the request // Uncaught TypeError: Cannot read property 'toLowerCase' of undefined value.toLowerCause() // ... } ``` By using `Partial`, TypeScript now knows that these objects don't have values defined for every `key` and accessing a given `key` might resolve to `undefined`. --- The only obvious error this caused within this repo was on line 333 of the same file. For better or worse, I ended up casting that cookie value to a `string`. There's a series of `if` statements before it that, I guess, are guaranteeing that it's truly a string. Potentially, that stretch could be refactored such that TypeScript _knows_ it's a string. Also, I tried to follow the contributing guidelines. However, running `yarn types` kicked out a bunch of errors about overwriting files: ``` $ yarn types yarn run v1.22.10 $ lerna run types --stream lerna notice cli v4.0.0 lerna info Executing command in 2 packages: "yarn run types" @next/env: $ tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop next: $ tsc --declaration --emitDeclarationOnly --declarationDir dist next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/index.d.ts' because it would overwrite input file. next: error TS5055: Cannot write file '/Users/mbrandly/code/next.js/packages/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts' because it would overwrite input file. ... ... ... ``` Let me know if there's anything I can improve here! Thanks again.
2022-05-23 02:48:26 +02:00
}>
body: any
env: Env
preview?: boolean
/**
* Preview data set on the request, if any
* */
previewData?: PreviewData
}
/**
* Send body of response
*/
type Send<T> = (body: T) => void
/**
* Next `API` route response
*/
export type NextApiResponse<T = any> = ServerResponse & {
/**
* Send data `any` data in response
*/
send: Send<T>
/**
* Send data `json` data in response
*/
json: Send<T>
status: (statusCode: number) => NextApiResponse<T>
redirect(url: string): NextApiResponse<T>
redirect(status: number, url: string): NextApiResponse<T>
/**
* Set preview data for Next.js' prerender mode
*/
setPreviewData: (
data: object | string,
options?: {
/**
* Specifies the number (in seconds) for the preview session to last for.
* The given number will be converted to an integer by rounding down.
* By default, no maximum age is set and the preview session finishes
* when the client shuts down (browser is closed).
*/
maxAge?: number
/**
* Specifies the path for the preview session to work under. By default,
* the path is considered the "default path", i.e., any pages under "/".
*/
path?: string
}
) => NextApiResponse<T>
/**
* Clear preview data for Next.js' prerender mode
*/
clearPreviewData: (options?: { path?: string }) => NextApiResponse<T>
/**
* @deprecated `unstable_revalidate` has been renamed to `revalidate`
*/
unstable_revalidate: () => void
revalidate: (
urlPath: string,
opts?: {
unstable_onlyGenerated?: boolean
}
) => Promise<void>
}
/**
* Next `API` route handler
*/
export type NextApiHandler<T = any> = (
req: NextApiRequest,
res: NextApiResponse<T>
Improve NextApiHandler type for early returns (#35166) I would like to be able to write handlers with early returns like this: ```typescript import { NextApiHandler } from 'next' const handler: NextApiHandler = (req, res) => { const value = getStuff() if (value === 'branch') { return res.json({}) } res.status(400) } ``` but `NextApiHandler`'s current return type is `void | Promise<void>`, which causes compilation to fail with ``` Error:(11, 3) TS2322: Type '(req: NextApiRequest, res: NextApiResponse<any>) => Promise<NextApiResponse<any> | undefined>' is not assignable to type 'NextApiHandler<any>'. Type 'Promise<NextApiResponse<any> | undefined>' is not assignable to type 'void | Promise<void>'. Type 'Promise<NextApiResponse<any> | undefined>' is not assignable to type 'Promise<void>'. Type 'NextApiResponse<any> | undefined' is not assignable to type 'void'. Type 'NextApiResponse<any>' is not assignable to type 'void'. ``` to avoid that the above snippet needs to be written as ```typescript if (value === 'branch') { res.json({}) return } ``` which looks odd to me. Changing the return type of `NextApiHandler` to `unknown | Promise<unknown>` would allow for shorter early returns and still communicates to users that nothing is expected to be returned from a handler. Augmenting the type like this, makes the first snippet work: ```typescript import { NextApiRequest, NextApiResponse } from 'next' declare module 'next' { export declare type NextApiHandler<T = any> = ( req: NextApiRequest, res: NextApiResponse<T> ) => unknown | Promise<unknown> } ``` ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint`
2022-04-16 06:00:52 +02:00
) => unknown | Promise<unknown>
/**
* Utils
*/
2020-04-06 17:59:44 +02:00
export function execOnce<T extends (...args: any[]) => ReturnType<T>>(
fn: T
): T {
let used = false
2020-04-06 17:59:44 +02:00
let result: ReturnType<T>
Initial plugins implementation (#9139) * Add initial bit for plugins * Add checks for needed metadata values * Add test * Initial plugins changes * Add handling for _app middleware * Add loading of _document middleware and handling of multiple default export syntaxes * Fix insert order for middleware member expression * Remove early return from middleware plugin from testing * Add tests for current plugin middlewares * Update test plugin package.json * Update handling for class default export * Update to use webpack loader instead of babel plugin, remove redundant middleware naming, and add field for required env for plugins * Add middleware to support material-ui use case and example material-ui plugin * Update tests and remove tests stuff from google analytics plugin * Remove old plugin suite * Add init-server middleware * Exit hard without stack trace when error in collecting plugins * Add on-error-client and on-error-server and update to run init-server with next-start in serverless mode * Update init-client for google analytics plugin * Add example Sentry plugin and update with-sentry-simple * Remove middleware field/folder and use src dir for plugins * Add post-hydration middleware and update material-ui plugin * Put plugins code behind flag * Update chromedriver * Revert "Update chromedriver" This reverts commit 1461e978e677f7da05e29e0415ec614a04bf65f9. * Update lock file * Remove un-needed _app for sentry example * Add auto loading of scoped packages, add plugins config for manually listing plugins, and update to only collect plugins once * Update example plugins * Expose plugins' config * Rename plugin lifecycles and add babel-preset-build * Rename other methods with unstable * Update log when plugin config overrides auto-detecting
2019-11-01 20:13:13 +01:00
2020-04-06 17:59:44 +02:00
return ((...args: any[]) => {
if (!used) {
used = true
2020-04-06 17:59:44 +02:00
result = fn(...args)
}
Initial plugins implementation (#9139) * Add initial bit for plugins * Add checks for needed metadata values * Add test * Initial plugins changes * Add handling for _app middleware * Add loading of _document middleware and handling of multiple default export syntaxes * Fix insert order for middleware member expression * Remove early return from middleware plugin from testing * Add tests for current plugin middlewares * Update test plugin package.json * Update handling for class default export * Update to use webpack loader instead of babel plugin, remove redundant middleware naming, and add field for required env for plugins * Add middleware to support material-ui use case and example material-ui plugin * Update tests and remove tests stuff from google analytics plugin * Remove old plugin suite * Add init-server middleware * Exit hard without stack trace when error in collecting plugins * Add on-error-client and on-error-server and update to run init-server with next-start in serverless mode * Update init-client for google analytics plugin * Add example Sentry plugin and update with-sentry-simple * Remove middleware field/folder and use src dir for plugins * Add post-hydration middleware and update material-ui plugin * Put plugins code behind flag * Update chromedriver * Revert "Update chromedriver" This reverts commit 1461e978e677f7da05e29e0415ec614a04bf65f9. * Update lock file * Remove un-needed _app for sentry example * Add auto loading of scoped packages, add plugins config for manually listing plugins, and update to only collect plugins once * Update example plugins * Expose plugins' config * Rename plugin lifecycles and add babel-preset-build * Rename other methods with unstable * Update log when plugin config overrides auto-detecting
2019-11-01 20:13:13 +01:00
return result
2020-04-06 17:59:44 +02:00
}) as T
}
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
const ABSOLUTE_URL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/
export const isAbsoluteUrl = (url: string) => ABSOLUTE_URL_REGEX.test(url)
export function getLocationOrigin() {
const { protocol, hostname, port } = window.location
return `${protocol}//${hostname}${port ? ':' + port : ''}`
}
export function getURL() {
const { href } = window.location
const origin = getLocationOrigin()
return href.substring(origin.length)
}
2020-04-06 17:59:44 +02:00
export function getDisplayName<P>(Component: ComponentType<P>) {
return typeof Component === 'string'
? Component
: Component.displayName || Component.name || 'Unknown'
}
export function isResSent(res: ServerResponse) {
return res.finished || res.headersSent
}
export function normalizeRepeatedSlashes(url: string) {
const urlParts = url.split('?')
const urlNoQuery = urlParts[0]
return (
urlNoQuery
// first we replace any non-encoded backslashes with forward
// then normalize repeated forward slashes
.replace(/\\/g, '/')
.replace(/\/\/+/g, '/') +
(urlParts[1] ? `?${urlParts.slice(1).join('?')}` : '')
)
}
export async function loadGetInitialProps<
C extends BaseContext,
IP = {},
P = {}
>(App: NextComponentType<C, IP, P>, ctx: C): Promise<IP> {
if (process.env.NODE_ENV !== 'production') {
if (App.prototype?.getInitialProps) {
const message = `"${getDisplayName(
App
)}.getInitialProps()" is defined as an instance method - visit https://nextjs.org/docs/messages/get-initial-props-as-an-instance-method for more information.`
throw new Error(message)
}
}
// when called from _app `ctx` is nested in `ctx`
const res = ctx.res || (ctx.ctx && ctx.ctx.res)
if (!App.getInitialProps) {
if (ctx.ctx && ctx.Component) {
// @ts-ignore pageProps default
return {
pageProps: await loadGetInitialProps(ctx.Component, ctx.ctx),
}
}
2020-04-06 17:59:44 +02:00
return {} as IP
}
const props = await App.getInitialProps(ctx)
if (res && isResSent(res)) {
return props
}
if (!props) {
const message = `"${getDisplayName(
App
)}.getInitialProps()" should resolve to an object. But found "${props}" instead.`
throw new Error(message)
}
if (process.env.NODE_ENV !== 'production') {
if (Object.keys(props).length === 0 && !ctx.ctx) {
console.warn(
`${getDisplayName(
App
)} returned an empty object from \`getInitialProps\`. This de-optimizes and prevents automatic static optimization. https://nextjs.org/docs/messages/empty-object-getInitialProps`
)
}
}
return props
}
let warnOnce = (_: string) => {}
if (process.env.NODE_ENV !== 'production') {
const warnings = new Set<string>()
warnOnce = (msg: string) => {
if (!warnings.has(msg)) {
console.warn(msg)
}
warnings.add(msg)
}
}
export { warnOnce }
export const SP = typeof performance !== 'undefined'
export const ST =
SP &&
(['mark', 'measure', 'getEntriesByName'] as const).every(
(method) => typeof performance[method] === 'function'
)
2021-07-05 18:31:32 +02:00
export class DecodeError extends Error {}
export class NormalizeError extends Error {}
export class PageNotFoundError extends Error {
code: string
constructor(page: string) {
super()
this.code = 'ENOENT'
this.message = `Cannot find module for page: ${page}`
}
}
export class MissingStaticPage extends Error {
constructor(page: string, message: string) {
super()
this.message = `Failed to load static file for page: ${page} ${message}`
}
}
export class MiddlewareNotFoundError extends Error {
code: string
constructor() {
super()
this.code = 'ENOENT'
this.message = `Cannot find the middleware module`
}
}
export interface CacheFs {
readFile(f: string): Promise<string>
readFileSync(f: string): string
writeFile(f: string, d: any): Promise<void>
mkdir(dir: string): Promise<void | string>
stat(f: string): Promise<{ mtime: Date }>
}