diff --git a/package.json b/package.json index 019ab72308b51..a1beb68feb661 100644 --- a/package.json +++ b/package.json @@ -179,8 +179,8 @@ "react-17": "npm:react@17.0.2", "react-dom": "18.2.0", "react-dom-17": "npm:react-dom@17.0.2", - "react-dom-exp": "npm:react-dom@0.0.0-experimental-e6a062bd2-20220913", - "react-exp": "npm:react@0.0.0-experimental-e6a062bd2-20220913", + "react-dom-exp": "npm:react-dom@0.0.0-experimental-8951c5fc9-20220915", + "react-exp": "npm:react@0.0.0-experimental-8951c5fc9-20220915", "react-ssr-prepass": "1.0.8", "react-virtualized": "9.22.3", "relay-compiler": "13.0.2", @@ -207,7 +207,7 @@ }, "resolutions": { "browserslist": "4.20.2", - "caniuse-lite": "1.0.30001332", + "caniuse-lite": "1.0.30001406", "@babel/core": "7.18.0", "@babel/parser": "7.18.0", "@babel/types": "7.18.0", 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 b172ea47fc4ed..af5e959f2c6e1 100644 --- a/packages/next-swc/crates/core/src/react_server_components.rs +++ b/packages/next-swc/crates/core/src/react_server_components.rs @@ -1,3 +1,4 @@ +use regex::Regex; use serde::Deserialize; use swc_core::{ @@ -63,7 +64,7 @@ impl VisitMut for ReactServerComponents { return; } } else { - self.assert_client_graph(&imports); + self.assert_client_graph(&imports, module); } module.visit_mut_children_with(self) } @@ -226,11 +227,7 @@ impl ReactServerComponents { handler .struct_span_err( import.source.1, - format!( - "Disallowed import of `{}` in the Server Components compilation.", - source - ) - .as_str(), + format!("NEXT_RSC_ERR_SERVER_IMPORT: {}", source).as_str(), ) .emit() }) @@ -242,12 +239,7 @@ impl ReactServerComponents { handler .struct_span_err( specifier.1, - format!( - "Disallowed React API `{}` in the Server Components \ - compilation.", - &specifier.0 - ) - .as_str(), + format!("NEXT_RSC_ERR_REACT_API: {}", &specifier.0).as_str(), ) .emit() }) @@ -261,12 +253,7 @@ impl ReactServerComponents { handler .struct_span_err( specifier.1, - format!( - "Disallowed ReactDOM API `{}` in the Server Components \ - compilation.", - &specifier.0 - ) - .as_str(), + format!("NEXT_RSC_ERR_REACT_API: {}", &specifier.0).as_str(), ) .emit() }) @@ -276,7 +263,7 @@ impl ReactServerComponents { } } - fn assert_client_graph(&self, imports: &Vec) { + fn assert_client_graph(&self, imports: &Vec, module: &Module) { for import in imports { let source = import.source.0.clone(); if self.invalid_client_imports.contains(&source) { @@ -284,9 +271,103 @@ impl ReactServerComponents { handler .struct_span_err( import.source.1, + format!("NEXT_RSC_ERR_CLIENT_IMPORT: {}", source).as_str(), + ) + .emit() + }) + } + } + + // 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; + let mut has_get_static_props = false; + + 'matcher: for export in &module.body { + match export { + ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)) => { + for specifier in &export.specifiers { + if let ExportSpecifier::Named(named) = specifier { + match &named.orig { + ModuleExportName::Ident(i) => { + if i.sym == *"getServerSideProps" { + has_get_server_side_props = true; + span = named.span; + break 'matcher; + } + if i.sym == *"getStaticProps" { + has_get_static_props = true; + span = named.span; + break 'matcher; + } + } + ModuleExportName::Str(s) => { + if s.value == *"getServerSideProps" { + has_get_server_side_props = true; + span = named.span; + break 'matcher; + } + if s.value == *"getStaticProps" { + has_get_static_props = true; + span = named.span; + break 'matcher; + } + } + } + } + } + } + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => match &export.decl { + Decl::Fn(f) => { + if f.ident.sym == *"getServerSideProps" { + has_get_server_side_props = true; + span = f.ident.span; + break 'matcher; + } + if f.ident.sym == *"getStaticProps" { + has_get_static_props = true; + span = f.ident.span; + break 'matcher; + } + } + Decl::Var(v) => { + for decl in &v.decls { + if let Pat::Ident(i) = &decl.name { + if i.sym == *"getServerSideProps" { + has_get_server_side_props = true; + span = i.span; + break 'matcher; + } + if i.sym == *"getStaticProps" { + has_get_static_props = true; + span = i.span; + break 'matcher; + } + } + } + } + _ => {} + }, + _ => {} + } + } + + if has_get_server_side_props || has_get_static_props { + HANDLER.with(|handler| { + handler + .struct_span_err( + span, format!( - "Disallowed import of `{}` in the Client Components compilation.", - source + "`{}` is not allowed in Client Components.", + if has_get_server_side_props { + "getServerSideProps" + } else { + "getStaticProps" + } ) .as_str(), ) diff --git a/packages/next-swc/crates/core/tests/errors.rs b/packages/next-swc/crates/core/tests/errors.rs index 3b6996807e5d1..5571406e6a22b 100644 --- a/packages/next-swc/crates/core/tests/errors.rs +++ b/packages/next-swc/crates/core/tests/errors.rs @@ -83,7 +83,7 @@ fn react_server_components_client_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/page.js")), next_swc::react_server_components::Config::WithOptions( next_swc::react_server_components::Options { is_server: false }, ), diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/input.js b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/input.js new file mode 100644 index 0000000000000..54ca0a245d2fd --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-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/client-graph/get-server-side-props/output.js b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.js new file mode 100644 index 0000000000000..c2a54dc35918f --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-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/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 new file mode 100644 index 0000000000000..8c649c3e39f8a --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-server-side-props/output.stderr @@ -0,0 +1,6 @@ + + x `getServerSideProps` is not allowed in Client Components. + ,-[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/input.js b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/input.js new file mode 100644 index 0000000000000..7b8b6b06871d5 --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-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/client-graph/get-static-props/output.js b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.js new file mode 100644 index 0000000000000..26b9fcd3f86bc --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-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/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 new file mode 100644 index 0000000000000..67cae2d6ee789 --- /dev/null +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/get-static-props/output.stderr @@ -0,0 +1,6 @@ + + x `getStaticProps` is not allowed in Client Components. + ,-[input.js:1:1] + 1 | export function getStaticProps (){ + : ^^^^^^^^^^^^^^ + `---- diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/server-only/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/server-only/output.stderr index 30b0a47ff4721..e0a757b819f4a 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/server-only/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/client-graph/server-only/output.stderr @@ -1,5 +1,5 @@ - x Disallowed import of `server-only` in the Client Components compilation. + x NEXT_RSC_ERR_CLIENT_IMPORT: server-only ,-[input.js:9:1] 9 | import "server-only" : ^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/client-only/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/client-only/output.stderr index f3d8e080827dc..72cebf17ca4e6 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/client-only/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/client-only/output.stderr @@ -1,5 +1,5 @@ - x Disallowed import of `client-only` in the Server Components compilation. + x NEXT_RSC_ERR_SERVER_IMPORT: client-only ,-[input.js:9:1] 9 | import "client-only" : ^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-api/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-api/output.stderr index dde1083903f3e..b6a27316de048 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-api/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-api/output.stderr @@ -1,77 +1,77 @@ - x Disallowed React API `useState` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useState ,-[input.js:1:1] 1 | import { useState } from 'react' : ^^^^^^^^ `---- - x Disallowed React API `createContext` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: createContext ,-[input.js:3:1] 3 | import { createContext } from 'react' : ^^^^^^^^^^^^^ `---- - x Disallowed React API `useEffect` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useEffect ,-[input.js:5:1] 5 | import { useEffect, useImperativeHandle } from 'react' : ^^^^^^^^^ `---- - x Disallowed React API `useImperativeHandle` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useImperativeHandle ,-[input.js:5:1] 5 | import { useEffect, useImperativeHandle } from 'react' : ^^^^^^^^^^^^^^^^^^^ `---- - x Disallowed React API `Component` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: Component ,-[input.js:8:5] 8 | Component, : ^^^^^^^^^ `---- - x Disallowed React API `createFactory` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: createFactory ,-[input.js:9:5] 9 | createFactory, : ^^^^^^^^^^^^^ `---- - x Disallowed React API `PureComponent` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: PureComponent ,-[input.js:10:5] 10 | PureComponent, : ^^^^^^^^^^^^^ `---- - x Disallowed React API `useDeferredValue` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useDeferredValue ,-[input.js:11:3] 11 | useDeferredValue, : ^^^^^^^^^^^^^^^^ `---- - x Disallowed React API `useInsertionEffect` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useInsertionEffect ,-[input.js:12:5] 12 | useInsertionEffect, : ^^^^^^^^^^^^^^^^^^ `---- - x Disallowed React API `useLayoutEffect` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useLayoutEffect ,-[input.js:13:5] 13 | useLayoutEffect, : ^^^^^^^^^^^^^^^ `---- - x Disallowed React API `useReducer` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useReducer ,-[input.js:14:5] 14 | useReducer, : ^^^^^^^^^^ `---- - x Disallowed React API `useRef` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useRef ,-[input.js:15:5] 15 | useRef, : ^^^^^^ `---- - x Disallowed React API `useSyncExternalStore` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: useSyncExternalStore ,-[input.js:16:5] 16 | useSyncExternalStore : ^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-api/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-api/output.stderr index 1e943500e0dd3..678b237104ea9 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-api/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-api/output.stderr @@ -1,17 +1,17 @@ - x Disallowed ReactDOM API `findDOMNode` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: findDOMNode ,-[input.js:2:5] 2 | findDOMNode, : ^^^^^^^^^^^ `---- - x Disallowed ReactDOM API `flushSync` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: flushSync ,-[input.js:3:3] 3 | flushSync, : ^^^^^^^^^ `---- - x Disallowed ReactDOM API `unstable_batchedUpdates` in the Server Components compilation. + x NEXT_RSC_ERR_REACT_API: unstable_batchedUpdates ,-[input.js:4:3] 4 | unstable_batchedUpdates, : ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-server-client/output.stderr b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-server-client/output.stderr index 8e4f211415c98..474c257d8840c 100644 --- a/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-server-client/output.stderr +++ b/packages/next-swc/crates/core/tests/errors/react-server-components/server-graph/react-dom-server-client/output.stderr @@ -1,11 +1,11 @@ - x Disallowed import of `react-dom/server` in the Server Components compilation. + x NEXT_RSC_ERR_SERVER_IMPORT: react-dom/server ,-[input.js:9:1] 9 | import "react-dom/server" : ^^^^^^^^^^^^^^^^^^^^^^^^^ `---- - x Disallowed import of `react-dom/client` in the Server Components compilation. + x NEXT_RSC_ERR_SERVER_IMPORT: react-dom/client ,-[input.js:11:1] 11 | import "react-dom/client" : ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index 1afb4e062538f..3e6c4de580e67 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -13,6 +13,7 @@ import { SERVER_RUNTIME } from '../../lib/constants' import { ServerRuntime } from 'next/types' import { checkCustomRoutes } from '../../lib/load-custom-routes' import { matcher } from 'next/dist/compiled/micromatch' +import { RSC_MODULE_TYPES } from '../../shared/lib/constants' export interface MiddlewareConfig { matchers: MiddlewareMatcher[] @@ -29,9 +30,18 @@ export interface PageStaticInfo { runtime?: ServerRuntime ssg?: boolean ssr?: boolean + rsc?: RSCModuleType middleware?: Partial } +const CLIENT_MODULE_LABEL = `/* __next_internal_client_entry_do_not_use__ */` +export type RSCModuleType = 'server' | 'client' +export function getRSCModuleType(source: string): RSCModuleType { + return source.includes(CLIENT_MODULE_LABEL) + ? RSC_MODULE_TYPES.client + : RSC_MODULE_TYPES.server +} + /** * Receives a parsed AST from SWC and checks if it belongs to a module that * requires a runtime to be specified. Those are: @@ -252,6 +262,7 @@ export async function getPageStaticInfo(params: { ) { const swcAST = await parseModule(pageFilePath, fileContent) const { ssg, ssr } = checkExports(swcAST) + const rsc = getRSCModuleType(fileContent) // default / failsafe value for config let config: any = {} @@ -303,10 +314,16 @@ export async function getPageStaticInfo(params: { return { ssr, ssg, + rsc, ...(middlewareConfig && { middleware: middlewareConfig }), ...(runtime && { runtime }), } } - return { ssr: false, ssg: false, runtime: nextConfig.experimental?.runtime } + return { + ssr: false, + ssg: false, + rsc: RSC_MODULE_TYPES.server, + runtime: nextConfig.experimental?.runtime, + } } diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index aeb86af4f849e..2fc0253ba59a6 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -21,6 +21,7 @@ import { SERVER_RUNTIME, WEBPACK_LAYERS, } from '../lib/constants' +import { RSC_MODULE_TYPES } from '../shared/lib/constants' import { CLIENT_STATIC_FILES_RUNTIME_AMP, CLIENT_STATIC_FILES_RUNTIME_MAIN, @@ -37,14 +38,12 @@ import { warn } from './output/log' import { isMiddlewareFile, isMiddlewareFilename, - isServerComponentPage, NestedMiddlewareError, MiddlewareInServerlessTargetError, } from './utils' import { getPageStaticInfo } from './analysis/get-page-static-info' import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep' import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' -import { serverComponentRegex } from './webpack/loaders/utils' import { ServerRuntime } from '../types' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' import { encodeMatchers } from './webpack/loaders/next-middleware-loader' @@ -66,14 +65,12 @@ export function getPageFromPath(pagePath: string, pageExtensions: string[]) { } export function createPagesMapping({ - hasServerComponents, isDev, pageExtensions, pagePaths, pagesType, pagesDir, }: { - hasServerComponents: boolean isDev: boolean pageExtensions: string[] pagePaths: string[] @@ -90,13 +87,6 @@ export function createPagesMapping({ const pageKey = getPageFromPath(pagePath, pageExtensions) - // Assume that if there's a Client Component, that there is - // a matching Server Component that will map to the page. - // so we will not process it - if (hasServerComponents && /\.client$/.test(pageKey)) { - return result - } - if (pageKey in result) { warn( `Duplicate page detected. ${chalk.cyan( @@ -208,10 +198,7 @@ export function getEdgeServerEntry(opts: { absolutePagePath: opts.absolutePagePath, buildId: opts.buildId, dev: opts.isDev, - isServerComponent: isServerComponentPage( - opts.config, - opts.absolutePagePath - ), + isServerComponent: opts.isServerComponent, page: opts.page, stringifiedConfig: JSON.stringify(opts.config), pagesType: opts.pagesType, @@ -418,8 +405,10 @@ export async function createEntrypoints(params: CreateEntrypointsParams) { nestedMiddleware.push(page) } - const isServerComponent = serverComponentRegex.test(absolutePagePath) - const isInsideAppDir = appDir && absolutePagePath.startsWith(appDir) + const isInsideAppDir = + !!appDir && + (absolutePagePath.startsWith(APP_DIR_ALIAS) || + absolutePagePath.startsWith(appDir)) const staticInfo = await getPageStaticInfo({ nextConfig: config, @@ -428,6 +417,9 @@ export async function createEntrypoints(params: CreateEntrypointsParams) { page, }) + const isServerComponent = + isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client + if (isMiddlewareFile(page)) { middlewareMatchers = staticInfo.middleware?.matchers ?? [ { regexp: '.*' }, diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 12355d9721c9d..898072e333e3d 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -58,6 +58,7 @@ import { COMPILER_NAMES, APP_BUILD_MANIFEST, FLIGHT_SERVER_CSS_MANIFEST, + RSC_MODULE_TYPES, } from '../shared/lib/constants' import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils' import { __ApiPreviewProps } from '../server/api-utils' @@ -96,7 +97,6 @@ import { printTreeView, copyTracedFiles, isReservedPage, - isServerComponentPage, AppConfig, } from './utils' import getBaseWebpackConfig from './webpack-config' @@ -490,7 +490,6 @@ export default async function build( .traceChild('create-pages-mapping') .traceFn(() => createPagesMapping({ - hasServerComponents, isDev: false, pageExtensions: config.pageExtensions, pagesType: 'pages', @@ -507,7 +506,6 @@ export default async function build( .traceFn(() => createPagesMapping({ pagePaths: appPaths!, - hasServerComponents, isDev: false, pagesType: 'app', pageExtensions: config.pageExtensions, @@ -519,7 +517,6 @@ export default async function build( let mappedRootPaths: { [page: string]: string } = {} if (rootPaths.length > 0) { mappedRootPaths = createPagesMapping({ - hasServerComponents, isDev: false, pageExtensions: config.pageExtensions, pagePaths: rootPaths, @@ -1311,23 +1308,20 @@ export default async function build( } } - const pageRuntime = pagePath - ? ( - await getPageStaticInfo({ - pageFilePath: join( - (pageType === 'pages' ? pagesDir : appDir) || '', - pagePath - ), - nextConfig: config, - }) - ).runtime + const staticInfo = pagePath + ? await getPageStaticInfo({ + pageFilePath: join( + (pageType === 'pages' ? pagesDir : appDir) || '', + pagePath + ), + nextConfig: config, + }) : undefined - if (hasServerComponents && pagePath) { - if (isServerComponentPage(config, pagePath)) { - isServerComponent = true - } - } + const pageRuntime = staticInfo?.runtime + isServerComponent = + pageType === 'app' && + staticInfo?.rsc !== RSC_MODULE_TYPES.client if (!isReservedPage(page)) { try { diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 224ada566def8..500c4d9a98d9c 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -1542,33 +1542,6 @@ export function detectConflictingPaths( } } -/** - * With RSC we automatically add .server and .client to page extensions. This - * function allows to remove them for cases where we just need to strip out - * the actual extension keeping the .server and .client. - */ -export function withoutRSCExtensions(pageExtensions: string[]): string[] { - return pageExtensions.filter( - (ext) => !ext.startsWith('client.') && !ext.startsWith('server.') - ) -} - -export function isServerComponentPage( - nextConfig: NextConfigComplete, - filePath: string -): boolean { - if (!nextConfig.experimental.serverComponents) { - return false - } - - const rawPageExtensions = withoutRSCExtensions( - nextConfig.pageExtensions || [] - ) - return rawPageExtensions.some((ext) => { - return filePath.endsWith(`.server.${ext}`) - }) -} - export async function copyTracedFiles( dir: string, distDir: string, diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 5c505d69e2156..6aaa9ceeccae6 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -56,7 +56,6 @@ import type { } from './webpack/plugins/telemetry-plugin' import type { Span } from '../trace' import type { MiddlewareMatcher } from './analysis/get-page-static-info' -import { withoutRSCExtensions } from './utils' import browserslist from 'next/dist/compiled/browserslist' import loadJsConfig from './load-jsconfig' import { loadBindings } from './swc' @@ -687,19 +686,14 @@ export default async function getBaseWebpackConfig( babel: getBabelOrSwcLoader(), } - const rawPageExtensions = hasServerComponents - ? withoutRSCExtensions(config.pageExtensions) - : config.pageExtensions - - const serverComponentsRegex = new RegExp( - `\\.server\\.(${rawPageExtensions.join('|')})$` - ) + const pageExtensions = config.pageExtensions const babelIncludeRegexes: RegExp[] = [ /next[\\/]dist[\\/]shared[\\/]lib/, /next[\\/]dist[\\/]client/, /next[\\/]dist[\\/]pages/, /[\\/](strip-ansi|ansi-regex)[\\/]/, + /styled-jsx[\\/]/, ] // Support for NODE_PATH @@ -801,7 +795,7 @@ export default async function getBaseWebpackConfig( if (dev) { customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [ ...(pagesDir - ? rawPageExtensions.reduce((prev, ext) => { + ? pageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_app.${ext}`)) return prev }, [] as string[]) @@ -810,7 +804,7 @@ export default async function getBaseWebpackConfig( ] customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [ ...(pagesDir - ? rawPageExtensions.reduce((prev, ext) => { + ? pageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_error.${ext}`)) return prev }, [] as string[]) @@ -819,7 +813,7 @@ export default async function getBaseWebpackConfig( ] customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [ ...(pagesDir - ? rawPageExtensions.reduce((prev, ext) => { + ? pageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_document.${ext}`)) return prev }, [] as string[]) @@ -874,7 +868,7 @@ export default async function getBaseWebpackConfig( ...getReactProfilingInProduction(), [RSC_MOD_REF_PROXY_ALIAS]: - 'next/dist/build/webpack/loaders/next-flight-client-loader/module-proxy', + 'next/dist/build/webpack/loaders/next-flight-loader/module-proxy', ...(isClient || isEdgeServer ? { @@ -1151,11 +1145,6 @@ export default async function getBaseWebpackConfig( }, } - const serverComponentCodeCondition = { - test: serverComponentsRegex, - include: [dir, /next[\\/]dist[\\/]pages/], - } - const rscSharedRegex = /(node_modules\/react\/|\/shared\/lib\/(head-manager-context|router-context|flush-effects)\.js|node_modules\/styled-jsx\/)/ @@ -1451,8 +1440,7 @@ export default async function getBaseWebpackConfig( 'next-image-loader', 'next-serverless-loader', 'next-style-loader', - 'next-flight-client-loader', - 'next-flight-server-loader', + 'next-flight-loader', 'next-flight-client-entry-loader', 'noop-loader', 'next-middleware-loader', @@ -1487,31 +1475,22 @@ export default async function getBaseWebpackConfig( } as any, ] : []), - ...(hasServerComponents - ? isNodeServer || isEdgeServer - ? [ - // RSC server compilation loaders - { - ...serverComponentCodeCondition, - issuerLayer: WEBPACK_LAYERS.server, - use: { - loader: 'next-flight-server-loader', - }, - }, - // { - // test: clientComponentRegex, - // issuerLayer: WEBPACK_LAYERS.server, - // use: { - // loader: 'next-flight-client-loader', - // }, - // }, - // _app should be treated as a client component as well as all its dependencies. - { - test: new RegExp(`_app\\.(${rawPageExtensions.join('|')})$`), - layer: WEBPACK_LAYERS.client, + ...(hasServerComponents && (isNodeServer || isEdgeServer) + ? [ + // RSC server compilation loaders + { + test: codeCondition.test, + include: [ + dir, + // To let the internal client components passing through flight loader + /next[\\/]dist/, + ], + issuerLayer: WEBPACK_LAYERS.server, + use: { + loader: 'next-flight-loader', }, - ] - : [] + }, + ] : []), ...(hasServerComponents && isEdgeServer ? [ @@ -1841,11 +1820,10 @@ export default async function getBaseWebpackConfig( isClient && new AppBuildManifestPlugin({ dev }), hasServerComponents && + !!config.experimental.appDir && (isClient ? new FlightManifestPlugin({ dev, - appDir: !!config.experimental.appDir, - pageExtensions: rawPageExtensions, }) : new FlightClientEntryPlugin({ dev, @@ -1995,7 +1973,7 @@ export default async function getBaseWebpackConfig( const configVars = JSON.stringify({ crossOrigin: config.crossOrigin, - pageExtensions: rawPageExtensions, + pageExtensions: pageExtensions, trailingSlash: config.trailingSlash, buildActivity: config.devIndicators.buildActivity, buildActivityPosition: config.devIndicators.buildActivityPosition, diff --git a/packages/next/build/webpack/config/blocks/css/index.ts b/packages/next/build/webpack/config/blocks/css/index.ts index 011615134b04f..5b81ffc7da103 100644 --- a/packages/next/build/webpack/config/blocks/css/index.ts +++ b/packages/next/build/webpack/config/blocks/css/index.ts @@ -501,7 +501,7 @@ export const css = curry(async function css( // If it's inside the app dir, but not importing from a layout file, // throw an error. and: [ctx.rootDirectory], - not: [/layout(\.client|\.server)?\.(js|mjs|jsx|ts|tsx)$/], + not: [/layout\.(js|mjs|jsx|ts|tsx)$/], } : undefined, use: { diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 848399cd32a07..bd3dd8a32b31a 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -1,4 +1,7 @@ -import type { MiddlewareMatcher } from '../../analysis/get-page-static-info' +import type { + MiddlewareMatcher, + RSCModuleType, +} from '../../analysis/get-page-static-info' import { webpack } from 'next/dist/compiled/webpack/webpack' /** @@ -17,9 +20,15 @@ export function getModuleBuildInfo(webpackModule: webpack.Module) { route?: RouteMeta importLocByPath?: Map rootDir?: string + rsc?: RSCMeta } } +export interface RSCMeta { + type?: RSCModuleType + requests?: string[] // client requests in flight client entry +} + export interface RouteMeta { page: string absolutePagePath: string diff --git a/packages/next/build/webpack/loaders/next-client-pages-loader.ts b/packages/next/build/webpack/loaders/next-client-pages-loader.ts index b01a8ec6342a7..0cd1cf277830a 100644 --- a/packages/next/build/webpack/loaders/next-client-pages-loader.ts +++ b/packages/next/build/webpack/loaders/next-client-pages-loader.ts @@ -3,7 +3,6 @@ import { stringifyRequest } from '../stringify-request' export type ClientPagesLoaderOptions = { absolutePagePath: string page: string - isServerComponent?: boolean } // this parameter: https://www.typescriptlang.org/docs/handbook/functions.html#this-parameters @@ -13,14 +12,12 @@ function nextClientPagesLoader(this: any) { ) return pagesLoaderSpan.traceFn(() => { - const { absolutePagePath, page, isServerComponent } = + const { absolutePagePath, page } = this.getOptions() as ClientPagesLoaderOptions pagesLoaderSpan.setAttribute('absolutePagePath', absolutePagePath) - const stringifiedPageRequest = isServerComponent - ? JSON.stringify(absolutePagePath + '!') - : stringifyRequest(this, absolutePagePath) + const stringifiedPageRequest = stringifyRequest(this, absolutePagePath) const stringifiedPage = JSON.stringify(page) return ` diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts index cd47aadac3117..1a1dc07d23106 100644 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -118,7 +118,7 @@ export default async function edgeSSRLoader(this: any) { config: ${stringifiedConfig}, buildId: ${JSON.stringify(buildId)}, }) - + export const ComponentMod = pageMod export default function(opts) { diff --git a/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts b/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts index c2fdce03388f1..8a24db9e083bb 100644 --- a/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts +++ b/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts @@ -1,3 +1,6 @@ +import { RSC_MODULE_TYPES } from '../../../shared/lib/constants' +import { getModuleBuildInfo } from './get-module-build-info' + export type ClientComponentImports = string[] export type CssImports = Record @@ -37,5 +40,11 @@ export default async function transformSource(this: any): Promise { export default function RSC() {}; ` + const buildInfo = getModuleBuildInfo(this._module) + buildInfo.rsc = { + type: RSC_MODULE_TYPES.client, + requests, + } + return code } diff --git a/packages/next/build/webpack/loaders/next-flight-client-loader/index.ts b/packages/next/build/webpack/loaders/next-flight-client-loader/index.ts deleted file mode 100644 index ba20bbb722290..0000000000000 --- a/packages/next/build/webpack/loaders/next-flight-client-loader/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path' -import { checkExports } from '../../../analysis/get-page-static-info' -import { parse } from '../../../swc' - -function containsPath(parent: string, child: string) { - const relation = path.relative(parent, child) - return !!relation && !relation.startsWith('..') && !path.isAbsolute(relation) -} - -export default async function transformSource( - this: any, - source: string, - map: any -) { - if (typeof source !== 'string') { - throw new Error('Expected source to have been transformed to a string.') - } - - const callback = this.async() - const appDir = path.join(this.rootContext, 'app') - const isUnderAppDir = containsPath(appDir, this.resourcePath) - const filename = path.basename(this.resourcePath) - const isPageOrLayoutFile = /^(page|layout)\.client\.\w+$/.test(filename) - - const createError = (name: string) => - new Error( - `${name} is not supported in client components.\nFrom: ${this.resourcePath}` - ) - - if (isUnderAppDir && isPageOrLayoutFile) { - const swcAST = await parse(source, { - filename: this.resourcePath, - isModule: 'unknown', - }) - const { ssg, ssr } = checkExports(swcAST) - if (ssg) { - this.emitError(createError('getStaticProps')) - } - if (ssr) { - this.emitError(createError('getServerSideProps')) - } - } - - const output = ` -const { createProxy } = require("private-next-rsc-mod-ref-proxy")\n -module.exports = createProxy(${JSON.stringify(this.resourcePath)}) -` - // Pass empty sourcemap - callback(null, output, map) - return -} diff --git a/packages/next/build/webpack/loaders/next-flight-loader/index.ts b/packages/next/build/webpack/loaders/next-flight-loader/index.ts new file mode 100644 index 0000000000000..1f938f1256a03 --- /dev/null +++ b/packages/next/build/webpack/loaders/next-flight-loader/index.ts @@ -0,0 +1,45 @@ +import { RSC_MODULE_TYPES } from '../../../../shared/lib/constants' +import { getRSCModuleType } from '../../../analysis/get-page-static-info' +import { parse } from '../../../swc' +import { getModuleBuildInfo } from '../get-module-build-info' + +function transformServer(source: string, isESModule: boolean) { + return ( + source + + (isESModule ? `export const __next_rsc__` : `exports.__next_rsc__`) + + ` = { __webpack_require__, server: true }\n` + ) +} + +export default async function transformSource( + this: any, + source: string, + sourceMap: any +) { + // Avoid buffer to be consumed + if (typeof source !== 'string') { + throw new Error('Expected source to have been transformed to a string.') + } + + const { resourcePath } = this + const callback = this.async() + const buildInfo = getModuleBuildInfo(this._module) + const swcAST = await parse(source, { + filename: resourcePath, + isModule: 'unknown', + }) + + const rscType = getRSCModuleType(source) + + // Assign the RSC meta information to buildInfo. + // Exclude next internal files which are not marked as client files + buildInfo.rsc = { type: rscType } + + if (buildInfo.rsc?.type === RSC_MODULE_TYPES.client) { + return callback(null, source, sourceMap) + } + + const isModule = swcAST.type === 'Module' + const code = transformServer(source, isModule) + return callback(null, code, sourceMap) +} diff --git a/packages/next/build/webpack/loaders/next-flight-client-loader/module-proxy.ts b/packages/next/build/webpack/loaders/next-flight-loader/module-proxy.ts similarity index 100% rename from packages/next/build/webpack/loaders/next-flight-client-loader/module-proxy.ts rename to packages/next/build/webpack/loaders/next-flight-loader/module-proxy.ts diff --git a/packages/next/build/webpack/loaders/next-flight-server-loader.ts b/packages/next/build/webpack/loaders/next-flight-server-loader.ts deleted file mode 100644 index ef0efd4001f18..0000000000000 --- a/packages/next/build/webpack/loaders/next-flight-server-loader.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { parse } from '../../swc' - -export default async function transformSource( - this: any, - source: string -): Promise { - const { resourcePath } = this - - const ast = await parse(source, { - filename: resourcePath, - isModule: 'unknown', - }) - const isModule = ast.type === 'Module' - - return ( - source + - (isModule - ? ` - export const __next_rsc__ = { - __webpack_require__, - server: true - } - ` - : ` - exports.__next_rsc__ = { - __webpack_require__, - server: true - } - `) - ) -} diff --git a/packages/next/build/webpack/loaders/utils.ts b/packages/next/build/webpack/loaders/utils.ts index 1bc58a101c1fb..6bbcacc2268a6 100644 --- a/packages/next/build/webpack/loaders/utils.ts +++ b/packages/next/build/webpack/loaders/utils.ts @@ -1,56 +1,12 @@ -import { getPageStaticInfo } from '../../analysis/get-page-static-info' +import { RSC_MODULE_TYPES } from '../../../shared/lib/constants' -export const defaultJsFileExtensions = ['js', 'mjs', 'jsx', 'ts', 'tsx'] const imageExtensions = ['jpg', 'jpeg', 'png', 'webp', 'avif'] -const nextClientComponents = [ - 'dist/client/link', - 'dist/client/image', - 'dist/client/future/image', - 'dist/shared/lib/head', - 'dist/client/script', - 'dist/shared/lib/dynamic', -] - -export function buildExports(moduleExports: any, isESM: boolean) { - let ret = '' - Object.keys(moduleExports).forEach((key) => { - const exportExpression = isESM - ? `export ${key === 'default' ? key : `const ${key} =`} ${ - moduleExports[key] - }` - : `exports.${key} = ${moduleExports[key]}` - - ret += exportExpression + '\n' - }) - return ret -} - -// Special cases for Next.js APIs that are considered as client components: -// - .client.[ext] -// - next built-in client components -// - .[imageExt] -export const clientComponentRegex = new RegExp( - '(' + - `\\.client(\\.(${defaultJsFileExtensions.join('|')}))?|` + - `next[\\\\/](${nextClientComponents.join('|')})(\\.js)?|` + - `\\.(${imageExtensions.join('|')})` + - ')$' -) - -export const serverComponentRegex = new RegExp( - `\\.server(\\.(${defaultJsFileExtensions.join('|')}))?$` -) - -export async function loadEdgeFunctionConfigFromFile( - absolutePagePath: string, - resolve: (context: string, request: string) => Promise -) { - const pageFilePath = await resolve('/', absolutePagePath) - return ( - await getPageStaticInfo({ - nextConfig: {}, - pageFilePath, - isDev: false, - }) - ).middleware +const imageRegex = new RegExp(`\\.(${imageExtensions.join('|')})$`) + +export function isClientComponentModule(mod: { + resource: string + buildInfo: any +}) { + const hasClientDirective = mod.buildInfo.rsc?.type === RSC_MODULE_TYPES.client + return hasClientDirective || imageRegex.test(mod.resource) } diff --git a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts index fc873e08e0a52..c4cb536d04afb 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1,7 +1,6 @@ import { stringify } from 'querystring' import path from 'path' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' -import { clientComponentRegex } from '../loaders/utils' import { getInvalidator, entries, @@ -18,6 +17,7 @@ import { FLIGHT_SERVER_CSS_MANIFEST, } from '../../../shared/lib/constants' import { FlightCSSManifest } from './flight-manifest-plugin' +import { isClientComponentModule } from '../loaders/utils' interface Options { dev: boolean @@ -131,10 +131,7 @@ export class FlightClientEntryPlugin { : layoutOrPageRequest // Replace file suffix as `.js` will be added. - const bundlePath = relativeRequest.replace( - /(\.server|\.client)?\.(js|ts)x?$/, - '' - ) + const bundlePath = relativeRequest.replace(/\.(js|ts)x?$/, '') promises.push( this.injectClientEntryAndSSRModules({ @@ -240,7 +237,7 @@ export class FlightClientEntryPlugin { visitedBySegment[layoutOrPageRequest].add(modRequest) const isCSS = regexCSS.test(modRequest) - const isClientComponent = clientComponentRegex.test(modRequest) + const isClientComponent = isClientComponentModule(mod) if (isCSS) { serverCSSImports[layoutOrPageRequest] = diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index a7c32f98293fe..f4996c20d81cd 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -7,8 +7,8 @@ import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { FLIGHT_MANIFEST } from '../../../shared/lib/constants' -import { clientComponentRegex } from '../loaders/utils' import { relative } from 'path' +import { isClientComponentModule } from '../loaders/utils' // This is the module that will be used to anchor all client references to. // I.e. it will have all the client files as async deps from this point on. @@ -19,8 +19,6 @@ import { relative } from 'path' interface Options { dev: boolean - appDir: boolean - pageExtensions: string[] } /** @@ -64,13 +62,9 @@ const PLUGIN_NAME = 'FlightManifestPlugin' export class FlightManifestPlugin { dev: Options['dev'] = false - pageExtensions: Options['pageExtensions'] - appDir: Options['appDir'] = false constructor(options: Options) { this.dev = options.dev - this.appDir = options.appDir - this.pageExtensions = options.pageExtensions } apply(compiler: webpack.Compiler) { @@ -109,9 +103,36 @@ export class FlightManifestPlugin { const manifest: FlightManifest = { __ssr_module_mapping__: {}, } - const appDir = this.appDir const dev = this.dev + const clientRequestsSet = new Set() + + // Collect client requests + function collectClientRequest(mod: webpack.NormalModule) { + if (mod.resource === '' && mod.buildInfo.rsc) { + const { requests = [] } = mod.buildInfo.rsc + requests.forEach((r: string) => { + clientRequestsSet.add(require.resolve(r)) + }) + } + } + + compilation.chunkGroups.forEach((chunkGroup) => { + chunkGroup.chunks.forEach((chunk: webpack.Chunk) => { + const chunkModules = compilation.chunkGraph.getChunkModulesIterable( + chunk + // TODO: Update type so that it doesn't have to be cast. + ) as Iterable + for (const mod of chunkModules) { + collectClientRequest(mod) + const anyModule = mod as any + if (anyModule.modules) { + for (const subMod of anyModule.modules) collectClientRequest(subMod) + } + } + }) + }) + compilation.chunkGroups.forEach((chunkGroup) => { const cssResourcesInChunkGroup = new Set() let entryFilepath: string = '' @@ -122,8 +143,9 @@ export class FlightManifestPlugin { mod: webpack.NormalModule ) { const isCSSModule = + mod.resource?.endsWith('.css') || mod.type === 'css/mini-extract' || - (mod.loaders && + (!!mod.loaders && (dev ? mod.loaders.some((item) => item.loader.includes('next-style-loader/index.js') @@ -138,7 +160,9 @@ export class FlightManifestPlugin { mod._identifier.slice(mod._identifier.lastIndexOf('!') + 1) : mod.resource - if (!resource) return + if (!resource) { + return + } const moduleExports = manifest[resource] || {} const moduleIdMapping = manifest.__ssr_module_mapping__ @@ -173,14 +197,13 @@ export class FlightManifestPlugin { return } - // TODO: Hook into deps instead of the target module. - // That way we know by the type of dep whether to include. - // It also resolves conflicts when the same module is in multiple chunks. - if (!clientComponentRegex.test(resource)) { + // Only apply following logic to client module requests from client entry, + // or if the module is marked as client module. + if (!clientRequestsSet.has(resource) && !isClientComponentModule(mod)) { return } - if (/\/(page|layout)\.client\.(ts|js)x?$/.test(resource)) { + if (/[\\/](page|layout)\.(ts|js)x?$/.test(resource)) { entryFilepath = resource } @@ -207,6 +230,17 @@ export class FlightManifestPlugin { ]), ] + function getAppPathRequiredChunks() { + return chunkGroup.chunks.map((requiredChunk: webpack.Chunk) => { + return ( + requiredChunk.id + + ':' + + (requiredChunk.name || requiredChunk.id) + + (dev ? '' : '-' + requiredChunk.hash) + ) + }) + } + const moduleExportedKeys = ['', '*'] .concat( [...exportsInfo.exports] @@ -217,29 +251,10 @@ export class FlightManifestPlugin { .filter((name) => name !== null) moduleExportedKeys.forEach((name) => { - let requiredChunks: ManifestChunks = [] - if (!moduleExports[name]) { - const isRelatedChunk = (c: webpack.Chunk) => { - // If current chunk is a page, it should require the related page chunk; - // If current chunk is a component, it should filter out the related page chunk; - return ( - chunk.name?.startsWith('pages/') || - !c.name?.startsWith('pages/') - ) - } - - if (appDir) { - requiredChunks = chunkGroup.chunks - .filter(isRelatedChunk) - .map((requiredChunk: webpack.Chunk) => { - return ( - requiredChunk.id + - ':' + - (requiredChunk.name || requiredChunk.id) + - (dev ? '' : '-' + requiredChunk.hash) - ) - }) - } + // If the chunk is from `app/` chunkGroup, use it first. + // This make sure not to load the overlapped chunk from `pages/` chunkGroup + if (!moduleExports[name] || chunkGroup.name?.startsWith('app/')) { + const requiredChunks = getAppPathRequiredChunks() moduleExports[name] = { id, @@ -249,11 +264,9 @@ export class FlightManifestPlugin { } moduleIdMapping[id] = moduleIdMapping[id] || {} - if (!moduleIdMapping[id][name]) { - moduleIdMapping[id][name] = { - ...moduleExports[name], - id: ssrNamedModuleId, - } + moduleIdMapping[id][name] = { + ...moduleExports[name], + id: ssrNamedModuleId, } }) diff --git a/packages/next/client/app-index.tsx b/packages/next/client/app-index.tsx index fa475942a2f96..c8ede9eaa67da 100644 --- a/packages/next/client/app-index.tsx +++ b/packages/next/client/app-index.tsx @@ -2,9 +2,12 @@ import '../build/polyfills/polyfill-module' // @ts-ignore react-dom/client exists when using React 18 import ReactDOMClient from 'react-dom/client' -import React from 'react' +// TODO-APP: change to React.use once it becomes stable +import React, { experimental_use as use } from 'react' import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack' +import measureWebVitals from './performance-relayer' + /// // Override chunk URL mapping in the webpack runtime @@ -14,9 +17,6 @@ declare global { const __webpack_require__: any } -// TODO-APP: change to React.use once it becomes stable -const use = (React as any).experimental_use - // eslint-disable-next-line no-undef const getChunkScriptFilename = __webpack_require__.u const chunkFilenameMap: any = {} @@ -125,7 +125,7 @@ function createResponseCache() { } const rscCache = createResponseCache() -function useInitialServerResponse(cacheKey: string) { +function useInitialServerResponse(cacheKey: string): Promise { const response = rscCache.get(cacheKey) if (response) return response @@ -141,7 +141,7 @@ function useInitialServerResponse(cacheKey: string) { return newResponse } -function ServerRoot({ cacheKey }: { cacheKey: string }) { +function ServerRoot({ cacheKey }: { cacheKey: string }): JSX.Element { React.useEffect(() => { rscCache.delete(cacheKey) }) @@ -151,6 +151,10 @@ function ServerRoot({ cacheKey }: { cacheKey: string }) { } function Root({ children }: React.PropsWithChildren<{}>): React.ReactElement { + React.useEffect(() => { + measureWebVitals() + }, []) + if (process.env.__NEXT_TEST_MODE) { // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { @@ -165,7 +169,7 @@ function Root({ children }: React.PropsWithChildren<{}>): React.ReactElement { return children as React.ReactElement } -function RSCComponent(props: any) { +function RSCComponent(props: any): JSX.Element { const cacheKey = getCacheKey() return } diff --git a/packages/next/client/components/app-router.client.tsx b/packages/next/client/components/app-router.client.tsx index 5136d5faa20d7..9bd71e4376d6d 100644 --- a/packages/next/client/components/app-router.client.tsx +++ b/packages/next/client/components/app-router.client.tsx @@ -2,7 +2,7 @@ import type { PropsWithChildren, ReactElement, ReactNode } from 'react' import React, { useEffect, useMemo, useCallback } from 'react' -import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack' +import { createFromFetch } from 'next/dist/compiled/react-server-dom-webpack' import { AppRouterContext, LayoutRouterContext, @@ -32,11 +32,11 @@ import { useReducerWithReduxDevtools } from './use-reducer-with-devtools' /** * Fetch the flight data for the provided url. Takes in the current router state to decide what to render server-side. */ -function fetchFlight( +export async function fetchServerResponse( url: URL, flightRouterState: FlightRouterState, prefetch?: true -): ReadableStream { +): Promise<[FlightData: FlightData]> { const flightUrl = new URL(url) const searchParams = flightUrl.searchParams // Enable flight response @@ -50,26 +50,10 @@ function fetchFlight( searchParams.append('__flight_prefetch__', '1') } - // TODO-APP: Verify that TransformStream is supported. - const { readable, writable } = new TransformStream() - - fetch(flightUrl.toString()).then((res) => { - res.body?.pipeTo(writable) - }) - - return readable -} - -/** - * Fetch the flight data for the provided url. Takes in the current router state to decide what to render server-side. - */ -export function fetchServerResponse( - url: URL, - flightRouterState: FlightRouterState, - prefetch?: true -): Promise { + const res = await fetch(flightUrl.toString()) // Handle the `fetch` readable stream that can be unwrapped by `React.use`. - return createFromReadableStream(fetchFlight(url, flightRouterState, prefetch)) + const flightData: FlightData = await createFromFetch(Promise.resolve(res)) + return [flightData] } /** @@ -214,7 +198,7 @@ export default function AppRouter({ window.history.state?.tree || initialTree, true ) - const flightData = await r + const [flightData] = await r // @ts-ignore startTransition exists React.startTransition(() => { dispatch({ diff --git a/packages/next/client/components/layout-router.client.tsx b/packages/next/client/components/layout-router.client.tsx index 5dfa3fb80fd4b..4e85432cf2ca8 100644 --- a/packages/next/client/components/layout-router.client.tsx +++ b/packages/next/client/components/layout-router.client.tsx @@ -1,6 +1,12 @@ 'client' -import React, { useContext, useEffect, useRef } from 'react' +import React, { + useContext, + useEffect, + useRef, + // TODO-APP: change to React.use once it becomes stable + experimental_use as use, +} from 'react' import type { ChildProp, //Segment @@ -19,9 +25,6 @@ import { import { fetchServerResponse } from './app-router.client' // import { matchSegment } from './match-segments' -// TODO-APP: change to React.use once it becomes stable -const use = (React as any).experimental_use - /** * Check if every segment in array a and b matches */ @@ -164,7 +167,11 @@ export function InnerLayoutRouter({ focusAndScrollElementRef.current.focus() // Only scroll into viewport when the layout is not visible currently. if (!topOfElementInViewport(focusAndScrollElementRef.current)) { + const htmlElement = document.documentElement + const existing = htmlElement.style.scrollBehavior + htmlElement.style.scrollBehavior = 'auto' focusAndScrollElementRef.current.scrollIntoView() + htmlElement.style.scrollBehavior = existing } } }, [focusAndScrollRef]) @@ -230,7 +237,7 @@ export function InnerLayoutRouter({ * Flight response data */ // When the data has not resolved yet `use` will suspend here. - const flightData = use(childNode.data) + const [flightData] = use(childNode.data) // Handle case when navigating to page in `pages` from `app` if (typeof flightData === 'string') { diff --git a/packages/next/client/components/reducer.ts b/packages/next/client/components/reducer.ts index d8c6cb2c3bec8..60c33a9916938 100644 --- a/packages/next/client/components/reducer.ts +++ b/packages/next/client/components/reducer.ts @@ -6,13 +6,11 @@ import type { FlightSegmentPath, Segment, } from '../../server/app-render' -import React from 'react' +// TODO-APP: change to React.use once it becomes stable +import { experimental_use as use } from 'react' import { matchSegment } from './match-segments' import { fetchServerResponse } from './app-router.client' -// TODO-APP: change to React.use once it becomes stable -const use = (React as any).experimental_use - /** * Invalidate cache one level down from the router state. */ @@ -233,7 +231,7 @@ function fillCacheWithDataProperty( newCache: CacheNode, existingCache: CacheNode, segments: string[], - fetchResponse: any + fetchResponse: () => ReturnType ): { bailOptimistic: boolean } | undefined { const isLastEntry = segments.length === 1 @@ -734,7 +732,7 @@ export function reducer( cache, state.cache, segments.slice(1), - (): Promise => fetchServerResponse(url, optimisticTree) + () => fetchServerResponse(url, optimisticTree) ) // If optimistic fetch couldn't happen it falls back to the non-optimistic case. @@ -765,7 +763,7 @@ export function reducer( } // Unwrap cache data with `use` to suspend here (in the reducer) until the fetch resolves. - const flightData = use(cache.data) + const [flightData] = use(cache.data) // Handle case when navigating to page in `pages` from `app` if (typeof flightData === 'string') { @@ -956,7 +954,7 @@ export function reducer( 'refetch', ]) } - const flightData = use(cache.data) + const [flightData] = use(cache.data) // Handle case when navigating to page in `pages` from `app` if (typeof flightData === 'string') { diff --git a/packages/next/client/dev/error-overlay/format-webpack-messages.js b/packages/next/client/dev/error-overlay/format-webpack-messages.js index ed32aa2c0294b..f49af448a65ac 100644 --- a/packages/next/client/dev/error-overlay/format-webpack-messages.js +++ b/packages/next/client/dev/error-overlay/format-webpack-messages.js @@ -37,14 +37,14 @@ function isLikelyASyntaxError(message) { let hadMissingSassError = false // Cleans up webpack error messages. -function formatMessage(message, verbose) { +function formatMessage(message, verbose, importTraceNote) { // TODO: Replace this once webpack 5 is stable if (typeof message === 'object' && message.message) { const filteredModuleTrace = message.moduleTrace && message.moduleTrace.filter( (trace) => - !/next-(middleware|client-pages|edge-function|flight-(client|server))-loader\.js/.test( + !/next-(middleware|client-pages|edge-function)-loader\.js/.test( trace.originName ) ) @@ -61,8 +61,8 @@ function formatMessage(message, verbose) { body + (message.details && verbose ? '\n' + message.details : '') + (filteredModuleTrace && filteredModuleTrace.length && verbose - ? '\n\nImport trace for requested module:' + - filteredModuleTrace.map((trace) => `\n${trace.originName}`).join('') + ? (importTraceNote || '\n\nImport trace for requested module:') + + filteredModuleTrace.map((trace) => `\n${trace.moduleName}`).join('') : '') + (message.stack && verbose ? '\n' + message.stack : '') } @@ -171,7 +171,44 @@ function formatMessage(message, verbose) { function formatWebpackMessages(json, verbose) { const formattedErrors = json.errors.map(function (message) { - return formatMessage(message, verbose) + let importTraceNote + + // TODO: Shall we use invisible characters in the original error + // message as meta information? + if (message && message.message && /NEXT_RSC_ERR_/.test(message.message)) { + // Comes from the "React Server Components" transform in SWC, always + // attach the module trace. + const NEXT_RSC_ERR_REACT_API = /.+NEXT_RSC_ERR_REACT_API: (.*?)\n/s + const NEXT_RSC_ERR_SERVER_IMPORT = + /.+NEXT_RSC_ERR_SERVER_IMPORT: (.*?)\n/s + const NEXT_RSC_ERR_CLIENT_IMPORT = + /.+NEXT_RSC_ERR_CLIENT_IMPORT: (.*?)\n/s + + if (NEXT_RSC_ERR_REACT_API.test(message.message)) { + message.message = message.message.replace( + NEXT_RSC_ERR_REACT_API, + `\n\nYou're importing a component that needs $1. It only works in a Client Component but none of its parents are marked with "client", so they're Server Components by default.\n\n` + ) + importTraceNote = + '\n\nMaybe one of these should be marked as a "client" entry:\n' + } else if (NEXT_RSC_ERR_SERVER_IMPORT.test(message.message)) { + message.message = message.message.replace( + NEXT_RSC_ERR_SERVER_IMPORT, + `\n\nYou're importing a component that imports $1. It only works in a Client Component but none of its parents are marked with "client", so they're Server Components by default.\n\n` + ) + importTraceNote = + '\n\nMaybe one of these should be marked as a "client" entry:\n' + } else if (NEXT_RSC_ERR_CLIENT_IMPORT.test(message.message)) { + message.message = message.message.replace( + NEXT_RSC_ERR_CLIENT_IMPORT, + `\n\nYou're importing a component that needs $1. That only works in a Server Component but one of its parents is marked with "client", so it's a Client Component.\n\n` + ) + importTraceNote = '\n\nOne of these is marked as a "client" entry:\n' + } + + verbose = true + } + return formatMessage(message, verbose, importTraceNote) }) const formattedWarnings = json.warnings.map(function (message) { return formatMessage(message, verbose) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 7324cccd7d908..c0f5d8be03373 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -688,7 +688,11 @@ function doRender(input: RenderRouteInfo): Promise { } if (input.scroll) { + const htmlElement = document.documentElement + const existing = htmlElement.style.scrollBehavior + htmlElement.style.scrollBehavior = 'auto' window.scrollTo(input.scroll.x, input.scroll.y) + htmlElement.style.scrollBehavior = existing } } diff --git a/packages/next/client/performance-relayer.ts b/packages/next/client/performance-relayer.ts index c1d87fed8784b..685b9289c78a2 100644 --- a/packages/next/client/performance-relayer.ts +++ b/packages/next/client/performance-relayer.ts @@ -32,7 +32,7 @@ function onReport(metric: Metric): void { const body: Record = { dsn: process.env.__NEXT_ANALYTICS_ID, id: metric.id, - page: window.__NEXT_DATA__.page, + page: window.__NEXT_DATA__?.page, href: initialHref, event_name: metric.name, value: metric.value.toString(), diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.development.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.development.js index 7bee808a29046..64752adc0b44d 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.development.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.development.js @@ -57,9 +57,13 @@ function resolveModuleReference(bundlerConfig, moduleData) { // replicate it in user space. null means that it has already loaded. var chunkCache = new Map(); -var asyncModuleCache = new Map(); // Start preloading the modules since we might need them soon. +var asyncModuleCache = new Map(); + +function ignoreReject() {// We rely on rejected promises to be handled by another listener. +} // Start preloading the modules since we might need them soon. // This function doesn't suspend. + function preloadModule(moduleData) { var chunks = moduleData.chunks; var promises = []; @@ -73,9 +77,10 @@ function preloadModule(moduleData) { promises.push(thenable); var resolve = chunkCache.set.bind(chunkCache, chunkId, null); - var reject = chunkCache.set.bind(chunkCache, chunkId); - thenable.then(resolve, reject); + thenable.then(resolve, ignoreReject); chunkCache.set(chunkId, thenable); + } else if (entry !== null) { + promises.push(entry); } } diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.production.min.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.production.min.js index 8436cfe0d149c..de1bcd821c6ac 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.production.min.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack.production.min.js @@ -7,20 +7,20 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict';var k=require("react"),l={stream:!0};function m(a,b){return a?(a=a[b.id][b.name],b.async?{id:a.id,chunks:a.chunks,name:a.name,async:!0}:a):b}var n=new Map,p=new Map; -function q(a){for(var b=a.chunks,c=[],d=0;d=12.22.0" diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 3b90c674ceb4f..4e4112a428ab7 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -2,7 +2,8 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { LoadComponentsReturnType } from './load-components' import type { ServerRuntime } from '../types' -import React from 'react' +// TODO-APP: change to React.use once it becomes stable +import React, { experimental_use as use } from 'react' import { ParsedUrlQuery, stringify as stringifyQuery } from 'querystring' import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack' import { renderToReadableStream } from 'next/dist/compiled/react-server-dom-webpack/writer.browser.server' @@ -23,13 +24,10 @@ import { FlightCSSManifest, FlightManifest, } from '../build/webpack/plugins/flight-manifest-plugin' -import { FlushEffectsContext } from '../client/components/hooks-client' +import { FlushEffectsContext } from '../shared/lib/flush-effects' import { stripInternalQueries } from './internal-utils' import type { ComponentsType } from '../build/webpack/loaders/next-app-loader' -// TODO-APP: change to React.use once it becomes stable -const use = (React as any).experimental_use - // this needs to be required lazily so that `next-server` can set // the env before we require const ReactDOMServer = shouldUseReactRoot @@ -49,6 +47,15 @@ export type RenderOptsPartial = { export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial +/** + * Flight Response is always set to application/octet-stream to ensure it does not + */ +class FlightRenderResult extends RenderResult { + constructor(response: string | ReadableStream) { + super(response, { contentType: 'application/octet-stream' }) + } +} + /** * Interop between "export default" and "module.exports". */ @@ -118,6 +125,10 @@ function patchFetch() { } } +interface FlightResponseRef { + current: Promise | null +} + /** * Render Flight stream. * This is only used for renderToHTML, the Flight response does not need additional wrappers. @@ -126,20 +137,19 @@ function useFlightResponse( writable: WritableStream, req: ReadableStream, serverComponentManifest: any, - flightResponseRef: { - current: ReturnType | null - }, rscChunks: Uint8Array[], + flightResponseRef: FlightResponseRef, nonce?: string -) { - if (flightResponseRef.current) { +): Promise { + if (flightResponseRef.current !== null) { return flightResponseRef.current } const [renderStream, forwardStream] = readableStreamTee(req) - flightResponseRef.current = createFromReadableStream(renderStream, { + const res = createFromReadableStream(renderStream, { moduleMap: serverComponentManifest.__ssr_module_mapping__, }) + flightResponseRef.current = res let bootstrapped = false // We only attach CSS chunks to the inlined data. @@ -181,7 +191,7 @@ function useFlightResponse( } process() - return flightResponseRef.current + return res } /** @@ -210,7 +220,7 @@ function createServerComponentRenderer( rscChunks: Uint8Array[] }, nonce?: string -) { +): () => JSX.Element { // We need to expose the `__webpack_require__` API globally for // react-server-dom-webpack. This is a hack until we find a better way. if (ComponentMod.__next_app_webpack_require__ || ComponentMod.__next_rsc__) { @@ -238,17 +248,17 @@ function createServerComponentRenderer( return RSCStream } - const flightResponseRef = { current: null } + const flightResponseRef: FlightResponseRef = { current: null } const writable = transformStream.writable - return function ServerComponentWrapper() { + return function ServerComponentWrapper(): JSX.Element { const reqStream = createRSCStream() const response = useFlightResponse( writable, reqStream, serverComponentManifest, - flightResponseRef, rscChunks, + flightResponseRef, nonce ) return use(response) @@ -503,7 +513,7 @@ export async function renderToHTMLOrFlight( // Empty so that the client-side router will do a full page navigation. const flightData: FlightData = pathname + (search ? `?${search}` : '') - return new RenderResult( + return new FlightRenderResult( renderToReadableStream(flightData, serverComponentManifest, { onError, }).pipeThrough(createBufferedTransformStream()) @@ -1006,9 +1016,9 @@ export async function renderToHTMLOrFlight( let staticHtml = Buffer.from( (await readable.getReader().read()).value || '' ).toString() - return new RenderResult(staticHtml) + return new FlightRenderResult(staticHtml) } - return new RenderResult(readable) + return new FlightRenderResult(readable) } // Below this line is handling for rendering to HTML. diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index c0b2ea637e37a..e4ebb99edcfad 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -20,7 +20,11 @@ import * as Log from '../../build/output/log' import getBaseWebpackConfig from '../../build/webpack-config' import { APP_DIR_ALIAS } from '../../lib/constants' import { recursiveDelete } from '../../lib/recursive-delete' -import { BLOCKED_PAGES, COMPILER_NAMES } from '../../shared/lib/constants' +import { + BLOCKED_PAGES, + COMPILER_NAMES, + RSC_MODULE_TYPES, +} from '../../shared/lib/constants' import { __ApiPreviewProps } from '../api-utils' import { getPathMatch } from '../../shared/lib/router/utils/path-match' import { findPageFile } from '../lib/find-page-file' @@ -35,18 +39,13 @@ import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep' import getRouteFromEntrypoint from '../get-route-from-entrypoint' import { fileExists } from '../../lib/file-exists' -import { - difference, - withoutRSCExtensions, - isMiddlewareFilename, -} from '../../build/utils' +import { difference, isMiddlewareFilename } from '../../build/utils' import { DecodeError } from '../../shared/lib/utils' import { Span, trace } from '../../trace' import { getProperError } from '../../lib/is-error' import ws from 'next/dist/compiled/ws' import { promises as fs } from 'fs' import { getPageStaticInfo } from '../../build/analysis/get-page-static-info' -import { serverComponentRegex } from '../../build/webpack/loaders/utils' import { UnwrapPromise } from '../../lib/coalesced-function' function diff(a: Set, b: Set) { @@ -404,9 +403,7 @@ export default class HotReloader { private async getWebpackConfig(span: Span) { const webpackConfigSpan = span.traceChild('get-webpack-config') - const rawPageExtensions = this.hasServerComponents - ? withoutRSCExtensions(this.config.pageExtensions) - : this.config.pageExtensions + const pageExtensions = this.config.pageExtensions return webpackConfigSpan.traceAsyncFn(async () => { const pagePaths = !this.pagesDir @@ -415,11 +412,11 @@ export default class HotReloader { .traceChild('get-page-paths') .traceAsyncFn(() => Promise.all([ - findPageFile(this.pagesDir!, '/_app', rawPageExtensions, false), + findPageFile(this.pagesDir!, '/_app', pageExtensions, false), findPageFile( this.pagesDir!, '/_document', - rawPageExtensions, + pageExtensions, false ), ]) @@ -429,7 +426,6 @@ export default class HotReloader { .traceChild('create-pages-mapping') .traceFn(() => createPagesMapping({ - hasServerComponents: this.hasServerComponents, isDev: true, pageExtensions: this.config.pageExtensions, pagesType: 'pages', @@ -444,6 +440,7 @@ export default class HotReloader { .traceChild('create-entrypoints') .traceAsyncFn(() => createEntrypoints({ + appDir: this.appDir, buildId: this.buildId, config: this.config, envFiles: [], @@ -511,6 +508,7 @@ export default class HotReloader { isDevFallback: true, entrypoints: ( await createEntrypoints({ + appDir: this.appDir, buildId: this.buildId, config: this.config, envFiles: [], @@ -598,10 +596,7 @@ export default class HotReloader { } } - const isServerComponent = isEntry - ? serverComponentRegex.test(entryData.absolutePagePath) - : false - + const isAppPath = !!this.appDir && bundlePath.startsWith('app/') const staticInfo = isEntry ? await getPageStaticInfo({ pageFilePath: entryData.absolutePagePath, @@ -609,6 +604,8 @@ export default class HotReloader { isDev: true, }) : {} + const isServerComponent = + isAppPath && staticInfo.rsc !== RSC_MODULE_TYPES.client await runDependingOnPageType({ page, @@ -616,9 +613,8 @@ export default class HotReloader { onEdgeServer: () => { // TODO-APP: verify if child entry should support. if (!isEdgeServerCompilation || !isEntry) return - const isApp = this.appDir && bundlePath.startsWith('app/') const appDirLoader = - isApp && this.appDir + isAppPath && this.appDir ? getAppEntry({ name: bundlePath, appPaths: entryData.appPaths, @@ -649,7 +645,7 @@ export default class HotReloader { pages: this.pagesMapping, isServerComponent, appDirLoader, - pagesType: isApp ? 'app' : undefined, + pagesType: isAppPath ? 'app' : undefined, }), appDir: this.config.experimental.appDir, }) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 4784fcf4489f2..a564c71b20122 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -12,7 +12,6 @@ import { ensureLeadingSlash } from '../../shared/lib/page-path/ensure-leading-sl import { removePagePathTail } from '../../shared/lib/page-path/remove-page-path-tail' import { reportTrigger } from '../../build/output' import getRouteFromEntrypoint from '../get-route-from-entrypoint' -import { serverComponentRegex } from '../../build/webpack/loaders/utils' import { getPageStaticInfo } from '../../build/analysis/get-page-static-info' import { isMiddlewareFile, isMiddlewareFilename } from '../../build/utils' import { PageNotFoundError } from '../../shared/lib/utils' @@ -21,6 +20,7 @@ import { CompilerNameValues, COMPILER_INDEXES, COMPILER_NAMES, + RSC_MODULE_TYPES, } from '../../shared/lib/constants' const debug = origDebug('next:on-demand-entry-handler') @@ -578,11 +578,8 @@ export function onDemandEntryHandler({ appDir ) - const isServerComponent = serverComponentRegex.test( - pagePathData.absolutePagePath - ) const isInsideAppDir = - appDir && pagePathData.absolutePagePath.startsWith(appDir) + !!appDir && pagePathData.absolutePagePath.startsWith(appDir) const addEntry = ( compilerType: CompilerNameValues @@ -636,6 +633,8 @@ export function onDemandEntryHandler({ }) const added = new Map>() + const isServerComponent = + isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client await runDependingOnPageType({ page: pagePathData.page, diff --git a/packages/next/server/render-result.ts b/packages/next/server/render-result.ts index e7c2cd51b4b63..20b22c3eff228 100644 --- a/packages/next/server/render-result.ts +++ b/packages/next/server/render-result.ts @@ -1,10 +1,21 @@ import type { ServerResponse } from 'http' +type ContentTypeOption = string | undefined + export default class RenderResult { - _result: string | ReadableStream + private _result: string | ReadableStream + private _contentType: ContentTypeOption - constructor(response: string | ReadableStream) { + constructor( + response: string | ReadableStream, + { contentType }: { contentType?: ContentTypeOption } = {} + ) { this._result = response + this._contentType = contentType + } + + contentType(): ContentTypeOption { + return this._contentType } toUnchunkedString(): string { diff --git a/packages/next/server/send-payload/index.ts b/packages/next/server/send-payload/index.ts index ed4b3e78d3c59..fc2be15b23445 100644 --- a/packages/next/server/send-payload/index.ts +++ b/packages/next/server/send-payload/index.ts @@ -71,10 +71,16 @@ export async function sendRenderResult({ } } + const resultContentType = result.contentType() + if (!res.getHeader('Content-Type')) { res.setHeader( 'Content-Type', - type === 'json' ? 'application/json' : 'text/html; charset=utf-8' + resultContentType + ? resultContentType + : type === 'json' + ? 'application/json' + : 'text/html; charset=utf-8' ) } diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index db19c6ce486d4..2326ec5bf8136 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -370,10 +370,14 @@ export default class NextWebServer extends BaseServer { if (options.poweredByHeader && options.type === 'html') { res.setHeader('X-Powered-By', 'Next.js') } + const resultContentType = options.result.contentType() + if (!res.getHeader('Content-Type')) { res.setHeader( 'Content-Type', - options.type === 'json' + resultContentType + ? resultContentType + : options.type === 'json' ? 'application/json' : 'text/html; charset=utf-8' ) diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index f9f739ca36e0d..fe527c83685e7 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -96,6 +96,11 @@ export const DEFAULT_SANS_SERIF_FONT = 'Arial' export const STATIC_STATUS_PAGES = ['/500'] export const TRACE_OUTPUT_VERSION = 1 +export const RSC_MODULE_TYPES = { + client: 'client', + server: 'server', +} as const + // comparing // https://nextjs.org/docs/api-reference/edge-runtime // with diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index cda9125962a07..c0e6bd190168d 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -655,6 +655,14 @@ interface FetchNextDataParams { unstable_skipClientCache?: boolean } +function handleSmoothScroll(fn: () => void) { + const htmlElement = document.documentElement + const existing = htmlElement.style.scrollBehavior + htmlElement.style.scrollBehavior = 'auto' + fn() + htmlElement.style.scrollBehavior = existing +} + function tryToParseAsJSON(text: string) { try { return JSON.parse(text) @@ -2141,7 +2149,7 @@ export default class Router implements BaseRouter { // Scroll to top if the hash is just `#` with no value or `#top` // To mirror browsers if (hash === '' || hash === 'top') { - window.scrollTo(0, 0) + handleSmoothScroll(() => window.scrollTo(0, 0)) return } @@ -2150,14 +2158,14 @@ export default class Router implements BaseRouter { // First we check if the element by id is found const idEl = document.getElementById(rawHash) if (idEl) { - idEl.scrollIntoView() + handleSmoothScroll(() => idEl.scrollIntoView()) return } // If there's no element with the id, we check the `name` property // To mirror browsers const nameEl = document.getElementsByName(rawHash)[0] if (nameEl) { - nameEl.scrollIntoView() + handleSmoothScroll(() => nameEl.scrollIntoView()) } } diff --git a/packages/next/taskfile-swc.js b/packages/next/taskfile-swc.js index 8699201451120..bf61332c316a8 100644 --- a/packages/next/taskfile-swc.js +++ b/packages/next/taskfile-swc.js @@ -114,7 +114,7 @@ module.exports = function (task) { // Make sure the output content keeps the `"client"` directive. // TODO: Remove this once SWC fixes the issue. - if (source.startsWith("'client'")) { + if (/^['"]client['"]/.test(source)) { output.code = '"client";\n' + output.code } diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index 25b63fe255331..28866d91b134c 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -39,6 +39,9 @@ declare module 'react' { interface LinkHTMLAttributes extends HTMLAttributes { nonce?: string } + + // TODO-APP: check if this is the right type. + function experimental_use(promise: Promise): Awaited } export type Redirect = diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08b7b467097dc..20b007f183525 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,7 +2,7 @@ lockfileVersion: 5.4 overrides: browserslist: 4.20.2 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 '@babel/core': 7.18.0 '@babel/parser': 7.18.0 '@babel/types': 7.18.0 @@ -140,8 +140,8 @@ importers: react-17: npm:react@17.0.2 react-dom: 18.2.0 react-dom-17: npm:react-dom@17.0.2 - react-dom-exp: npm:react-dom@0.0.0-experimental-e6a062bd2-20220913 - react-exp: npm:react@0.0.0-experimental-e6a062bd2-20220913 + react-dom-exp: npm:react-dom@0.0.0-experimental-8951c5fc9-20220915 + react-exp: npm:react@0.0.0-experimental-8951c5fc9-20220915 react-ssr-prepass: 1.0.8 react-virtualized: 9.22.3 relay-compiler: 13.0.2 @@ -295,8 +295,8 @@ importers: react-17: /react/17.0.2 react-dom: 18.2.0_react@18.2.0 react-dom-17: /react-dom/17.0.2_react@18.2.0 - react-dom-exp: /react-dom/0.0.0-experimental-e6a062bd2-20220913_react@18.2.0 - react-exp: /react/0.0.0-experimental-e6a062bd2-20220913 + react-dom-exp: /react-dom/0.0.0-experimental-8951c5fc9-20220915_react@18.2.0 + react-exp: /react/0.0.0-experimental-8951c5fc9-20220915 react-ssr-prepass: 1.0.8_qncsgtzehe3fgiqp6tr7lwq6fm react-virtualized: 9.22.3_biqbaboplfbrettd7655fr4n2y relay-compiler: 13.0.2 @@ -481,7 +481,7 @@ importers: browserslist: 4.20.2 buffer: 5.6.0 bytes: 3.1.1 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 chalk: 2.4.2 ci-info: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540 cli-select: 1.1.2 @@ -548,7 +548,7 @@ importers: raw-body: 2.4.1 react-is: 17.0.2 react-refresh: 0.12.0 - react-server-dom-webpack: 0.0.0-experimental-975b64464-20220914 + react-server-dom-webpack: 0.0.0-experimental-8951c5fc9-20220915 regenerator-runtime: 0.13.4 sass-loader: 12.4.0 schema-utils2: npm:schema-utils@2.7.1 @@ -584,7 +584,7 @@ importers: dependencies: '@next/env': link:../next-env '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 postcss: 8.4.14 styled-jsx: 5.0.7_@babel+core@7.18.0 use-sync-external-store: 1.2.0 @@ -737,7 +737,7 @@ importers: raw-body: 2.4.1 react-is: 17.0.2 react-refresh: 0.12.0 - react-server-dom-webpack: 0.0.0-experimental-975b64464-20220914_webpack@5.74.0 + react-server-dom-webpack: 0.0.0-experimental-8951c5fc9-20220915_webpack@5.74.0 regenerator-runtime: 0.13.4 sass-loader: 12.4.0_webpack@5.74.0 schema-utils2: /schema-utils/2.7.1 @@ -9051,7 +9051,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.20.2 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -9070,7 +9070,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.20.2 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -9087,7 +9087,7 @@ packages: hasBin: true dependencies: browserslist: 4.20.2 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 chalk: 2.4.2 normalize-range: 0.1.2 num2fraction: 1.2.2 @@ -9749,7 +9749,7 @@ packages: engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true dependencies: - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 electron-to-chromium: 1.4.118 escalade: 3.1.1 node-releases: 2.0.3 @@ -10147,15 +10147,15 @@ packages: } dependencies: browserslist: 4.20.2 - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite/1.0.30001332: + /caniuse-lite/1.0.30001406: resolution: { - integrity: sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==, + integrity: sha512-bWTlaXUy/rq0BBtYShc/jArYfBPjEV95euvZ8JVtO43oQExEN/WquoqpufFjNu4kSpi5cy5kMbNvzztWDfv1Jg==, } /capital-case/1.0.4: @@ -11894,7 +11894,7 @@ packages: peerDependencies: postcss: ^8.2.1 dependencies: - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 postcss: 8.2.13 dev: true @@ -11906,7 +11906,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - caniuse-lite: 1.0.30001332 + caniuse-lite: 1.0.30001406 postcss: 8.4.14 dev: true @@ -24369,17 +24369,17 @@ packages: strip-json-comments: 2.0.1 dev: true - /react-dom/0.0.0-experimental-e6a062bd2-20220913_react@18.2.0: + /react-dom/0.0.0-experimental-8951c5fc9-20220915_react@18.2.0: resolution: { - integrity: sha512-4e/0ZV5I4gUWRw6EohI9lEglfGYcSxPWoUcAXVLvRxzLTfFqnuLFplu33Z6Bvrs9Jnr6skDdskyyzFn0rRSJnQ==, + integrity: sha512-zRK9YAhohnYYJ4WhtZ1MyfR1w2XA+yiaAJAqx5Zg6Wyi0tHE1tQfhmoF7wdI4k3ZTvPjQO+IxkI+/sfOcu6OTA==, } peerDependencies: - react: 0.0.0-experimental-e6a062bd2-20220913 + react: 0.0.0-experimental-8951c5fc9-20220915 dependencies: loose-envify: 1.4.0 react: 18.2.0 - scheduler: 0.0.0-experimental-e6a062bd2-20220913 + scheduler: 0.0.0-experimental-8951c5fc9-20220915 dev: true /react-dom/17.0.2_react@18.2.0: @@ -24444,14 +24444,14 @@ packages: engines: { node: '>=0.10.0' } dev: true - /react-server-dom-webpack/0.0.0-experimental-975b64464-20220914_webpack@5.74.0: + /react-server-dom-webpack/0.0.0-experimental-8951c5fc9-20220915_webpack@5.74.0: resolution: { - integrity: sha512-RU8yWstn6EpbprfOEnpCpp4fzSTgRLIjvWjJYZq2AiVrXXaAl54stVC1LlZ5Ox4hNXBc6K/FAuQqF4nopXx5Aw==, + integrity: sha512-gIz7hb0Sp8v3y66K5sETigHSC1VS2k/w2UvJ5vuIr+oXsp30M3QzINxHKVuausgIP+2X8c5J9BZukh3fMutiyw==, } engines: { node: '>=0.10.0' } peerDependencies: - react: 0.0.0-experimental-975b64464-20220914 + react: 0.0.0-experimental-8951c5fc9-20220915 webpack: ^5.59.0 dependencies: acorn: 6.4.2 @@ -24493,10 +24493,10 @@ packages: react-lifecycles-compat: 3.0.4 dev: true - /react/0.0.0-experimental-e6a062bd2-20220913: + /react/0.0.0-experimental-8951c5fc9-20220915: resolution: { - integrity: sha512-osFjPYWDSg98xRjj0RDMF9gLczBcixdsC3vb1+vq6tw99Po31KgkiIfLtLMYGjD6qmASXlMVKZgFDbYDNGGC9Q==, + integrity: sha512-/pGYDqlIZAF4UMSTvlTU1Gd9etFqsUAT51vebc9Az13tE0hV0dmFdHa4T79Qx1JJ6Bd146X2RjScBhbx/ROmQA==, } engines: { node: '>=0.10.0' } dependencies: @@ -25838,10 +25838,10 @@ packages: xmlchars: 2.2.0 dev: true - /scheduler/0.0.0-experimental-e6a062bd2-20220913: + /scheduler/0.0.0-experimental-8951c5fc9-20220915: resolution: { - integrity: sha512-Rv+woqQhdMt6JaiDCHpRsbG0f3LxIYKX/eo3tK+KIqi4TiTYZAYTxO5HJXvJgKPHegAtKMcjm2D3HXDWl2m/nw==, + integrity: sha512-3ar+dNPu8yBC0DK1aEgT+dPUwEFoVQjVmsjcqKuYK7inTwsgQkDeTlGVQV1ELULf9vUwyuodHjGBFQI+XBQ0bg==, } dependencies: loose-envify: 1.4.0 diff --git a/test/e2e/app-dir/app-prefetch/app/dashboard/[id]/page.server.js b/test/e2e/app-dir/app-prefetch/app/dashboard/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app-prefetch/app/dashboard/[id]/page.server.js rename to test/e2e/app-dir/app-prefetch/app/dashboard/[id]/page.js diff --git a/test/e2e/app-dir/app-prefetch/app/dashboard/layout.server.js b/test/e2e/app-dir/app-prefetch/app/dashboard/layout.js similarity index 100% rename from test/e2e/app-dir/app-prefetch/app/dashboard/layout.server.js rename to test/e2e/app-dir/app-prefetch/app/dashboard/layout.js diff --git a/test/e2e/app-dir/app-prefetch/app/dashboard/page.server.js b/test/e2e/app-dir/app-prefetch/app/dashboard/page.js similarity index 100% rename from test/e2e/app-dir/app-prefetch/app/dashboard/page.server.js rename to test/e2e/app-dir/app-prefetch/app/dashboard/page.js diff --git a/test/e2e/app-dir/app-prefetch/app/layout.server.js b/test/e2e/app-dir/app-prefetch/app/layout.js similarity index 100% rename from test/e2e/app-dir/app-prefetch/app/layout.server.js rename to test/e2e/app-dir/app-prefetch/app/layout.js diff --git a/test/e2e/app-dir/app-prefetch/app/page.server.js b/test/e2e/app-dir/app-prefetch/app/page.js similarity index 100% rename from test/e2e/app-dir/app-prefetch/app/page.server.js rename to test/e2e/app-dir/app-prefetch/app/page.js diff --git a/test/e2e/app-dir/app-prefetch/pages/.gitkeep b/test/e2e/app-dir/app-prefetch/pages/.gitkeep deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/e2e/app-dir/app-rendering/app/layout.server.js b/test/e2e/app-dir/app-rendering/app/layout.js similarity index 100% rename from test/e2e/app-dir/app-rendering/app/layout.server.js rename to test/e2e/app-dir/app-rendering/app/layout.js diff --git a/test/e2e/app-dir/app-rendering/app/page.server.js b/test/e2e/app-dir/app-rendering/app/page.js similarity index 100% rename from test/e2e/app-dir/app-rendering/app/page.server.js rename to test/e2e/app-dir/app-rendering/app/page.js diff --git a/test/e2e/app-dir/app/app/(newroot)/dashboard/another/page.server.js b/test/e2e/app-dir/app/app/(newroot)/dashboard/another/page.js similarity index 100% rename from test/e2e/app-dir/app/app/(newroot)/dashboard/another/page.server.js rename to test/e2e/app-dir/app/app/(newroot)/dashboard/another/page.js diff --git a/test/e2e/app-dir/app/app/(newroot)/layout.server.js b/test/e2e/app-dir/app/app/(newroot)/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/(newroot)/layout.server.js rename to test/e2e/app-dir/app/app/(newroot)/layout.js diff --git a/test/e2e/app-dir/app/app/(rootonly)/dashboard/changelog/page.server.js b/test/e2e/app-dir/app/app/(rootonly)/dashboard/changelog/page.js similarity index 100% rename from test/e2e/app-dir/app/app/(rootonly)/dashboard/changelog/page.server.js rename to test/e2e/app-dir/app/app/(rootonly)/dashboard/changelog/page.js diff --git a/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.server.js b/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js similarity index 100% rename from test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.server.js rename to test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js diff --git a/test/e2e/app-dir/app/app/catch-all/[...slug]/page.server.js b/test/e2e/app-dir/app/app/catch-all/[...slug]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/catch-all/[...slug]/page.server.js rename to test/e2e/app-dir/app/app/catch-all/[...slug]/page.js diff --git a/test/e2e/app-dir/app/app/client-component-route/page.client.js b/test/e2e/app-dir/app/app/client-component-route/page.js similarity index 100% rename from test/e2e/app-dir/app/app/client-component-route/page.client.js rename to test/e2e/app-dir/app/app/client-component-route/page.js diff --git a/test/e2e/app-dir/app/app/client-nested/layout.client.js b/test/e2e/app-dir/app/app/client-nested/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/client-nested/layout.client.js rename to test/e2e/app-dir/app/app/client-nested/layout.js diff --git a/test/e2e/app-dir/app/app/client-nested/page.server.js b/test/e2e/app-dir/app/app/client-nested/page.js similarity index 100% rename from test/e2e/app-dir/app/app/client-nested/page.server.js rename to test/e2e/app-dir/app/app/client-nested/page.js diff --git a/test/e2e/app-dir/app/app/client-with-errors/get-server-side-props/page.client.js b/test/e2e/app-dir/app/app/client-with-errors/get-server-side-props/page.js similarity index 100% rename from test/e2e/app-dir/app/app/client-with-errors/get-server-side-props/page.client.js rename to test/e2e/app-dir/app/app/client-with-errors/get-server-side-props/page.js diff --git a/test/e2e/app-dir/app/app/client-with-errors/get-static-props/page.client.js b/test/e2e/app-dir/app/app/client-with-errors/get-static-props/page.js similarity index 100% rename from test/e2e/app-dir/app/app/client-with-errors/get-static-props/page.client.js rename to test/e2e/app-dir/app/app/client-with-errors/get-static-props/page.js diff --git a/test/e2e/app-dir/app/app/css/css-client/layout.client.js b/test/e2e/app-dir/app/app/css/css-client/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-client/layout.client.js rename to test/e2e/app-dir/app/app/css/css-client/layout.js diff --git a/test/e2e/app-dir/app/app/css/css-client/page.client.js b/test/e2e/app-dir/app/app/css/css-client/page.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-client/page.client.js rename to test/e2e/app-dir/app/app/css/css-client/page.js diff --git a/test/e2e/app-dir/app/app/css/css-nested/layout.client.js b/test/e2e/app-dir/app/app/css/css-nested/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-nested/layout.client.js rename to test/e2e/app-dir/app/app/css/css-nested/layout.js diff --git a/test/e2e/app-dir/app/app/css/css-nested/page.client.js b/test/e2e/app-dir/app/app/css/css-nested/page.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-nested/page.client.js rename to test/e2e/app-dir/app/app/css/css-nested/page.js diff --git a/test/e2e/app-dir/app/app/css/css-page/layout.server.js b/test/e2e/app-dir/app/app/css/css-page/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-page/layout.server.js rename to test/e2e/app-dir/app/app/css/css-page/layout.js diff --git a/test/e2e/app-dir/app/app/css/css-page/page.server.js b/test/e2e/app-dir/app/app/css/css-page/page.js similarity index 100% rename from test/e2e/app-dir/app/app/css/css-page/page.server.js rename to test/e2e/app-dir/app/app/css/css-page/page.js diff --git a/test/e2e/app-dir/app/app/css/layout.server.js b/test/e2e/app-dir/app/app/css/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/css/layout.server.js rename to test/e2e/app-dir/app/app/css/layout.js diff --git a/test/e2e/app-dir/app/app/dashboard/(custom)/deployments/breakdown/page.server.js b/test/e2e/app-dir/app/app/dashboard/(custom)/deployments/breakdown/page.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/(custom)/deployments/breakdown/page.server.js rename to test/e2e/app-dir/app/app/dashboard/(custom)/deployments/breakdown/page.js diff --git a/test/e2e/app-dir/app/app/dashboard/(custom)/layout.server.js b/test/e2e/app-dir/app/app/dashboard/(custom)/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/(custom)/layout.server.js rename to test/e2e/app-dir/app/app/dashboard/(custom)/layout.js diff --git a/test/e2e/app-dir/app/app/dashboard/client-comp.client.jsx b/test/e2e/app-dir/app/app/dashboard/client-comp-client.jsx similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/client-comp.client.jsx rename to test/e2e/app-dir/app/app/dashboard/client-comp-client.jsx diff --git a/test/e2e/app-dir/app/app/dashboard/deployments/[id]/page.server.js b/test/e2e/app-dir/app/app/dashboard/deployments/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/deployments/[id]/page.server.js rename to test/e2e/app-dir/app/app/dashboard/deployments/[id]/page.js diff --git a/test/e2e/app-dir/app/app/dashboard/deployments/info/page.server.js b/test/e2e/app-dir/app/app/dashboard/deployments/info/page.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/deployments/info/page.server.js rename to test/e2e/app-dir/app/app/dashboard/deployments/info/page.js diff --git a/test/e2e/app-dir/app/app/dashboard/deployments/layout.server.js b/test/e2e/app-dir/app/app/dashboard/deployments/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/deployments/layout.server.js rename to test/e2e/app-dir/app/app/dashboard/deployments/layout.js diff --git a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.client.js b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-client.js similarity index 69% rename from test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.client.js rename to test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-client.js index ccaf16110d81e..63dcccc63c1cf 100644 --- a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.client.js +++ b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-client.js @@ -2,7 +2,7 @@ import dynamic from 'next/dist/client/components/shared/dynamic' -const Dynamic = dynamic(() => import('../text-dynamic.client')) +const Dynamic = dynamic(() => import('../text-dynamic-client')) export function NextDynamicClientComponent() { return diff --git a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.server.js b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-server.js similarity index 68% rename from test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.server.js rename to test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-server.js index 18ac428cfc37c..0d9406e90f4a0 100644 --- a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic.server.js +++ b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/dynamic-server.js @@ -1,6 +1,6 @@ import dynamic from 'next/dist/client/components/shared/dynamic' -const Dynamic = dynamic(() => import('../text-dynamic.server')) +const Dynamic = dynamic(() => import('../text-dynamic-server')) export function NextDynamicServerComponent() { return diff --git a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy.client.js b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy-client.js similarity index 79% rename from test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy.client.js rename to test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy-client.js index 85727b55afd73..18fff4ea973d6 100644 --- a/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy.client.js +++ b/test/e2e/app-dir/app/app/dashboard/index/dynamic-imports/react-lazy-client.js @@ -2,7 +2,7 @@ import { useState, lazy } from 'react' -const Lazy = lazy(() => import('../text-lazy.client.js')) +const Lazy = lazy(() => import('../text-lazy-client.js')) export function LazyClientComponent() { let [state] = useState('client') diff --git a/test/e2e/app-dir/app/app/dashboard/index/page.server.js b/test/e2e/app-dir/app/app/dashboard/index/page.js similarity index 91% rename from test/e2e/app-dir/app/app/dashboard/index/page.server.js rename to test/e2e/app-dir/app/app/dashboard/index/page.js index 6dca33b19e801..c946b76e18cc9 100644 --- a/test/e2e/app-dir/app/app/dashboard/index/page.server.js +++ b/test/e2e/app-dir/app/app/dashboard/index/page.js @@ -1,6 +1,6 @@ -import { LazyClientComponent } from './dynamic-imports/react-lazy.client' -import { NextDynamicServerComponent } from './dynamic-imports/dynamic.server' -import { NextDynamicClientComponent } from './dynamic-imports/dynamic.client' +import { LazyClientComponent } from './dynamic-imports/react-lazy-client' +import { NextDynamicServerComponent } from './dynamic-imports/dynamic-server' +import { NextDynamicClientComponent } from './dynamic-imports/dynamic-client' export default function DashboardIndexPage() { return ( diff --git a/test/e2e/app-dir/app/app/dashboard/index/text-dynamic.client.js b/test/e2e/app-dir/app/app/dashboard/index/text-dynamic-client.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/index/text-dynamic.client.js rename to test/e2e/app-dir/app/app/dashboard/index/text-dynamic-client.js diff --git a/test/e2e/app-dir/app/app/dashboard/index/text-dynamic.server.js b/test/e2e/app-dir/app/app/dashboard/index/text-dynamic-server.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/index/text-dynamic.server.js rename to test/e2e/app-dir/app/app/dashboard/index/text-dynamic-server.js diff --git a/test/e2e/app-dir/app/app/dashboard/index/text-lazy.client.js b/test/e2e/app-dir/app/app/dashboard/index/text-lazy-client.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/index/text-lazy.client.js rename to test/e2e/app-dir/app/app/dashboard/index/text-lazy-client.js diff --git a/test/e2e/app-dir/app/app/dashboard/integrations/page.server.js b/test/e2e/app-dir/app/app/dashboard/integrations/page.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/integrations/page.server.js rename to test/e2e/app-dir/app/app/dashboard/integrations/page.js diff --git a/test/e2e/app-dir/app/app/dashboard/layout.server.js b/test/e2e/app-dir/app/app/dashboard/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dashboard/layout.server.js rename to test/e2e/app-dir/app/app/dashboard/layout.js diff --git a/test/e2e/app-dir/app/app/dashboard/page.server.js b/test/e2e/app-dir/app/app/dashboard/page.js similarity index 87% rename from test/e2e/app-dir/app/app/dashboard/page.server.js rename to test/e2e/app-dir/app/app/dashboard/page.js index a4773bfaba832..966a2a3e03b0b 100644 --- a/test/e2e/app-dir/app/app/dashboard/page.server.js +++ b/test/e2e/app-dir/app/app/dashboard/page.js @@ -1,4 +1,5 @@ -import ClientComp from './client-comp.client' +import ClientComp from './client-comp-client' + export default function DashboardPage(props) { return ( <> diff --git a/test/e2e/app-dir/app/app/dynamic/[category]/[id]/layout.server.js b/test/e2e/app-dir/app/app/dynamic/[category]/[id]/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dynamic/[category]/[id]/layout.server.js rename to test/e2e/app-dir/app/app/dynamic/[category]/[id]/layout.js diff --git a/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.server.js b/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.server.js rename to test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js diff --git a/test/e2e/app-dir/app/app/dynamic/[category]/layout.server.js b/test/e2e/app-dir/app/app/dynamic/[category]/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dynamic/[category]/layout.server.js rename to test/e2e/app-dir/app/app/dynamic/[category]/layout.js diff --git a/test/e2e/app-dir/app/app/dynamic/layout.server.js b/test/e2e/app-dir/app/app/dynamic/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/dynamic/layout.server.js rename to test/e2e/app-dir/app/app/dynamic/layout.js diff --git a/test/e2e/app-dir/app/app/error/clientcomponent/error.client.js b/test/e2e/app-dir/app/app/error/clientcomponent/error.js similarity index 100% rename from test/e2e/app-dir/app/app/error/clientcomponent/error.client.js rename to test/e2e/app-dir/app/app/error/clientcomponent/error.js diff --git a/test/e2e/app-dir/app/app/error/clientcomponent/page.client.js b/test/e2e/app-dir/app/app/error/clientcomponent/page.js similarity index 100% rename from test/e2e/app-dir/app/app/error/clientcomponent/page.client.js rename to test/e2e/app-dir/app/app/error/clientcomponent/page.js diff --git a/test/e2e/app-dir/app/app/error/ssr-error-client-component/page.client.js b/test/e2e/app-dir/app/app/error/ssr-error-client-component/page.js similarity index 100% rename from test/e2e/app-dir/app/app/error/ssr-error-client-component/page.client.js rename to test/e2e/app-dir/app/app/error/ssr-error-client-component/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-cookies/client/page.client.js b/test/e2e/app-dir/app/app/hooks/use-cookies/client/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-cookies/client/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-cookies/client/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-cookies/page.server.js b/test/e2e/app-dir/app/app/hooks/use-cookies/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-cookies/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-cookies/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-headers/client/page.client.js b/test/e2e/app-dir/app/app/hooks/use-headers/client/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-headers/client/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-headers/client/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-headers/page.server.js b/test/e2e/app-dir/app/app/hooks/use-headers/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-headers/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-headers/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-layout-segments/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-layout-segments/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-layout-segments/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-layout-segments/server/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-params/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-params/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-params/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-params/server/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-pathname/page.client.js b/test/e2e/app-dir/app/app/hooks/use-pathname/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-pathname/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-pathname/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-pathname/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-pathname/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-pathname/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-pathname/server/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-preview-data/client/page.client.js b/test/e2e/app-dir/app/app/hooks/use-preview-data/client/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-preview-data/client/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-preview-data/client/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-preview-data/page.server.js b/test/e2e/app-dir/app/app/hooks/use-preview-data/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-preview-data/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-preview-data/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-router/page.client.js b/test/e2e/app-dir/app/app/hooks/use-router/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-router/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-router/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-router/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-router/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-router/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-router/server/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-router/sub-page/page.client.js b/test/e2e/app-dir/app/app/hooks/use-router/sub-page/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-router/sub-page/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-router/sub-page/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-search-params/page.client.js b/test/e2e/app-dir/app/app/hooks/use-search-params/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-search-params/page.client.js rename to test/e2e/app-dir/app/app/hooks/use-search-params/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-search-params/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-search-params/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-search-params/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-search-params/server/page.js diff --git a/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/server/page.server.js b/test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/server/page.server.js rename to test/e2e/app-dir/app/app/hooks/use-selected-layout-segment/server/page.js diff --git a/test/e2e/app-dir/app/app/internal/failure/page.server.js b/test/e2e/app-dir/app/app/internal/failure/page.js similarity index 100% rename from test/e2e/app-dir/app/app/internal/failure/page.server.js rename to test/e2e/app-dir/app/app/internal/failure/page.js diff --git a/test/e2e/app-dir/app/app/internal/page.server.js b/test/e2e/app-dir/app/app/internal/page.js similarity index 100% rename from test/e2e/app-dir/app/app/internal/page.server.js rename to test/e2e/app-dir/app/app/internal/page.js diff --git a/test/e2e/app-dir/app/app/internal/success/page.server.js b/test/e2e/app-dir/app/app/internal/success/page.js similarity index 100% rename from test/e2e/app-dir/app/app/internal/success/page.server.js rename to test/e2e/app-dir/app/app/internal/success/page.js diff --git a/test/e2e/app-dir/app/app/layout.server.js b/test/e2e/app-dir/app/app/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/layout.server.js rename to test/e2e/app-dir/app/app/layout.js diff --git a/test/e2e/app-dir/app/app/link-hard-push/[id]/page.server.js b/test/e2e/app-dir/app/app/link-hard-push/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-hard-push/[id]/page.server.js rename to test/e2e/app-dir/app/app/link-hard-push/[id]/page.js diff --git a/test/e2e/app-dir/app/app/link-hard-replace/[id]/page.server.js b/test/e2e/app-dir/app/app/link-hard-replace/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-hard-replace/[id]/page.server.js rename to test/e2e/app-dir/app/app/link-hard-replace/[id]/page.js diff --git a/test/e2e/app-dir/app/app/link-hard-replace/page.server.js b/test/e2e/app-dir/app/app/link-hard-replace/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-hard-replace/page.server.js rename to test/e2e/app-dir/app/app/link-hard-replace/page.js diff --git a/test/e2e/app-dir/app/app/link-hard-replace/subpage/page.server.js b/test/e2e/app-dir/app/app/link-hard-replace/subpage/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-hard-replace/subpage/page.server.js rename to test/e2e/app-dir/app/app/link-hard-replace/subpage/page.js diff --git a/test/e2e/app-dir/app/app/link-soft-push/page.server.js b/test/e2e/app-dir/app/app/link-soft-push/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-soft-push/page.server.js rename to test/e2e/app-dir/app/app/link-soft-push/page.js diff --git a/test/e2e/app-dir/app/app/link-soft-replace/page.server.js b/test/e2e/app-dir/app/app/link-soft-replace/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-soft-replace/page.server.js rename to test/e2e/app-dir/app/app/link-soft-replace/page.js diff --git a/test/e2e/app-dir/app/app/link-soft-replace/subpage/page.server.js b/test/e2e/app-dir/app/app/link-soft-replace/subpage/page.js similarity index 100% rename from test/e2e/app-dir/app/app/link-soft-replace/subpage/page.server.js rename to test/e2e/app-dir/app/app/link-soft-replace/subpage/page.js diff --git a/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js b/test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.server.js rename to test/e2e/app-dir/app/app/loading-bug/[categorySlug]/page.js diff --git a/test/e2e/app-dir/app/app/navigation/link.client.js b/test/e2e/app-dir/app/app/navigation/link.js similarity index 99% rename from test/e2e/app-dir/app/app/navigation/link.client.js rename to test/e2e/app-dir/app/app/navigation/link.js index 5c1a21d1cccc6..9d39caab31586 100644 --- a/test/e2e/app-dir/app/app/navigation/link.client.js +++ b/test/e2e/app-dir/app/app/navigation/link.js @@ -3,6 +3,7 @@ import { useRouter } from 'next/dist/client/components/hooks-client' import React from 'react' import { useEffect } from 'react' + export default function HardLink({ href, children, ...props }) { const router = useRouter() useEffect(() => { diff --git a/test/e2e/app-dir/app/app/navigation/page.server.js b/test/e2e/app-dir/app/app/navigation/page.js similarity index 91% rename from test/e2e/app-dir/app/app/navigation/page.server.js rename to test/e2e/app-dir/app/app/navigation/page.js index 2640936d496a9..62238fcf7b727 100644 --- a/test/e2e/app-dir/app/app/navigation/page.server.js +++ b/test/e2e/app-dir/app/app/navigation/page.js @@ -1,5 +1,5 @@ import { nanoid } from 'nanoid' -import Link from './link.client.js' +import Link from './link.js' export default function Page() { return ( diff --git a/test/e2e/app-dir/app/app/old-router/Router.client.js b/test/e2e/app-dir/app/app/old-router/client-router.js similarity index 90% rename from test/e2e/app-dir/app/app/old-router/Router.client.js rename to test/e2e/app-dir/app/app/old-router/client-router.js index a33b07f8da42e..3dc229767c4fa 100644 --- a/test/e2e/app-dir/app/app/old-router/Router.client.js +++ b/test/e2e/app-dir/app/app/old-router/client-router.js @@ -1,7 +1,7 @@ 'client' import { useRouter, withRouter } from 'next/router' -import IsNull from './IsNull' +import IsNull from './is-null' function ClientRouter({ router: withRouter }) { const router = useRouter() diff --git a/test/e2e/app-dir/app/app/old-router/IsNull.js b/test/e2e/app-dir/app/app/old-router/is-null.js similarity index 100% rename from test/e2e/app-dir/app/app/old-router/IsNull.js rename to test/e2e/app-dir/app/app/old-router/is-null.js diff --git a/test/e2e/app-dir/app/app/old-router/page.server.js b/test/e2e/app-dir/app/app/old-router/page.js similarity index 77% rename from test/e2e/app-dir/app/app/old-router/page.server.js rename to test/e2e/app-dir/app/app/old-router/page.js index d74b7650d38c6..3a2d5f594fad7 100644 --- a/test/e2e/app-dir/app/app/old-router/page.server.js +++ b/test/e2e/app-dir/app/app/old-router/page.js @@ -1,4 +1,4 @@ -import Router from './Router' +import Router from './router' export default function Page() { return ( diff --git a/test/e2e/app-dir/app/app/old-router/Router.js b/test/e2e/app-dir/app/app/old-router/router.js similarity index 73% rename from test/e2e/app-dir/app/app/old-router/Router.js rename to test/e2e/app-dir/app/app/old-router/router.js index 930b9ddac284c..7cddbe4181dc8 100644 --- a/test/e2e/app-dir/app/app/old-router/Router.js +++ b/test/e2e/app-dir/app/app/old-router/router.js @@ -1,7 +1,7 @@ import { useRouter, withRouter } from 'next/router' -import IsNull from './IsNull' -import ServerRouter from './Router.server' -import ClientRouter from './Router.client' +import IsNull from './is-null' +import ServerRouter from './server-router' +import ClientRouter from './client-router' function SharedRouter({ router: withRouter }) { const router = useRouter() diff --git a/test/e2e/app-dir/app/app/old-router/Router.server.js b/test/e2e/app-dir/app/app/old-router/server-router.js similarity index 89% rename from test/e2e/app-dir/app/app/old-router/Router.server.js rename to test/e2e/app-dir/app/app/old-router/server-router.js index ca08c6668f252..9e1291329c497 100644 --- a/test/e2e/app-dir/app/app/old-router/Router.server.js +++ b/test/e2e/app-dir/app/app/old-router/server-router.js @@ -1,5 +1,5 @@ import { useRouter, withRouter } from 'next/router' -import IsNull from './IsNull' +import IsNull from './is-null' function ServerRouter({ router: withRouter }) { const router = useRouter() diff --git a/test/e2e/app-dir/app/app/optional-catch-all/[[...slug]]/page.server.js b/test/e2e/app-dir/app/app/optional-catch-all/[[...slug]]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/optional-catch-all/[[...slug]]/page.server.js rename to test/e2e/app-dir/app/app/optional-catch-all/[[...slug]]/page.js diff --git a/test/e2e/app-dir/app/app/pages-linking/page.server.js b/test/e2e/app-dir/app/app/pages-linking/page.js similarity index 100% rename from test/e2e/app-dir/app/app/pages-linking/page.server.js rename to test/e2e/app-dir/app/app/pages-linking/page.js diff --git a/test/e2e/app-dir/app/app/parallel/(new)/@baz/nested-2/page.server.js b/test/e2e/app-dir/app/app/parallel/(new)/@baz/nested-2/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/(new)/@baz/nested-2/page.server.js rename to test/e2e/app-dir/app/app/parallel/(new)/@baz/nested-2/page.js diff --git a/test/e2e/app-dir/app/app/parallel/(new)/layout.server.js b/test/e2e/app-dir/app/app/parallel/(new)/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/(new)/layout.server.js rename to test/e2e/app-dir/app/app/parallel/(new)/layout.js diff --git a/test/e2e/app-dir/app/app/parallel/@bar/nested/@a/page.server.js b/test/e2e/app-dir/app/app/parallel/@bar/nested/@a/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@bar/nested/@a/page.server.js rename to test/e2e/app-dir/app/app/parallel/@bar/nested/@a/page.js diff --git a/test/e2e/app-dir/app/app/parallel/@bar/nested/@b/page.server.js b/test/e2e/app-dir/app/app/parallel/@bar/nested/@b/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@bar/nested/@b/page.server.js rename to test/e2e/app-dir/app/app/parallel/@bar/nested/@b/page.js diff --git a/test/e2e/app-dir/app/app/parallel/@bar/nested/layout.server.js b/test/e2e/app-dir/app/app/parallel/@bar/nested/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@bar/nested/layout.server.js rename to test/e2e/app-dir/app/app/parallel/@bar/nested/layout.js diff --git a/test/e2e/app-dir/app/app/parallel/@bar/page.server.js b/test/e2e/app-dir/app/app/parallel/@bar/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@bar/page.server.js rename to test/e2e/app-dir/app/app/parallel/@bar/page.js diff --git a/test/e2e/app-dir/app/app/parallel/@foo/nested/@a/page.server.js b/test/e2e/app-dir/app/app/parallel/@foo/nested/@a/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@foo/nested/@a/page.server.js rename to test/e2e/app-dir/app/app/parallel/@foo/nested/@a/page.js diff --git a/test/e2e/app-dir/app/app/parallel/@foo/nested/@b/page.server.js b/test/e2e/app-dir/app/app/parallel/@foo/nested/@b/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@foo/nested/@b/page.server.js rename to test/e2e/app-dir/app/app/parallel/@foo/nested/@b/page.js diff --git a/test/e2e/app-dir/app/app/parallel/@foo/nested/layout.server.js b/test/e2e/app-dir/app/app/parallel/@foo/nested/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@foo/nested/layout.server.js rename to test/e2e/app-dir/app/app/parallel/@foo/nested/layout.js diff --git a/test/e2e/app-dir/app/app/parallel/@foo/page.server.js b/test/e2e/app-dir/app/app/parallel/@foo/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/@foo/page.server.js rename to test/e2e/app-dir/app/app/parallel/@foo/page.js diff --git a/test/e2e/app-dir/app/app/parallel/layout.server.js b/test/e2e/app-dir/app/app/parallel/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/layout.server.js rename to test/e2e/app-dir/app/app/parallel/layout.js diff --git a/test/e2e/app-dir/app/app/parallel/nested/page.server.js b/test/e2e/app-dir/app/app/parallel/nested/page.js similarity index 100% rename from test/e2e/app-dir/app/app/parallel/nested/page.server.js rename to test/e2e/app-dir/app/app/parallel/nested/page.js diff --git a/test/e2e/app-dir/app/app/param-and-query/[slug]/page.client.js b/test/e2e/app-dir/app/app/param-and-query/[slug]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/param-and-query/[slug]/page.client.js rename to test/e2e/app-dir/app/app/param-and-query/[slug]/page.js diff --git a/test/e2e/app-dir/app/app/partial-match-[id]/page.server.js b/test/e2e/app-dir/app/app/partial-match-[id]/page.js similarity index 100% rename from test/e2e/app-dir/app/app/partial-match-[id]/page.server.js rename to test/e2e/app-dir/app/app/partial-match-[id]/page.js diff --git a/test/e2e/app-dir/app/app/same-layout/first/page.server.js b/test/e2e/app-dir/app/app/same-layout/first/page.js similarity index 100% rename from test/e2e/app-dir/app/app/same-layout/first/page.server.js rename to test/e2e/app-dir/app/app/same-layout/first/page.js diff --git a/test/e2e/app-dir/app/app/same-layout/layout.server.js b/test/e2e/app-dir/app/app/same-layout/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/same-layout/layout.server.js rename to test/e2e/app-dir/app/app/same-layout/layout.js diff --git a/test/e2e/app-dir/app/app/same-layout/second/page.server.js b/test/e2e/app-dir/app/app/same-layout/second/page.js similarity index 100% rename from test/e2e/app-dir/app/app/same-layout/second/page.server.js rename to test/e2e/app-dir/app/app/same-layout/second/page.js diff --git a/test/e2e/app-dir/app/app/should-not-serve-client/page.client.js b/test/e2e/app-dir/app/app/should-not-serve-client/page.js similarity index 100% rename from test/e2e/app-dir/app/app/should-not-serve-client/page.client.js rename to test/e2e/app-dir/app/app/should-not-serve-client/page.js diff --git a/test/e2e/app-dir/app/app/should-not-serve-server/page.server.js b/test/e2e/app-dir/app/app/should-not-serve-server/page.js similarity index 100% rename from test/e2e/app-dir/app/app/should-not-serve-server/page.server.js rename to test/e2e/app-dir/app/app/should-not-serve-server/page.js diff --git a/test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/layout.server.js b/test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/layout.server.js rename to test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/layout.js diff --git a/test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/page.server.js b/test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/page.js similarity index 100% rename from test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/page.server.js rename to test/e2e/app-dir/app/app/slow-layout-and-page-with-loading/slow/page.js diff --git a/test/e2e/app-dir/app/app/slow-layout-with-loading/slow/layout.server.js b/test/e2e/app-dir/app/app/slow-layout-with-loading/slow/layout.js similarity index 100% rename from test/e2e/app-dir/app/app/slow-layout-with-loading/slow/layout.server.js rename to test/e2e/app-dir/app/app/slow-layout-with-loading/slow/layout.js diff --git a/test/e2e/app-dir/app/app/slow-layout-with-loading/slow/page.server.js b/test/e2e/app-dir/app/app/slow-layout-with-loading/slow/page.js similarity index 100% rename from test/e2e/app-dir/app/app/slow-layout-with-loading/slow/page.server.js rename to test/e2e/app-dir/app/app/slow-layout-with-loading/slow/page.js diff --git a/test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js b/test/e2e/app-dir/app/app/slow-page-with-loading/page.js similarity index 100% rename from test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js rename to test/e2e/app-dir/app/app/slow-page-with-loading/page.js diff --git a/test/e2e/app-dir/app/app/template/clientcomponent/other/page.server.js b/test/e2e/app-dir/app/app/template/clientcomponent/other/page.js similarity index 100% rename from test/e2e/app-dir/app/app/template/clientcomponent/other/page.server.js rename to test/e2e/app-dir/app/app/template/clientcomponent/other/page.js diff --git a/test/e2e/app-dir/app/app/template/clientcomponent/page.server.js b/test/e2e/app-dir/app/app/template/clientcomponent/page.js similarity index 100% rename from test/e2e/app-dir/app/app/template/clientcomponent/page.server.js rename to test/e2e/app-dir/app/app/template/clientcomponent/page.js diff --git a/test/e2e/app-dir/app/app/template/clientcomponent/template.client.js b/test/e2e/app-dir/app/app/template/clientcomponent/template.js similarity index 100% rename from test/e2e/app-dir/app/app/template/clientcomponent/template.client.js rename to test/e2e/app-dir/app/app/template/clientcomponent/template.js diff --git a/test/e2e/app-dir/app/app/template/servercomponent/other/page.server.js b/test/e2e/app-dir/app/app/template/servercomponent/other/page.js similarity index 100% rename from test/e2e/app-dir/app/app/template/servercomponent/other/page.server.js rename to test/e2e/app-dir/app/app/template/servercomponent/other/page.js diff --git a/test/e2e/app-dir/app/app/template/servercomponent/page.server.js b/test/e2e/app-dir/app/app/template/servercomponent/page.js similarity index 100% rename from test/e2e/app-dir/app/app/template/servercomponent/page.server.js rename to test/e2e/app-dir/app/app/template/servercomponent/page.js diff --git a/test/e2e/app-dir/app/app/template/servercomponent/template.server.js b/test/e2e/app-dir/app/app/template/servercomponent/template.js similarity index 100% rename from test/e2e/app-dir/app/app/template/servercomponent/template.server.js rename to test/e2e/app-dir/app/app/template/servercomponent/template.js diff --git a/test/e2e/app-dir/app/app/test-page/page.server.js b/test/e2e/app-dir/app/app/test-page/page.js similarity index 100% rename from test/e2e/app-dir/app/app/test-page/page.server.js rename to test/e2e/app-dir/app/app/test-page/page.js diff --git a/test/e2e/app-dir/app/app/with-id/page.server.js b/test/e2e/app-dir/app/app/with-id/page.js similarity index 100% rename from test/e2e/app-dir/app/app/with-id/page.server.js rename to test/e2e/app-dir/app/app/with-id/page.js diff --git a/test/e2e/app-dir/app/pages/api/preview.js b/test/e2e/app-dir/app/pages/api/preview.js index 1f5c35ef50ddb..5b4890a34afeb 100644 --- a/test/e2e/app-dir/app/pages/api/preview.js +++ b/test/e2e/app-dir/app/pages/api/preview.js @@ -1,4 +1,4 @@ export default function handler(req, res) { res.setPreviewData({ key: 'value' }) - res.end() + res.status(200).end() } diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index 37eb9875b9c3f..10efc212720a4 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -29,6 +29,9 @@ describe('app dir', () => { 'react-dom': 'experimental', }, skipStart: true, + env: { + VERCEL_ANALYTICS_ID: 'fake-analytics-id', + }, }) if (assetPrefix) { @@ -44,6 +47,19 @@ describe('app dir', () => { }) afterAll(() => next.destroy()) + it('should use application/octet-stream for flight', async () => { + const res = await fetchViaHTTP( + next.url, + '/dashboard/deployments/123?__flight__' + ) + expect(res.headers.get('Content-Type')).toBe('application/octet-stream') + }) + + it('should use application/octet-stream for flight with edge runtime', async () => { + const res = await fetchViaHTTP(next.url, '/dashboard?__flight__') + expect(res.headers.get('Content-Type')).toBe('application/octet-stream') + }) + it('should pass props from getServerSideProps in root layout', async () => { const html = await renderViaHTTP(next.url, '/dashboard') const $ = cheerio.load(html) @@ -1061,9 +1077,9 @@ describe('app dir', () => { }) if (isDev) { - it.skip('should throw an error when getServerSideProps is used', async () => { + it('should throw an error when getServerSideProps is used', async () => { const pageFile = - 'app/client-with-errors/get-server-side-props/page.client.js' + 'app/client-with-errors/get-server-side-props/page.js' const content = await next.readFile(pageFile) const uncomment = content.replace( '// export function getServerSideProps', @@ -1086,13 +1102,12 @@ describe('app dir', () => { expect(res.status).toBe(500) expect(await res.text()).toContain( - 'getServerSideProps is not supported in client components' + '`getServerSideProps` is not allowed in Client Components' ) }) - it.skip('should throw an error when getStaticProps is used', async () => { - const pageFile = - 'app/client-with-errors/get-static-props/page.client.js' + it('should throw an error when getStaticProps is used', async () => { + const pageFile = 'app/client-with-errors/get-static-props/page.js' const content = await next.readFile(pageFile) const uncomment = content.replace( '// export function getStaticProps', @@ -1114,7 +1129,7 @@ describe('app dir', () => { expect(res.status).toBe(500) expect(await res.text()).toContain( - 'getStaticProps is not supported in client components' + '`getStaticProps` is not allowed in Client Components' ) }) } @@ -1442,6 +1457,40 @@ describe('app dir', () => { }) }) + // Analytics events are only sent in production + ;(isDev ? describe.skip : describe)('Vercel analytics', () => { + it('should send web vitals to Vercel analytics', async () => { + let eventsCount = 0 + let countEvents = false + const browser = await webdriver(next.url, '/client-nested', { + beforePageLoad(page) { + page.route( + 'https://vitals.vercel-insights.com/v1/vitals', + (route) => { + if (countEvents) { + eventsCount += 1 + } + + route.fulfill() + } + ) + }, + }) + + // Start counting analytics events + countEvents = true + + // Refresh will trigger CLS and LCP. When page loads FCP and TTFB will trigger: + await browser.refresh() + + // After interaction LCP and FID will trigger + await browser.elementByCss('button').click() + + // Make sure all registered events in performance-relayer has fired + await check(() => eventsCount, /6/) + }) + }) + describe('known bugs', () => { it('should not share flight data between requests', async () => { const fetches = await Promise.all( diff --git a/test/e2e/app-dir/rsc-basic.test.ts b/test/e2e/app-dir/rsc-basic.test.ts index fb4a6fefdb26e..4d5196ffcad32 100644 --- a/test/e2e/app-dir/rsc-basic.test.ts +++ b/test/e2e/app-dir/rsc-basic.test.ts @@ -150,17 +150,6 @@ describe('app dir - react server components', () => { expect(sharedServerModule[0][1]).toBe(sharedServerModule[1][1]) expect(sharedClientModule[0][1]).toBe(sharedClientModule[1][1]) expect(sharedServerModule[0][1]).not.toBe(sharedClientModule[0][1]) - - // Note: This is currently unsupported because packages from another layer - // will not be re-initialized by webpack. - // Should import 2 module instances for node_modules too. - // const modFromClient = main.match( - // /node_modules instance from \.client\.js:(\d+)/ - // ) - // const modFromServer = main.match( - // /node_modules instance from \.server\.js:(\d+)/ - // ) - // expect(modFromClient[1]).not.toBe(modFromServer[1]) }) it('should be able to navigate between rsc routes', async () => { diff --git a/test/e2e/app-dir/rsc-basic/app/css-in-js/page.server.js b/test/e2e/app-dir/rsc-basic/app/css-in-js/page.js similarity index 54% rename from test/e2e/app-dir/rsc-basic/app/css-in-js/page.server.js rename to test/e2e/app-dir/rsc-basic/app/css-in-js/page.js index 82dec40e278e9..6a69632037f02 100644 --- a/test/e2e/app-dir/rsc-basic/app/css-in-js/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/css-in-js/page.js @@ -1,5 +1,5 @@ -import Comp from './styled-jsx.client' -import StyledComp from './styled-components.client' +import Comp from './styled-jsx' +import StyledComp from './styled-components' export default function Page() { return ( diff --git a/test/e2e/app-dir/rsc-basic/app/css-in-js/styled-components.client.js b/test/e2e/app-dir/rsc-basic/app/css-in-js/styled-components.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/css-in-js/styled-components.client.js rename to test/e2e/app-dir/rsc-basic/app/css-in-js/styled-components.js diff --git a/test/e2e/app-dir/rsc-basic/app/css-in-js/styled-jsx.client.js b/test/e2e/app-dir/rsc-basic/app/css-in-js/styled-jsx.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/css-in-js/styled-jsx.client.js rename to test/e2e/app-dir/rsc-basic/app/css-in-js/styled-jsx.js diff --git a/test/e2e/app-dir/rsc-basic/app/css-modules/page.server.js b/test/e2e/app-dir/rsc-basic/app/css-modules/page.js similarity index 78% rename from test/e2e/app-dir/rsc-basic/app/css-modules/page.server.js rename to test/e2e/app-dir/rsc-basic/app/css-modules/page.js index 9fcdad027d780..0e8dda27cfd67 100644 --- a/test/e2e/app-dir/rsc-basic/app/css-modules/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/css-modules/page.js @@ -1,5 +1,5 @@ // CSS modules can only be imported inside client components for now. -import RedText from '../../components/red/index.client' +import RedText from '../../components/red/index' export default function CSSM() { return ( diff --git a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.server.js b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.server.js rename to test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.server.js b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.server.js rename to test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/escaping-rsc/page.server.js b/test/e2e/app-dir/rsc-basic/app/escaping-rsc/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/escaping-rsc/page.server.js rename to test/e2e/app-dir/rsc-basic/app/escaping-rsc/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/external-imports/page.client.js b/test/e2e/app-dir/rsc-basic/app/external-imports/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/external-imports/page.client.js rename to test/e2e/app-dir/rsc-basic/app/external-imports/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/global-styles-rsc/page.server.js b/test/e2e/app-dir/rsc-basic/app/global-styles-rsc/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/global-styles-rsc/page.server.js rename to test/e2e/app-dir/rsc-basic/app/global-styles-rsc/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/layout.server.js b/test/e2e/app-dir/rsc-basic/app/layout.js similarity index 83% rename from test/e2e/app-dir/rsc-basic/app/layout.server.js rename to test/e2e/app-dir/rsc-basic/app/layout.js index 23d1538c24872..55cded827d1a8 100644 --- a/test/e2e/app-dir/rsc-basic/app/layout.server.js +++ b/test/e2e/app-dir/rsc-basic/app/layout.js @@ -1,5 +1,5 @@ import React from 'react' -import RootStyleRegistry from './root-style-registry.client' +import RootStyleRegistry from './root-style-registry' export const config = { revalidate: 0, diff --git a/test/e2e/app-dir/rsc-basic/app/multi/page.server.js b/test/e2e/app-dir/rsc-basic/app/multi/page.js similarity index 54% rename from test/e2e/app-dir/rsc-basic/app/multi/page.server.js rename to test/e2e/app-dir/rsc-basic/app/multi/page.js index 07304236d8926..e8b74e4a7ecdf 100644 --- a/test/e2e/app-dir/rsc-basic/app/multi/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/multi/page.js @@ -1,4 +1,4 @@ -import Bar from '../../components/bar.server' +import Bar from '../../components/bar' export default function Multi() { return diff --git a/test/e2e/app-dir/rsc-basic/app/native-module/page.server.js b/test/e2e/app-dir/rsc-basic/app/native-module/page.js similarity index 80% rename from test/e2e/app-dir/rsc-basic/app/native-module/page.server.js rename to test/e2e/app-dir/rsc-basic/app/native-module/page.js index c74dcf3163b75..67dffb1a5259f 100644 --- a/test/e2e/app-dir/rsc-basic/app/native-module/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/native-module/page.js @@ -1,6 +1,6 @@ import fs from 'fs' -import Foo from '../../components/foo.client' +import Foo from '../../components/foo' export default function Page() { return ( diff --git a/test/e2e/app-dir/rsc-basic/app/next-api/image/page.server.js b/test/e2e/app-dir/rsc-basic/app/next-api/image/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/next-api/image/page.server.js rename to test/e2e/app-dir/rsc-basic/app/next-api/image/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/next-api/link/page.server.js b/test/e2e/app-dir/rsc-basic/app/next-api/link/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/next-api/link/page.server.js rename to test/e2e/app-dir/rsc-basic/app/next-api/link/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/page.server.js b/test/e2e/app-dir/rsc-basic/app/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/page.server.js rename to test/e2e/app-dir/rsc-basic/app/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/partial-hydration/page.server.js b/test/e2e/app-dir/rsc-basic/app/partial-hydration/page.js similarity index 90% rename from test/e2e/app-dir/rsc-basic/app/partial-hydration/page.server.js rename to test/e2e/app-dir/rsc-basic/app/partial-hydration/page.js index b551f10f04909..f656e0b80b121 100644 --- a/test/e2e/app-dir/rsc-basic/app/partial-hydration/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/partial-hydration/page.js @@ -1,6 +1,6 @@ import { Suspense } from 'react' -import Counter from '../../components/partial-hydration-counter.client' +import Counter from '../../components/partial-hydration-counter' let result let promise diff --git a/test/e2e/app-dir/rsc-basic/app/root-style-registry.client.js b/test/e2e/app-dir/rsc-basic/app/root-style-registry.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/root-style-registry.client.js rename to test/e2e/app-dir/rsc-basic/app/root-style-registry.js diff --git a/test/e2e/app-dir/rsc-basic/app/root/page.server.js b/test/e2e/app-dir/rsc-basic/app/root/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/root/page.server.js rename to test/e2e/app-dir/rsc-basic/app/root/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/routes/[dynamic]/page.server.js.bak b/test/e2e/app-dir/rsc-basic/app/routes/[dynamic]/page.server.js.bak deleted file mode 100644 index 97d7c734596e8..0000000000000 --- a/test/e2e/app-dir/rsc-basic/app/routes/[dynamic]/page.server.js.bak +++ /dev/null @@ -1,21 +0,0 @@ -import { parse } from 'url' -import RouterPath from '../../../components/router-path.client' - -export default function Pid({ text, pathname }) { - return ( - <> -
{`query: ${text}`}
-
{`pathname: ${pathname}`}
- - - ) -} - -export function getServerSideProps({ params, req }) { - return { - props: { - pathname: parse(req.url).pathname, - text: params.dynamic, - }, - } -} diff --git a/test/e2e/app-dir/rsc-basic/app/shared/page.server.js b/test/e2e/app-dir/rsc-basic/app/shared/page.js similarity index 66% rename from test/e2e/app-dir/rsc-basic/app/shared/page.server.js rename to test/e2e/app-dir/rsc-basic/app/shared/page.js index 58f0c6cb7dad7..761727eca2e7d 100644 --- a/test/e2e/app-dir/rsc-basic/app/shared/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/shared/page.js @@ -1,10 +1,8 @@ -import ClientFromDirect from '../../components/client.client' +import ClientFromDirect from '../../components/client' import ClientFromShared from '../../components/shared' -import SharedFromClient from '../../components/shared.client' -import Random from '../../components/random-module-instance.client' -import Bar from '../../components/bar.server' - -// import { random } from 'random-module-instance' +import SharedFromClient from '../../components/shared-client' +import Random from '../../components/random-module-instance' +import Bar from '../../components/bar' export default function Page() { // All three client components should be rendered correctly, but only @@ -16,7 +14,6 @@ export default function Page() {

