Skip to content

Commit bfb9408

Browse files
committed
refactor: extracted zod configuration
1 parent 334f335 commit bfb9408

File tree

2 files changed

+76
-66
lines changed

2 files changed

+76
-66
lines changed

packages/next/src/server/config.ts

Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,87 +22,28 @@ import { setHttpClientAndAgentOptions } from './setup-http-agent-env'
2222
import { pathHasPrefix } from '../shared/lib/router/utils/path-has-prefix'
2323
import { matchRemotePattern } from '../shared/lib/match-remote-pattern'
2424

25-
import { ZodParsedType, util as ZodUtil } from 'next/dist/compiled/zod'
26-
import type { ZodError, ZodIssue } from 'next/dist/compiled/zod'
25+
import type { ZodError } from 'next/dist/compiled/zod'
2726
import { hasNextSupport } from '../server/ci-info'
2827
import { transpileConfig } from '../build/next-config-ts/transpile-config'
2928
import { dset } from '../shared/lib/dset'
29+
import { normalizeZodErrors } from '../shared/lib/zod'
3030

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

34-
function processZodErrorMessage(issue: ZodIssue) {
35-
let message = issue.message
36-
37-
let path = ''
38-
39-
if (issue.path.length > 0) {
40-
if (issue.path.length === 1) {
41-
const identifier = issue.path[0]
42-
if (typeof identifier === 'number') {
43-
// The first identifier inside path is a number
44-
path = `index ${identifier}`
45-
} else {
46-
path = `"${identifier}"`
47-
}
48-
} else {
49-
// joined path to be shown in the error message
50-
path = `"${issue.path.reduce<string>((acc, cur) => {
51-
if (typeof cur === 'number') {
52-
// array index
53-
return `${acc}[${cur}]`
54-
}
55-
if (cur.includes('"')) {
56-
// escape quotes
57-
return `${acc}["${cur.replaceAll('"', '\\"')}"]`
58-
}
59-
// dot notation
60-
const separator = acc.length === 0 ? '' : '.'
61-
return acc + separator + cur
62-
}, '')}"`
63-
}
64-
}
65-
66-
if (
67-
issue.code === 'invalid_type' &&
68-
issue.received === ZodParsedType.undefined
69-
) {
70-
// missing key in object
71-
return `${path} is missing, expected ${issue.expected}`
72-
}
73-
if (issue.code === 'invalid_enum_value') {
74-
// Remove "Invalid enum value" prefix from zod default error message
75-
return `Expected ${ZodUtil.joinValues(issue.options)}, received '${
76-
issue.received
77-
}' at ${path}`
78-
}
79-
80-
return message + (path ? ` at ${path}` : '')
81-
}
82-
83-
function normalizeZodErrors(
34+
function normalizeNextConfigZodErrors(
8435
error: ZodError<NextConfig>
8536
): [errorMessages: string[], shouldExit: boolean] {
8637
let shouldExit = false
38+
const issues = normalizeZodErrors(error)
8739
return [
88-
error.issues.flatMap((issue) => {
89-
const messages = [processZodErrorMessage(issue)]
40+
issues.flatMap(({ issue, message }) => {
9041
if (issue.path[0] === 'images') {
9142
// We exit the build when encountering an error in the images config
9243
shouldExit = true
9344
}
9445

95-
if ('unionErrors' in issue) {
96-
issue.unionErrors
97-
.map(normalizeZodErrors)
98-
.forEach(([unionMessages, unionShouldExit]) => {
99-
messages.push(...unionMessages)
100-
// If any of the union results shows exit the build, we exit the build
101-
shouldExit = shouldExit || unionShouldExit
102-
})
103-
}
104-
105-
return messages
46+
return message
10647
}),
10748
shouldExit,
10849
]
@@ -1085,7 +1026,9 @@ export default async function loadConfig(
10851026
// error message header
10861027
const messages = [`Invalid ${configFileName} options detected: `]
10871028

1088-
const [errorMessages, shouldExit] = normalizeZodErrors(state.error)
1029+
const [errorMessages, shouldExit] = normalizeNextConfigZodErrors(
1030+
state.error
1031+
)
10891032
// ident list item
10901033
for (const error of errorMessages) {
10911034
messages.push(` ${error}`)

packages/next/src/shared/lib/zod.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { ZodError } from 'next/dist/compiled/zod'
2+
import { ZodParsedType, util, type ZodIssue } from 'next/dist/compiled/zod'
3+
4+
function processZodErrorMessage(issue: ZodIssue) {
5+
let message = issue.message
6+
7+
let path: string
8+
9+
if (issue.path.length > 0) {
10+
if (issue.path.length === 1) {
11+
const identifier = issue.path[0]
12+
if (typeof identifier === 'number') {
13+
// The first identifier inside path is a number
14+
path = `index ${identifier}`
15+
} else {
16+
path = `"${identifier}"`
17+
}
18+
} else {
19+
// joined path to be shown in the error message
20+
path = `"${issue.path.reduce<string>((acc, cur) => {
21+
if (typeof cur === 'number') {
22+
// array index
23+
return `${acc}[${cur}]`
24+
}
25+
if (cur.includes('"')) {
26+
// escape quotes
27+
return `${acc}["${cur.replaceAll('"', '\\"')}"]`
28+
}
29+
// dot notation
30+
const separator = acc.length === 0 ? '' : '.'
31+
return acc + separator + cur
32+
}, '')}"`
33+
}
34+
} else {
35+
path = ''
36+
}
37+
38+
if (
39+
issue.code === 'invalid_type' &&
40+
issue.received === ZodParsedType.undefined
41+
) {
42+
// Missing key in object.
43+
return `${path} is missing, expected ${issue.expected}`
44+
}
45+
46+
if (issue.code === 'invalid_enum_value') {
47+
// Remove "Invalid enum value" prefix from zod default error message
48+
return `Expected ${util.joinValues(issue.options)}, received '${
49+
issue.received
50+
}' at ${path}`
51+
}
52+
53+
return message + (path ? ` at ${path}` : '')
54+
}
55+
56+
export function normalizeZodErrors(error: ZodError) {
57+
return error.issues.flatMap((issue) => {
58+
const issues = [{ issue, message: processZodErrorMessage(issue) }]
59+
if ('unionErrors' in issue) {
60+
for (const unionError of issue.unionErrors) {
61+
issues.push(...normalizeZodErrors(unionError))
62+
}
63+
}
64+
65+
return issues
66+
})
67+
}

0 commit comments

Comments
 (0)