diff --git a/packages/next-swc/crates/next-core/src/middleware.rs b/packages/next-swc/crates/next-core/src/middleware.rs index ff372feabd15c..2f61ea870e434 100644 --- a/packages/next-swc/crates/next-core/src/middleware.rs +++ b/packages/next-swc/crates/next-core/src/middleware.rs @@ -36,6 +36,7 @@ pub async fn get_middleware_module( project_root, indexmap! { "VAR_USERLAND" => INNER.to_string(), + "VAR_DEFINITION_PAGE" => "/middleware".to_string(), }, indexmap! {}, ) diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs index a3c06f3954071..7a433bc11b97e 100644 --- a/packages/next-swc/crates/next-core/src/next_import_map.rs +++ b/packages/next-swc/crates/next-core/src/next_import_map.rs @@ -365,6 +365,52 @@ pub async fn get_next_edge_import_map( ) -> Result> { let mut import_map = ImportMap::empty(); + // /~https://github.com/vercel/next.js/blob/786ef25e529e1fb2dda398aebd02ccbc8d0fb673/packages/next/src/build/webpack-config.ts#L815-L861 + + // Alias next/dist imports to next/dist/esm assets + insert_wildcard_alias_map( + &mut import_map, + project_path, + indexmap! { + "next/dist/build/" => "next/dist/esm/build/*".to_string(), + "next/dist/client/" => "next/dist/esm/client/*".to_string(), + "next/dist/shared/" => "next/dist/esm/shared/*".to_string(), + "next/dist/pages/" => "next/dist/esm/pages/*".to_string(), + "next/dist/lib/" => "next/dist/esm/lib/*".to_string(), + "next/dist/server/" => "next/dist/esm/server/*".to_string(), + }, + ); + + // Alias the usage of next public APIs + insert_exact_alias_map( + &mut import_map, + project_path, + indexmap! { + "next/app" => "next/dist/esm/pages/_app".to_string(), + "next/document" => "next/dist/esm/pages/_document".to_string(), + "next/dynamic" => "next/dist/esm/shared/lib/dynamic".to_string(), + "next/head" => "next/dist/esm/shared/lib/head".to_string(), + "next/headers" => "next/dist/esm/client/components/headers".to_string(), + "next/image" => "next/dist/esm/shared/lib/image-external".to_string(), + "next/link" => "next/dist/esm/client/link".to_string(), + "next/navigation" => "next/dist/esm/client/components/navigation".to_string(), + "next/router" => "next/dist/esm/client/router".to_string(), + "next/script" => "next/dist/esm/client/script".to_string(), + "next/server" => "next/dist/esm/server/web/exports/index".to_string(), + + "next/dist/client/components/headers" => "next/dist/esm/client/components/headers".to_string(), + "next/dist/client/components/navigation" => "next/dist/esm/client/components/navigation".to_string(), + "next/dist/client/link" => "next/dist/esm/client/link".to_string(), + "next/dist/client/router" => "next/dist/esm/client/router".to_string(), + "next/dist/client/script" => "next/dist/esm/client/script".to_string(), + "next/dist/pages/_app" => "next/dist/esm/pages/_app".to_string(), + "next/dist/pages/_document" => "next/dist/esm/pages/_document".to_string(), + "next/dist/shared/lib/dynamic" => "next/dist/esm/shared/lib/dynamic".to_string(), + "next/dist/shared/lib/head" => "next/dist/esm/shared/lib/head".to_string(), + "next/dist/shared/lib/image-external" => "next/dist/esm/shared/lib/image-external".to_string(), + }, + ); + insert_next_shared_aliases( &mut import_map, project_path, @@ -828,17 +874,41 @@ async fn insert_next_server_special_aliases( } // see /~https://github.com/vercel/next.js/blob/8013ef7372fc545d49dbd060461224ceb563b454/packages/next/src/build/webpack-config.ts#L1449-L1531 - insert_exact_alias_map( - import_map, - project_path, - indexmap! { - "server-only" => "next/dist/compiled/server-only/empty".to_string(), - "client-only" => "next/dist/compiled/client-only/index".to_string(), - "next/dist/compiled/server-only" => "next/dist/compiled/server-only/empty".to_string(), - "next/dist/compiled/client-only" => "next/dist/compiled/client-only/index".to_string(), - }, - ); + match ty { + ServerContextType::Pages { .. } + | ServerContextType::PagesData { .. } + | ServerContextType::AppSSR { .. } => { + insert_exact_alias_map( + import_map, + project_path, + indexmap! { + "server-only" => "next/dist/compiled/server-only/index".to_string(), + "client-only" => "next/dist/compiled/client-only/index".to_string(), + "next/dist/compiled/server-only" => "next/dist/compiled/server-only/index".to_string(), + "next/dist/compiled/client-only" => "next/dist/compiled/client-only/index".to_string(), + }, + ); + } + // TODO: should include `ServerContextType::PagesApi` routes, but that type doesn't exist. + ServerContextType::AppRSC { .. } + | ServerContextType::AppRoute { .. } + | ServerContextType::Middleware => { + insert_exact_alias_map( + import_map, + project_path, + indexmap! { + "server-only" => "next/dist/compiled/server-only/empty".to_string(), + "client-only" => "next/dist/compiled/client-only/error".to_string(), + "next/dist/compiled/server-only" => "next/dist/compiled/server-only/empty".to_string(), + "next/dist/compiled/client-only" => "next/dist/compiled/client-only/error".to_string(), + }, + ); + } + } + // Potential the bundle introduced into middleware and api can be poisoned by + // client-only but not being used, so we disabled the `client-only` erroring + // on these layers. `server-only` is still available. if ty == ServerContextType::Middleware { insert_exact_alias_map( import_map, @@ -1054,6 +1124,17 @@ fn insert_exact_alias_map( } } +fn insert_wildcard_alias_map( + import_map: &mut ImportMap, + project_path: Vc, + map: IndexMap<&'static str, String>, +) { + for (pattern, request) in map { + import_map + .insert_wildcard_alias(pattern, request_to_import_mapping(project_path, &request)); + } +} + /// Inserts an alias to an alternative of import mappings into an import map. fn insert_alias_to_alternatives<'a>( import_map: &mut ImportMap, diff --git a/packages/next/src/build/load-entrypoint.ts b/packages/next/src/build/load-entrypoint.ts index ec97ac1527264..ecbbbf255cb8c 100644 --- a/packages/next/src/build/load-entrypoint.ts +++ b/packages/next/src/build/load-entrypoint.ts @@ -26,11 +26,12 @@ const TEMPLATES_ESM_FOLDER = path.normalize( export async function loadEntrypoint( entrypoint: - | 'pages' - | 'pages-api' | 'app-page' | 'app-route' - | 'edge-app-route', + | 'edge-app-route' + | 'middleware' + | 'pages' + | 'pages-api', replacements: Record<`VAR_${string}`, string>, injections?: Record ): Promise { diff --git a/packages/next/src/build/polyfills/process.ts b/packages/next/src/build/polyfills/process.ts index 26a02f5b34f7f..161c875ec0139 100644 --- a/packages/next/src/build/polyfills/process.ts +++ b/packages/next/src/build/polyfills/process.ts @@ -1,4 +1,4 @@ module.exports = global.process?.env && typeof global.process?.env === 'object' ? global.process - : require('../../compiled/process') + : require('next/dist/compiled/process') diff --git a/packages/next/src/build/templates/middleware.ts b/packages/next/src/build/templates/middleware.ts index 3ede209e4d28c..b143df9006035 100644 --- a/packages/next/src/build/templates/middleware.ts +++ b/packages/next/src/build/templates/middleware.ts @@ -10,18 +10,20 @@ import * as _mod from 'VAR_USERLAND' const mod = { ..._mod } const handler = mod.middleware || mod.default +const page = 'VAR_DEFINITION_PAGE' + if (typeof handler !== 'function') { throw new Error( - `The Middleware must export a \`middleware\` or a \`default\` function` + `The Middleware "${page}" must export a \`middleware\` or a \`default\` function` ) } -export default function ( +export default function nHandler( opts: Omit ) { return adapter({ ...opts, - page: '', + page, handler, }) } diff --git a/packages/next/src/build/webpack/loaders/next-middleware-loader.ts b/packages/next/src/build/webpack/loaders/next-middleware-loader.ts index 36a1d6ece07e4..6847797a3d8c6 100644 --- a/packages/next/src/build/webpack/loaders/next-middleware-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-middleware-loader.ts @@ -3,8 +3,8 @@ import type { MiddlewareMatcher, } from '../../analysis/get-page-static-info' import { getModuleBuildInfo } from './get-module-build-info' -import { stringifyRequest } from '../stringify-request' import { MIDDLEWARE_LOCATION_REGEXP } from '../../../lib/constants' +import { loadEntrypoint } from '../../load-entrypoint' export type MiddlewareLoaderOptions = { absolutePagePath: string @@ -27,7 +27,7 @@ export function decodeMatchers(encodedMatchers: string) { ) as MiddlewareMatcher[] } -export default function middlewareLoader(this: any) { +export default async function middlewareLoader(this: any) { const { absolutePagePath, page, @@ -37,7 +37,11 @@ export default function middlewareLoader(this: any) { middlewareConfig: middlewareConfigBase64, }: MiddlewareLoaderOptions = this.getOptions() const matchers = encodedMatchers ? decodeMatchers(encodedMatchers) : undefined - const stringifiedPagePath = stringifyRequest(this, absolutePagePath) + const pagePath = this.utils.contextify( + this.context || this.rootContext, + absolutePagePath + ) + const middlewareConfig: MiddlewareConfig = JSON.parse( Buffer.from(middlewareConfigBase64, 'base64').toString() ) @@ -55,24 +59,8 @@ export default function middlewareLoader(this: any) { middlewareConfig, } - return ` - import 'next/dist/esm/server/web/globals' - import { adapter } from 'next/dist/esm/server/web/adapter' - import * as _mod from ${stringifiedPagePath} - - const mod = { ..._mod } - const handler = mod.middleware || mod.default - - if (typeof handler !== 'function') { - throw new Error('The Middleware "pages${page}" must export a \`middleware\` or a \`default\` function'); - } - - export default function nHandler(opts) { - return adapter({ - ...opts, - page: ${JSON.stringify(page)}, - handler, - }) - } - ` + return await loadEntrypoint('middleware', { + VAR_USERLAND: pagePath, + VAR_DEFINITION_PAGE: page, + }) }