diff --git a/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.input.tsx b/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.input.tsx new file mode 100644 index 0000000000000..b48cfeedc80f1 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.input.tsx @@ -0,0 +1,28 @@ +interface Props { + params: { + slug: string; + } +} + +export function generateMetadata(props: Props, parent: any) { + console.log({ ...parent }) +} + +export default async function Page(props: Props) { + const config = { ...props } + return ( + + ) +} + +export function GET(req, ctx) { + console.log( + { ...ctx } + ) +} + +export function POST(req, ctx) { + console.log( + { ...req } + ) +} diff --git a/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.output.tsx b/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.output.tsx new file mode 100644 index 0000000000000..1ff3c160564c3 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-27.output.tsx @@ -0,0 +1,31 @@ +interface Props { + params: Promise<{ + slug: string; + }> +} + +export function generateMetadata(props: Props, parent: any) { + console.log({ ...parent }) +} + +export default async function Page(props: Props) { + const config = { /* Next.js Dynamic Async API Codemod: 'props' is used with spread syntax (...). Any asynchronous properties of 'props' must be awaited when accessed. */ + ...props } + return ( + () + ); +} + +export function GET(req, ctx) { + console.log( + { /* Next.js Dynamic Async API Codemod: 'ctx' is used with spread syntax (...). Any asynchronous properties of 'ctx' must be awaited when accessed. */ + ...ctx } + ) +} + +export function POST(req, ctx) { + console.log( + { ...req } + ) +} diff --git a/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts b/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts index f720e9217ea0a..98a68e0cb4548 100644 --- a/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts +++ b/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts @@ -433,6 +433,13 @@ export function transformDynamicProps( // Override the first param to `props` params[propsArgumentIndex] = propsIdentifier + modified = true + } + } else { + // When the prop argument is not destructured, we need to add comments to the spread properties + if (j.Identifier.check(currentParam)) { + commentSpreadProps(path, currentParam.name, j) + modifyTypes(currentParam.typeAnnotation, propsIdentifier, root, j) modified = true } } @@ -812,3 +819,30 @@ function findAllTypes( return types } + +function commentSpreadProps( + path: ASTPath, + propsIdentifierName: string, + j: API['jscodeshift'] +) { + const functionBody = findFunctionBody(path) + const functionBodyCollection = j(functionBody) + // Find all the usage of spreading properties of `props` + const jsxSpreadProperties = functionBodyCollection.find( + j.JSXSpreadAttribute, + { argument: { name: propsIdentifierName } } + ) + const objSpreadProperties = functionBodyCollection.find(j.SpreadElement, { + argument: { name: propsIdentifierName }, + }) + const comment = ` Next.js Dynamic Async API Codemod: '${propsIdentifierName}' is used with spread syntax (...). Any asynchronous properties of '${propsIdentifierName}' must be awaited when accessed. ` + + // Add comment before it + jsxSpreadProperties.forEach((spread) => { + insertCommentOnce(spread.value, j, comment) + }) + + objSpreadProperties.forEach((spread) => { + insertCommentOnce(spread.value, j, comment) + }) +}