2022-05-05 22:42:22 +02:00
|
|
|
import path from 'path'
|
2022-05-05 13:15:32 +02:00
|
|
|
import type webpack from 'webpack5'
|
2022-05-05 22:42:22 +02:00
|
|
|
import { NODE_RESOLVE_OPTIONS } from '../../webpack-config'
|
2022-05-13 19:48:53 +02:00
|
|
|
import { getModuleBuildInfo } from './get-module-build-info'
|
2022-05-05 22:42:22 +02:00
|
|
|
|
|
|
|
function pathToUrlPath(pathname: string) {
|
2022-05-25 11:46:26 +02:00
|
|
|
let urlPath = pathname.replace(/^private-next-app-dir/, '')
|
2022-05-05 22:42:22 +02:00
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
// For `app/layout.js`
|
2022-05-05 22:42:22 +02:00
|
|
|
if (urlPath === '') {
|
|
|
|
urlPath = '/'
|
|
|
|
}
|
|
|
|
|
|
|
|
return urlPath
|
|
|
|
}
|
|
|
|
|
2022-06-02 12:02:05 +02:00
|
|
|
async function resolvePathsByPage({
|
|
|
|
name,
|
2022-05-05 22:42:22 +02:00
|
|
|
pagePath,
|
|
|
|
resolve,
|
|
|
|
}: {
|
2022-06-02 12:02:05 +02:00
|
|
|
name: 'layout' | 'loading'
|
2022-05-05 22:42:22 +02:00
|
|
|
pagePath: string
|
|
|
|
resolve: (pathname: string) => Promise<string | undefined>
|
|
|
|
}) {
|
2022-06-02 12:02:05 +02:00
|
|
|
const paths = new Map<string, string | undefined>()
|
2022-05-05 22:42:22 +02:00
|
|
|
const parts = pagePath.split('/')
|
2022-05-10 18:57:14 +02:00
|
|
|
const isNewRootLayout =
|
|
|
|
parts[1]?.length > 2 && parts[1]?.startsWith('(') && parts[1]?.endsWith(')')
|
2022-05-05 22:42:22 +02:00
|
|
|
|
2022-05-10 18:57:14 +02:00
|
|
|
for (let i = parts.length; i >= 0; i--) {
|
2022-05-05 22:42:22 +02:00
|
|
|
const pathWithoutSlashLayout = parts.slice(0, i).join('/')
|
|
|
|
|
2022-05-10 18:57:14 +02:00
|
|
|
if (!pathWithoutSlashLayout) {
|
|
|
|
continue
|
|
|
|
}
|
2022-06-02 12:02:05 +02:00
|
|
|
const layoutPath = `${pathWithoutSlashLayout}/${name}`
|
2022-05-10 18:57:14 +02:00
|
|
|
let resolvedLayoutPath = await resolve(layoutPath)
|
2022-05-05 22:42:22 +02:00
|
|
|
let urlPath = pathToUrlPath(pathWithoutSlashLayout)
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
// if we are in a new root app/(root) and a custom root layout was
|
|
|
|
// not provided or a root layout app/layout is not present, we use
|
2022-05-10 18:57:14 +02:00
|
|
|
// a default root layout to provide the html/body tags
|
2022-06-02 12:02:05 +02:00
|
|
|
const isCustomRootLayout = name === 'layout' && isNewRootLayout && i === 2
|
2022-05-10 18:57:14 +02:00
|
|
|
|
2022-06-02 12:02:05 +02:00
|
|
|
if (
|
|
|
|
name === 'layout' &&
|
|
|
|
(isCustomRootLayout || i === 1) &&
|
|
|
|
!resolvedLayoutPath
|
|
|
|
) {
|
2022-05-25 11:46:26 +02:00
|
|
|
resolvedLayoutPath = await resolve('next/dist/lib/app-layout')
|
2022-05-10 18:57:14 +02:00
|
|
|
}
|
2022-06-02 12:02:05 +02:00
|
|
|
paths.set(urlPath, resolvedLayoutPath)
|
2022-05-05 22:42:22 +02:00
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
// if we're in a new root layout don't add the top-level app/layout
|
2022-05-10 18:57:14 +02:00
|
|
|
if (isCustomRootLayout) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2022-06-02 12:02:05 +02:00
|
|
|
return paths
|
2022-05-05 22:42:22 +02:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
const nextAppLoader: webpack.LoaderDefinitionFunction<{
|
2022-05-13 19:48:53 +02:00
|
|
|
name: string
|
2022-05-05 22:42:22 +02:00
|
|
|
pagePath: string
|
2022-05-25 11:46:26 +02:00
|
|
|
appDir: string
|
2022-05-07 15:37:14 +02:00
|
|
|
pageExtensions: string[]
|
2022-05-25 11:46:26 +02:00
|
|
|
}> = async function nextAppLoader() {
|
|
|
|
const { name, appDir, pagePath, pageExtensions } = this.getOptions() || {}
|
2022-05-13 19:48:53 +02:00
|
|
|
|
|
|
|
const buildInfo = getModuleBuildInfo((this as any)._module)
|
|
|
|
buildInfo.route = {
|
2022-05-25 11:46:26 +02:00
|
|
|
page: name.replace(/^app/, ''),
|
|
|
|
absolutePagePath: appDir + pagePath.replace(/^private-next-app-dir/, ''),
|
2022-05-13 19:48:53 +02:00
|
|
|
}
|
2022-05-07 15:37:14 +02:00
|
|
|
|
|
|
|
const extensions = pageExtensions.map((extension) => `.${extension}`)
|
|
|
|
const resolveOptions: any = {
|
|
|
|
...NODE_RESOLVE_OPTIONS,
|
|
|
|
extensions,
|
|
|
|
}
|
2022-05-05 22:42:22 +02:00
|
|
|
const resolve = this.getResolve(resolveOptions)
|
2022-05-05 13:15:32 +02:00
|
|
|
|
2022-06-02 12:02:05 +02:00
|
|
|
const loadingPaths = await resolvePathsByPage({
|
|
|
|
name: 'loading',
|
|
|
|
pagePath: pagePath,
|
|
|
|
resolve: async (pathname) => {
|
|
|
|
try {
|
|
|
|
return await resolve(this.rootContext, pathname)
|
|
|
|
} catch (err: any) {
|
|
|
|
if (err.message.includes("Can't resolve")) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
const loadingComponentsCode = []
|
|
|
|
for (const [loadingPath, resolvedLoadingPath] of loadingPaths) {
|
|
|
|
if (resolvedLoadingPath) {
|
|
|
|
this.addDependency(resolvedLoadingPath)
|
|
|
|
// use require so that we can bust the require cache
|
|
|
|
const codeLine = `'${loadingPath}': () => require('${resolvedLoadingPath}')`
|
|
|
|
loadingComponentsCode.push(codeLine)
|
|
|
|
} else {
|
|
|
|
for (const ext of extensions) {
|
|
|
|
this.addMissingDependency(
|
|
|
|
path.join(appDir, loadingPath, `layout${ext}`)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const layoutPaths = await resolvePathsByPage({
|
|
|
|
name: 'layout',
|
2022-05-07 15:37:14 +02:00
|
|
|
pagePath: pagePath,
|
2022-05-05 22:42:22 +02:00
|
|
|
resolve: async (pathname) => {
|
|
|
|
try {
|
|
|
|
return await resolve(this.rootContext, pathname)
|
|
|
|
} catch (err: any) {
|
|
|
|
if (err.message.includes("Can't resolve")) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
const componentsCode = []
|
|
|
|
for (const [layoutPath, resolvedLayoutPath] of layoutPaths) {
|
|
|
|
if (resolvedLayoutPath) {
|
|
|
|
this.addDependency(resolvedLayoutPath)
|
|
|
|
// use require so that we can bust the require cache
|
|
|
|
const codeLine = `'${layoutPath}': () => require('${resolvedLayoutPath}')`
|
|
|
|
componentsCode.push(codeLine)
|
|
|
|
} else {
|
|
|
|
for (const ext of extensions) {
|
2022-05-25 11:46:26 +02:00
|
|
|
this.addMissingDependency(path.join(appDir, layoutPath, `layout${ext}`))
|
2022-05-05 22:42:22 +02:00
|
|
|
}
|
2022-05-05 13:15:32 +02:00
|
|
|
}
|
2022-05-05 22:42:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add page itself to the list of components
|
|
|
|
componentsCode.push(
|
2022-05-07 15:37:14 +02:00
|
|
|
`'${pathToUrlPath(pagePath).replace(
|
2022-05-29 20:53:12 +02:00
|
|
|
new RegExp(`(${extensions.join('|')})$`),
|
2022-05-05 22:42:22 +02:00
|
|
|
''
|
|
|
|
// use require so that we can bust the require cache
|
2022-05-07 15:37:14 +02:00
|
|
|
)}': () => require('${pagePath}')`
|
2022-05-05 22:42:22 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const result = `
|
|
|
|
export const components = {
|
|
|
|
${componentsCode.join(',\n')}
|
|
|
|
};
|
2022-05-16 11:46:45 +02:00
|
|
|
|
2022-06-02 12:02:05 +02:00
|
|
|
export const loadingComponents = {
|
|
|
|
${loadingComponentsCode.join(',\n')}
|
|
|
|
};
|
|
|
|
|
2022-05-29 20:53:12 +02:00
|
|
|
export const AppRouter = require('next/dist/client/components/app-router.client.js').default
|
2022-06-01 13:52:57 +02:00
|
|
|
export const LayoutRouter = require('next/dist/client/components/layout-router.client.js').default
|
2022-05-29 20:53:12 +02:00
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
export const __next_app_webpack_require__ = __webpack_require__
|
2022-05-05 13:15:32 +02:00
|
|
|
`
|
2022-05-05 22:42:22 +02:00
|
|
|
return result
|
2022-05-05 13:15:32 +02:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
export default nextAppLoader
|