- {/* {`node_modules instance from .server.js:` + random} */}

diff --git a/test/e2e/app-dir/rsc-basic/app/streaming-rsc/page.server.js b/test/e2e/app-dir/rsc-basic/app/streaming-rsc/page.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/app/streaming-rsc/page.server.js rename to test/e2e/app-dir/rsc-basic/app/streaming-rsc/page.js diff --git a/test/e2e/app-dir/rsc-basic/app/various-exports/page.server.js b/test/e2e/app-dir/rsc-basic/app/various-exports/page.js similarity index 77% rename from test/e2e/app-dir/rsc-basic/app/various-exports/page.server.js rename to test/e2e/app-dir/rsc-basic/app/various-exports/page.js index 865d7904362c8..603ba8b5e9e78 100644 --- a/test/e2e/app-dir/rsc-basic/app/various-exports/page.server.js +++ b/test/e2e/app-dir/rsc-basic/app/various-exports/page.js @@ -3,12 +3,13 @@ import { a, b, c, d, e } from '../../components/shared-exports' // client default, named exports import DefaultArrow, { Named as ClientNamed, -} from '../../components/client-exports.client' +} from '../../components/client-exports' -import { Cjs as CjsShared } from '../../components/cjs' -import { Cjs as CjsClient } from '../../components/cjs.client' +import { Cjs as CjsShared } from '../../components/cjs-server' +import { Cjs as CjsClient } from '../../components/cjs-client' -import { One, Two, TwoAliased } from '../../components/export-all/index.client' +// client exports all +import { One, Two, TwoAliased } from '../../components/export-all' export default function Page() { return ( diff --git a/test/e2e/app-dir/rsc-basic/components/bar.client.js b/test/e2e/app-dir/rsc-basic/components/bar-client.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/bar.client.js rename to test/e2e/app-dir/rsc-basic/components/bar-client.js diff --git a/test/e2e/app-dir/rsc-basic/components/bar.server.js b/test/e2e/app-dir/rsc-basic/components/bar.js similarity index 79% rename from test/e2e/app-dir/rsc-basic/components/bar.server.js rename to test/e2e/app-dir/rsc-basic/components/bar.js index 9b5c231fccc38..dd460ddda586d 100644 --- a/test/e2e/app-dir/rsc-basic/components/bar.server.js +++ b/test/e2e/app-dir/rsc-basic/components/bar.js @@ -1,4 +1,4 @@ -import Foo from './foo.client' +import Foo from './foo' export default function Bar() { return ( diff --git a/test/e2e/app-dir/rsc-basic/components/cjs.client.js b/test/e2e/app-dir/rsc-basic/components/cjs-client.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/cjs.client.js rename to test/e2e/app-dir/rsc-basic/components/cjs-client.js diff --git a/test/e2e/app-dir/rsc-basic/components/cjs.js b/test/e2e/app-dir/rsc-basic/components/cjs-server.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/cjs.js rename to test/e2e/app-dir/rsc-basic/components/cjs-server.js diff --git a/test/e2e/app-dir/rsc-basic/components/client-exports.client.js b/test/e2e/app-dir/rsc-basic/components/client-exports.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/client-exports.client.js rename to test/e2e/app-dir/rsc-basic/components/client-exports.js diff --git a/test/e2e/app-dir/rsc-basic/components/client.client.js b/test/e2e/app-dir/rsc-basic/components/client.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/client.client.js rename to test/e2e/app-dir/rsc-basic/components/client.js diff --git a/test/e2e/app-dir/rsc-basic/components/container.server.js b/test/e2e/app-dir/rsc-basic/components/container.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/container.server.js rename to test/e2e/app-dir/rsc-basic/components/container.js diff --git a/test/e2e/app-dir/rsc-basic/components/export-all/index.client.js b/test/e2e/app-dir/rsc-basic/components/export-all/index.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/export-all/index.client.js rename to test/e2e/app-dir/rsc-basic/components/export-all/index.js diff --git a/test/e2e/app-dir/rsc-basic/components/foo.client.js b/test/e2e/app-dir/rsc-basic/components/foo.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/foo.client.js rename to test/e2e/app-dir/rsc-basic/components/foo.js diff --git a/test/e2e/app-dir/rsc-basic/components/partial-hydration-counter.client.js b/test/e2e/app-dir/rsc-basic/components/partial-hydration-counter.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/partial-hydration-counter.client.js rename to test/e2e/app-dir/rsc-basic/components/partial-hydration-counter.js diff --git a/test/e2e/app-dir/rsc-basic/components/random-module-instance.client.js b/test/e2e/app-dir/rsc-basic/components/random-module-instance.js similarity index 59% rename from test/e2e/app-dir/rsc-basic/components/random-module-instance.client.js rename to test/e2e/app-dir/rsc-basic/components/random-module-instance.js index 6b3e418e27a39..d3badf9b35ef0 100644 --- a/test/e2e/app-dir/rsc-basic/components/random-module-instance.client.js +++ b/test/e2e/app-dir/rsc-basic/components/random-module-instance.js @@ -3,5 +3,5 @@ import { random } from 'random-module-instance' export default function () { - return `node_modules instance from .client.js:${random}` + return `node_modules instance from client module ${random}` } diff --git a/test/e2e/app-dir/rsc-basic/components/red/index.client.js b/test/e2e/app-dir/rsc-basic/components/red/index.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/red/index.client.js rename to test/e2e/app-dir/rsc-basic/components/red/index.js diff --git a/test/e2e/app-dir/rsc-basic/components/router-path.client.js b/test/e2e/app-dir/rsc-basic/components/router-path.client.js deleted file mode 100644 index 40d08dd3c7e57..0000000000000 --- a/test/e2e/app-dir/rsc-basic/components/router-path.client.js +++ /dev/null @@ -1,8 +0,0 @@ -'client' - -import { useRouter } from 'next/router' - -export default () => { - const { pathname } = useRouter() - return
{`router pathname: ${pathname}`}
-} diff --git a/test/e2e/app-dir/rsc-basic/components/shared.client.js b/test/e2e/app-dir/rsc-basic/components/shared-client.js similarity index 100% rename from test/e2e/app-dir/rsc-basic/components/shared.client.js rename to test/e2e/app-dir/rsc-basic/components/shared-client.js diff --git a/test/e2e/app-dir/rsc-basic/components/shared.js b/test/e2e/app-dir/rsc-basic/components/shared.js index 8cb90b9a87c59..f1a59405238b6 100644 --- a/test/e2e/app-dir/rsc-basic/components/shared.js +++ b/test/e2e/app-dir/rsc-basic/components/shared.js @@ -1,5 +1,5 @@ import React from 'react' -import Client from './client.client' +import Client from './client' const random = ~~(Math.random() * 10000) diff --git a/test/e2e/app-dir/trailingslash/app/a/page.server.js b/test/e2e/app-dir/trailingslash/app/a/page.js similarity index 100% rename from test/e2e/app-dir/trailingslash/app/a/page.server.js rename to test/e2e/app-dir/trailingslash/app/a/page.js diff --git a/test/e2e/app-dir/trailingslash/app/layout.server.js b/test/e2e/app-dir/trailingslash/app/layout.js similarity index 100% rename from test/e2e/app-dir/trailingslash/app/layout.server.js rename to test/e2e/app-dir/trailingslash/app/layout.js diff --git a/test/e2e/app-dir/trailingslash/app/page.server.js b/test/e2e/app-dir/trailingslash/app/page.js similarity index 100% rename from test/e2e/app-dir/trailingslash/app/page.server.js rename to test/e2e/app-dir/trailingslash/app/page.js diff --git a/test/integration/react-streaming/app/components/bar.client.js b/test/integration/react-streaming/app/components/bar.js similarity index 60% rename from test/integration/react-streaming/app/components/bar.client.js rename to test/integration/react-streaming/app/components/bar.js index b58488a9c3b5c..97cd1c985e192 100644 --- a/test/integration/react-streaming/app/components/bar.client.js +++ b/test/integration/react-streaming/app/components/bar.js @@ -1,3 +1,3 @@ export default function bar() { - return 'bar.client' + return 'bar' } diff --git a/test/integration/react-streaming/app/components/bar.server.js b/test/integration/react-streaming/app/components/bar.server.js deleted file mode 100644 index e8b54be31993b..0000000000000 --- a/test/integration/react-streaming/app/components/bar.server.js +++ /dev/null @@ -1,9 +0,0 @@ -import Foo from './foo.client' - -export default function Bar() { - return ( -
- bar.server.js: -
- ) -} diff --git a/test/integration/react-streaming/app/components/cjs.client.js b/test/integration/react-streaming/app/components/cjs.client.js deleted file mode 100644 index d3c078184ab64..0000000000000 --- a/test/integration/react-streaming/app/components/cjs.client.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.Cjs = function Cjs() { - return 'cjs-client' -} diff --git a/test/integration/react-streaming/app/components/cjs.js b/test/integration/react-streaming/app/components/cjs.js deleted file mode 100644 index 444f1229a2272..0000000000000 --- a/test/integration/react-streaming/app/components/cjs.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.Cjs = function Cjs() { - return 'cjs-shared' -} diff --git a/test/integration/react-streaming/app/components/client-exports.client.js b/test/integration/react-streaming/app/components/client-exports.client.js deleted file mode 100644 index eca931680a232..0000000000000 --- a/test/integration/react-streaming/app/components/client-exports.client.js +++ /dev/null @@ -1,5 +0,0 @@ -export function Named() { - return 'named.client' -} - -export default () => 'default-export-arrow.client' diff --git a/test/integration/react-streaming/app/components/client.client.js b/test/integration/react-streaming/app/components/client.client.js deleted file mode 100644 index 7b20e208abd7c..0000000000000 --- a/test/integration/react-streaming/app/components/client.client.js +++ /dev/null @@ -1,7 +0,0 @@ -import { useState } from 'react' - -export default function Client() { - // To ensure that this component is rendered as a client component, we use a - // state here. - return useState('client_component')[0] -} diff --git a/test/integration/react-streaming/app/components/container.server.js b/test/integration/react-streaming/app/components/container.server.js deleted file mode 100644 index ab63ebb0d37f0..0000000000000 --- a/test/integration/react-streaming/app/components/container.server.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Container({ children }) { - return
{children}
-} diff --git a/test/integration/react-streaming/app/components/export-all/index.client.js b/test/integration/react-streaming/app/components/export-all/index.client.js deleted file mode 100644 index 1087c98723264..0000000000000 --- a/test/integration/react-streaming/app/components/export-all/index.client.js +++ /dev/null @@ -1 +0,0 @@ -export * from './one' diff --git a/test/integration/react-streaming/app/components/export-all/one.js b/test/integration/react-streaming/app/components/export-all/one.js deleted file mode 100644 index c06971f1cbd5d..0000000000000 --- a/test/integration/react-streaming/app/components/export-all/one.js +++ /dev/null @@ -1,6 +0,0 @@ -export function One() { - return 'one' -} - -export * from './two' -export { Two as TwoAliased } from './two' diff --git a/test/integration/react-streaming/app/components/export-all/two.js b/test/integration/react-streaming/app/components/export-all/two.js deleted file mode 100644 index efcee31004001..0000000000000 --- a/test/integration/react-streaming/app/components/export-all/two.js +++ /dev/null @@ -1,3 +0,0 @@ -export function Two() { - return 'two' -} diff --git a/test/integration/react-streaming/app/components/foo.client.js b/test/integration/react-streaming/app/components/foo.js similarity index 79% rename from test/integration/react-streaming/app/components/foo.client.js rename to test/integration/react-streaming/app/components/foo.js index 30d3854497926..66d4ff2dabfa4 100644 --- a/test/integration/react-streaming/app/components/foo.client.js +++ b/test/integration/react-streaming/app/components/foo.js @@ -1,5 +1,5 @@ export default function foo() { - return 'foo.client' + return 'foo' } export const config = 'this is not page config' diff --git a/test/integration/react-streaming/app/components/nav.js b/test/integration/react-streaming/app/components/nav.js deleted file mode 100644 index 23fb83d933a36..0000000000000 --- a/test/integration/react-streaming/app/components/nav.js +++ /dev/null @@ -1,23 +0,0 @@ -import Link from 'next/link' - -export default function Nav() { - return ( - <> -
- - next link - -
-
- - streaming rsc - -
-
- - home - -
- - ) -} diff --git a/test/integration/react-streaming/app/components/partial-hydration-counter.client.js b/test/integration/react-streaming/app/components/partial-hydration-counter.client.js deleted file mode 100644 index 9f860a79c153f..0000000000000 --- a/test/integration/react-streaming/app/components/partial-hydration-counter.client.js +++ /dev/null @@ -1,20 +0,0 @@ -import { useState, useEffect } from 'react' - -export default function Counter() { - const [count, setCount] = useState(0) - - useEffect(() => { - // When this component is hydrated, there might be other parts still pending - // on streaming. So we test the interactivity of the document before it's - // fully loaded. - const counter = document.querySelector('button') - const suspense = document.querySelector('.suspense') - counter.click() - setTimeout(() => { - window.partial_hydration_suspense_result = suspense.textContent - window.partial_hydration_counter_result = counter.textContent - }, 0) - }, []) - - return -} diff --git a/test/integration/react-streaming/app/components/random-module-instance.client.js b/test/integration/react-streaming/app/components/random-module-instance.client.js deleted file mode 100644 index 02696a05605b2..0000000000000 --- a/test/integration/react-streaming/app/components/random-module-instance.client.js +++ /dev/null @@ -1,5 +0,0 @@ -import { random } from 'random-module-instance' - -export default function () { - return `node_modules instance from .client.js:${random}` -} diff --git a/test/integration/react-streaming/app/components/red/index.client.js b/test/integration/react-streaming/app/components/red/index.client.js deleted file mode 100644 index 1bc538c3db8e1..0000000000000 --- a/test/integration/react-streaming/app/components/red/index.client.js +++ /dev/null @@ -1,5 +0,0 @@ -import styles from './style.module.css' - -export default function RedText(props) { - return
-} diff --git a/test/integration/react-streaming/app/components/red/style.module.css b/test/integration/react-streaming/app/components/red/style.module.css deleted file mode 100644 index 3efa99e6fb171..0000000000000 --- a/test/integration/react-streaming/app/components/red/style.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.text { - color: red; -} diff --git a/test/integration/react-streaming/app/components/router-path.client.js b/test/integration/react-streaming/app/components/router-path.client.js deleted file mode 100644 index cbcb0f9ff6c63..0000000000000 --- a/test/integration/react-streaming/app/components/router-path.client.js +++ /dev/null @@ -1,6 +0,0 @@ -import { useRouter } from 'next/router' - -export default () => { - const { pathname } = useRouter() - return
{`router pathname: ${pathname}`}
-} diff --git a/test/integration/react-streaming/app/components/shared-exports.js b/test/integration/react-streaming/app/components/shared-exports.js deleted file mode 100644 index 6fd69b677b4d4..0000000000000 --- a/test/integration/react-streaming/app/components/shared-exports.js +++ /dev/null @@ -1,10 +0,0 @@ -const a = 'a' -const b = 'b' -const _c = 'c' -const _d = 'd' -const _e = 'e' -const _eArr = [_e] - -export const c = _c -export { a, b } -export { _d as d, _eArr as e } diff --git a/test/integration/react-streaming/app/components/shared.client.js b/test/integration/react-streaming/app/components/shared.client.js deleted file mode 100644 index 201654274422a..0000000000000 --- a/test/integration/react-streaming/app/components/shared.client.js +++ /dev/null @@ -1,3 +0,0 @@ -import Shared from './shared' - -export default Shared diff --git a/test/integration/react-streaming/app/components/shared.js b/test/integration/react-streaming/app/components/shared.js deleted file mode 100644 index f66f284b9c8a7..0000000000000 --- a/test/integration/react-streaming/app/components/shared.js +++ /dev/null @@ -1,21 +0,0 @@ -import { useState } from 'react' -import Client from './client.client' - -const random = ~~(Math.random() * 10000) - -export default function Shared() { - let isServerComponent - try { - useState() - isServerComponent = false - } catch (e) { - isServerComponent = true - } - - return ( - <> - ,{' '} - {(isServerComponent ? 'shared:server' : 'shared:client') + ':' + random} - - ) -} diff --git a/test/integration/react-streaming/app/components/styled-jsx.client.js b/test/integration/react-streaming/app/components/styled-jsx.client.js deleted file mode 100644 index 6759ea8e21ca5..0000000000000 --- a/test/integration/react-streaming/app/components/styled-jsx.client.js +++ /dev/null @@ -1,12 +0,0 @@ -export default function Comp() { - return ( -
-

Red

- -
- ) -} diff --git a/test/integration/react-streaming/app/pages/dynamic-imports.js b/test/integration/react-streaming/app/pages/dynamic-imports.js index 2b3c34f65a807..81db3d3cbe800 100644 --- a/test/integration/react-streaming/app/pages/dynamic-imports.js +++ b/test/integration/react-streaming/app/pages/dynamic-imports.js @@ -1,8 +1,8 @@ import { lazy, Suspense } from 'react' import dynamic from 'next/dynamic' -const Foo = lazy(() => import('../components/foo.client')) -const Bar = dynamic(() => import('../components/bar.client'), { +const Foo = lazy(() => import('../components/foo')) +const Bar = dynamic(() => import('../components/bar'), { suspense: true, }) diff --git a/test/integration/react-streaming/app/pages/global-styles.js b/test/integration/react-streaming/app/pages/global-styles.js deleted file mode 100644 index 9db6f53f3eb83..0000000000000 --- a/test/integration/react-streaming/app/pages/global-styles.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function GlobalStyle() { - return ( -
-

This should be in red

-
- ) -} diff --git a/test/integration/react-streaming/app/tsconfig.json b/test/integration/react-streaming/app/tsconfig.json deleted file mode 100644 index 1563f3e878573..0000000000000 --- a/test/integration/react-streaming/app/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "incremental": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve" - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] -} diff --git a/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js b/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js index 53edd542b66c7..16b931125b8d2 100644 --- a/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js +++ b/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js @@ -1,4 +1,4 @@ -import { createHandler, Get, Param } from '@storyofams/next-api-decorators' +import { createHandler, Get, Param } from 'next-api-decorators' class HelloHandler { @Get('/:myParam') diff --git a/test/production/emit-decorator-metadata/index.test.ts b/test/production/emit-decorator-metadata/index.test.ts index 68476ecf08663..4fe4549963807 100644 --- a/test/production/emit-decorator-metadata/index.test.ts +++ b/test/production/emit-decorator-metadata/index.test.ts @@ -15,7 +15,7 @@ describe('emitDecoratorMetadata SWC option', () => { pages: new FileRef(join(__dirname, 'app/pages')), }, dependencies: { - '@storyofams/next-api-decorators': '1.6.0', + 'next-api-decorators': '2.0.0', 'reflect-metadata': '0.1.13', 'path-to-regexp': '6.2.0', tsyringe: '4.6.0',