Skip to content

Commit

Permalink
Merge branch 'canary' into wyattjoh/null-navigation-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Feb 15, 2023
2 parents da910cf + c8fe390 commit 158b7dc
Show file tree
Hide file tree
Showing 141 changed files with 5,223 additions and 706 deletions.
4 changes: 4 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,10 @@
"title": "context-in-server-component",
"path": "/errors/context-in-server-component.md"
},
{
"title": "next-response-next-in-app-route-handler",
"path": "/errors/next-response-next-in-app-route-handler.md"
},
{
"title": "react-client-hook-in-server-component",
"path": "/errors/react-client-hook-in-server-component.md"
Expand Down
14 changes: 14 additions & 0 deletions errors/next-response-next-in-app-route-handler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# `NextResponse.next()` used in a App Route Handler

#### Why This Error Occurred

App Route Handler's do not currently support using the `NextResponse.next()` method to forward to the next middleware because the handler is considered the endpoint to the middleware chain. Handlers must always return a `Response` object instead.

#### Possible Ways to Fix It

Remove the `NextResponse.next()` and replace it with a correct response handler.

### Useful Links

- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
- [`NextResponse`](https://nextjs.org/docs/api-reference/next/server#nextresponse)
6 changes: 3 additions & 3 deletions examples/app-dir-mdx/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { MDXComponents } from 'mdx/types'

// This file is required to use MDX in `app` directory.
export function useMDXComponents(components: {
[component: string]: React.ComponentType
}) {
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// Allows customizing built-in components, e.g. to add styling.
// h1: ({ children }) => <h1 style={{ fontSize: "100px" }}>{children}</h1>,
Expand Down
10 changes: 4 additions & 6 deletions packages/next/src/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export function getAppEntry(opts: {
name: string
pagePath: string
appDir: string
appPaths: string[] | null
appPaths: ReadonlyArray<string> | null
pageExtensions: string[]
assetPrefix: string
isDev?: boolean
Expand Down Expand Up @@ -310,7 +310,7 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
let appPathsPerRoute: Record<string, string[]> = {}
if (appDir && appPaths) {
for (const pathname in appPaths) {
const normalizedPath = normalizeAppPath(pathname) || '/'
const normalizedPath = normalizeAppPath(pathname)
if (!appPathsPerRoute[normalizedPath]) {
appPathsPerRoute[normalizedPath] = []
}
Expand Down Expand Up @@ -403,8 +403,7 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
},
onServer: () => {
if (pagesType === 'app' && appDir) {
const matchedAppPaths =
appPathsPerRoute[normalizeAppPath(page) || '/']
const matchedAppPaths = appPathsPerRoute[normalizeAppPath(page)]
server[serverBundlePath] = getAppEntry({
name: serverBundlePath,
pagePath: mappings[page],
Expand All @@ -420,8 +419,7 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
onEdgeServer: () => {
let appDirLoader: string = ''
if (pagesType === 'app') {
const matchedAppPaths =
appPathsPerRoute[normalizeAppPath(page) || '/']
const matchedAppPaths = appPathsPerRoute[normalizeAppPath(page)]
appDirLoader = getAppEntry({
name: serverBundlePath,
pagePath: mappings[page],
Expand Down
24 changes: 17 additions & 7 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
PAGES_MANIFEST,
PHASE_PRODUCTION_BUILD,
PRERENDER_MANIFEST,
FLIGHT_MANIFEST,
CLIENT_REFERENCE_MANIFEST,
REACT_LOADABLE_MANIFEST,
ROUTES_MANIFEST,
SERVER_DIRECTORY,
Expand Down Expand Up @@ -501,7 +501,9 @@ export default async function build(
.traceAsyncFn(() =>
recursiveReadDir(
appDir,
new RegExp(`^page\\.(?:${config.pageExtensions.join('|')})$`)
new RegExp(
`^(page|route)\\.(?:${config.pageExtensions.join('|')})$`
)
)
)
}
Expand Down Expand Up @@ -578,7 +580,7 @@ export default async function build(
if (mappedAppPages) {
denormalizedAppPages = Object.keys(mappedAppPages)
for (const appKey of denormalizedAppPages) {
const normalizedAppPageKey = normalizeAppPath(appKey) || '/'
const normalizedAppPageKey = normalizeAppPath(appKey)
const pagePath = mappedPages[normalizedAppPageKey]
if (pagePath) {
const appPath = mappedAppPages[appKey]
Expand Down Expand Up @@ -904,8 +906,14 @@ export default async function build(
: []),
path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST),
APP_BUILD_MANIFEST,
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.json'),
path.join(
SERVER_DIRECTORY,
CLIENT_REFERENCE_MANIFEST + '.js'
),
path.join(
SERVER_DIRECTORY,
CLIENT_REFERENCE_MANIFEST + '.json'
),
path.join(
SERVER_DIRECTORY,
FLIGHT_SERVER_CSS_MANIFEST + '.js'
Expand Down Expand Up @@ -1091,7 +1099,7 @@ export default async function build(
)

Object.keys(appPathsManifest).forEach((entry) => {
appPathRoutes[entry] = normalizeAppPath(entry) || '/'
appPathRoutes[entry] = normalizeAppPath(entry)
})
await promises.writeFile(
path.join(distDir, APP_PATH_ROUTES_MANIFEST),
Expand Down Expand Up @@ -1382,7 +1390,9 @@ export default async function build(
if (
(!isDynamicRoute(page) ||
!workerResult.prerenderRoutes?.length) &&
workerResult.appConfig?.revalidate !== 0
workerResult.appConfig?.revalidate !== 0 &&
// TODO-APP: (wyattjoh) this may be where we can enable prerendering for app handlers
originalAppPath.endsWith('/page')
) {
appStaticPaths.set(originalAppPath, [page])
appStaticPathsEncoded.set(originalAppPath, [page])
Expand Down
41 changes: 39 additions & 2 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { APP_DIR_ALIAS } from '../../../lib/constants'
import { buildMetadata, discoverStaticMetadataFiles } from './metadata/discover'

const isNotResolvedError = (err: any) => err.message.includes("Can't resolve")
import { isAppRouteRoute } from '../../../lib/is-app-route-route'

const FILE_TYPES = {
layout: 'layout',
Expand All @@ -25,6 +26,10 @@ const FILE_TYPES = {
const GLOBAL_ERROR_FILE_TYPE = 'global-error'
const PAGE_SEGMENT = 'page$'

type PathResolver = (
pathname: string,
resolveDir?: boolean
) => Promise<string | undefined>
export type ComponentsType = {
readonly [componentKey in ValueOf<typeof FILE_TYPES>]?: ModuleReference
} & {
Expand All @@ -33,6 +38,35 @@ export type ComponentsType = {
readonly metadata?: CollectedMetadata
}

async function createAppRouteCode({
pagePath,
resolver,
}: {
pagePath: string
resolver: PathResolver
}): Promise<string> {
// Split based on any specific path separators (both `/` and `\`)...
const splittedPath = pagePath.split(/[\\/]/)
// Then join all but the last part with the same separator, `/`...
const segmentPath = splittedPath.slice(0, -1).join('/')
// Then add the `/route` suffix...
const matchedPagePath = `${segmentPath}/route`
// This, when used with the resolver will give us the pathname to the built
// route handler file.
const resolvedPagePath = await resolver(matchedPagePath)

// TODO: verify if other methods need to be injected
// TODO: validate that the handler exports at least one of the supported methods

return `
import 'next/dist/server/node-polyfill-headers'
export * as handlers from ${JSON.stringify(resolvedPagePath)}
export { requestAsyncStorage } from 'next/dist/client/components/request-async-storage'
`
}

async function createTreeCodeFromPath(
pagePath: string,
{
Expand Down Expand Up @@ -279,8 +313,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {

return Object.entries(matched)
}

const resolver = async (pathname: string, resolveDir?: boolean) => {
const resolver: PathResolver = async (pathname, resolveDir) => {
if (resolveDir) {
return createAbsolutePath(appDir, pathname)
}
Expand All @@ -302,6 +335,10 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
}
}

if (isAppRouteRoute(name)) {
return createAppRouteCode({ pagePath, resolver })
}

const {
treeCode,
pages: pageListCode,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { IncomingMessage, ServerResponse } from 'http'
import type { Rewrite } from '../../../../lib/load-custom-routes'
import type { BuildManifest } from '../../../../server/get-page-files'
import type { RouteMatch } from '../../../../shared/lib/router/utils/route-matcher'
import type { RouteMatchFn } from '../../../../shared/lib/router/utils/route-matcher'
import type { NextConfig } from '../../../../server/config'
import type {
GetServerSideProps,
Expand Down Expand Up @@ -144,7 +144,7 @@ export function getUtils({
trailingSlash?: boolean
}) {
let defaultRouteRegex: ReturnType<typeof getNamedRouteRegex> | undefined
let dynamicRouteMatcher: RouteMatch | undefined
let dynamicRouteMatcher: RouteMatchFn | undefined
let defaultRouteMatches: ParsedUrlQuery | undefined

if (pageIsDynamic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
APP_CLIENT_INTERNALS,
COMPILER_NAMES,
EDGE_RUNTIME_WEBPACK,
ACTIONS_MANIFEST,
SERVER_REFERENCE_MANIFEST,
FLIGHT_SERVER_CSS_MANIFEST,
} from '../../../shared/lib/constants'
import { ASYNC_CLIENT_MODULES } from './flight-manifest-plugin'
Expand Down Expand Up @@ -756,9 +756,8 @@ export class FlightClientEntryPlugin {
}
}

const file = ACTIONS_MANIFEST
const json = JSON.stringify(serverActions, null, this.dev ? 2 : undefined)
assets[file + '.json'] = new sources.RawSource(
assets[SERVER_REFERENCE_MANIFEST + '.json'] = new sources.RawSource(
json
) as unknown as webpack.sources.RawSource
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import { FLIGHT_MANIFEST } from '../../../shared/lib/constants'
import { CLIENT_REFERENCE_MANIFEST } from '../../../shared/lib/constants'
import { relative, sep } from 'path'
import { isClientComponentModule, regexCSS } from '../loaders/utils'

Expand Down Expand Up @@ -369,7 +369,7 @@ export class FlightManifestPlugin {
manifest.__entry_css_files__ = entryCSSFiles
})

const file = 'server/' + FLIGHT_MANIFEST
const file = 'server/' + CLIENT_REFERENCE_MANIFEST
const json = JSON.stringify(manifest, null, this.dev ? 2 : undefined)

ASYNC_CLIENT_MODULES.clear()
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/build/webpack/plugins/middleware-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
EDGE_RUNTIME_WEBPACK,
EDGE_UNSUPPORTED_NODE_APIS,
MIDDLEWARE_BUILD_MANIFEST,
FLIGHT_MANIFEST,
CLIENT_REFERENCE_MANIFEST,
MIDDLEWARE_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
NEXT_CLIENT_SSR_ENTRY_SUFFIX,
Expand Down Expand Up @@ -95,7 +95,7 @@ function getEntryFiles(
const files: string[] = []
if (meta.edgeSSR) {
if (meta.edgeSSR.isServerComponent) {
files.push(`server/${FLIGHT_MANIFEST}.js`)
files.push(`server/${CLIENT_REFERENCE_MANIFEST}.js`)
files.push(`server/${FLIGHT_SERVER_CSS_MANIFEST}.js`)
if (opts.sriEnabled) {
files.push(`server/${SUBRESOURCE_INTEGRITY_MANIFEST}.js`)
Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/client/components/layout-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ErrorBoundary } from './error-boundary'
import { matchSegment } from './match-segments'
import { useRouter } from './navigation'
import { handleSmoothScroll } from '../../shared/lib/router/utils/handle-smooth-scroll'
import { getURLFromRedirectError, isRedirectError } from './redirect'

/**
* Add refetch marker to router state at the point of the current layout segment.
Expand Down Expand Up @@ -380,8 +381,8 @@ class RedirectErrorBoundary extends React.Component<
}

static getDerivedStateFromError(error: any) {
if (error?.digest?.startsWith('NEXT_REDIRECT')) {
const url = error.digest.split(';')[1]
if (isRedirectError(error)) {
const url = getURLFromRedirectError(error)
return { redirect: url }
}
// Re-throw if error is not for redirect
Expand Down
21 changes: 19 additions & 2 deletions packages/next/src/client/components/not-found.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
export const NOT_FOUND_ERROR_CODE = 'NEXT_NOT_FOUND'
const NOT_FOUND_ERROR_CODE = 'NEXT_NOT_FOUND'

type NotFoundError = Error & { digest: typeof NOT_FOUND_ERROR_CODE }

/**
* When used in a React server component, this will set the status code to 404.
* When used in a custom app route it will just send a 404 status.
*/
export function notFound(): never {
// eslint-disable-next-line no-throw-literal
const error = new Error(NOT_FOUND_ERROR_CODE)
;(error as any).digest = NOT_FOUND_ERROR_CODE
;(error as NotFoundError).digest = NOT_FOUND_ERROR_CODE
throw error
}

/**
* Checks an error to determine if it's an error generated by the `notFound()`
* helper.
*
* @param error the error that may reference a not found error
* @returns true if the error is a not found error
*/
export function isNotFoundError(error: any): error is NotFoundError {
return error?.digest === NOT_FOUND_ERROR_CODE
}
Loading

0 comments on commit 158b7dc

Please sign in to comment.