Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pick esm main fields and condition names first for RSC server layer #50548

Merged
merged 12 commits into from
Jun 8, 2023
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@
"semver": "7.3.7",
"shell-quote": "1.7.3",
"strip-ansi": "6.0.0",
"styled-components": "6.0.0-beta.5",
"styled-components": "6.0.0-rc.3",
"styled-jsx": "5.1.1",
"styled-jsx-plugin-postcss": "3.0.2",
"swr": "^2.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { adapter, enhanceGlobals } from 'next/dist/server/web/adapter'
import 'next/dist/server/web/globals'
import { adapter } from 'next/dist/server/web/adapter'
import { NAME, PAGE } from 'BOOTSTRAP_CONFIG'

enhanceGlobals()

var mod = require('ENTRY')
var handler = mod.middleware || mod.default

Expand Down
30 changes: 16 additions & 14 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import crypto from 'crypto'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import path from 'path'
import semver from 'next/dist/compiled/semver'

import { escapeStringRegexp } from '../shared/lib/escape-regexp'
import {
DOT_NEXT_ALIAS,
Expand Down Expand Up @@ -98,15 +99,23 @@ const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/
const asyncStoragesRegex =
/next[\\/]dist[\\/]client[\\/]components[\\/](static-generation-async-storage|action-async-storage|request-async-storage)/

// exports.<conditionName>
const edgeConditionNames = [
'edge-light',
'worker',
// inherits the default conditions
'...',
]

// packageJson.<mainField>
const mainFieldsPerCompiler: Record<CompilerNameValues, string[]> = {
[COMPILER_NAMES.server]: ['main', 'module'],
[COMPILER_NAMES.client]: ['browser', 'module', 'main'],
[COMPILER_NAMES.edgeServer]: [
'edge-light',
'worker',
'browser',
'module',
'main',
// inherits the default conditions
'...',
],
}

Expand Down Expand Up @@ -915,12 +924,9 @@ export default async function getBaseWebpackConfig(

const reactServerCondition = [
'react-server',
...mainFieldsPerCompiler[
isEdgeServer ? COMPILER_NAMES.edgeServer : COMPILER_NAMES.server
],
'node',
'import',
'require',
...(isEdgeServer ? edgeConditionNames : []),
// inherits the default conditions
'...',
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
]

const clientEntries = isClient
Expand Down Expand Up @@ -1166,11 +1172,7 @@ export default async function getBaseWebpackConfig(
: undefined),
mainFields: mainFieldsPerCompiler[compilerType],
...(isEdgeServer && {
conditionNames: [
...mainFieldsPerCompiler[COMPILER_NAMES.edgeServer],
'import',
'node',
],
conditionNames: edgeConditionNames,
}),
plugins: [],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ const nextEdgeFunctionLoader: webpack.LoaderDefinitionFunction<EdgeFunctionLoade
buildInfo.rootDir = rootDir

return `
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import 'next/dist/esm/server/web/globals'
import { adapter } from 'next/dist/esm/server/web/adapter'
import { IncrementalCache } from 'next/dist/esm/server/lib/incremental-cache'

enhanceGlobals()

const mod = require(${stringifiedPagePath})
const handler = mod.middleware || mod.default
import handler from ${stringifiedPagePath}
huozhi marked this conversation as resolved.
Show resolved Hide resolved

if (typeof handler !== 'function') {
throw new Error('The Edge Function "pages${page}" must export a \`default\` function');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
)}${isAppDir ? `?${WEBPACK_RESOURCE_QUERIES.edgeSSREntry}` : ''}`

const transformed = `
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import 'next/dist/esm/server/web/globals'
import { adapter } from 'next/dist/esm/server/web/adapter'
import { getRender } from 'next/dist/esm/build/webpack/loaders/next-edge-ssr-loader/render'
import { IncrementalCache } from 'next/dist/esm/server/lib/incremental-cache'

enhanceGlobals()

const pagesType = ${JSON.stringify(pagesType)}
${
isAppDir
Expand Down Expand Up @@ -132,10 +131,10 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
`
}

const incrementalCacheHandler = ${
${
incrementalCacheHandlerPath
? `require("${incrementalCacheHandlerPath}")`
: 'null'
? `import incrementalCacheHandler from "${incrementalCacheHandlerPath}"`
: 'const incrementalCacheHandler = null'
}

const buildManifest = self.__BUILD_MANIFEST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,21 @@ export default function middlewareLoader(this: any) {
buildInfo.rootDir = rootDir

return `
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import 'next/dist/esm/server/web/globals'
import { adapter } from 'next/dist/esm/server/web/adapter'
import * as mod from ${stringifiedPagePath}

enhanceGlobals()

var mod = require(${stringifiedPagePath})
var handler = mod.middleware || mod.default;
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 (opts) {
return adapter({
...opts,
page: ${JSON.stringify(page)},
handler,
...opts,
page: ${JSON.stringify(page)},
handler,
})
}
`
Expand Down
32 changes: 16 additions & 16 deletions packages/next/src/compiled/babel-packages/packages-bundle.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/next/src/server/dev/next-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1330,10 +1330,12 @@ export default class DevServer extends Server {
if (isError(err) && err.stack) {
try {
const frames = parseStack(err.stack!)
// Filter out internal edge related runtime stack
const frame = frames.find(
({ file }) =>
!file?.startsWith('eval') &&
!file?.includes('web/adapter') &&
!file?.includes('web/globals') &&
!file?.includes('sandbox/context') &&
!file?.includes('<anonymous>')
)
Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/server/require-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

// This module will only be loaded once per process.

const { dirname } = require('path')
const mod = require('module')
const resolveFilename = mod._resolveFilename
const hookPropertyMap = new Map()
Expand All @@ -20,9 +21,9 @@ export function addHookAliases(aliases: [string, string][] = []) {
addHookAliases([
// Use `require.resolve` explicitly to make them statically analyzable
// styled-jsx needs to be resolved as the external dependency.
['styled-jsx', require.resolve('styled-jsx')],
['styled-jsx/style', require.resolve('styled-jsx/style')],
['styled-jsx', dirname(require.resolve('styled-jsx/package.json'))],
['styled-jsx/style', require.resolve('styled-jsx/style')],
['zod', dirname(require.resolve('zod/package.json'))],
])

// Override built-in React packages if necessary
Expand Down
73 changes: 1 addition & 72 deletions packages/next/src/server/web/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
RSC,
} from '../../client/components/app-router-headers'
import { NEXT_QUERY_PARAM_PREFIX } from '../../lib/constants'

declare const _ENTRIES: any
import { ensureInstrumentationRegistered } from './globals'

class NextRequestHint extends NextRequest {
sourcePage: string
Expand Down Expand Up @@ -59,29 +58,6 @@ export type AdapterOptions = {
IncrementalCache?: typeof import('../lib/incremental-cache').IncrementalCache
}

async function registerInstrumentation() {
if (
'_ENTRIES' in globalThis &&
_ENTRIES.middleware_instrumentation &&
_ENTRIES.middleware_instrumentation.register
) {
try {
await _ENTRIES.middleware_instrumentation.register()
} catch (err: any) {
err.message = `An error occurred while loading instrumentation hook: ${err.message}`
throw err
}
}
}

let registerInstrumentationPromise: Promise<void> | null = null
function ensureInstrumentationRegistered() {
if (!registerInstrumentationPromise) {
registerInstrumentationPromise = registerInstrumentation()
}
return registerInstrumentationPromise
}

export async function adapter(
params: AdapterOptions
): Promise<FetchEventResult> {
Expand Down Expand Up @@ -318,50 +294,3 @@ export async function adapter(
waitUntil: Promise.all(event[waitUntilSymbol]),
}
}

function getUnsupportedModuleErrorMessage(module: string) {
// warning: if you change these messages, you must adjust how react-dev-overlay's middleware detects modules not found
return `The edge runtime does not support Node.js '${module}' module.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`
}

function __import_unsupported(moduleName: string) {
const proxy: any = new Proxy(function () {}, {
get(_obj, prop) {
if (prop === 'then') {
return {}
}
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
construct() {
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
apply(_target, _this, args) {
if (typeof args[0] === 'function') {
return args[0](proxy)
}
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
})
return new Proxy({}, { get: () => proxy })
}

export function enhanceGlobals() {
// The condition is true when the "process" module is provided
if (process !== global.process) {
// prefer local process but global.process has correct "env"
process.env = global.process.env
global.process = process
}

// to allow building code that import but does not use node.js modules,
// webpack will expect this function to exist in global scope
Object.defineProperty(globalThis, '__import_unsupported', {
value: __import_unsupported,
enumerable: false,
configurable: false,
})

// Eagerly fire instrumentation hook to make the startup faster.
void ensureInstrumentationRegistered()
}
4 changes: 2 additions & 2 deletions packages/next/src/server/web/edge-route-module-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { RouteDefinition } from '../future/route-definitions/route-definiti
import type { RouteModule } from '../future/route-modules/route-module'
import type { NextRequest } from './spec-extension/request'

import { adapter, enhanceGlobals, type AdapterOptions } from './adapter'
import './globals'
import { adapter, type AdapterOptions } from './adapter'
import { IncrementalCache } from '../lib/incremental-cache'
enhanceGlobals()

import { removeTrailingSlash } from '../../shared/lib/router/utils/remove-trailing-slash'
import { RouteMatcher } from '../future/route-matchers/route-matcher'
Expand Down
73 changes: 73 additions & 0 deletions packages/next/src/server/web/globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
declare const _ENTRIES: any

async function registerInstrumentation() {
if (
'_ENTRIES' in globalThis &&
_ENTRIES.middleware_instrumentation &&
_ENTRIES.middleware_instrumentation.register
) {
try {
await _ENTRIES.middleware_instrumentation.register()
} catch (err: any) {
err.message = `An error occurred while loading instrumentation hook: ${err.message}`
throw err
}
}
}

let registerInstrumentationPromise: Promise<void> | null = null
export function ensureInstrumentationRegistered() {
if (!registerInstrumentationPromise) {
registerInstrumentationPromise = registerInstrumentation()
}
return registerInstrumentationPromise
}

function getUnsupportedModuleErrorMessage(module: string) {
// warning: if you change these messages, you must adjust how react-dev-overlay's middleware detects modules not found
return `The edge runtime does not support Node.js '${module}' module.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`
}

function __import_unsupported(moduleName: string) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
const proxy: any = new Proxy(function () {}, {
get(_obj, prop) {
if (prop === 'then') {
return {}
}
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
construct() {
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
apply(_target, _this, args) {
if (typeof args[0] === 'function') {
return args[0](proxy)
}
throw new Error(getUnsupportedModuleErrorMessage(moduleName))
},
})
return new Proxy({}, { get: () => proxy })
}

function enhanceGlobals() {
// The condition is true when the "process" module is provided
if (process !== global.process) {
// prefer local process but global.process has correct "env"
process.env = global.process.env
global.process = process
}

// to allow building code that import but does not use node.js modules,
// webpack will expect this function to exist in global scope
Object.defineProperty(globalThis, '__import_unsupported', {
value: __import_unsupported,
enumerable: false,
configurable: false,
})

// Eagerly fire instrumentation hook to make the startup faster.
void ensureInstrumentationRegistered()
}

enhanceGlobals()
6 changes: 3 additions & 3 deletions packages/next/src/server/web/sandbox/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ function withTaggedErrors(fn: RunnerFn): RunnerFn {
})
}

export const getRuntimeContext = async (params: {
export async function getRuntimeContext(params: {
name: string
onWarning?: any
useCache: boolean
edgeFunctionEntry: any
distDir: string
paths: string[]
incrementalCache?: any
}): Promise<EdgeRuntime<any>> => {
}): Promise<EdgeRuntime<any>> {
const { runtime, evaluateInContext } = await getModuleContext({
moduleName: params.name,
onWarning: params.onWarning ?? (() => {}),
Expand All @@ -71,7 +71,7 @@ export const getRuntimeContext = async (params: {
return runtime
}

export const run = withTaggedErrors(async (params) => {
export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
const runtime = await getRuntimeContext(params)
const subreq = params.request.headers[`x-middleware-subrequest`]
const subrequests = typeof subreq === 'string' ? subreq.split(':') : []
Expand Down