diff --git a/.eslintrc.json b/.eslintrc.json index a294358bdefbd..8c46d8c02736f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,8 @@ "browser": true, "commonjs": true, "es6": true, - "node": true + "node": true, + "jest": true }, "parserOptions": { "requireConfigFile": false, diff --git a/jest.config.js b/jest.config.js index b2a36e9323338..9646d6686bd57 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,13 @@ const createJestConfig = nextJest() // Any custom config you want to pass to Jest /** @type {import('jest').Config} */ const customJestConfig = { - testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'], + testMatch: [ + '**/*.test.js', + '**/*.test.ts', + '**/*.test.tsx', + '**/jest/**/*.test.jsx', + '**/jest/**/*.test.tsx', + ], setupFilesAfterEnv: ['/jest-setup-after-env.ts'], verbose: true, rootDir: 'test', diff --git a/package.json b/package.json index efbaec07b4229..8cd2092a7d593 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "@swc/cli": "0.1.55", "@swc/core": "1.3.55", "@swc/helpers": "0.5.1", + "@testing-library/jest-dom": "6.1.2", "@testing-library/react": "13.0.0", "@types/cheerio": "0.22.16", "@types/cookie": "0.3.3", diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index 24ec042918138..25ab91d72f896 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -95,6 +95,9 @@ pub struct TransformOptions { #[serde(default)] pub is_server: bool, + #[serde(default)] + pub disable_checks: bool, + #[serde(default)] pub server_components: Option, @@ -190,7 +193,8 @@ where file.name.clone(), config.clone(), comments.clone(), - opts.app_dir.clone() + opts.app_dir.clone(), + opts.disable_checks )), _ => Either::Right(noop()), }, 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 1435e38eafa3c..5165d13fdf4bc 100644 --- a/packages/next-swc/crates/core/src/react_server_components.rs +++ b/packages/next-swc/crates/core/src/react_server_components.rs @@ -50,6 +50,7 @@ struct ReactServerComponents { invalid_client_imports: Vec, invalid_server_react_apis: Vec, invalid_server_react_dom_apis: Vec, + disable_checks: bool, } struct ModuleImports { @@ -128,7 +129,7 @@ impl ReactServerComponents { if is_action_file { panic_both_directives(expr_stmt.span) } - } else { + } else if !self.disable_checks { HANDLER.with(|handler| { handler .struct_span_err( @@ -333,6 +334,9 @@ impl ReactServerComponents { } fn assert_server_graph(&self, imports: &[ModuleImports], module: &Module) { + if self.disable_checks { + return; + } for import in imports { let source = import.source.0.clone(); if self.invalid_server_imports.contains(&source) { @@ -405,6 +409,9 @@ impl ReactServerComponents { } fn assert_client_graph(&self, imports: &[ModuleImports], module: &Module) { + if self.disable_checks { + return; + } for import in imports { let source = import.source.0.clone(); if self.invalid_client_imports.contains(&source) { @@ -560,12 +567,14 @@ pub fn server_components( config: Config, comments: C, app_dir: Option, + disable_checks: bool, ) -> impl Fold + VisitMut { - let is_server: bool = match config { + let is_server: bool = match &config { Config::WithOptions(x) => x.is_server, _ => true, }; as_folder(ReactServerComponents { + disable_checks, is_server, comments, filepath: filename.to_string(), diff --git a/packages/next-swc/crates/core/tests/errors.rs b/packages/next-swc/crates/core/tests/errors.rs index 3333b76116c94..0de686177a78a 100644 --- a/packages/next-swc/crates/core/tests/errors.rs +++ b/packages/next-swc/crates/core/tests/errors.rs @@ -97,6 +97,7 @@ fn react_server_components_server_graph_errors(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false, ) }, &input, @@ -121,6 +122,7 @@ fn react_server_components_client_graph_errors(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false, ) }, &input, @@ -167,6 +169,7 @@ fn react_server_actions_server_errors(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false ), server_actions( &FileName::Real("/app/item.js".into()), @@ -202,6 +205,7 @@ fn react_server_actions_client_errors(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false ), server_actions( &FileName::Real("/app/item.js".into()), diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index bb2a627ae4cc6..418e2c480206a 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -325,6 +325,29 @@ fn react_server_components_server_graph_fixture(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false, + ) + }, + &input, + &output, + Default::default(), + ); +} + +#[fixture("tests/fixture/react-server-components/server-graph-no-checks/**/input.js")] +fn react_server_components_no_checks_server_graph_fixture(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); + test_fixture( + syntax(), + &|tr| { + server_components( + FileName::Real(PathBuf::from("/some-project/src/some-file.js")), + next_swc::react_server_components::Config::WithOptions( + next_swc::react_server_components::Options { is_server: true }, + ), + tr.comments.as_ref().clone(), + None, + true, ) }, &input, @@ -346,6 +369,29 @@ fn react_server_components_client_graph_fixture(input: PathBuf) { ), tr.comments.as_ref().clone(), None, + false, + ) + }, + &input, + &output, + Default::default(), + ); +} + +#[fixture("tests/fixture/react-server-components/client-graph-no-checks/**/input.js")] +fn react_server_components_no_checks_client_graph_fixture(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); + test_fixture( + syntax(), + &|tr| { + server_components( + FileName::Real(PathBuf::from("/some-project/src/some-file.js")), + next_swc::react_server_components::Config::WithOptions( + next_swc::react_server_components::Options { is_server: false }, + ), + tr.comments.as_ref().clone(), + None, + true, ) }, &input, diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/input.js new file mode 100644 index 0000000000000..e48a0d016732e --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/input.js @@ -0,0 +1,13 @@ +// This is a comment. + +'use strict' + +/** + * This is a comment. + */ + +import 'client-only' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/output.js new file mode 100644 index 0000000000000..3ac3698ea087b --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/client-only/output.js @@ -0,0 +1,8 @@ +// This is a comment. +'use strict'; +/** + * This is a comment. + */ import 'client-only'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-server-side-props/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-server-side-props/input.js new file mode 100644 index 0000000000000..4b448f2031e4d --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-server-side-props/input.js @@ -0,0 +1,5 @@ +export function getServerSideProps() {} + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-server-side-props/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-server-side-props/output.js new file mode 100644 index 0000000000000..c2a54dc35918f --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/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/fixture/react-server-components/client-graph-no-checks/get-static-props/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-static-props/input.js new file mode 100644 index 0000000000000..2f629e3a152b2 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-static-props/input.js @@ -0,0 +1,5 @@ +export function getStaticProps() {} + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-static-props/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/get-static-props/output.js new file mode 100644 index 0000000000000..26b9fcd3f86bc --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/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/fixture/react-server-components/client-graph-no-checks/server-only/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/server-only/input.js new file mode 100644 index 0000000000000..3abc20f8f6510 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/server-only/input.js @@ -0,0 +1,13 @@ +// This is a comment. + +'use strict' + +/** + * This is a comment. + */ + +import 'server-only' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/server-only/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/server-only/output.js new file mode 100644 index 0000000000000..4766155152a11 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/server-only/output.js @@ -0,0 +1,8 @@ +// This is a comment. +'use strict'; +/** + * This is a comment. + */ import 'server-only'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/input.js new file mode 100644 index 0000000000000..40c4c830b0a34 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/input.js @@ -0,0 +1,8 @@ +import 'react' + +// prettier-ignore +'use client' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/output.js new file mode 100644 index 0000000000000..bc702e4c65f04 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/client-graph-no-checks/use-client/output.js @@ -0,0 +1,4 @@ +import 'react'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/input.js new file mode 100644 index 0000000000000..e48a0d016732e --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/input.js @@ -0,0 +1,13 @@ +// This is a comment. + +'use strict' + +/** + * This is a comment. + */ + +import 'client-only' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/output.js new file mode 100644 index 0000000000000..3ac3698ea087b --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/client-only/output.js @@ -0,0 +1,8 @@ +// This is a comment. +'use strict'; +/** + * This is a comment. + */ import 'client-only'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/input.js new file mode 100644 index 0000000000000..43efcd92752b6 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/input.js @@ -0,0 +1,6 @@ +export default function () { + return null +} + +// prettier-ignore +'use client' diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/output.js new file mode 100644 index 0000000000000..33997bb6e2495 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/fake-client-entry/output.js @@ -0,0 +1,3 @@ +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-server-side-props/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-server-side-props/input.js new file mode 100644 index 0000000000000..4b448f2031e4d --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-server-side-props/input.js @@ -0,0 +1,5 @@ +export function getServerSideProps() {} + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-server-side-props/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-server-side-props/output.js new file mode 100644 index 0000000000000..c2a54dc35918f --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/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/fixture/react-server-components/server-graph-no-checks/get-static-props/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-static-props/input.js new file mode 100644 index 0000000000000..2f629e3a152b2 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-static-props/input.js @@ -0,0 +1,5 @@ +export function getStaticProps() {} + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-static-props/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/get-static-props/output.js new file mode 100644 index 0000000000000..26b9fcd3f86bc --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/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/fixture/react-server-components/server-graph-no-checks/react-api/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-api/input.js new file mode 100644 index 0000000000000..bec5b9683a33c --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-api/input.js @@ -0,0 +1,21 @@ +import { useState } from 'react' + +import { createContext } from 'react' + +import { useEffect, useImperativeHandle } from 'react' + +import { + Component, + createFactory, + PureComponent, + useDeferredValue, + useInsertionEffect, + useLayoutEffect, + useReducer, + useRef, + useSyncExternalStore, +} from 'react' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-api/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-api/output.js new file mode 100644 index 0000000000000..1a8c93cd6e0fc --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-api/output.js @@ -0,0 +1,7 @@ +import { useState } from 'react'; +import { createContext } from 'react'; +import { useEffect, useImperativeHandle } from 'react'; +import { Component, createFactory, PureComponent, useDeferredValue, useInsertionEffect, useLayoutEffect, useReducer, useRef, useSyncExternalStore } from 'react'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/input.js new file mode 100644 index 0000000000000..722799c241272 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/input.js @@ -0,0 +1,10 @@ +import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom' + +import { + experimental_useOptimistic as useOptimistic, + experimental_useFormStatus, +} from 'react-dom' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/output.js new file mode 100644 index 0000000000000..d7bf00f6bd08a --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-api/output.js @@ -0,0 +1,5 @@ +import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom'; +import { experimental_useOptimistic as useOptimistic, experimental_useFormStatus } from 'react-dom'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/input.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/input.js new file mode 100644 index 0000000000000..8bf5402ee91e3 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/input.js @@ -0,0 +1,15 @@ +// This is a comment. + +'use strict' + +/** + * This is a comment. + */ + +import 'react-dom/server' + +import 'react-dom/client' + +export default function () { + return null +} diff --git a/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/output.js b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/output.js new file mode 100644 index 0000000000000..700e09c25e187 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/react-server-components/server-graph-no-checks/react-dom-server-client/output.js @@ -0,0 +1,9 @@ +// This is a comment. +'use strict'; +/** + * This is a comment. + */ import 'react-dom/server'; +import 'react-dom/client'; +export default function() { + return null; +} diff --git a/packages/next-swc/crates/core/tests/full.rs b/packages/next-swc/crates/core/tests/full.rs index 92af9032f1e90..3e95881ca0055 100644 --- a/packages/next-swc/crates/core/tests/full.rs +++ b/packages/next-swc/crates/core/tests/full.rs @@ -80,6 +80,7 @@ fn test(input: &Path, minify: bool) { cjs_require_optimizer: None, auto_modularize_imports: None, optimize_barrel_exports: None, + disable_checks: false, }; let unresolved_mark = Mark::new(); diff --git a/packages/next/package.json b/packages/next/package.json index 4ab5309570331..cfaa8fea317e9 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -243,7 +243,6 @@ "image-size": "1.0.0", "is-docker": "2.0.0", "is-wsl": "2.2.0", - "jest-docblock": "29.4.3", "jest-worker": "27.0.0-next.5", "json5": "2.2.3", "jsonwebtoken": "9.0.0", diff --git a/packages/next/src/build/swc/jest-transformer.ts b/packages/next/src/build/swc/jest-transformer.ts index d269113948866..3ab9cf8d9d8fd 100644 --- a/packages/next/src/build/swc/jest-transformer.ts +++ b/packages/next/src/build/swc/jest-transformer.ts @@ -29,7 +29,6 @@ DEALINGS IN THE SOFTWARE. import vm from 'vm' import { transformSync } from './index' import { getJestSWCOptions } from './options' -import * as docblock from 'next/dist/compiled/jest-docblock' import type { TransformerCreator, TransformOptions, @@ -77,30 +76,14 @@ function isEsm( ) } -function getTestEnvironment( - src: string, - jestConfig: Config.ProjectConfig -): string { - const docblockPragmas = docblock.parse(docblock.extract(src)) - const pragma = docblockPragmas['jest-environment'] - const environment = - (Array.isArray(pragma) ? pragma[0] : pragma) ?? jestConfig.testEnvironment - return environment -} - const createTransformer: TransformerCreator< SyncTransformer, JestTransformerConfig > = (inputOptions) => ({ process(src, filename, jestOptions) { const jestConfig = getJestConfig(jestOptions) - const testEnvironment = getTestEnvironment(src, jestConfig) const swcTransformOpts = getJestSWCOptions({ - // When target is node it's similar to the server option set in SWC. - isServer: - testEnvironment === 'node' || - testEnvironment.includes('jest-environment-node'), filename, jsConfig: inputOptions?.jsConfig, resolvedBaseUrl: inputOptions?.resolvedBaseUrl, diff --git a/packages/next/src/build/swc/options.ts b/packages/next/src/build/swc/options.ts index 38e0fa1df3c72..7cda8ec614236 100644 --- a/packages/next/src/build/swc/options.ts +++ b/packages/next/src/build/swc/options.ts @@ -183,6 +183,7 @@ function getBaseSWCOptions({ isServer: !!isServerLayer, } : undefined, + disableChecks: false, } } @@ -236,7 +237,6 @@ function getEmotionOptions( } export function getJestSWCOptions({ - isServer, filename, esm, modularizeImports, @@ -247,7 +247,6 @@ export function getJestSWCOptions({ pagesDir, hasServerComponents, }: { - isServer: boolean filename: string esm: boolean modularizeImports?: NextConfig['modularizeImports'] @@ -263,20 +262,20 @@ export function getJestSWCOptions({ jest: true, development: false, hasReactRefresh: false, - globalWindow: !isServer, + globalWindow: true, modularizeImports, swcPlugins, compilerOptions, jsConfig, hasServerComponents, resolvedBaseUrl, - isServerLayer: isServer, }) const isNextDist = nextDistPath.test(filename) return { ...baseOptions, + disableChecks: true, env: { targets: { // Targets the current version of Node.js diff --git a/packages/next/src/compiled/jest-docblock/LICENSE b/packages/next/src/compiled/jest-docblock/LICENSE deleted file mode 100644 index b93be90515ccd..0000000000000 --- a/packages/next/src/compiled/jest-docblock/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) Meta Platforms, Inc. and affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/next/src/compiled/jest-docblock/index.js b/packages/next/src/compiled/jest-docblock/index.js deleted file mode 100644 index 240ca16b9aac3..0000000000000 --- a/packages/next/src/compiled/jest-docblock/index.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e={381:e=>{const detectNewline=e=>{if(typeof e!=="string"){throw new TypeError("Expected a string")}const t=e.match(/(?:\r?\n)/g)||[];if(t.length===0){return}const r=t.filter((e=>e==="\r\n")).length;const n=t.length-r;return r>n?"\r\n":"\n"};e.exports=detectNewline;e.exports.graceful=e=>typeof e==="string"&&detectNewline(e)||"\n"},37:e=>{e.exports=require("os")}};var t={};function __nccwpck_require__(r){var n=t[r];if(n!==undefined){return n.exports}var s=t[r]={exports:{}};var c=true;try{e[r](s,s.exports,__nccwpck_require__);c=false}finally{if(c)delete t[r]}return s.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:true});e.extract=extract;e.parse=parse;e.parseWithComments=parseWithComments;e.print=print;e.strip=strip;function _os(){const e=__nccwpck_require__(37);_os=function(){return e};return e}function _detectNewline(){const e=_interopRequireDefault(__nccwpck_require__(381));_detectNewline=function(){return e};return e}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const t=/\*\/$/;const n=/^\/\*\*?/;const s=/^\s*(\/\*\*?(.|\r?\n)*?\*\/)/;const c=/(^|\s+)\/\/([^\r\n]*)/g;const o=/^(\r?\n)+/;const i=/(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *(?![^@\r\n]*\/\/[^]*)([^@\r\n\s][^@\r\n]+?) *\r?\n/g;const a=/(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g;const u=/(\r?\n|^) *\* ?/g;const p=[];function extract(e){const t=e.match(s);return t?t[0].trimLeft():""}function strip(e){const t=e.match(s);return t&&t[0]?e.substring(t[0].length):e}function parse(e){return parseWithComments(e).pragmas}function parseWithComments(e){const r=(0,_detectNewline().default)(e)??_os().EOL;e=e.replace(n,"").replace(t,"").replace(u,"$1");let s="";while(s!==e){s=e;e=e.replace(i,`${r}$1 $2${r}`)}e=e.replace(o,"").trimRight();const _=Object.create(null);const l=e.replace(a,"").replace(o,"").trimRight();let f;while(f=a.exec(e)){const e=f[2].replace(c,"");if(typeof _[f[1]]==="string"||Array.isArray(_[f[1]])){_[f[1]]=p.concat(_[f[1]],e)}else{_[f[1]]=e}}return{comments:l,pragmas:_}}function print({comments:e="",pragmas:t={}}){const r=(0,_detectNewline().default)(e)??_os().EOL;const n="/**";const s=" *";const c=" */";const o=Object.keys(t);const i=o.map((e=>printKeyValues(e,t[e]))).reduce(((e,t)=>e.concat(t)),[]).map((e=>`${s} ${e}${r}`)).join("");if(!e){if(o.length===0){return""}if(o.length===1&&!Array.isArray(t[o[0]])){const e=t[o[0]];return`${n} ${printKeyValues(o[0],e)[0]}${c}`}}const a=e.split(r).map((e=>`${s} ${e}`)).join(r)+r;return n+r+(e?a:"")+(e&&o.length?s+r:"")+i+c}function printKeyValues(e,t){return p.concat(t).map((t=>`@${e} ${t}`.trim()))}})();module.exports=r})(); \ No newline at end of file diff --git a/packages/next/src/compiled/jest-docblock/package.json b/packages/next/src/compiled/jest-docblock/package.json deleted file mode 100644 index 78bc406c0d040..0000000000000 --- a/packages/next/src/compiled/jest-docblock/package.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"jest-docblock","main":"index.js","license":"MIT"} diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 495da5ef09467..f38289c3d4984 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2195,15 +2195,6 @@ export async function ncc_https_proxy_agent(task, opts) { .target('src/compiled/https-proxy-agent') } -// eslint-disable-next-line camelcase -externals['jest-docblock'] = 'next/dist/compiled/jest-docblock' -export async function ncc_jest_docblock(task, opts) { - await task - .source(relative(__dirname, require.resolve('jest-docblock'))) - .ncc({ packageName: 'jest-docblock', externals }) - .target('src/compiled/jest-docblock') -} - export async function precompile(task, opts) { await task.parallel( [ @@ -2338,7 +2329,6 @@ export async function ncc(task, opts) { 'ncc_opentelemetry_api', 'ncc_http_proxy_agent', 'ncc_https_proxy_agent', - 'ncc_jest_docblock', 'ncc_mini_css_extract_plugin', ], opts diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts index 61a33ebfe9529..b22d2bd2957ee 100644 --- a/packages/next/types/misc.d.ts +++ b/packages/next/types/misc.d.ts @@ -459,8 +459,3 @@ declare module 'next/dist/compiled/@opentelemetry/api' { import * as m from '@opentelemetry/api' export = m } - -declare module 'next/dist/compiled/jest-docblock' { - import m from 'jest-docblock' - export = m -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c37cbf03e5048..bb0141c847b9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,6 +107,9 @@ importers: '@swc/helpers': specifier: 0.5.1 version: 0.5.1 + '@testing-library/jest-dom': + specifier: 6.1.2 + version: 6.1.2(@types/jest@27.4.1)(jest@27.0.6) '@testing-library/react': specifier: 13.0.0 version: 13.0.0(react-dom@18.2.0)(react@18.2.0) @@ -1210,9 +1213,6 @@ importers: is-wsl: specifier: 2.2.0 version: 2.2.0 - jest-docblock: - specifier: 29.4.3 - version: 29.4.3 jest-worker: specifier: 27.0.0-next.5 version: 27.0.0-next.5 @@ -1696,6 +1696,10 @@ importers: packages: + /@adobe/css-tools@4.3.1: + resolution: {integrity: sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==} + dev: true + /@ampproject/remapping@2.1.2: resolution: {integrity: sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==} engines: {node: '>=6.0.0'} @@ -4961,7 +4965,7 @@ packages: resolution: {integrity: sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==} engines: {node: '>=6.9.0'} dependencies: - regenerator-runtime: 0.13.5 + regenerator-runtime: 0.13.4 dev: true /@babel/runtime@7.22.3: @@ -7911,15 +7915,45 @@ packages: engines: {node: '>=12'} dependencies: '@babel/code-frame': 7.12.11 - '@babel/runtime': 7.16.7 + '@babel/runtime': 7.15.4 '@types/aria-query': 5.0.1 - aria-query: 5.0.0 + aria-query: 5.3.0 chalk: 4.1.2 dom-accessibility-api: 0.5.13 lz-string: 1.5.0 pretty-format: 27.5.1 dev: true + /@testing-library/jest-dom@6.1.2(@types/jest@27.4.1)(jest@27.0.6): + resolution: {integrity: sha512-NP9jl1Q2qDDtx+cqogowtQtmgD2OVs37iMSIsTv5eN5ETRkf26Kj6ugVwA93/gZzzFWQAsgkKkcftDe91BJCkQ==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@jest/globals': '>= 28' + '@types/jest': '>= 28' + jest: '>= 28' + vitest: '>= 0.32' + peerDependenciesMeta: + '@jest/globals': + optional: true + '@types/jest': + optional: true + jest: + optional: true + vitest: + optional: true + dependencies: + '@adobe/css-tools': 4.3.1 + '@babel/runtime': 7.15.4 + '@types/jest': 27.4.1 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.5.13 + jest: 27.0.6 + lodash: 4.17.20 + redent: 3.0.0 + dev: true + /@testing-library/react@13.0.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-p0lYA1M7uoEmk2LnCbZLGmHJHyH59sAaZVXChTXlyhV/PRW9LoIh4mdf7tiXsO8BoNG+vN8UnFJff1hbZeXv+w==} engines: {node: '>=12'} @@ -9409,16 +9443,10 @@ packages: mri: 1.1.0 dev: true - /aria-query@5.0.0: - resolution: {integrity: sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==} - engines: {node: '>=6.0'} - dev: true - /aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} dependencies: dequal: 2.0.3 - dev: false /arr-diff@4.0.0: resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} @@ -16795,13 +16823,6 @@ packages: detect-newline: 3.1.0 dev: true - /jest-docblock@29.4.3: - resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - /jest-each@27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} diff --git a/test/jest-setup-after-env.ts b/test/jest-setup-after-env.ts index a5cfd5cfdd4bb..a1e63f8daa96e 100644 --- a/test/jest-setup-after-env.ts +++ b/test/jest-setup-after-env.ts @@ -1,5 +1,6 @@ // @ts-ignore import * as matchers from 'jest-extended' +import '@testing-library/jest-dom' expect.extend(matchers) // A default max-timeout of 90 seconds is allowed diff --git a/test/production/jest/rsc/app/[blog]/page.jsx b/test/production/jest/rsc/app/[blog]/page.jsx new file mode 100644 index 0000000000000..dc5dd0e05a495 --- /dev/null +++ b/test/production/jest/rsc/app/[blog]/page.jsx @@ -0,0 +1,7 @@ +export async function generateMetadata({ params: { blog: title } }) { + return { title, description: 'A blog post about ' + title } +} + +export default function Page({ params }) { + return

All about {params.blog}

+} diff --git a/test/production/jest/rsc/app/[blog]/page.test.jsx b/test/production/jest/rsc/app/[blog]/page.test.jsx new file mode 100644 index 0000000000000..b1599b5753efa --- /dev/null +++ b/test/production/jest/rsc/app/[blog]/page.test.jsx @@ -0,0 +1,11 @@ +/** + * @jest-environment jsdom + */ +import { render, screen } from '@testing-library/react' +import '@testing-library/jest-dom' +import Page from './page' + +it('works with code using Next server APIs', () => { + render() + expect(screen.getByRole('heading')).toHaveTextContent('All about Jane') +}) diff --git a/test/production/jest/rsc/app/client-component.jsx b/test/production/jest/rsc/app/client-component.jsx new file mode 100644 index 0000000000000..2cc542f6f39c5 --- /dev/null +++ b/test/production/jest/rsc/app/client-component.jsx @@ -0,0 +1,5 @@ +'use client' + +export default function Counter() { + return

Hello

+} diff --git a/test/production/jest/rsc/app/client-component.test.jsx b/test/production/jest/rsc/app/client-component.test.jsx new file mode 100644 index 0000000000000..ed8995692550a --- /dev/null +++ b/test/production/jest/rsc/app/client-component.test.jsx @@ -0,0 +1,10 @@ +/** + * @jest-environment jsdom + */ +import { render, screen } from '@testing-library/react' +import Component from './client-component' + +it('works with client-only code', () => { + render() + expect(screen.getByRole('heading')).toHaveTextContent('Hello') +}) diff --git a/test/production/jest/rsc/app/component.jsx b/test/production/jest/rsc/app/component.jsx new file mode 100644 index 0000000000000..8ab5ce04e4f43 --- /dev/null +++ b/test/production/jest/rsc/app/component.jsx @@ -0,0 +1,12 @@ +import { useState } from 'react' +export default function Counter() { + const [count, setCount] = useState(0) + return ( + <> +

{count}

+ + + ) +} diff --git a/test/production/jest/rsc/app/component.test.jsx b/test/production/jest/rsc/app/component.test.jsx new file mode 100644 index 0000000000000..fb1c99458b843 --- /dev/null +++ b/test/production/jest/rsc/app/component.test.jsx @@ -0,0 +1,12 @@ +/** + * @jest-environment jsdom + */ +import { fireEvent, render, screen } from '@testing-library/react' +import Component from './component' + +it('works with client-only code', () => { + render() + expect(screen.getByRole('heading')).toHaveTextContent('0') + fireEvent.click(screen.getByRole('button')) + expect(screen.getByRole('heading')).toHaveTextContent('1') +}) diff --git a/test/production/jest/rsc/app/layout.jsx b/test/production/jest/rsc/app/layout.jsx new file mode 100644 index 0000000000000..4ee00a218505a --- /dev/null +++ b/test/production/jest/rsc/app/layout.jsx @@ -0,0 +1,7 @@ +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} diff --git a/test/production/jest/rsc/app/page.jsx b/test/production/jest/rsc/app/page.jsx new file mode 100644 index 0000000000000..5bcc92279f5cf --- /dev/null +++ b/test/production/jest/rsc/app/page.jsx @@ -0,0 +1,4 @@ +import { PI } from '../lib/utils' +export default function Home() { + return

{PI}

+} diff --git a/test/production/jest/rsc/app/page.test.jsx b/test/production/jest/rsc/app/page.test.jsx new file mode 100644 index 0000000000000..5cf851bf3100b --- /dev/null +++ b/test/production/jest/rsc/app/page.test.jsx @@ -0,0 +1,10 @@ +/** + * @jest-environment jsdom + */ +import { render, screen } from '@testing-library/react' +import Page from './page' + +it('works with server-only imported code', () => { + render() + expect(screen.getByRole('heading')).toHaveTextContent('3.14') +}) diff --git a/test/production/jest/rsc/lib/utils.js b/test/production/jest/rsc/lib/utils.js new file mode 100644 index 0000000000000..5a371aaa010db --- /dev/null +++ b/test/production/jest/rsc/lib/utils.js @@ -0,0 +1,2 @@ +import 'server-only' +export const PI = 3.14 diff --git a/test/production/jest/rsc/lib/utils.test.js b/test/production/jest/rsc/lib/utils.test.js new file mode 100644 index 0000000000000..3dff82b949157 --- /dev/null +++ b/test/production/jest/rsc/lib/utils.test.js @@ -0,0 +1,5 @@ +import { PI } from './utils' + +it('works with server-only code', () => { + expect(PI).toEqual(3.14) +}) diff --git a/test/production/jest/server-only.test.ts b/test/production/jest/server-only.test.ts deleted file mode 100644 index 8cff3b170cfff..0000000000000 --- a/test/production/jest/server-only.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' - -describe('next/jest', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - skipStart: true, - files: { - 'app/page.jsx': `import { PI } from '../lib/util' - export default function Home() { - return

{PI}

- }`, - 'app/layout.jsx': `export default function RootLayout({ children }) { - return ( - - {children} - - ) - }`, - 'app/page.test.jsx': `import { render, screen } from '@testing-library/react' - import '@testing-library/jest-dom' - import Page from './page' - - it('works from client-side code', () => { - render() - expect(screen.getByRole('heading')).toHaveTextContent('3.14') - })`, - 'lib/util.js': `/** @jest-environment node */ - import 'server-only' - export const PI = 3.14;`, - 'lib/utils.test.ts': `import { PI } from './util' - it('works from server-side code', () => { - expect(PI).toEqual(3.14) - })`, - 'jest.config.js': `module.exports = require('next/jest')({ dir: './' })({ testEnvironment: 'jsdom' })`, - }, - buildCommand: `yarn jest`, - dependencies: { - '@types/react': 'latest', - '@testing-library/jest-dom': '5.16.5', - '@testing-library/react': '13.0.0', - jest: '27.4.7', - }, - }) - }) - - afterAll(() => next.destroy()) - - it('can run test against server side components', async () => { - try { - await next.start() - } finally { - expect(next.cliOutput).toInclude('Tests: 2 passed, 2 total') - } - }) -})