Fix esm property def in flight loader (#66990)
This commit is contained in:
parent
50b9966ba9
commit
0c6dac466a
6 changed files with 35 additions and 21 deletions
|
@ -517,6 +517,13 @@ export default async function getBaseWebpackConfig(
|
||||||
babel: useSWCLoader ? swcDefaultLoader : babelLoader!,
|
babel: useSWCLoader ? swcDefaultLoader : babelLoader!,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nextFlightLoader = {
|
||||||
|
loader: 'next-flight-loader',
|
||||||
|
options: {
|
||||||
|
isEdgeServer,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const appServerLayerLoaders = hasAppDir
|
const appServerLayerLoaders = hasAppDir
|
||||||
? [
|
? [
|
||||||
// When using Babel, we will have to add the SWC loader
|
// When using Babel, we will have to add the SWC loader
|
||||||
|
@ -530,7 +537,7 @@ export default async function getBaseWebpackConfig(
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const instrumentLayerLoaders = [
|
const instrumentLayerLoaders = [
|
||||||
'next-flight-loader',
|
nextFlightLoader,
|
||||||
// When using Babel, we will have to add the SWC loader
|
// When using Babel, we will have to add the SWC loader
|
||||||
// as an additional pass to handle RSC correctly.
|
// as an additional pass to handle RSC correctly.
|
||||||
// This will cause some performance overhead but
|
// This will cause some performance overhead but
|
||||||
|
@ -540,7 +547,7 @@ export default async function getBaseWebpackConfig(
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
|
|
||||||
const middlewareLayerLoaders = [
|
const middlewareLayerLoaders = [
|
||||||
'next-flight-loader',
|
nextFlightLoader,
|
||||||
// When using Babel, we will have to use SWC to do the optimization
|
// When using Babel, we will have to use SWC to do the optimization
|
||||||
// for middleware to tree shake the unused default optimized imports like "next/server".
|
// for middleware to tree shake the unused default optimized imports like "next/server".
|
||||||
// This will cause some performance overhead but
|
// This will cause some performance overhead but
|
||||||
|
@ -1352,9 +1359,7 @@ export default async function getBaseWebpackConfig(
|
||||||
isEdgeServer,
|
isEdgeServer,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
use: {
|
use: nextFlightLoader,
|
||||||
loader: 'next-flight-loader',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
|
@ -54,6 +54,9 @@ export default function transformSource(
|
||||||
throw new Error('Expected source to have been transformed to a string.')
|
throw new Error('Expected source to have been transformed to a string.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = this.getOptions()
|
||||||
|
const { isEdgeServer } = options
|
||||||
|
|
||||||
// Assign the RSC meta information to buildInfo.
|
// Assign the RSC meta information to buildInfo.
|
||||||
// Exclude next internal files which are not marked as client files
|
// Exclude next internal files which are not marked as client files
|
||||||
const buildInfo = getModuleBuildInfo(this._module)
|
const buildInfo = getModuleBuildInfo(this._module)
|
||||||
|
@ -97,21 +100,29 @@ export default function transformSource(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `proxy` is the module proxy that we treat the module as a client boundary.
|
||||||
|
// For ESM, we access the property of the module proxy directly for each export.
|
||||||
|
// This is bit hacky that treating using a CJS like module proxy for ESM's exports,
|
||||||
|
// but this will avoid creating nested proxies for each export. It will be improved in the future.
|
||||||
|
|
||||||
|
// Explanation for: await createProxy(...)
|
||||||
|
// We need to await the module proxy creation because it can be async module for SSR layer
|
||||||
|
// due to having async dependencies.
|
||||||
|
// We only apply `the await` for Node.js as only Edge doesn't have external dependencies.
|
||||||
let esmSource = `\
|
let esmSource = `\
|
||||||
import { createProxy } from "${MODULE_PROXY_PATH}"
|
import { createProxy } from "${MODULE_PROXY_PATH}"
|
||||||
|
|
||||||
|
const proxy = ${isEdgeServer ? '' : 'await'} createProxy(String.raw\`${resourceKey}\`)
|
||||||
`
|
`
|
||||||
let cnt = 0
|
let cnt = 0
|
||||||
for (const ref of clientRefs) {
|
for (const ref of clientRefs) {
|
||||||
if (ref === '') {
|
if (ref === '') {
|
||||||
esmSource += `\nexports[''] = createProxy(String.raw\`${resourceKey}#\`);`
|
esmSource += `exports[''] = proxy['']\n`
|
||||||
} else if (ref === 'default') {
|
} else if (ref === 'default') {
|
||||||
esmSource += `\
|
esmSource += `export default proxy.default;\n`
|
||||||
export default createProxy(String.raw\`${resourceKey}#default\`);
|
|
||||||
`
|
|
||||||
} else {
|
} else {
|
||||||
esmSource += `
|
esmSource += `const e${cnt} = proxy["${ref}"];\n`
|
||||||
const e${cnt} = createProxy(String.raw\`${resourceKey}#${ref}\`);
|
esmSource += `export { e${cnt++} as ${ref} };\n`
|
||||||
export { e${cnt++} as ${ref} };`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -262,7 +262,7 @@ async function createComponentTreeInternal({
|
||||||
}
|
}
|
||||||
|
|
||||||
const LayoutOrPage: React.ComponentType<any> | undefined = layoutOrPageMod
|
const LayoutOrPage: React.ComponentType<any> | undefined = layoutOrPageMod
|
||||||
? await interopDefault(layoutOrPageMod)
|
? interopDefault(layoutOrPageMod)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,7 +15,7 @@ function convertModule<P>(
|
||||||
// Cases:
|
// Cases:
|
||||||
// mod: { default: Component }
|
// mod: { default: Component }
|
||||||
// mod: Component
|
// mod: Component
|
||||||
// mod: { $$typeof, default: proxy(Component) }
|
// mod: { default: proxy(Component) }
|
||||||
// mod: proxy(Component)
|
// mod: proxy(Component)
|
||||||
const hasDefault = mod && 'default' in mod
|
const hasDefault = mod && 'default' in mod
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { FileRef, nextTestSetup } from 'e2e-utils'
|
import { nextTestSetup } from 'e2e-utils'
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
describe('referencing a client component in an app route', () => {
|
describe('referencing a client component in an app route', () => {
|
||||||
const { next } = nextTestSetup({
|
const { next } = nextTestSetup({
|
||||||
files: new FileRef(path.join(__dirname)),
|
files: __dirname,
|
||||||
})
|
})
|
||||||
|
|
||||||
it('responds without error', async () => {
|
it('responds without error', async () => {
|
||||||
expect(JSON.parse(await next.render('/runtime'))).toEqual({
|
expect(JSON.parse(await next.render('/runtime'))).toEqual({
|
||||||
// Turbopack's proxy components are functions
|
clientComponent: 'function',
|
||||||
clientComponent: process.env.TURBOPACK ? 'function' : 'object',
|
myModuleClientComponent: 'function',
|
||||||
myModuleClientComponent: process.env.TURBOPACK ? 'function' : 'object',
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
const Button = dynamic(() =>
|
const Button = dynamic(() =>
|
||||||
import('./client').then((mod) => {
|
import('./client').then((mod) => {
|
||||||
return mod.Button
|
return { default: mod.Button }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue