diff --git a/.github/workflows/validate_issue.yml b/.github/workflows/validate_issue.yml index 3c6e3a4a2e43..abd6c0784d5f 100644 --- a/.github/workflows/validate_issue.yml +++ b/.github/workflows/validate_issue.yml @@ -15,4 +15,3 @@ jobs: run: node ./.github/actions/issue-validator/index.mjs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DEBUG: 1 diff --git a/examples/with-context-api/components/Counter.js b/examples/with-context-api/components/Counter.js deleted file mode 100644 index 19911d6eabc4..000000000000 --- a/examples/with-context-api/components/Counter.js +++ /dev/null @@ -1,31 +0,0 @@ -import { useReducer, useContext, createContext } from 'react' - -const CounterStateContext = createContext() -const CounterDispatchContext = createContext() - -const reducer = (state, action) => { - switch (action.type) { - case 'INCREASE': - return state + 1 - case 'DECREASE': - return state - 1 - case 'INCREASE_BY': - return state + action.payload - default: - throw new Error(`Unknown action: ${action.type}`) - } -} - -export const CounterProvider = ({ children }) => { - const [state, dispatch] = useReducer(reducer, 0) - return ( - - - {children} - - - ) -} - -export const useCount = () => useContext(CounterStateContext) -export const useDispatchCount = () => useContext(CounterDispatchContext) diff --git a/examples/with-context-api/components/Counter.tsx b/examples/with-context-api/components/Counter.tsx new file mode 100644 index 000000000000..0f8eae33512d --- /dev/null +++ b/examples/with-context-api/components/Counter.tsx @@ -0,0 +1,57 @@ +import { + useReducer, + useContext, + createContext, + ReactNode, + Dispatch, +} from 'react' + +type CounterState = number +type CounterAction = + | { + type: 'INCREASE' | 'DECREASE' + } + | { + type: 'INCREASE_BY' + payload: number + } + +const CounterStateContext = createContext(0) +const CounterDispatchContext = createContext>( + () => null +) + +const reducer = (state: CounterState, action: CounterAction) => { + switch (action.type) { + case 'INCREASE': + return state + 1 + case 'DECREASE': + return state - 1 + case 'INCREASE_BY': + return state + action.payload + default: + throw new Error(`Unknown action: ${JSON.stringify(action)}`) + } +} + +type CounterProviderProps = { + children: ReactNode + initialValue?: number +} + +export const CounterProvider = ({ + children, + initialValue = 0, +}: CounterProviderProps) => { + const [state, dispatch] = useReducer(reducer, initialValue) + return ( + + + {children} + + + ) +} + +export const useCount = () => useContext(CounterStateContext) +export const useDispatchCount = () => useContext(CounterDispatchContext) diff --git a/examples/with-context-api/package.json b/examples/with-context-api/package.json index 349de02f4d84..46775cdf8044 100644 --- a/examples/with-context-api/package.json +++ b/examples/with-context-api/package.json @@ -7,7 +7,12 @@ }, "dependencies": { "next": "latest", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/node": "18.7.15", + "@types/react": "16.9.17", + "typescript": "4.8.2" } } diff --git a/examples/with-context-api/pages/_app.js b/examples/with-context-api/pages/_app.tsx similarity index 58% rename from examples/with-context-api/pages/_app.js rename to examples/with-context-api/pages/_app.tsx index 4fe390a79d9b..6f852027a9e9 100644 --- a/examples/with-context-api/pages/_app.js +++ b/examples/with-context-api/pages/_app.tsx @@ -1,11 +1,10 @@ +import type { AppProps } from 'next/app' import { CounterProvider } from '../components/Counter' -function MyApp({ Component, pageProps }) { +export default function MyApp({ Component, pageProps }: AppProps) { return ( ) } - -export default MyApp diff --git a/examples/with-context-api/pages/about.js b/examples/with-context-api/pages/about.tsx similarity index 77% rename from examples/with-context-api/pages/about.js rename to examples/with-context-api/pages/about.tsx index 481c9c048159..f45d699aabf4 100644 --- a/examples/with-context-api/pages/about.js +++ b/examples/with-context-api/pages/about.tsx @@ -1,3 +1,4 @@ +import type { MouseEvent } from 'react' import Link from 'next/link' import { useCount, useDispatchCount } from '../components/Counter' @@ -5,11 +6,11 @@ const AboutPage = () => { const count = useCount() const dispatch = useDispatchCount() - const handleIncrease = (event) => + const handleIncrease = (event: MouseEvent) => dispatch({ type: 'INCREASE', }) - const handleIncrease15 = (event) => + const handleIncrease15 = (event: MouseEvent) => dispatch({ type: 'INCREASE_BY', payload: 15, diff --git a/examples/with-context-api/pages/index.js b/examples/with-context-api/pages/index.tsx similarity index 77% rename from examples/with-context-api/pages/index.js rename to examples/with-context-api/pages/index.tsx index 7e26a78c30aa..585aacff1c45 100644 --- a/examples/with-context-api/pages/index.js +++ b/examples/with-context-api/pages/index.tsx @@ -1,3 +1,4 @@ +import type { MouseEvent } from 'react' import Link from 'next/link' import { useCount, useDispatchCount } from '../components/Counter' @@ -5,11 +6,11 @@ const IndexPage = () => { const count = useCount() const dispatch = useDispatchCount() - const handleIncrease = (event) => + const handleIncrease = (event: MouseEvent) => dispatch({ type: 'INCREASE', }) - const handleDecrease = (event) => + const handleDecrease = (event: MouseEvent) => dispatch({ type: 'DECREASE', }) diff --git a/examples/with-context-api/tsconfig.json b/examples/with-context-api/tsconfig.json new file mode 100644 index 000000000000..ccaea0672d6e --- /dev/null +++ b/examples/with-context-api/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "moduleResolution": "node", + "module": "esnext", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index cc18cc8d743a..bcaaac6bd2cc 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -53,6 +53,19 @@ export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } { } } + if ( + node.type === 'ExportDeclaration' && + node.declaration?.type === 'VariableDeclaration' + ) { + const id = node.declaration?.declarations[0]?.id.value + if (['getStaticProps', 'getServerSideProps'].includes(id)) { + return { + ssg: id === 'getStaticProps', + ssr: id === 'getServerSideProps', + } + } + } + if (node.type === 'ExportNamedDeclaration') { const values = node.specifiers.map( (specifier: any) => diff --git a/test/unit/fixtures/page-runtime/ssr-variable-gssp.js b/test/unit/fixtures/page-runtime/ssr-variable-gssp.js new file mode 100644 index 000000000000..e09007babef5 --- /dev/null +++ b/test/unit/fixtures/page-runtime/ssr-variable-gssp.js @@ -0,0 +1,12 @@ +export default function Nodejs() { + return 'nodejs' +} + +// export an identifier instead of function +export const getServerSideProps = async () => { + return { props: {} } +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/test/unit/parse-page-runtime.test.ts b/test/unit/parse-page-static-info.test.ts similarity index 69% rename from test/unit/parse-page-runtime.test.ts rename to test/unit/parse-page-static-info.test.ts index a9c4a358faaf..772081e53fb0 100644 --- a/test/unit/parse-page-runtime.test.ts +++ b/test/unit/parse-page-static-info.test.ts @@ -9,21 +9,25 @@ function createNextConfig(runtime?: 'experimental-edge' | 'nodejs') { } } -describe('parse page runtime config', () => { +describe('parse page static info', () => { it('should parse nodejs runtime correctly', async () => { - const { runtime } = await getPageStaticInfo({ + const { runtime, ssr, ssg } = await getPageStaticInfo({ pageFilePath: join(fixtureDir, 'page-runtime/nodejs-ssr.js'), nextConfig: createNextConfig(), }) expect(runtime).toBe('nodejs') + expect(ssr).toBe(true) + expect(ssg).toBe(false) }) it('should parse static runtime correctly', async () => { - const { runtime } = await getPageStaticInfo({ + const { runtime, ssr, ssg } = await getPageStaticInfo({ pageFilePath: join(fixtureDir, 'page-runtime/nodejs.js'), nextConfig: createNextConfig(), }) expect(runtime).toBe(undefined) + expect(ssr).toBe(false) + expect(ssg).toBe(false) }) it('should parse edge runtime correctly', async () => { @@ -41,22 +45,35 @@ describe('parse page runtime config', () => { }) expect(runtime).toBe(undefined) }) + + it('should parse ssr info with variable exported gSSP correctly', async () => { + const { ssr, ssg } = await getPageStaticInfo({ + pageFilePath: join(fixtureDir, 'page-runtime/ssr-variable-gssp.js'), + nextConfig: createNextConfig(), + }) + expect(ssr).toBe(true) + expect(ssg).toBe(false) + }) }) describe('fallback to the global runtime configuration', () => { it('should fallback when gSP is defined and exported', async () => { - const { runtime } = await getPageStaticInfo({ + const { runtime, ssr, ssg } = await getPageStaticInfo({ pageFilePath: join(fixtureDir, 'page-runtime/fallback-with-gsp.js'), nextConfig: createNextConfig('experimental-edge'), }) expect(runtime).toBe('experimental-edge') + expect(ssr).toBe(false) + expect(ssg).toBe(true) }) it('should fallback when gSP is re-exported from other module', async () => { - const { runtime } = await getPageStaticInfo({ + const { runtime, ssr, ssg } = await getPageStaticInfo({ pageFilePath: join(fixtureDir, 'page-runtime/fallback-re-export-gsp.js'), nextConfig: createNextConfig('experimental-edge'), }) expect(runtime).toBe('experimental-edge') + expect(ssr).toBe(false) + expect(ssg).toBe(true) }) })