From 59466f8a15d8af0887568ea1d55b84981862a794 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Mon, 28 Nov 2022 01:15:56 +0100 Subject: [PATCH] Add gSP and gSSP checks for both server and client layers in the SWC transform (#43391) We currently have these as a build runtime check. This PR ensures that it happens during the SWC compilation time, and applies to both server and client entries. Also adds link to the docs: CleanShot 2022-11-25 at 18 14 54@2x ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) --- .../crates/core/src/react_server_components.rs | 13 ++++++++++--- packages/next-swc/crates/core/tests/errors.rs | 2 +- .../get-server-side-props/output.stderr | 2 +- .../client-graph/get-static-props/output.stderr | 2 +- .../server-graph/get-server-side-props/input.js | 6 ++++++ .../server-graph/get-server-side-props/output.js | 4 ++++ .../get-server-side-props/output.stderr | 6 ++++++ .../server-graph/get-static-props/input.js | 6 ++++++ .../server-graph/get-static-props/output.js | 4 ++++ .../server-graph/get-static-props/output.stderr | 6 ++++++ .../plugins/wellknown-errors-plugin/parseRSC.ts | 7 +++++++ packages/next/server/app-render.tsx | 14 -------------- test/e2e/app-dir/rsc-errors.test.ts | 4 ++-- 13 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/input.js create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.js create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.stderr create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/input.js create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.js create mode 100644 packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.stderr diff --git a/packages/next-swc/crates/core/src/react_server_components.rs b/packages/next-swc/crates/core/src/react_server_components.rs index 45a52f3bbe35..5322e9bef98e 100644 --- a/packages/next-swc/crates/core/src/react_server_components.rs +++ b/packages/next-swc/crates/core/src/react_server_components.rs @@ -58,7 +58,7 @@ impl VisitMut for ReactServerComponents { if self.is_server { if !is_client_entry { - self.assert_server_graph(&imports); + self.assert_server_graph(&imports, module); } else { self.to_module_ref(module); return; @@ -246,7 +246,7 @@ impl ReactServerComponents { ); } - fn assert_server_graph(&self, imports: &Vec) { + fn assert_server_graph(&self, imports: &Vec, module: &Module) { for import in imports { let source = import.source.0.clone(); if self.invalid_server_imports.contains(&source) { @@ -288,6 +288,8 @@ impl ReactServerComponents { } } } + + self.assert_invalid_api(module); } fn assert_client_graph(&self, imports: &Vec, module: &Module) { @@ -305,10 +307,15 @@ impl ReactServerComponents { } } + self.assert_invalid_api(module); + } + + fn assert_invalid_api(&self, module: &Module) { // Assert `getServerSideProps` and `getStaticProps` exports. let is_layout_or_page = Regex::new(r"/(page|layout)\.(ts|js)x?$") .unwrap() .is_match(&self.filepath); + if is_layout_or_page { let mut span = DUMMY_SP; let mut has_get_server_side_props = false; @@ -389,7 +396,7 @@ impl ReactServerComponents { .struct_span_err( span, format!( - "`{}` is not allowed in Client Components.", + "NEXT_RSC_ERR_INVALID_API: {}", if has_get_server_side_props { "getServerSideProps" } else { diff --git a/packages/next-swc/crates/core/tests/errors.rs b/packages/next-swc/crates/core/tests/errors.rs index 8bee4c9b5bf8..6d627c12e665 100644 --- a/packages/next-swc/crates/core/tests/errors.rs +++ b/packages/next-swc/crates/core/tests/errors.rs @@ -82,7 +82,7 @@ fn react_server_components_server_graph_errors(input: PathBuf) { syntax(), &|tr| { server_components( - FileName::Real(PathBuf::from("/some-project/src/some-file.js")), + FileName::Real(PathBuf::from("/some-project/src/layout.js")), next_swc::react_server_components::Config::WithOptions( next_swc::react_server_components::Options { is_server: true }, ), diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.stderr index 8c649c3e39f8..eece7921e79a 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.stderr @@ -1,5 +1,5 @@ - x `getServerSideProps` is not allowed in Client Components. + x NEXT_RSC_ERR_INVALID_API: getServerSideProps ,-[input.js:1:1] 1 | export function getServerSideProps (){ : ^^^^^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.stderr index 67cae2d6ee78..536cab2cb8da 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.stderr @@ -1,5 +1,5 @@ - x `getStaticProps` is not allowed in Client Components. + x NEXT_RSC_ERR_INVALID_API: getStaticProps ,-[input.js:1:1] 1 | export function getStaticProps (){ : ^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/input.js b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/input.js new file mode 100644 index 000000000000..54ca0a245d2f --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/input.js @@ -0,0 +1,6 @@ +export function getServerSideProps (){ +} + +export default function () { + return null; +} diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.js b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.js new file mode 100644 index 000000000000..c2a54dc35918 --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.js @@ -0,0 +1,4 @@ +export function getServerSideProps() {} +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.stderr new file mode 100644 index 000000000000..eece7921e79a --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-server-side-props/output.stderr @@ -0,0 +1,6 @@ + + x NEXT_RSC_ERR_INVALID_API: getServerSideProps + ,-[input.js:1:1] + 1 | export function getServerSideProps (){ + : ^^^^^^^^^^^^^^^^^^ + `---- diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/input.js b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/input.js new file mode 100644 index 000000000000..7b8b6b06871d --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/input.js @@ -0,0 +1,6 @@ +export function getStaticProps (){ +} + +export default function () { + return null; +} diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.js b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.js new file mode 100644 index 000000000000..26b9fcd3f86b --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.js @@ -0,0 +1,4 @@ +export function getStaticProps() {} +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.stderr new file mode 100644 index 000000000000..536cab2cb8da --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/get-static-props/output.stderr @@ -0,0 +1,6 @@ + + x NEXT_RSC_ERR_INVALID_API: getStaticProps + ,-[input.js:1:1] + 1 | export function getStaticProps (){ + : ^^^^^^^^^^^^^^ + `---- diff --git a/packages/next/build/webpack/plugins/wellknown-errors-plugin/parseRSC.ts b/packages/next/build/webpack/plugins/wellknown-errors-plugin/parseRSC.ts index 2fd483002860..21ff31b30b95 100644 --- a/packages/next/build/webpack/plugins/wellknown-errors-plugin/parseRSC.ts +++ b/packages/next/build/webpack/plugins/wellknown-errors-plugin/parseRSC.ts @@ -16,6 +16,7 @@ function formatRSCErrorMessage(message: string): null | [string, string] { const NEXT_RSC_ERR_CLIENT_DIRECTIVE = /.+NEXT_RSC_ERR_CLIENT_DIRECTIVE\n/s const NEXT_RSC_ERR_CLIENT_DIRECTIVE_PAREN = /.+NEXT_RSC_ERR_CLIENT_DIRECTIVE_PAREN\n/s + const NEXT_RSC_ERR_INVALID_API = /.+NEXT_RSC_ERR_INVALID_API: (.*?)\n/s if (NEXT_RSC_ERR_REACT_API.test(message)) { formattedMessage = message.replace( @@ -62,6 +63,12 @@ function formatRSCErrorMessage(message: string): null | [string, string] { `\n\n"use client" must be a directive, and placed before other expressions. Remove the parentheses and move it to the top of the file to resolve this issue.\n\n` ) formattedVerboseMessage = '\n\nImport path:\n' + } else if (NEXT_RSC_ERR_INVALID_API.test(message)) { + formattedMessage = message.replace( + NEXT_RSC_ERR_INVALID_API, + `\n\n"$1" is not supported in app/. Read more: https://beta.nextjs.org/docs/data-fetching/fundamentals\n\n` + ) + formattedVerboseMessage = '\n\nFile path:\n' } return [formattedMessage, formattedVerboseMessage] diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 9c2723e8ec50..cfa39d4708cf 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -1127,20 +1127,6 @@ export async function renderToHTMLOrFlight( } } - // TODO-APP: move these errors to the loader instead? - // we will also need a migration doc here to link to - if (typeof layoutOrPageMod?.getServerSideProps === 'function') { - throw new Error( - `getServerSideProps is not supported in app/, detected in ${segment}` - ) - } - - if (typeof layoutOrPageMod?.getStaticProps === 'function') { - throw new Error( - `getStaticProps is not supported in app/, detected in ${segment}` - ) - } - /** * The React Component to render. */ diff --git a/test/e2e/app-dir/rsc-errors.test.ts b/test/e2e/app-dir/rsc-errors.test.ts index 75bd36be8f57..5c60ec3b0be9 100644 --- a/test/e2e/app-dir/rsc-errors.test.ts +++ b/test/e2e/app-dir/rsc-errors.test.ts @@ -52,7 +52,7 @@ describe('app dir - rsc errors', () => { expect(res.status).toBe(500) expect(await res.text()).toContain( - '`getServerSideProps` is not allowed in Client Components' + '"getServerSideProps\\" is not supported in app/' ) }) @@ -79,7 +79,7 @@ describe('app dir - rsc errors', () => { expect(res.status).toBe(500) expect(await res.text()).toContain( - '`getStaticProps` is not allowed in Client Components' + '"getStaticProps\\" is not supported in app/' ) })