Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dont apply existing externals path changing to rsc layers #41744

Merged
merged 9 commits into from Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 51 additions & 50 deletions packages/next/build/webpack-config.ts
Expand Up @@ -2,7 +2,7 @@ import ReactRefreshWebpackPlugin from 'next/dist/compiled/@next/react-refresh-ut
import chalk from 'next/dist/compiled/chalk'
import crypto from 'crypto'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import path, { join as pathJoin, relative as relativePath } from 'path'
import path from 'path'
import { escapeStringRegexp } from '../shared/lib/escape-regexp'
import {
DOT_NEXT_ALIAS,
Expand Down Expand Up @@ -62,9 +62,12 @@ import { SubresourceIntegrityPlugin } from './webpack/plugins/subresource-integr
import { FontLoaderManifestPlugin } from './webpack/plugins/font-loader-manifest-plugin'
import { getSupportedBrowsers } from './utils'

const NEXT_PROJECT_ROOT = pathJoin(__dirname, '..', '..')
const NEXT_PROJECT_ROOT_DIST = pathJoin(NEXT_PROJECT_ROOT, 'dist')
const NEXT_PROJECT_ROOT_DIST_CLIENT = pathJoin(NEXT_PROJECT_ROOT_DIST, 'client')
const NEXT_PROJECT_ROOT = path.join(__dirname, '..', '..')
const NEXT_PROJECT_ROOT_DIST = path.join(NEXT_PROJECT_ROOT, 'dist')
const NEXT_PROJECT_ROOT_DIST_CLIENT = path.join(
NEXT_PROJECT_ROOT_DIST,
'client'
)

const babelIncludeRegexes: RegExp[] = [
/next[\\/]dist[\\/](esm[\\/])?shared[\\/]lib/,
Expand All @@ -73,6 +76,8 @@ const babelIncludeRegexes: RegExp[] = [
/[\\/](strip-ansi|ansi-regex|styled-jsx)[\\/]/,
]

const reactPackagesRegex = /^(react(?:$|\/)|react-dom(?:$|\/))/

const staticGenerationAsyncStorageRegex =
/next[\\/]dist[\\/]client[\\/]components[\\/]static-generation-async-storage/

Expand Down Expand Up @@ -130,20 +135,12 @@ function isResourceInPackages(
? resource.startsWith(packageDirMapping.get(p)! + path.sep)
: resource.includes(
path.sep +
pathJoin('node_modules', p.replace(/\//g, path.sep)) +
path.join('node_modules', p.replace(/\//g, path.sep)) +
path.sep
)
)
}

const bundledReactImports = [
'react',
'react-dom',
'react/jsx-runtime',
'react/jsx-dev-runtime',
'next/dist/compiled/react-server-dom-webpack/server.browser',
]

export function getDefineEnv({
dev,
config,
Expand Down Expand Up @@ -747,10 +744,12 @@ export default async function getBaseWebpackConfig(
),
[CLIENT_STATIC_FILES_RUNTIME_AMP]:
`./` +
relativePath(
dir,
pathJoin(NEXT_PROJECT_ROOT_DIST_CLIENT, 'dev', 'amp-dev')
).replace(/\\/g, '/'),
path
.relative(
dir,
path.join(NEXT_PROJECT_ROOT_DIST_CLIENT, 'dev', 'amp-dev')
)
.replace(/\\/g, '/'),
}
: {}),
[CLIENT_STATIC_FILES_RUNTIME_MAIN]:
Expand Down Expand Up @@ -1069,7 +1068,10 @@ export default async function getBaseWebpackConfig(

// Special internal modules that must be bundled for Server Components.
if (layer === WEBPACK_LAYERS.server) {
if (bundledReactImports.includes(request)) {
if (
reactPackagesRegex.test(request) ||
request === 'next/dist/compiled/react-server-dom-webpack/server.browser'
) {
return
}
}
Expand All @@ -1082,9 +1084,13 @@ export default async function getBaseWebpackConfig(
if (/^(?:next$)/.test(request)) {
return `commonjs ${request}`
}

if (/^(react(?:$|\/)|react-dom(?:$|\/))/.test(request)) {
// override react-dom to server-rendering-stub for server
if (request === 'react-dom' && hasAppDir && !isClient) {
if (
request === 'react-dom' &&
(layer === WEBPACK_LAYERS.client || layer === WEBPACK_LAYERS.server)
) {
request = 'react-dom/server-rendering-stub'
}
return `commonjs ${hasAppDir ? 'next/dist/compiled/' : ''}${request}`
Expand Down Expand Up @@ -1113,9 +1119,12 @@ export default async function getBaseWebpackConfig(
// we need to process shared `router/router` and `dynamic`,
// so that the DefinePlugin can inject process.env values
const isNextExternal =
/next[/\\]dist[/\\](shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic))/.test(
localRes
)
// Treat next internals as non-external for server layer
layer === WEBPACK_LAYERS.server
? false
: /next[/\\]dist[/\\](shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic))/.test(
localRes
)

if (isNextExternal) {
// Generate Next.js external import
Expand All @@ -1132,7 +1141,7 @@ export default async function getBaseWebpackConfig(
.replace(/\\/g, '/')
)
return `commonjs ${externalRequest}`
} else {
} else if (layer !== WEBPACK_LAYERS.client) {
// We don't want to retry local requests
// with other preferEsm options
return
Expand Down Expand Up @@ -1206,7 +1215,7 @@ export default async function getBaseWebpackConfig(
// It doesn't matter what the extension is, as we'll transpile it anyway.
if (config.experimental.transpilePackages && !resolvedExternalPackageDirs) {
resolvedExternalPackageDirs = new Map()
// We need to reoslve all the external package dirs initially.
// We need to resolve all the external package dirs initially.
for (const pkg of config.experimental.transpilePackages) {
const pkgRes = await resolveExternal(
dir,
Expand Down Expand Up @@ -1241,22 +1250,13 @@ export default async function getBaseWebpackConfig(
return
}

// Treat react packages as external for SSR layer,
// then let require-hook mapping them to internals.
// Treat react packages and next internals as external for SSR layer,
// also map react to builtin ones with require-hook.
if (layer === WEBPACK_LAYERS.client) {
if (
[
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime',
'react-dom',
'scheduler',
].includes(request)
) {
if (reactPackagesRegex.test(request)) {
return `commonjs next/dist/compiled/${request}`
} else {
return
}
return
}

if (shouldBeBundled) return
Expand Down Expand Up @@ -1634,10 +1634,8 @@ export default async function getBaseWebpackConfig(
// react to the direct file path, not the package name. In that case the condition
// will be ignored completely.
react: 'next/dist/compiled/react',
'react-dom$': isClient
? 'next/dist/compiled/react-dom/index'
: 'next/dist/compiled/react-dom/server-rendering-stub',
'react-dom/client$': 'next/dist/compiled/react-dom/client',
'react-dom$':
'next/dist/compiled/react-dom/server-rendering-stub',
},
},
},
Expand Down Expand Up @@ -1677,11 +1675,6 @@ export default async function getBaseWebpackConfig(
// RSC server compilation loaders
{
test: codeCondition.test,
include: [
dir,
// To let the internal client components passing through flight loader
NEXT_PROJECT_ROOT_DIST,
],
exclude: [staticGenerationAsyncStorageRegex],
issuerLayer: WEBPACK_LAYERS.server,
use: {
Expand Down Expand Up @@ -1715,7 +1708,6 @@ export default async function getBaseWebpackConfig(
// Alias react for switching between default set and share subset.
oneOf: [
{
// test: codeCondition.test,
exclude: [staticGenerationAsyncStorageRegex],
issuerLayer: WEBPACK_LAYERS.server,
test(req: string) {
Expand All @@ -1742,14 +1734,23 @@ export default async function getBaseWebpackConfig(
},
},
},
{
issuerLayer: WEBPACK_LAYERS.client,
test: codeCondition.test,
resolve: {
alias: {
react: 'next/dist/compiled/react',
'react-dom$':
'next/dist/compiled/react-dom/server-rendering-stub',
},
},
},
{
test: codeCondition.test,
resolve: {
alias: {
react: 'next/dist/compiled/react',
'react-dom$': isClient
? 'next/dist/compiled/react-dom/index'
: 'next/dist/compiled/react-dom/server-rendering-stub',
'react-dom$': 'next/dist/compiled/react-dom',
'react-dom/client$':
'next/dist/compiled/react-dom/client',
},
Expand Down
2 changes: 2 additions & 0 deletions packages/next/client/components/hooks-client-context.ts
@@ -1,3 +1,5 @@
'use client'

import { createContext } from 'react'

export const SearchParamsContext = createContext<URLSearchParams>(null as any)
Expand Down
@@ -1,12 +1,17 @@
'use client'
// useLayoutSegments() // Only the segments for the current place. ['children', 'dashboard', 'children', 'integrations'] -> /dashboard/integrations (/dashboard/layout.js would get ['children', 'dashboard', 'children', 'integrations'])

import { useContext, useMemo } from 'react'
import type { FlightRouterState } from '../../server/app-render'
import {
AppRouterContext,
LayoutRouterContext,
} from '../../shared/lib/app-router-context'
import {
SearchParamsContext,
// ParamsContext,
PathnameContext,
// LayoutSegmentsContext,
} from '../hooks-client-context'
} from './hooks-client-context'

const INTERNAL_URLSEARCHPARAMS_INSTANCE = Symbol(
'internal for urlsearchparams readonly'
Expand Down Expand Up @@ -91,4 +96,73 @@ export function usePathname(): string {
export {
ServerInsertedHTMLContext,
useServerInsertedHTML,
} from '../../../shared/lib/server-inserted-html'
} from '../../shared/lib/server-inserted-html'

// TODO-APP: Move the other router context over to this one
/**
* Get the router methods. For example router.push('/dashboard')
*/
export function useRouter(): import('../../shared/lib/app-router-context').AppRouterInstance {
return useContext(AppRouterContext)
}

// TODO-APP: handle parallel routes
function getSelectedLayoutSegmentPath(
tree: FlightRouterState,
parallelRouteKey: string,
first = true,
segmentPath: string[] = []
): string[] {
let node: FlightRouterState
if (first) {
// Use the provided parallel route key on the first parallel route
node = tree[1][parallelRouteKey]
} else {
// After first parallel route prefer children, if there's no children pick the first parallel route.
const parallelRoutes = tree[1]
node = parallelRoutes.children ?? Object.values(parallelRoutes)[0]
}

if (!node) return segmentPath
const segment = node[0]
const segmentValue = Array.isArray(segment) ? segment[1] : segment
if (!segmentValue) return segmentPath

segmentPath.push(segmentValue)

return getSelectedLayoutSegmentPath(
node,
parallelRouteKey,
false,
segmentPath
)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the canonical segment path from the current level to the leaf node.
*/
export function useSelectedLayoutSegments(
parallelRouteKey: string = 'children'
): string[] {
const { tree } = useContext(LayoutRouterContext)
return getSelectedLayoutSegmentPath(tree, parallelRouteKey)
}

// TODO-APP: Expand description when the docs are written for it.
/**
* Get the segment below the current level
*/
export function useSelectedLayoutSegment(
parallelRouteKey: string = 'children'
): string {
const selectedLayoutSegments = useSelectedLayoutSegments(parallelRouteKey)
if (selectedLayoutSegments.length === 0) {
throw new Error('No selected layout segment below the current level')
}
Comment on lines +160 to +162
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes useSelectedLayoutSegment inside the RootLayout unusable.

https://youtu.be/pC2dl8hNVGg?t=1454


return selectedLayoutSegments[0]
}

export { redirect } from './redirect'
export { notFound } from './not-found'