diff --git a/packages/next/server/next-typescript.ts b/packages/next/server/next-typescript.ts index b4946c6eb445979..db244af381debea 100644 --- a/packages/next/server/next-typescript.ts +++ b/packages/next/server/next-typescript.ts @@ -41,6 +41,7 @@ const API_DOCS: Record< { description: string options: Record + link: string type?: string isValid?: (value: string) => boolean getHint?: (value: any) => string @@ -59,6 +60,7 @@ const API_DOCS: Record< '"force-static"': 'This forces caching of all fetches and returns empty values from `useCookies`, `useHeaders` and `useSearchParams`.', }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#dynamic', }, fetchCache: { description: @@ -79,6 +81,7 @@ const API_DOCS: Record< '"force-cache"': "This lets you intentionally opt-in to all caching of data. This option forces all fetches to be cache even if the `cache: 'no-store'` option is passed to `fetch()`.", }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#fetchcache', }, preferredRegion: { description: @@ -89,6 +92,7 @@ const API_DOCS: Record< '"home"': 'Prefer deploying to the Home region.', '"edge"': 'Prefer deploying to the Edge globally.', }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#preferredregion', }, revalidate: { description: @@ -100,6 +104,7 @@ const API_DOCS: Record< 0: 'Specifying `0` implies that this layout or page should never be static.', 30: 'Set the revalidation time to `30` seconds. The value can be `0` or any positive number.', }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#revalidate', isValid: (value: string) => { return value === 'false' || Number(value) >= 0 }, @@ -115,6 +120,7 @@ const API_DOCS: Record< false: 'Disallow rendering dynamic params that are not generated by `generateStaticParams`.', }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#dynamicparams', }, runtime: { description: @@ -123,6 +129,7 @@ const API_DOCS: Record< '"nodejs"': 'Prefer the Node.js runtime.', '"experimental-edge"': 'Prefer the experimental Edge runtime.', }, + link: 'https://beta.nextjs.org/docs/api-reference/segment-config#runtime', }, } @@ -217,8 +224,7 @@ export function createTSPlugin(modules: { messageText: 'The `"use client"` directive must be put at the top of the file.', start: node.expression.getStart(), - length: - node.expression.getEnd() - node.expression.getStart(), + length: node.expression.getWidth(), } throw e } @@ -428,6 +434,13 @@ export function createTSPlugin(modules: { const name = declarartion.name const value = declarartion.initializer + const docsLink = { + kind: 'text', + text: + `\n\nRead more about the "${entryConfig}" option: ` + + API_DOCS[entryConfig].link, + } + if ( value && value.getFullStart() <= position && @@ -459,28 +472,41 @@ export function createTSPlugin(modules: { API_DOCS[entryConfig].getHint?.(key) || '', }, + docsLink, ], } - } - } else { - // Hovers the name of the config - if (API_DOCS[entryConfig]) { + } else { + // Wrong value, display the docs link overriden = { kind: ts.ScriptElementKind.enumElement, kindModifiers: ts.ScriptElementKindModifier.none, textSpan: { - start: name.getStart(), - length: name.getWidth(), + start: value.getStart(), + length: value.getWidth(), }, displayParts: [], - documentation: [ - { - kind: 'text', - text: getAPIDescription(entryConfig), - }, - ], + documentation: [docsLink], } } + } else { + // Hovers the name of the config + + overriden = { + kind: ts.ScriptElementKind.enumElement, + kindModifiers: ts.ScriptElementKindModifier.none, + textSpan: { + start: name.getStart(), + length: name.getWidth(), + }, + displayParts: [], + documentation: [ + { + kind: 'text', + text: getAPIDescription(entryConfig), + }, + docsLink, + ], + } } }) if (overriden) return overriden @@ -529,8 +555,7 @@ export function createTSPlugin(modules: { code: NEXT_TS_ERRORS.INVALID_SERVER_API, messageText: `"${name}" is not allowed in Server Components.`, start: element.name.getStart(), - length: - element.name.getEnd() - element.name.getStart(), + length: element.name.getWidth(), }) } } @@ -556,7 +581,7 @@ export function createTSPlugin(modules: { code: NEXT_TS_ERRORS.INVALID_ENTRY_EXPORT, messageText: `"${name.text}" is not a valid Next.js entry export value.`, start: name.getStart(), - length: name.getEnd() - name.getStart(), + length: name.getWidth(), }) } else if (API_DOCS[name.text]) { // Check if the value is valid @@ -612,7 +637,8 @@ export function createTSPlugin(modules: { ts.isBigIntLiteral(value) || ts.isArrayLiteralExpression(value) || ts.isObjectLiteralExpression(value) || - ts.isRegularExpressionLiteral(value) + ts.isRegularExpressionLiteral(value) || + ts.isPrefixUnaryExpression(value) ) { isInvalid = true displayedValue = value.getText() @@ -632,7 +658,7 @@ export function createTSPlugin(modules: { errorMessage || `"${displayedValue}" is not a valid value for the "${name.text}" option.`, start: value.getStart(), - length: value.getEnd() - value.getStart(), + length: value.getWidth(), }) } }