feat(ts): add JSDoc comments for public APIs (#61649)

### What?

This PR adds JSDoc comments to the most common public APIs to improve
the DX in IDEs like VSCode.

### Why?

Currently, we provide no information on some of the most used APIs in
IDEs, which makes it harder than it needs to be to look up the extra
information.

<details>
<summary><b>Before:</b></summary>
<img
src="https://github.com/vercel/next.js/assets/18369201/8b6092e8-8f9b-49da-a3df-b07a59982069"
width="640"/>
</details>

<details>
<summary><b>After:</b></summary>
<img
src="https://github.com/vercel/next.js/assets/18369201/30216f76-414a-43b0-9aa6-2fdd742ab3fe"
width="640"/>
</details>

### How?

Using JSDoc comments, I added a basic description to most public APIs
that link back to the current docs for more details. The description is
kept minimal to avoid out-of-sync documentation.

Note: In the future, the flow could be reversed here, and our API
Reference docs could actually be generated from a single source of
truth, the source code itself. However, this will require more work by
re-organizing our public API submodules in a single directory, (related
#61525), so the API docs are easy to maintain even without a deeper
knowledge of the codebase.

Note: These comments should also be extended to
methods/properties/arguments in these public APIs in the future.

Closes NEXT-2357

[Slack
thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1706735292633029)
This commit is contained in:
Balázs Orbán 2024-02-05 21:31:59 +01:00 committed by GitHub
parent 2d488564e6
commit bb7577aa44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 256 additions and 51 deletions

View file

@ -35,21 +35,26 @@ function setUpEnv(dir: string) {
loadEnvConfig(dir, dev, Log)
}
/*
// Usage in jest.config.js
const nextJest = require('next/jest');
// Optionally provide path to Next.js app which will enable loading next.config.js and .env files
const createJestConfig = nextJest({ dir })
// Any custom config you want to pass to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
}
// createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)
*/
/**
* @example
* ```ts
* // Usage in jest.config.js
* const nextJest = require('next/jest');
*
* // Optionally provide path to Next.js app which will enable loading next.config.js and .env files
* const createJestConfig = nextJest({ dir })
*
* // Any custom config you want to pass to Jest
* const customJestConfig = {
* setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
* }
*
* // createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
* module.exports = createJestConfig(customJestConfig)
* ```
*
* Read more: [Next.js Docs: Setting up Jest with Next.js](https://nextjs.org/docs/app/building-your-application/testing/jest)
*/
export default function nextJest(options: { dir?: string } = {}) {
// createJestConfig
return (

View file

@ -10,6 +10,15 @@ import { trackDynamicDataAccessed } from '../../server/app-render/dynamic-render
import { staticGenerationAsyncStorage } from './static-generation-async-storage.external'
import { getExpectedRequestStore } from './request-async-storage.external'
/**
* This function allows you to read the HTTP incoming request headers in
* [Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components),
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations),
* [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) and
* [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware).
*
* Read more: [Next.js Docs: `headers`](https://nextjs.org/docs/app/api-reference/functions/headers)
*/
export function headers() {
const callingExpression = 'headers'
const staticGenerationStore = staticGenerationAsyncStorage.getStore()

View file

@ -4,6 +4,7 @@ import {
AppRouterContext,
GlobalLayoutRouterContext,
LayoutRouterContext,
type AppRouterInstance,
} from '../../shared/lib/app-router-context.shared-runtime'
import {
SearchParamsContext,
@ -67,8 +68,24 @@ export class ReadonlyURLSearchParams {
}
/**
* Get a read-only URLSearchParams object. For example searchParams.get('foo') would return 'bar' when ?foo=bar
* Learn more about URLSearchParams here: https://developer.mozilla.org/docs/Web/API/URLSearchParams
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
* that lets you *read* the current URL's search parameters.
*
* Learn more about [`URLSearchParams` on MDN](https://developer.mozilla.org/docs/Web/API/URLSearchParams)
*
* @example
* ```ts
* "use client"
* import { useSearchParams } from 'next/navigation'
*
* export default function Page() {
* const searchParams = useSearchParams()
* searchParams.get('foo') // returns 'bar' when ?foo=bar
* // ...
* }
* ```
*
* Read more: [Next.js Docs: `useSearchParams`](https://nextjs.org/docs/app/api-reference/functions/use-search-params)
*/
export function useSearchParams(): ReadonlyURLSearchParams {
clientHookInServerComponentError('useSearchParams')
@ -99,7 +116,21 @@ export function useSearchParams(): ReadonlyURLSearchParams {
}
/**
* Get the current pathname. For example usePathname() on /dashboard?foo=bar would return "/dashboard"
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
* that lets you read the current URL's pathname.
*
* @example
* ```ts
* "use client"
* import { usePathname } from 'next/navigation'
*
* export default function Page() {
* const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar
* // ...
* }
* ```
*
* Read more: [Next.js Docs: `usePathname`](https://nextjs.org/docs/app/api-reference/functions/use-pathname)
*/
export function usePathname(): string {
clientHookInServerComponentError('usePathname')
@ -114,9 +145,24 @@ export {
} from '../../shared/lib/server-inserted-html.shared-runtime'
/**
* Get the router methods. For example router.push('/dashboard')
*
* This hook allows you to programmatically change routes inside [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components).
*
* @example
* ```ts
* "use client"
* import { useRouter } from 'next/navigation'
*
* export default function Page() {
* const router = useRouter()
* // ...
* router.push('/dashboard') // Navigate to /dashboard
* }
* ```
*
* Read more: [Next.js Docs: `useRouter`](https://nextjs.org/docs/app/api-reference/functions/use-router)
*/
export function useRouter(): import('../../shared/lib/app-router-context.shared-runtime').AppRouterInstance {
export function useRouter(): AppRouterInstance {
clientHookInServerComponentError('useRouter')
const router = useContext(AppRouterContext)
if (router === null) {
@ -161,8 +207,21 @@ function getSelectedParams(
}
/**
* Get the current parameters. For example useParams() on /dashboard/[team]
* where pathname is /dashboard/nextjs would return { team: 'nextjs' }
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
* that lets you read a route's dynamic params filled in by the current URL.
*
* @example
* ```ts
* "use client"
* import { useParams } from 'next/navigation'
*
* export default function Page() {
* // on /dashboard/[team] where pathname is /dashboard/nextjs
* const { team } = useParams() // team === "nextjs"
* }
* ```
*
* Read more: [Next.js Docs: `useParams`](https://nextjs.org/docs/app/api-reference/functions/use-params)
*/
export function useParams<T extends Params = Params>(): T {
clientHookInServerComponentError('useParams')
@ -180,9 +239,7 @@ export function useParams<T extends Params = Params>(): T {
}, [globalLayoutRouter?.tree, pathParams])
}
/**
* Get the canonical parameters from the current level to the leaf node.
*/
/** Get the canonical parameters from the current level to the leaf node. */
function getSelectedLayoutSegmentPath(
tree: FlightRouterState,
parallelRouteKey: string,
@ -217,9 +274,30 @@ function getSelectedLayoutSegmentPath(
)
}
// TODO-APP: Expand description when the docs are written for it.
/**
* Get the canonical segment path from the current level to the leaf node.
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
* that lets you read the active route segments **below** the Layout it is called from.
*
* @example
* ```ts
* 'use client'
*
* import { useSelectedLayoutSegments } from 'next/navigation'
*
* export default function ExampleClientComponent() {
* const segments = useSelectedLayoutSegments()
*
* return (
* <ul>
* {segments.map((segment, index) => (
* <li key={index}>{segment}</li>
* ))}
* </ul>
* )
* }
* ```
*
* Read more: [Next.js Docs: `useSelectedLayoutSegments`](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments)
*/
export function useSelectedLayoutSegments(
parallelRouteKey: string = 'children'
@ -229,9 +307,23 @@ export function useSelectedLayoutSegments(
return getSelectedLayoutSegmentPath(tree, parallelRouteKey)
}
// TODO-APP: Expand description when the docs are written for it.
/**
* Get the segment below the current level
* A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook
* that lets you read the active route segment **one level below** the Layout it is called from.
*
* @example
* ```ts
* 'use client'
* import { useSelectedLayoutSegment } from 'next/navigation'
*
* export default function ExampleClientComponent() {
* const segment = useSelectedLayoutSegment()
*
* return <p>Active segment: {segment}</p>
* }
* ```
*
* Read more: [Next.js Docs: `useSelectedLayoutSegment`](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment)
*/
export function useSelectedLayoutSegment(
parallelRouteKey: string = 'children'

View file

@ -3,8 +3,18 @@ const NOT_FOUND_ERROR_CODE = 'NEXT_NOT_FOUND'
type NotFoundError = Error & { digest: typeof NOT_FOUND_ERROR_CODE }
/**
* When used in a React server component, this will set the status code to 404.
* When used in a custom app route it will just send a 404 status.
* This function allows you to render the [not-found file](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)
* within a route segment as well as inject a tag.
*
* This function allows you to redirect the user to another URL. It can be used in
* [Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components),
* [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers), and
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* - In a Server Component, this will insert a `<meta name="robots" content="noindex" />` meta tag and set the status code to 404.
* - In a Route Handler or Server Action, it will serve a 404 to the caller.
*
* Read more: [Next.js Docs: `notFound`](https://nextjs.org/docs/app/api-reference/functions/not-found)
*/
export function notFound(): never {
// eslint-disable-next-line no-throw-literal

View file

@ -30,13 +30,18 @@ export function getRedirectError(
}
/**
* When used in a streaming context, this will insert a meta tag to
* redirect the user to the target page. When used in a custom app route, it
* will serve a 307/303 to the caller.
* This function allows you to redirect the user to another URL. It can be used in
* [Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components),
* [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers), and
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* @param url the url to redirect to
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 307/303 to the caller.
*
* Read more: [Next.js Docs: `redirect`](https://nextjs.org/docs/app/api-reference/functions/redirect)
*/
export function redirect(
/** The URL to redirect to */
url: string,
type: RedirectType = RedirectType.replace
): never {
@ -54,13 +59,18 @@ export function redirect(
}
/**
* When used in a streaming context, this will insert a meta tag to
* redirect the user to the target page. When used in a custom app route, it
* will serve a 308/303 to the caller.
* This function allows you to redirect the user to another URL. It can be used in
* [Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components),
* [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers), and
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* @param url the url to redirect to
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 308/303 to the caller.
*
* Read more: [Next.js Docs: `redirect`](https://nextjs.org/docs/app/api-reference/functions/redirect)
*/
export function permanentRedirect(
/** The URL to redirect to */
url: string,
type: RedirectType = RedirectType.replace
): never {

View file

@ -352,6 +352,11 @@ function ImagePreload({
)
}
/**
* The `Image` component is used to optimize images.
*
* Read more: [Next.js docs: `Image`](https://nextjs.org/docs/app/api-reference/components/image)
*/
export const Image = forwardRef<HTMLImageElement | null, ImageProps>(
(props, forwardedRef) => {
const pagesRouter = useContext(RouterContext)

View file

@ -255,7 +255,12 @@ function formatStringOrUrl(urlObjOrString: UrlObject | string): string {
}
/**
* React Component that enables client-side transitions between routes.
* A React component that extends the HTML `<a>` element to provide [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching)
* and client-side navigation between routes.
*
* It is the primary way to navigate between routes in Next.js.
*
* Read more: [Next.js docs: `<Link>`](https://nextjs.org/docs/app/api-reference/components/link)
*/
const Link = React.forwardRef<HTMLAnchorElement, LinkPropsReal>(
function LinkComponent(props, forwardedRef) {

View file

@ -129,6 +129,12 @@ export default singletonRouter as SingletonRouter
// Reexport the withRouter HOC
export { default as withRouter } from './with-router'
/**
* This hook gives access the [router object](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object)
* inside the [Pages Router](https://nextjs.org/docs/pages/building-your-application).
*
* Read more: [Next.js Docs: `useRouter`](https://nextjs.org/docs/pages/api-reference/functions/use-router)
*/
export function useRouter(): NextRouter {
const router = React.useContext(RouterContext)
if (!router) {
@ -140,10 +146,6 @@ export function useRouter(): NextRouter {
return router
}
// INTERNAL APIS
// -------------
// (do not use following exports inside the app)
/**
* Create a router and assign it as the singleton instance.
* This is used in client side when we are initializing the app.

View file

@ -208,6 +208,11 @@ export function initScriptLoader(scriptLoaderItems: ScriptProps[]) {
addBeforeInteractiveToCache()
}
/**
* Load a third-party scripts in an optimized way.
*
* Read more: [Next.js Docs: `next/script`](https://nextjs.org/docs/app/api-reference/components/script)
*/
function Script(props: ScriptProps): JSX.Element | null {
const {
id,

View file

@ -404,8 +404,11 @@ export type ExportPathMap = {
}
/**
* Next configuration object
* @see [configuration documentation](https://nextjs.org/docs/api-reference/next.config.js/introduction)
* Next.js can be configured through a `next.config.js` file in the root of your project directory.
*
* This can change the behavior, enable experimental features, and configure other advanced options.
*
* Read more: [Next.js Docs: `next.config.js`](https://nextjs.org/docs/api-reference/next.config.js/introduction)
*/
export interface NextConfig extends Record<string, any> {
exportPathMap?: (

View file

@ -10,6 +10,12 @@ function importModule(): Promise<
)
}
/**
* The ImageResponse class allows you to generate dynamic images using JSX and CSS.
* This is useful for generating social media images such as Open Graph images, Twitter cards, and more.
*
* Read more: [Next.js Docs: `ImageResponse`](https://nextjs.org/docs/app/api-reference/functions/image-response)
*/
export class ImageResponse extends Response {
public static displayName = 'ImageResponse'
constructor(...args: ConstructorParameters<OgModule['ImageResponse']>) {

View file

@ -7,6 +7,11 @@ import { RequestCookies } from './cookies'
export const INTERNALS = Symbol('internal request')
/**
* This class extends the [Web `Request` API](https://developer.mozilla.org/docs/Web/API/Request) with additional convenience methods.
*
* Read more: [Next.js Docs: `NextRequest`](https://nextjs.org/docs/app/api-reference/functions/next-request)
*/
export class NextRequest extends Request {
[INTERNALS]: {
cookies: RequestCookies

View file

@ -26,6 +26,11 @@ function handleMiddlewareField(
}
}
/**
* This class extends the [Web `Response` API](https://developer.mozilla.org/docs/Web/API/Response) with additional convenience methods.
*
* Read more: [Next.js Docs: `NextResponse`](https://nextjs.org/docs/app/api-reference/functions/next-response)
*/
export class NextResponse<Body = unknown> extends Response {
[INTERNALS]: {
cookies: ResponseCookies

View file

@ -10,10 +10,20 @@ import {
} from '../../../lib/constants'
import { getPathname } from '../../../lib/url'
/**
* This function allows you to purge [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific cache tag.
*
* Read more: [Next.js Docs: `revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag)
*/
export function revalidateTag(tag: string) {
return revalidate(tag, `revalidateTag ${tag}`)
}
/**
* This function allows you to purge [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific path.
*
* Read more: [Next.js Docs: `revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath)
*/
export function revalidatePath(originalPath: string, type?: 'layout' | 'page') {
if (originalPath.length > NEXT_CACHE_SOFT_TAG_MAX_LENGTH) {
console.warn(

View file

@ -49,6 +49,11 @@ async function cacheNewResult<T>(
return
}
/**
* This function allows you to cache the results of expensive operations, like database queries, and reuse them across multiple requests.
*
* Read more: [Next.js Docs: `unstable_cache`](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)
*/
export function unstable_cache<T extends Callback>(
cb: T,
keyParts?: string[],

View file

@ -2,12 +2,19 @@ import { staticGenerationAsyncStorage } from '../../../client/components/static-
import { markCurrentScopeAsDynamic } from '../../app-render/dynamic-rendering'
/**
* Expects to be called in an App Router render and will error if not.
* This function can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.
*
* marks the current scope as dynamic. In non PPR cases this will make a static render
* halt and mark the page as dynamic. In PPR cases this will postpone the render at this location.
* It marks the current scope as dynamic.
*
* If we are inside a cache scope then this function is a noop
* - In [non-PPR](https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering) cases this will make a static render
* halt and mark the page as dynamic.
* - In PPR cases this will postpone the render at this location.
*
* If we are inside a cache scope then this function does nothing.
*
* @note It expects to be called within App Router and will error otherwise.
*
* Read more: [Next.js Docs: `unstable_noStore`](https://nextjs.org/docs/app/api-reference/functions/unstable_noStore)
*/
export function unstable_noStore() {
const callingExpression = 'unstable_noStore()'

View file

@ -50,6 +50,14 @@ export type NextMiddlewareResult =
| undefined
| void
/**
* Middleware allows you to run code before a request is completed.
* Then, based on the incoming request, you can modify the response
* by rewriting, redirecting, modifying the request or response headers,
* or responding directly.
*
* Read more: [Next.js Docs: Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)
*/
export type NextMiddleware = (
request: NextRequest,
event: NextFetchEvent

View file

@ -74,6 +74,12 @@ export function noSSR<P = {}>(
)
}
/**
* This function lets you dynamically import a component.
* It uses [React.lazy()](https://react.dev/reference/react/lazy) with [Suspense](https://react.dev/reference/react/Suspense) under the hood.
*
* Read more: [Next.js Docs: `next/dynamic`](https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#nextdynamic)
*/
export default function dynamic<P = {}>(
dynamicOptions: DynamicOptions<P> | Loader<P>,
options?: DynamicOptions<P>

View file

@ -7,7 +7,14 @@ import { Image } from '../../client/image-component'
// @ts-ignore - This is replaced by webpack alias
import defaultLoader from 'next/dist/shared/lib/image-loader'
export const getImageProps = (imgProps: ImageProps) => {
/**
* For more advanced use cases, you can call `getImageProps()`
* to get the props that would be passed to the underlying `<img>` element,
* and instead pass to them to another component, style, canvas, etc.
*
* Read more: [Next.js docs: `getImageProps`](https://nextjs.org/docs/app/api-reference/components/image#getimageprops)
*/
export function getImageProps(imgProps: ImageProps) {
const { props } = getImgProps(imgProps, {
defaultLoader,
// This is replaced by webpack define plugin