Skip to content

Commit

Permalink
handle directives by traversing tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Sep 30, 2024
1 parent 9f48150 commit 1c66699
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* this is a comment */
'use client'
export default function Page(props: {
params: any
}): JSX.Element {
return <div {...props.params} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* this is a comment */
'use client';
import { use } from "react";
export default function Page(props: {
params: Promise<any>
}): JSX.Element {
const params = use(props.params);
return <div {...params} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export function transformDynamicAPI(
})
}

const isClientComponent = determineClientDirective(root, j, source)
const isClientComponent = determineClientDirective(root)

// Only transform the valid calls in server or shared components
if (isClientComponent) return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ export function transformDynamicProps(
})
}

const isClientComponent = determineClientDirective(root, j, source)
const isClientComponent = determineClientDirective(root)

// Apply to `params` and `searchParams`
processAsyncPropOfEntryFile(isClientComponent)
Expand Down
58 changes: 18 additions & 40 deletions packages/next-codemod/transforms/lib/async-request-api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type {
ExportNamedDeclaration,
FunctionDeclaration,
FunctionExpression,
JSCodeshift,
} from 'jscodeshift'

export type FunctionScope =
Expand Down Expand Up @@ -103,46 +102,25 @@ export function isMatchedFunctionExported(
return isNamedExport
}

export function determineClientDirective(
root: Collection<any>,
j: JSCodeshift,
source: string
) {
const hasStringDirective =
root
.find(j.Literal)
.filter((path) => {
const expr = path.node

return (
expr.value === 'use client' && path.parentPath.node.type === 'Program'
)
})
.size() > 0

// 'use client';
const hasStringDirectiveWithSemicolon =
root
.find(j.StringLiteral)
.filter((path) => {
const expr = path.node
return (
expr.type === 'StringLiteral' &&
expr.value === 'use client' &&
path.parentPath.node.type === 'Program'
)
})
.size() > 0

if (hasStringDirective || hasStringDirectiveWithSemicolon) return true

// Since the client detection is not reliable with AST in jscodeshift,
// determine if 'use client' or "use client" is leading in the source code.
const trimmedSource = source.trim()
const containsClientDirective =
/^'use client'/.test(trimmedSource) || /^"use client"/g.test(trimmedSource)
// directive is not parsed into AST, so we need to manually find it
// by going through the tokens. Use the 1st string token as the directive
export function determineClientDirective(root: Collection<any>) {
const { tokens } = root.get().node
let directive = undefined
for (const token of tokens) {
if (token.type === 'CommentBlock') {
continue
} else if (token.type?.label === 'string') {
// Skip 'use strict' directive
if (token.value === 'use strict') continue
directive = token.value
break
} else {
break
}
}

return containsClientDirective
return directive === 'use client'
}

export function isPromiseType(typeAnnotation) {
Expand Down

0 comments on commit 1c66699

Please sign in to comment.