Skip to content

Commit

Permalink
fix(turbopack): middleware path and aliases (#56804)
Browse files Browse the repository at this point in the history
### Description

- Adds the page path to the middleware template (and also uses the template from the next.js loader)
- ESM aliases for the edge context
- Fix for the process polyfill to make it possible to import from `dist/esm`
- Fix for the `server-only`/`client-only` aliases


Closes WEB-1779
  • Loading branch information
ForsakenHarmony authored Oct 13, 2023
1 parent fe0bfbf commit f9d12d1
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 40 deletions.
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-core/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {},
)
Expand Down
101 changes: 91 additions & 10 deletions packages/next-swc/crates/next-core/src/next_import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,52 @@ pub async fn get_next_edge_import_map(
) -> Result<Vc<ImportMap>> {
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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1054,6 +1124,17 @@ fn insert_exact_alias_map(
}
}

fn insert_wildcard_alias_map(
import_map: &mut ImportMap,
project_path: Vc<FileSystemPath>,
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,
Expand Down
7 changes: 4 additions & 3 deletions packages/next/src/build/load-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>
): Promise<string> {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/build/polyfills/process.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports =
global.process?.env && typeof global.process?.env === 'object'
? global.process
: require('../../compiled/process')
: require('next/dist/compiled/process')
8 changes: 5 additions & 3 deletions packages/next/src/build/templates/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AdapterOptions, 'IncrementalCache' | 'page' | 'handler'>
) {
return adapter({
...opts,
page: '',
page,
handler,
})
}
34 changes: 11 additions & 23 deletions packages/next/src/build/webpack/loaders/next-middleware-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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()
)
Expand All @@ -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,
})
}

0 comments on commit f9d12d1

Please sign in to comment.