Skip to content

Commit

Permalink
Pages Module Transition (vercel#49962)
Browse files Browse the repository at this point in the history
This serves to create the loader that will allow us to transition more
of the rendering logic into each of the bundled entrypoints. This only
applies to pages routes inside the `pages/` folder in the node
environment.

fix NEXT-985

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
2 people authored and hydRAnger committed Jun 12, 2023
1 parent bcc2bb8 commit 847dfc2
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 75 deletions.
38 changes: 25 additions & 13 deletions packages/next/src/build/entries.ts
Expand Up @@ -52,6 +52,8 @@ import { EdgeFunctionLoaderOptions } from './webpack/loaders/next-edge-function-
import { isAppRouteRoute } from '../lib/is-app-route-route'
import { normalizeMetadataRoute } from '../lib/metadata/get-metadata-route'
import { fileExists } from '../lib/file-exists'
import { getRouteLoaderEntry } from './webpack/loaders/next-route-loader'
import { isInternalPathname } from '../lib/is-internal-pathname'

export async function getStaticInfoIncludingLayouts({
isInsideAppDir,
Expand Down Expand Up @@ -538,7 +540,7 @@ export async function createEntrypoints(
// server compiler inject them instead.
} else {
client[clientBundlePath] = getClientEntry({
absolutePagePath: mappings[page],
absolutePagePath,
page,
})
}
Expand All @@ -549,7 +551,7 @@ export async function createEntrypoints(
server[serverBundlePath] = getAppEntry({
page,
name: serverBundlePath,
pagePath: mappings[page],
pagePath: absolutePagePath,
appDir,
appPaths: matchedAppPaths,
pageExtensions,
Expand All @@ -558,16 +560,26 @@ export async function createEntrypoints(
nextConfigOutput: config.output,
preferredRegion: staticInfo.preferredRegion,
})
} else {
if (isInstrumentationHookFile(page) && pagesType === 'root') {
server[serverBundlePath.replace('src/', '')] = {
import: mappings[page],
// the '../' is needed to make sure the file is not chunked
filename: `../${INSTRUMENTATION_HOOK_FILENAME}.js`,
}
} else {
server[serverBundlePath] = [mappings[page]]
} else if (isInstrumentationHookFile(page) && pagesType === 'root') {
server[serverBundlePath.replace('src/', '')] = {
import: absolutePagePath,
// the '../' is needed to make sure the file is not chunked
filename: `../${INSTRUMENTATION_HOOK_FILENAME}.js`,
}
} else if (
!isAPIRoute(page) &&
!isMiddlewareFile(page) &&
!isInternalPathname(absolutePagePath)
) {
server[serverBundlePath] = [
getRouteLoaderEntry({
page,
absolutePagePath,
preferredRegion: staticInfo.preferredRegion,
}),
]
} else {
server[serverBundlePath] = [absolutePagePath]
}
},
onEdgeServer: () => {
Expand All @@ -577,7 +589,7 @@ export async function createEntrypoints(
appDirLoader = getAppEntry({
name: serverBundlePath,
page,
pagePath: mappings[page],
pagePath: absolutePagePath,
appDir: appDir!,
appPaths: matchedAppPaths,
pageExtensions,
Expand All @@ -596,7 +608,7 @@ export async function createEntrypoints(
edgeServer[normalizedServerBundlePath] = getEdgeServerEntry({
...params,
rootDir,
absolutePagePath: mappings[page],
absolutePagePath: absolutePagePath,
bundlePath: clientBundlePath,
isDev: false,
isServerComponent,
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/build/webpack-config.ts
Expand Up @@ -1781,6 +1781,7 @@ export default async function getBaseWebpackConfig(
'next-middleware-asset-loader',
'next-middleware-wasm-loader',
'next-app-loader',
'next-route-loader',
'next-font-loader',
'next-invalid-import-error-loader',
'next-metadata-route-loader',
Expand Down
64 changes: 64 additions & 0 deletions packages/next/src/build/webpack/loaders/next-route-loader.ts
@@ -0,0 +1,64 @@
import type { webpack } from 'next/dist/compiled/webpack/webpack'

import { stringify } from 'querystring'
import { getModuleBuildInfo } from './get-module-build-info'

/**
* The options for the route loader.
*/
type RouteLoaderOptions = {
/**
* The page name for this particular route.
*/
page: string

/**
* The preferred region for this route.
*/
preferredRegion: string | string[] | undefined

/**
* The absolute path to the userland page file.
*/
absolutePagePath: string
}

/**
* Returns the loader entry for a given page.
*
* @param query the options to create the loader entry
* @returns the encoded loader entry
*/
export function getRouteLoaderEntry(query: RouteLoaderOptions): string {
return `next-route-loader?${stringify(query)}!`
}

/**
* Handles the `next-route-loader` options.
* @returns the loader definition function
*/
const loader: webpack.LoaderDefinitionFunction<RouteLoaderOptions> =
function () {
const { page, preferredRegion, absolutePagePath } = this.getOptions()

// Ensure we only run this loader for as a module.
if (!this._module) {
throw new Error('Invariant: expected this to reference a module')
}

// Attach build info to the module.
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.route = {
page,
absolutePagePath,
preferredRegion,
}

return `
// Next.js Route Loader
export * from ${JSON.stringify(absolutePagePath)}
export { default } from ${JSON.stringify(absolutePagePath)}
`
}

export default loader
Expand Up @@ -285,20 +285,24 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
// don't include the entry itself in the trace
entryFiles.delete(nodePath.join(outputPath, `../${entrypoint.name}.js`))

const finalFiles: string[] = []

for (const file of new Set([
...entryFiles,
...allEntryFiles,
...(this.entryTraces.get(entrypoint.name) || []),
])) {
if (file) {
finalFiles.push(
nodePath.relative(traceOutputPath, file).replace(/\\/g, '/')
)
}
}

assets[traceOutputName] = new sources.RawSource(
JSON.stringify({
version: TRACE_OUTPUT_VERSION,
files: [
...new Set([
...entryFiles,
...allEntryFiles,
...(this.entryTraces.get(entrypoint.name) || []),
]),
].map((file) => {
return nodePath
.relative(traceOutputPath, file)
.replace(/\\/g, '/')
}),
files: finalFiles,
})
)
}
Expand Down Expand Up @@ -369,42 +373,35 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
entryModMap.set(absolutePath, entryMod)
entryNameMap.set(absolutePath, name)
}
} else {
// If there was no `route` property, we can assume that it was something custom instead.
// In order to trace these we add them to the additionalEntries map.
if (entryMod.request) {
let curMap = additionalEntries.get(name)

if (!curMap) {
curMap = new Map()
additionalEntries.set(name, curMap)
}
depModMap.set(entryMod.request, entryMod)
curMap.set(entryMod.resource, entryMod)
}
}
}

if (entryMod && entryMod.resource) {
const normalizedResource = entryMod.resource.replace(
/\\/g,
'/'
)

if (normalizedResource.includes('pages/')) {
entryNameMap.set(entryMod.resource, name)
entryModMap.set(entryMod.resource, entryMod)
} else {
// If there was no `route` property, we can assume that it was something custom instead.
// In order to trace these we add them to the additionalEntries map.
if (entryMod.request) {
let curMap = additionalEntries.get(name)

if (!curMap) {
curMap = new Map()
additionalEntries.set(name, curMap)
}
depModMap.set(entryMod.resource, entryMod)
depModMap.set(entryMod.request, entryMod)
curMap.set(entryMod.resource, entryMod)
}
}

if (entryMod && entryMod.resource) {
entryNameMap.set(entryMod.resource, name)
entryModMap.set(entryMod.resource, entryMod)

let curMap = additionalEntries.get(name)

if (!curMap) {
curMap = new Map()
additionalEntries.set(name, curMap)
}
depModMap.set(entryMod.resource, entryMod)
curMap.set(entryMod.resource, entryMod)
}
}
}
})
Expand Down Expand Up @@ -545,6 +542,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
this.tracingRoot,
entry
)

const curExtraEntries = additionalEntries.get(entryName)
const finalDeps = new Set<string>()

Expand Down
Expand Up @@ -136,7 +136,7 @@ export async function getNotFoundError(
.filter(
(name) =>
name &&
!/next-(app|middleware|client-pages|flight-(client|server|client-entry))-loader\.js/.test(
!/next-(app|middleware|client-pages|route|flight-(client|server|client-entry))-loader\.js/.test(
name
) &&
!/css-loader.+\.js/.test(name)
Expand Down
Expand Up @@ -48,7 +48,7 @@ function formatMessage(
message.moduleTrace &&
message.moduleTrace.filter(
(trace: any) =>
!/next-(middleware|client-pages|edge-function)-loader\.js/.test(
!/next-(middleware|client-pages|route|edge-function)-loader\.js/.test(
trace.originName
)
)
Expand Down
3 changes: 3 additions & 0 deletions packages/next/src/lib/is-internal-pathname.ts
@@ -0,0 +1,3 @@
export function isInternalPathname(pathname: string): boolean {
return pathname.startsWith('next/dist/pages/')
}
71 changes: 47 additions & 24 deletions packages/next/src/server/dev/hot-reloader.ts
Expand Up @@ -42,7 +42,11 @@ import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page
import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep'
import getRouteFromEntrypoint from '../get-route-from-entrypoint'
import { fileExists } from '../../lib/file-exists'
import { difference, isMiddlewareFilename } from '../../build/utils'
import {
difference,
isMiddlewareFile,
isMiddlewareFilename,
} from '../../build/utils'
import { DecodeError } from '../../shared/lib/utils'
import { Span, trace } from '../../trace'
import { getProperError } from '../../lib/is-error'
Expand All @@ -53,6 +57,9 @@ import { getRegistry } from '../../lib/helpers/get-registry'
import { RouteMatch } from '../future/route-matches/route-match'
import type { Telemetry } from '../../telemetry/storage'
import { parseVersionInfo, VersionInfo } from './parse-version-info'
import { isAPIRoute } from '../../lib/is-api-route'
import { getRouteLoaderEntry } from '../../build/webpack/loaders/next-route-loader'
import { isInternalPathname } from '../../lib/is-internal-pathname'

function diff(a: Set<any>, b: Set<any>) {
return new Set([...a].filter((v) => !b.has(v)))
Expand Down Expand Up @@ -820,33 +827,49 @@ export default class HotReloader {
) {
relativeRequest = `./${relativeRequest}`
}

let value: { import: string; layer?: string } | string
if (isAppPath) {
value = getAppEntry({
name: bundlePath,
page,
appPaths: entryData.appPaths,
pagePath: posix.join(
APP_DIR_ALIAS,
relative(
this.appDir!,
entryData.absolutePagePath
).replace(/\\/g, '/')
),
appDir: this.appDir!,
pageExtensions: this.config.pageExtensions,
rootDir: this.dir,
isDev: true,
tsconfigPath: this.config.typescript.tsconfigPath,
basePath: this.config.basePath,
assetPrefix: this.config.assetPrefix,
nextConfigOutput: this.config.output,
preferredRegion: staticInfo.preferredRegion,
})
} else if (
!isAPIRoute(page) &&
!isMiddlewareFile(page) &&
!isInternalPathname(relativeRequest)
) {
value = getRouteLoaderEntry({
page,
absolutePagePath: relativeRequest,
preferredRegion: staticInfo.preferredRegion,
})
} else {
value = relativeRequest
}

entrypoints[bundlePath] = finalizeEntrypoint({
compilerType: COMPILER_NAMES.server,
name: bundlePath,
isServerComponent,
value: isAppPath
? getAppEntry({
name: bundlePath,
page,
appPaths: entryData.appPaths,
pagePath: posix.join(
APP_DIR_ALIAS,
relative(
this.appDir!,
entryData.absolutePagePath
).replace(/\\/g, '/')
),
appDir: this.appDir!,
pageExtensions: this.config.pageExtensions,
rootDir: this.dir,
isDev: true,
tsconfigPath: this.config.typescript.tsconfigPath,
basePath: this.config.basePath,
assetPrefix: this.config.assetPrefix,
nextConfigOutput: this.config.output,
preferredRegion: staticInfo.preferredRegion,
})
: relativeRequest,
value,
hasAppDir,
})
},
Expand Down

0 comments on commit 847dfc2

Please sign in to comment.