Skip to content

Commit

Permalink
refactor: extracted zod configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Sep 25, 2024
1 parent 74e4178 commit 3e7ff6b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 66 deletions.
75 changes: 9 additions & 66 deletions packages/next/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,87 +22,28 @@ import { setHttpClientAndAgentOptions } from './setup-http-agent-env'
import { pathHasPrefix } from '../shared/lib/router/utils/path-has-prefix'
import { matchRemotePattern } from '../shared/lib/match-remote-pattern'

import { ZodParsedType, util as ZodUtil } from 'next/dist/compiled/zod'
import type { ZodError, ZodIssue } from 'next/dist/compiled/zod'
import type { ZodError } from 'next/dist/compiled/zod'
import { hasNextSupport } from '../telemetry/ci-info'
import { transpileConfig } from '../build/next-config-ts/transpile-config'
import { dset } from '../shared/lib/dset'
import { normalizeZodErrors } from '../shared/lib/zod'

export { normalizeConfig } from './config-shared'
export type { DomainLocale, NextConfig } from './config-shared'

function processZodErrorMessage(issue: ZodIssue) {
let message = issue.message

let path = ''

if (issue.path.length > 0) {
if (issue.path.length === 1) {
const identifier = issue.path[0]
if (typeof identifier === 'number') {
// The first identifier inside path is a number
path = `index ${identifier}`
} else {
path = `"${identifier}"`
}
} else {
// joined path to be shown in the error message
path = `"${issue.path.reduce<string>((acc, cur) => {
if (typeof cur === 'number') {
// array index
return `${acc}[${cur}]`
}
if (cur.includes('"')) {
// escape quotes
return `${acc}["${cur.replaceAll('"', '\\"')}"]`
}
// dot notation
const separator = acc.length === 0 ? '' : '.'
return acc + separator + cur
}, '')}"`
}
}

if (
issue.code === 'invalid_type' &&
issue.received === ZodParsedType.undefined
) {
// missing key in object
return `${path} is missing, expected ${issue.expected}`
}
if (issue.code === 'invalid_enum_value') {
// Remove "Invalid enum value" prefix from zod default error message
return `Expected ${ZodUtil.joinValues(issue.options)}, received '${
issue.received
}' at ${path}`
}

return message + (path ? ` at ${path}` : '')
}

function normalizeZodErrors(
function normalizeNextConfigZodErrors(
error: ZodError<NextConfig>
): [errorMessages: string[], shouldExit: boolean] {
let shouldExit = false
const issues = normalizeZodErrors(error)
return [
error.issues.flatMap((issue) => {
const messages = [processZodErrorMessage(issue)]
issues.flatMap(({ issue, message }) => {
if (issue.path[0] === 'images') {
// We exit the build when encountering an error in the images config
shouldExit = true
}

if ('unionErrors' in issue) {
issue.unionErrors
.map(normalizeZodErrors)
.forEach(([unionMessages, unionShouldExit]) => {
messages.push(...unionMessages)
// If any of the union results shows exit the build, we exit the build
shouldExit = shouldExit || unionShouldExit
})
}

return messages
return message
}),
shouldExit,
]
Expand Down Expand Up @@ -1085,7 +1026,9 @@ export default async function loadConfig(
// error message header
const messages = [`Invalid ${configFileName} options detected: `]

const [errorMessages, shouldExit] = normalizeZodErrors(state.error)
const [errorMessages, shouldExit] = normalizeNextConfigZodErrors(
state.error
)
// ident list item
for (const error of errorMessages) {
messages.push(` ${error}`)
Expand Down
67 changes: 67 additions & 0 deletions packages/next/src/shared/lib/zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { ZodError } from 'next/dist/compiled/zod'
import { ZodParsedType, util, type ZodIssue } from 'next/dist/compiled/zod'

function processZodErrorMessage(issue: ZodIssue) {
let message = issue.message

let path: string

if (issue.path.length > 0) {
if (issue.path.length === 1) {
const identifier = issue.path[0]
if (typeof identifier === 'number') {
// The first identifier inside path is a number
path = `index ${identifier}`
} else {
path = `"${identifier}"`
}
} else {
// joined path to be shown in the error message
path = `"${issue.path.reduce<string>((acc, cur) => {
if (typeof cur === 'number') {
// array index
return `${acc}[${cur}]`
}
if (cur.includes('"')) {
// escape quotes
return `${acc}["${cur.replaceAll('"', '\\"')}"]`
}
// dot notation
const separator = acc.length === 0 ? '' : '.'
return acc + separator + cur
}, '')}"`
}
} else {
path = ''
}

if (
issue.code === 'invalid_type' &&
issue.received === ZodParsedType.undefined
) {
// Missing key in object.
return `${path} is missing, expected ${issue.expected}`
}

if (issue.code === 'invalid_enum_value') {
// Remove "Invalid enum value" prefix from zod default error message
return `Expected ${util.joinValues(issue.options)}, received '${
issue.received
}' at ${path}`
}

return message + (path ? ` at ${path}` : '')
}

export function normalizeZodErrors(error: ZodError) {
return error.issues.flatMap((issue) => {
const issues = [{ issue, message: processZodErrorMessage(issue) }]
if ('unionErrors' in issue) {
for (const unionError of issue.unionErrors) {
issues.push(...normalizeZodErrors(unionError))
}
}

return issues
})
}

0 comments on commit 3e7ff6b

Please sign in to comment.