From dbe5bb3b18548a3925dfe7efba85bb97a7bb1fdb Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 23 Nov 2022 16:19:54 +0100 Subject: [PATCH 1/3] add error for invalid props --- packages/next/server/next-typescript.ts | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/next/server/next-typescript.ts b/packages/next/server/next-typescript.ts index db244af381deb..e2745e891e8d4 100644 --- a/packages/next/server/next-typescript.ts +++ b/packages/next/server/next-typescript.ts @@ -29,11 +29,14 @@ const DISALLOWED_SERVER_REACT_APIS: string[] = [ const ALLOWED_EXPORTS = ['config', 'generateStaticParams'] +const ALLOWED_PAGE_PROPS = ['params', 'searchParams'] + const NEXT_TS_ERRORS = { INVALID_SERVER_API: 71001, INVALID_ENTRY_EXPORT: 71002, INVALID_OPTION_VALUE: 71003, MISPLACED_CLIENT_ENTRY: 71004, + INVALID_PAGE_PROP: 71005, } const API_DOCS: Record< @@ -200,6 +203,12 @@ export function createTSPlugin(modules: { /(page|layout)\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) ) } + const isPageFile = (filePath: string) => { + return ( + filePath.startsWith(appDir) && + /page\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) + ) + } function getIsClientEntry( fileName: string, @@ -537,6 +546,7 @@ export function createTSPlugin(modules: { ts.forEachChild(source!, (node) => { if (ts.isImportDeclaration(node)) { + // if (!isClientEntry) { const importPath = node.moduleSpecifier.getText(source!) if (importPath === "'react'" || importPath === '"react"') { @@ -666,6 +676,44 @@ export function createTSPlugin(modules: { } } } + } else if (ts.isFunctionDeclaration(node)) { + let hasExportKeyword = false + let hasDefaultKeyword = false + + if (node.modifiers) { + for (const modifier of node.modifiers) { + if (modifier.kind === ts.SyntaxKind.ExportKeyword) { + hasExportKeyword = true + } else if (modifier.kind === ts.SyntaxKind.DefaultKeyword) { + hasDefaultKeyword = true + } + } + } + + // `export default function` + if (hasExportKeyword && hasDefaultKeyword) { + if (isPageFile(fileName)) { + const props = node.parameters?.[0]?.name + // For page entries (page.js), it can only have `params` and `searchParams` + // as the prop names. + if (props && ts.isObjectBindingPattern(props)) { + for (const prop of (props as ts.ObjectBindingPattern) + .elements) { + const propName = prop.name.getText() + if (!ALLOWED_PAGE_PROPS.includes(propName)) { + prior.push({ + file: source, + category: ts.DiagnosticCategory.Error, + code: NEXT_TS_ERRORS.INVALID_PAGE_PROP, + messageText: `"${propName}" is not a valid page property.`, + start: prop.getStart(), + length: prop.getWidth(), + }) + } + } + } + } + } } }) } From 3366cb1b2557ef23b428c50c041b88e803a23122 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 23 Nov 2022 19:10:06 +0100 Subject: [PATCH 2/3] fix --- packages/next/server/next-typescript.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/next/server/next-typescript.ts b/packages/next/server/next-typescript.ts index e2745e891e8d4..aaaac73cdddb5 100644 --- a/packages/next/server/next-typescript.ts +++ b/packages/next/server/next-typescript.ts @@ -200,13 +200,13 @@ export function createTSPlugin(modules: { const isAppEntryFile = (filePath: string) => { return ( filePath.startsWith(appDir) && - /(page|layout)\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) + /[\\/](page|layout)\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) ) } const isPageFile = (filePath: string) => { return ( filePath.startsWith(appDir) && - /page\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) + /[\\/]page\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) ) } @@ -546,7 +546,6 @@ export function createTSPlugin(modules: { ts.forEachChild(source!, (node) => { if (ts.isImportDeclaration(node)) { - // if (!isClientEntry) { const importPath = node.moduleSpecifier.getText(source!) if (importPath === "'react'" || importPath === '"react"') { From bea2af2b84211d456f3af9a2f0d179c7312fea92 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 23 Nov 2022 19:13:27 +0100 Subject: [PATCH 3/3] fix --- packages/next/server/next-typescript.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/server/next-typescript.ts b/packages/next/server/next-typescript.ts index aaaac73cdddb5..9ff0c6612ac50 100644 --- a/packages/next/server/next-typescript.ts +++ b/packages/next/server/next-typescript.ts @@ -200,13 +200,13 @@ export function createTSPlugin(modules: { const isAppEntryFile = (filePath: string) => { return ( filePath.startsWith(appDir) && - /[\\/](page|layout)\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) + /^(page|layout)\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) ) } const isPageFile = (filePath: string) => { return ( filePath.startsWith(appDir) && - /[\\/]page\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) + /^page\.(mjs|js|jsx|ts|tsx)$/.test(path.basename(filePath)) ) }