rsnext/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts
Jiachi Liu 3174c730b8
Alias next public api to esm on edge runtime (#42709)
Cases like `next/link` and `next/router` imports are not alias since
they're not matching the existing alias pattern setting for edge
runtime, which causes router-context being bundled twice (both with cjs
and esm) into edge worker bundle.
so we resolve their paths and alias them to esm bundle for webpack
bundling.

Other minor changes:

* update `require` calls to `import` expressions in edge ssr loaders
* remove client layer for apps without `appDir` enabled
* export `type` for ts typings in next/image to avoid alias to break
resolving

## Bug

- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`
2022-11-09 19:20:32 -08:00

165 lines
4.7 KiB
TypeScript

import { getModuleBuildInfo } from '../get-module-build-info'
import { stringifyRequest } from '../../stringify-request'
export type EdgeSSRLoaderQuery = {
absolute500Path: string
absoluteAppPath: string
absoluteDocumentPath: string
absoluteErrorPath: string
absolutePagePath: string
buildId: string
dev: boolean
isServerComponent: boolean
page: string
stringifiedConfig: string
appDirLoader?: string
pagesType: 'app' | 'pages' | 'root'
sriEnabled: boolean
hasFontLoaders: boolean
}
/*
For pages SSR'd at the edge, we bundle them with the ESM version of Next in order to
benefit from the better tree-shaking and thus, smaller bundle sizes.
The absolute paths for _app, _error and _document, used in this loader, link to the regular CJS modules.
They are generated in `createPagesMapping` where we don't have access to `isEdgeRuntime`,
so we have to do it here. It's not that bad because it keeps all references to ESM modules magic in this place.
*/
function swapDistFolderWithEsmDistFolder(path: string) {
return path.replace('next/dist/pages', 'next/dist/esm/pages')
}
export default async function edgeSSRLoader(this: any) {
const {
dev,
page,
buildId,
absolutePagePath,
absoluteAppPath,
absoluteDocumentPath,
absolute500Path,
absoluteErrorPath,
isServerComponent,
stringifiedConfig,
appDirLoader: appDirLoaderBase64,
pagesType,
sriEnabled,
hasFontLoaders,
} = this.getOptions()
const appDirLoader = Buffer.from(
appDirLoaderBase64 || '',
'base64'
).toString()
const isAppDir = pagesType === 'app'
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.nextEdgeSSR = {
isServerComponent: isServerComponent === 'true',
page: page,
isAppDir,
}
buildInfo.route = {
page,
absolutePagePath,
}
const stringifiedPagePath = stringifyRequest(this, absolutePagePath)
const stringifiedAppPath = stringifyRequest(
this,
swapDistFolderWithEsmDistFolder(absoluteAppPath)
)
const stringifiedErrorPath = stringifyRequest(
this,
swapDistFolderWithEsmDistFolder(absoluteErrorPath)
)
const stringifiedDocumentPath = stringifyRequest(
this,
swapDistFolderWithEsmDistFolder(absoluteDocumentPath)
)
const stringified500Path = absolute500Path
? stringifyRequest(this, absolute500Path)
: null
const pageModPath = `${appDirLoader}${stringifiedPagePath.substring(
1,
stringifiedPagePath.length - 1
)}${isAppDir ? '?__edge_ssr_entry__' : ''}`
const transformed = `
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import { getRender } from 'next/dist/esm/build/webpack/loaders/next-edge-ssr-loader/render'
enhanceGlobals()
const pageType = ${JSON.stringify(pagesType)}
${
isAppDir
? `
import { renderToHTMLOrFlight as appRenderToHTML } from 'next/dist/esm/server/app-render'
import * as pageMod from ${JSON.stringify(pageModPath)}
const Document = null
const pagesRenderToHTML = null
const appMod = null
const errorMod = null
const error500Mod = null
`
: `
import Document from ${stringifiedDocumentPath}
import { renderToHTML as pagesRenderToHTML } from 'next/dist/esm/server/render'
import * as pageMod from ${stringifiedPagePath}
import * as appMod from ${stringifiedAppPath}
import * as errorMod from ${stringifiedErrorPath}
${
stringified500Path
? `import * as error500Mod from ${stringified500Path}`
: `const error500Mod = null`
}
const appRenderToHTML = null
`
}
const buildManifest = self.__BUILD_MANIFEST
const reactLoadableManifest = self.__REACT_LOADABLE_MANIFEST
const rscManifest = self.__RSC_MANIFEST
const rscCssManifest = self.__RSC_CSS_MANIFEST
const subresourceIntegrityManifest = ${
sriEnabled ? 'self.__SUBRESOURCE_INTEGRITY_MANIFEST' : 'undefined'
}
const fontLoaderManifest = ${
hasFontLoaders ? 'self.__FONT_LOADER_MANIFEST' : 'undefined'
}
const render = getRender({
pageType,
dev: ${dev},
page: ${JSON.stringify(page)},
appMod,
pageMod,
errorMod,
error500Mod,
Document,
buildManifest,
appRenderToHTML,
pagesRenderToHTML,
reactLoadableManifest,
serverComponentManifest: ${isServerComponent} ? rscManifest : null,
serverCSSManifest: ${isServerComponent} ? rscCssManifest : null,
subresourceIntegrityManifest,
config: ${stringifiedConfig},
buildId: ${JSON.stringify(buildId)},
fontLoaderManifest,
})
export const ComponentMod = pageMod
export default function(opts) {
return adapter({
...opts,
handler: render
})
}`
return transformed
}