rsnext/packages/next/build/webpack/loaders/next-flight-client-loader/index.ts
Jiachi Liu b4f74ee1c1
Handle async module for client components (#39953)
### Problem

esm modules imports from client components will be compiled to `m = import('module-name')` when webpack bundles them for server components flight rendering. In this case, they will all become async modules since dyanmic imports will return a promise which react flight cannot handle it then results into module resolving error on server flight rendering.

### Solution

* React flight renderer supports handling async modules in https://github.com/facebook/react/pull/25138
* On next.js side leverage the module proxy change for each client reference, to make sure it always resolve the correct client module

The idea is wrapping each module with a module proxy, and if the module is async and accessed as thenable, it will return a new module reference with `async` label to tell react to handle it as async modules:

exported client reference `*` --> not async module (non thenable) --> original module reference `''`
exported client reference `*` --> it's async module (thenable) --> wrapped module reference `'*'` with `async` label

### Note

Since we need to check if user having incorrect gSSP/gSP specifying in layout client componet, so we still need to parse it and assign those info to the proxy (Does client module containing `ssr`, `ssg` exports). Otherwise the proxy will return the cached module reference
2022-08-29 14:47:06 +00:00

35 lines
993 B
TypeScript

/**
* 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 { checkExports } from '../../../analysis/get-page-static-info'
import { parse } from '../../../swc'
export default async function transformSource(
this: any,
source: string
): Promise<string> {
const { resourcePath } = this
const transformedSource = source
if (typeof transformedSource !== 'string') {
throw new Error('Expected source to have been transformed to a string.')
}
const swcAST = await parse(transformedSource, {
filename: resourcePath,
isModule: 'unknown',
})
const { ssg, ssr } = checkExports(swcAST)
const output = `
const { createProxy } = require("next/dist/build/webpack/loaders/next-flight-client-loader/module-proxy")\n
module.exports = createProxy(${JSON.stringify(
resourcePath
)}, { ssr: ${ssr}, ssg: ${ssg} })
`
return output
}