Skip to content

Commit

Permalink
Fix barrel optimizer conflicts with client entry module (#56020)
Browse files Browse the repository at this point in the history
The barrel optimization loader creates a virtual module to re-export
from the original file, which causes the situation that now there are 2
modules with the same resource but only one of them is the actual code.

When the code contains `"use client"`, the Flight plugin has to collect
its `buildInfo` and generate the manifest and client entry module.
However, we currently deduplicate module by resource in the traversal
logic to avoid unnecessary loops. To make it work together with the
virtual barrel module, we'll need to prefix the resource path to make it
different from the original module.

Closes #54967.
Closes #55609.
Closes #55566.
  • Loading branch information
shuding authored Sep 26, 2023
1 parent 4e44598 commit ed38f03
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 3 deletions.
5 changes: 3 additions & 2 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
SERVER_DIRECTORY,
COMPILER_NAMES,
CompilerNameValues,
BARREL_OPTIMIZATION_PREFIX,
} from '../shared/lib/constants'
import { execOnce } from '../shared/lib/utils'
import { NextConfigComplete } from '../server/config-shared'
Expand Down Expand Up @@ -1217,9 +1218,9 @@ export default async function getBaseWebpackConfig(
return
}

// __barrel_optimize__ is a special marker that tells Next.js to
// BARREL_OPTIMIZATION_PREFIX is a special marker that tells Next.js to
// optimize the import by removing unused exports. This has to be compiled.
if (request.startsWith('__barrel_optimize__')) {
if (request.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { WEBPACK_LAYERS } from '../../../lib/constants'
import {
APP_CLIENT_INTERNALS,
BARREL_OPTIMIZATION_PREFIX,
COMPILER_NAMES,
EDGE_RUNTIME_WEBPACK,
SERVER_REFERENCE_MANIFEST,
Expand Down Expand Up @@ -510,9 +511,17 @@ export class FlightClientEntryPlugin {
// We have to always use the resolved request here to make sure the
// server and client are using the same module path (required by RSC), as
// the server compiler and client compiler have different resolve configs.
const modRequest: string | undefined =
let modRequest: string | undefined =
mod.resourceResolveData?.path + mod.resourceResolveData?.query

// For the barrel optimization, we need to use the match resource instead
// because there will be 2 modules for the same file (same resource path)
// but they're different modules and can't be deduped via `visitedModule`.
// The first module is a virtual re-export module created by the loader.
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
modRequest = mod.matchResource + ':' + modRequest
}

if (!modRequest || visitedModule.has(modRequest)) return
visitedModule.add(modRequest)

Expand Down Expand Up @@ -596,6 +605,14 @@ export class FlightClientEntryPlugin {
modRequest = (mod as any)._identifier
}

// For the barrel optimization, we need to use the match resource instead
// because there will be 2 modules for the same file (same resource path)
// but they're different modules and can't be deduped via `visitedModule`.
// The first module is a virtual re-export module created by the loader.
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
modRequest = mod.matchResource + ':' + modRequest
}

if (!modRequest || visited.has(modRequest)) return
visited.add(modRequest)

Expand Down
1 change: 1 addition & 0 deletions packages/next/src/shared/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const CLIENT_PUBLIC_FILES_PATH = 'public'
export const CLIENT_STATIC_FILES_PATH = 'static'
export const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__'
export const NEXT_BUILTIN_DOCUMENT = '__NEXT_BUILTIN_DOCUMENT__'
export const BARREL_OPTIMIZATION_PREFIX = '__barrel_optimize__'

// server/[entry]/page_client-reference-manifest.js
export const CLIENT_REFERENCE_MANIFEST = 'client-reference-manifest'
Expand Down
5 changes: 5 additions & 0 deletions test/development/basic/barrel-optimization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,9 @@ describe('optimizePackageImports', () => {
const html = await next.render('/visx')
expect(html).toContain('<linearGradient')
})

it('should not break "use client" directive in optimized packages', async () => {
const html = await next.render('/client')
expect(html).toContain('this is a client component')
})
})
9 changes: 9 additions & 0 deletions test/development/basic/barrel-optimization/app/client/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Client } from 'my-lib'

export default function Page() {
return (
<h1>
<Client />
</h1>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use client'

export function Client() {
return 'this is a client component'
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './a'
export * from './client'

0 comments on commit ed38f03

Please sign in to comment.