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 Feb 8, 2024
1 parent 8bd70d5 commit c28deef
Show file tree
Hide file tree
Showing 16 changed files with 155 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 @@ -892,6 +892,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 @@ -720,7 +720,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 @@ -984,6 +983,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 @@ -1789,6 +1789,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
}
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
9 changes: 6 additions & 3 deletions packages/next/src/server/lib/router-utils/resolve-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,12 @@ export function getResolveRoutes(
}

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

if (result) {
return result
Expand Down
79 changes: 52 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 @@ -868,15 +872,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 @@ -2340,18 +2361,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 @@ -2367,19 +2389,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 @@ -373,6 +373,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 ?? []
}
}
3 changes: 3 additions & 0 deletions packages/next/src/shared/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ export const MIDDLEWARE_BUILD_MANIFEST = 'middleware-build-manifest'
// server/middleware-react-loadable-manifest.js
export const MIDDLEWARE_REACT_LOADABLE_MANIFEST =
'middleware-react-loadable-manifest'
// server/interception-route-rewrite-manifest.js
export const INTERCEPTION_ROUTE_REWRITE_MANIFEST =
'interception-route-rewrite-manifest'

// static/runtime/main.js
export const CLIENT_STATIC_FILES_RUNTIME_MAIN = `main`
Expand Down
Loading

0 comments on commit c28deef

Please sign in to comment.