Skip to content

Commit

Permalink
provide interception rewrites to edge runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Jan 31, 2024
1 parent 620b40b commit c438ed6
Show file tree
Hide file tree
Showing 17 changed files with 156 additions and 56 deletions.
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ impl AppEndpoint {
"server/middleware-build-manifest.js".to_string(),
"server/middleware-react-loadable-manifest.js".to_string(),
"server/next-font-manifest.js".to_string(),
"server/interception-route-rewrite-manifest.js".to_string(),
];
let mut wasm_paths_from_root = vec![];

Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,6 @@ export default async function build(
.traceAsyncFn(() => loadCustomRoutes(config))

const { headers, rewrites, redirects } = customRoutes
NextBuildContext.rewrites = rewrites
NextBuildContext.originalRewrites = config._originalRewrites
NextBuildContext.originalRedirects = config._originalRedirects

Expand Down Expand Up @@ -972,6 +971,8 @@ export default async function build(
...generateInterceptionRoutesRewrites(appPaths, config.basePath)
)

NextBuildContext.rewrites = rewrites

const totalAppPagesCount = appPaths.length

const pageKeys = {
Expand Down
4 changes: 4 additions & 0 deletions packages/next/src/build/templates/edge-ssr-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const subresourceIntegrityManifest = sriEnabled
: undefined
const nextFontManifest = maybeJSONParse(self.__NEXT_FONT_MANIFEST)

const interceptionRouteRewrites =
maybeJSONParse(self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST) ?? []

const render = getRender({
pagesType: PAGE_TYPES.APP,
dev,
Expand All @@ -65,6 +68,7 @@ const render = getRender({
buildId: 'VAR_BUILD_ID',
nextFontManifest,
incrementalCacheHandler,
interceptionRouteRewrites,
})

export const ComponentMod = pageMod
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,7 @@ export default async function getBaseWebpackConfig(
new MiddlewarePlugin({
dev,
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
rewrites,
}),
isClient &&
new BuildManifestPlugin({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
WebNextResponse,
} from '../../../../server/base-http/web'
import { SERVER_RUNTIME } from '../../../../lib/constants'
import type { PrerenderManifest } from '../../..'
import type { ManifestRewriteRoute, PrerenderManifest } from '../../..'
import { normalizeAppPath } from '../../../../shared/lib/router/utils/app-paths'
import type { SizeLimit } from '../../../../../types'
import { internal_getCurrentFunctionWaitUntil } from '../../../../server/web/internal-edge-wait-until'
Expand All @@ -31,6 +31,7 @@ export function getRender({
buildManifest,
prerenderManifest,
reactLoadableManifest,
interceptionRouteRewrites,
renderToHTML,
clientReferenceManifest,
subresourceIntegrityManifest,
Expand All @@ -54,6 +55,7 @@ export function getRender({
prerenderManifest: PrerenderManifest
reactLoadableManifest: ReactLoadableManifest
subresourceIntegrityManifest?: Record<string, string>
interceptionRouteRewrites?: ManifestRewriteRoute[]
clientReferenceManifest?: ClientReferenceManifest
serverActionsManifest?: any
serverActions?: {
Expand Down Expand Up @@ -85,6 +87,7 @@ export function getRender({
pathname: isAppPath ? normalizeAppPath(page) : page,
pagesType,
prerenderManifest,
interceptionRouteRewrites,
extendRenderOpts: {
buildId,
runtime: SERVER_RUNTIME.experimentalEdge,
Expand Down
40 changes: 31 additions & 9 deletions packages/next/src/build/webpack/plugins/middleware-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import {
NEXT_FONT_MANIFEST,
SERVER_REFERENCE_MANIFEST,
PRERENDER_MANIFEST,
INTERCEPTION_ROUTE_REWRITE_MANIFEST,
} from '../../../shared/lib/constants'
import type { MiddlewareConfig } from '../../analysis/get-page-static-info'
import type { Telemetry } from '../../../telemetry/storage'
import { traceGlobals } from '../../../trace/shared'
import { EVENT_BUILD_FEATURE_USAGE } from '../../../telemetry/events'
import { normalizeAppPath } from '../../../shared/lib/router/utils/app-paths'
import { INSTRUMENTATION_HOOK_FILENAME } from '../../../lib/constants'
import type { CustomRoutes } from '../../../lib/load-custom-routes'
import { isInterceptionRouteRewrite } from '../../../lib/generate-interception-routes-rewrites'

const KNOWN_SAFE_DYNAMIC_PACKAGES =
require('../../../lib/known-edge-safe-packages.json') as string[]
Expand Down Expand Up @@ -118,10 +121,10 @@ function getEntryFiles(

files.push(
`server/${MIDDLEWARE_BUILD_MANIFEST}.js`,
`server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`
`server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`,
`server/${NEXT_FONT_MANIFEST}.js`,
`server/${INTERCEPTION_ROUTE_REWRITE_MANIFEST}.js`
)

files.push(`server/${NEXT_FONT_MANIFEST}.js`)
}

if (hasInstrumentationHook) {
Expand All @@ -144,9 +147,7 @@ function getEntryFiles(
function getCreateAssets(params: {
compilation: webpack.Compilation
metadataByEntry: Map<string, EntryMetadata>
opts: {
sriEnabled: boolean
}
opts: Omit<Options, 'dev'>
}) {
const { compilation, metadataByEntry, opts } = params
return (assets: any) => {
Expand All @@ -161,6 +162,17 @@ function getCreateAssets(params: {
INSTRUMENTATION_HOOK_FILENAME
)

// we only emit this entry for the edge runtime since it doesn't have access to a routes manifest
// and we don't need to provide the entire route manifest, just the interception routes.
const interceptionRewrites = JSON.stringify(
opts.rewrites.beforeFiles.filter(isInterceptionRouteRewrite)
)
assets[`${INTERCEPTION_ROUTE_REWRITE_MANIFEST}.js`] = new sources.RawSource(
`self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST=${JSON.stringify(
interceptionRewrites
)}`
) as unknown as webpack.sources.RawSource

for (const entrypoint of compilation.entrypoints.values()) {
if (!entrypoint.name) {
continue
Expand Down Expand Up @@ -718,13 +730,22 @@ function getExtractMetadata(params: {
}
}
}

interface Options {
dev: boolean
sriEnabled: boolean
rewrites: CustomRoutes['rewrites']
}

export default class MiddlewarePlugin {
private readonly dev: boolean
private readonly sriEnabled: boolean
private readonly dev: Options['dev']
private readonly sriEnabled: Options['sriEnabled']
private readonly rewrites: Options['rewrites']

constructor({ dev, sriEnabled }: { dev: boolean; sriEnabled: boolean }) {
constructor({ dev, sriEnabled, rewrites }: Options) {
this.dev = dev
this.sriEnabled = sriEnabled
this.rewrites = rewrites
}

public apply(compiler: webpack.Compiler) {
Expand Down Expand Up @@ -769,6 +790,7 @@ export default class MiddlewarePlugin {
metadataByEntry,
opts: {
sriEnabled: this.sriEnabled,
rewrites: this.rewrites,
},
})
)
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/client/route-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ declare global {
__RSC_SERVER_MANIFEST?: any
__NEXT_FONT_MANIFEST?: any
__SUBRESOURCE_INTEGRITY_MANIFEST?: string
__INTERCEPTION_ROUTE_REWRITE_MANIFEST?: string
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
isInterceptionRouteAppPath,
} from '../server/future/helpers/interception-routes'
import type { Rewrite } from './load-custom-routes'
import type { ManifestRewriteRoute } from '../build'

// a function that converts normalised paths (e.g. /foo/[bar]/[baz]) to the format expected by pathToRegexp (e.g. /foo/:bar/:baz)
function toPathToRegexpPath(path: string): string {
Expand Down Expand Up @@ -88,7 +87,7 @@ export function generateInterceptionRoutesRewrites(
return rewrites
}

export function isInterceptionRouteRewrite(route: ManifestRewriteRoute) {
export function isInterceptionRouteRewrite(route: Rewrite) {
// When we generate interception rewrites in the above implementation, we always do so with only a single `has` condition.
return route.has?.[0].key === NEXT_URL
}
2 changes: 1 addition & 1 deletion packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2001,7 +2001,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
res.setHeader('vary', RSC_VARY_HEADER)

if (isPrefetchRSCRequest) {
const couldBeRewritten = this.interceptionRouteRewrites?.some(
const couldBeRewritten = this.interceptionRouteRewrites.some(
(rewrite) => {
return new RegExp(rewrite.regex).test(resolvedUrlPathname)
}
Expand Down
3 changes: 1 addition & 2 deletions packages/next/src/server/lib/router-utils/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ export async function setupFsCheck(opts: {
)
)
const rewrites = {
// TODO: add interception routes generateInterceptionRoutesRewrites()
beforeFiles: customRoutes.rewrites.beforeFiles.map((item) =>
buildCustomRoute('before_files_rewrite', item)
),
Expand Down Expand Up @@ -393,7 +392,7 @@ export async function setupFsCheck(opts: {
dynamicRoutes,
nextDataRoutes,

interceptionRoutes: undefined as
exportPathMapRoutes: undefined as
| undefined
| ReturnType<typeof buildCustomRoute<Rewrite>>[],

Expand Down
7 changes: 5 additions & 2 deletions packages/next/src/server/lib/router-utils/resolve-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,11 @@ export function getResolveRoutes(
}

if (params) {
if (fsChecker.interceptionRoutes && route.name === 'before_files_end') {
for (const interceptionRoute of fsChecker.interceptionRoutes) {
if (
fsChecker.exportPathMapRoutes &&
route.name === 'before_files_end'
) {
for (const interceptionRoute of fsChecker.exportPathMapRoutes) {
const result = await handleRoute(interceptionRoute)

if (result) {
Expand Down
80 changes: 53 additions & 27 deletions packages/next/src/server/lib/router-utils/setup-dev-bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ import { getRouteMatcher } from '../../../shared/lib/router/utils/route-matcher'
import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-sep'
import { createClientRouterFilter } from '../../../lib/create-client-router-filter'
import { absolutePathToPage } from '../../../shared/lib/page-path/absolute-path-to-page'
import { generateInterceptionRoutesRewrites } from '../../../lib/generate-interception-routes-rewrites'
import {
generateInterceptionRoutesRewrites,
isInterceptionRouteRewrite,
} from '../../../lib/generate-interception-routes-rewrites'
import { store as consoleStore } from '../../../build/output/store'

import {
Expand All @@ -87,6 +90,7 @@ import {
REACT_LOADABLE_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
MIDDLEWARE_BUILD_MANIFEST,
INTERCEPTION_ROUTE_REWRITE_MANIFEST,
} from '../../../shared/lib/constants'

import { getMiddlewareRouteMatcher } from '../../../shared/lib/router/utils/middleware-route-matcher'
Expand Down Expand Up @@ -136,6 +140,7 @@ import { writeFileAtomic } from '../../../lib/fs/write-atomic'
import { PAGE_TYPES } from '../../../lib/page-types'
import { trace } from '../../../trace'
import type { VersionInfo } from '../../dev/parse-version-info'
import type { Rewrite } from '../../../lib/load-custom-routes'

const MILLISECONDS_IN_NANOSECOND = 1_000_000
const wsServer = new ws.Server({ noServer: true })
Expand Down Expand Up @@ -832,15 +837,32 @@ async function startWatcher(opts: SetupOpts) {
'server',
`${MIDDLEWARE_BUILD_MANIFEST}.js`
)
const interceptionRewriteManifestPath = path.join(
distDir,
'server',
`${INTERCEPTION_ROUTE_REWRITE_MANIFEST}.js`
)
deleteCache(buildManifestPath)
deleteCache(middlewareBuildManifestPath)
deleteCache(interceptionRewriteManifestPath)
await writeFileAtomic(
buildManifestPath,
JSON.stringify(buildManifest, null, 2)
)
await writeFileAtomic(
middlewareBuildManifestPath,
`self.__BUILD_MANIFEST=${JSON.stringify(buildManifest)}`
`self.__BUILD_MANIFEST=${JSON.stringify(buildManifest)};`
)

const interceptionRewrites = JSON.stringify(
rewrites.beforeFiles.filter(isInterceptionRouteRewrite)
)

await writeFileAtomic(
interceptionRewriteManifestPath,
`self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST=${JSON.stringify(
interceptionRewrites
)};`
)

const content: ClientBuildManifest = {
Expand Down Expand Up @@ -2296,18 +2318,19 @@ async function startWatcher(opts: SetupOpts) {
? getMiddlewareRouteMatcher(serverFields.middleware?.matchers)
: undefined

opts.fsChecker.interceptionRoutes =
generateInterceptionRoutesRewrites(
Object.keys(appPaths),
opts.nextConfig.basePath
)?.map((item) =>
buildCustomRoute(
'before_files_rewrite',
item,
opts.nextConfig.basePath,
opts.nextConfig.experimental.caseSensitiveRoutes
)
) || []
const interceptionRoutes = generateInterceptionRoutesRewrites(
Object.keys(appPaths),
opts.nextConfig.basePath
).map((item) =>
buildCustomRoute(
'before_files_rewrite',
item,
opts.nextConfig.basePath,
opts.nextConfig.experimental.caseSensitiveRoutes
)
)

opts.fsChecker.rewrites.beforeFiles.push(...interceptionRoutes)

const exportPathMap =
(typeof nextConfig.exportPathMap === 'function' &&
Expand All @@ -2323,19 +2346,22 @@ async function startWatcher(opts: SetupOpts) {
))) ||
{}

for (const [key, value] of Object.entries(exportPathMap || {})) {
opts.fsChecker.interceptionRoutes.push(
buildCustomRoute(
'before_files_rewrite',
{
source: key,
destination: `${value.page}${
value.query ? '?' : ''
}${qs.stringify(value.query)}`,
},
opts.nextConfig.basePath,
opts.nextConfig.experimental.caseSensitiveRoutes
)
const exportPathMapEntries = Object.entries(exportPathMap || {})

if (exportPathMapEntries.length > 0) {
opts.fsChecker.exportPathMapRoutes = exportPathMapEntries.map(
([key, value]) =>
buildCustomRoute(
'before_files_rewrite',
{
source: key,
destination: `${value.page}${
value.query ? '?' : ''
}${qs.stringify(value.query)}`,
},
opts.nextConfig.basePath,
opts.nextConfig.experimental.caseSensitiveRoutes
)
)
}

Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ export default class NextNodeServer extends BaseServer {
}

protected getInterceptionRouteRewrites(): ManifestRewriteRoute[] {
if (!this.enabledDirectories.app) return []

const routesManifest = this.getRoutesManifest()
return (
routesManifest?.rewrites.beforeFiles.filter(isInterceptionRouteRewrite) ??
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/web-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ interface WebServerOptions extends Options {
| undefined
incrementalCacheHandler?: any
prerenderManifest: PrerenderManifest | undefined
interceptionRouteRewrites?: ManifestRewriteRoute[]
}
}

Expand Down Expand Up @@ -395,7 +396,6 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
}

protected getInterceptionRouteRewrites(): ManifestRewriteRoute[] {
// TODO: This needs to be implemented.
return []
return this.serverOptions.webServerConfig.interceptionRouteRewrites ?? []
}
}
Loading

0 comments on commit c438ed6

Please sign in to comment.