Skip to content

Commit ba16a69

Browse files
committed
refactor: added more strict app segment config parsing
1 parent 07a55e0 commit ba16a69

File tree

9 files changed

+408
-247
lines changed

9 files changed

+408
-247
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { AppSegmentConfigSchema } from './app-segment-config'
2+
3+
describe('AppConfigSchema', () => {
4+
it('should only support zero, a positive number or false for revalidate', () => {
5+
const valid = [0, 1, 100, false]
6+
7+
for (const value of valid) {
8+
expect(
9+
AppSegmentConfigSchema.safeParse({ revalidate: value }).success
10+
).toBe(true)
11+
}
12+
13+
const invalid = [-1, -100, true]
14+
15+
for (const value of invalid) {
16+
expect(
17+
AppSegmentConfigSchema.safeParse({ revalidate: value }).success
18+
).toBe(false)
19+
}
20+
})
21+
22+
it('should support an empty config', () => {
23+
expect(AppSegmentConfigSchema.safeParse({}).success).toBe(true)
24+
})
25+
26+
it('should support a boolean for dynamicParams', () => {
27+
expect(
28+
AppSegmentConfigSchema.safeParse({ dynamicParams: true }).success
29+
).toBe(true)
30+
expect(
31+
AppSegmentConfigSchema.safeParse({ dynamicParams: false }).success
32+
).toBe(true)
33+
expect(
34+
AppSegmentConfigSchema.safeParse({ dynamicParams: 'foo' }).success
35+
).toBe(false)
36+
})
37+
38+
it('should support "auto" | "force-dynamic" | "error" | "force-static" for dynamic', () => {
39+
expect(AppSegmentConfigSchema.safeParse({ dynamic: 'auto' }).success).toBe(
40+
true
41+
)
42+
expect(
43+
AppSegmentConfigSchema.safeParse({ dynamic: 'force-dynamic' }).success
44+
).toBe(true)
45+
expect(AppSegmentConfigSchema.safeParse({ dynamic: 'error' }).success).toBe(
46+
true
47+
)
48+
expect(
49+
AppSegmentConfigSchema.safeParse({ dynamic: 'force-static' }).success
50+
).toBe(true)
51+
})
52+
53+
it('should support "edge" | "nodejs" for runtime', () => {
54+
expect(AppSegmentConfigSchema.safeParse({ runtime: 'edge' }).success).toBe(
55+
true
56+
)
57+
expect(
58+
AppSegmentConfigSchema.safeParse({ runtime: 'nodejs' }).success
59+
).toBe(true)
60+
expect(AppSegmentConfigSchema.safeParse({ runtime: 'foo' }).success).toBe(
61+
false
62+
)
63+
})
64+
65+
it('should support a positive number or zero for maxDuration', () => {
66+
expect(AppSegmentConfigSchema.safeParse({ maxDuration: 0 }).success).toBe(
67+
true
68+
)
69+
expect(AppSegmentConfigSchema.safeParse({ maxDuration: 100 }).success).toBe(
70+
true
71+
)
72+
expect(AppSegmentConfigSchema.safeParse({ maxDuration: -1 }).success).toBe(
73+
false
74+
)
75+
})
76+
77+
it('should support "force-cache" | "only-cache" for fetchCache', () => {
78+
expect(
79+
AppSegmentConfigSchema.safeParse({ fetchCache: 'force-cache' }).success
80+
).toBe(true)
81+
expect(
82+
AppSegmentConfigSchema.safeParse({ fetchCache: 'only-cache' }).success
83+
).toBe(true)
84+
expect(
85+
AppSegmentConfigSchema.safeParse({ fetchCache: 'foo' }).success
86+
).toBe(false)
87+
})
88+
89+
it('should support a string or an array of strings for preferredRegion', () => {
90+
expect(
91+
AppSegmentConfigSchema.safeParse({ preferredRegion: 'foo' }).success
92+
).toBe(true)
93+
expect(
94+
AppSegmentConfigSchema.safeParse({ preferredRegion: ['foo', 'bar'] })
95+
.success
96+
).toBe(true)
97+
})
98+
99+
it('should support a boolean for experimental_ppr', () => {
100+
expect(
101+
AppSegmentConfigSchema.safeParse({ experimental_ppr: true }).success
102+
).toBe(true)
103+
expect(
104+
AppSegmentConfigSchema.safeParse({ experimental_ppr: false }).success
105+
).toBe(true)
106+
expect(
107+
AppSegmentConfigSchema.safeParse({ experimental_ppr: 'foo' }).success
108+
).toBe(false)
109+
})
110+
})
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { z } from 'next/dist/compiled/zod'
2+
3+
/**
4+
* The schema for the dynamic behavior of a page.
5+
*
6+
* @internal
7+
*/
8+
export const AppSegmentConfigDynamicSchema = z.enum([
9+
'auto',
10+
'error',
11+
'force-static',
12+
'force-dynamic',
13+
])
14+
15+
/**
16+
* The dynamic behavior of the page.
17+
*
18+
* @internal
19+
*/
20+
export type AppSegmentConfigDynamic = z.infer<
21+
typeof AppSegmentConfigDynamicSchema
22+
>
23+
24+
/**
25+
* The schema for configuration for a page.
26+
*
27+
* @internal
28+
*/
29+
export const AppSegmentConfigSchema = z.object({
30+
/**
31+
* The number of seconds to revalidate the page or false to disable revalidation.
32+
*/
33+
revalidate: z
34+
.union([z.number().int().nonnegative(), z.literal(false)])
35+
.optional(),
36+
37+
/**
38+
* Whether the page supports dynamic parameters.
39+
*/
40+
dynamicParams: z.boolean().optional(),
41+
42+
/**
43+
* The dynamic behavior of the page.
44+
*/
45+
dynamic: AppSegmentConfigDynamicSchema.optional(),
46+
47+
/**
48+
* The caching behavior of the page.
49+
*/
50+
fetchCache: z.enum(['force-cache', 'only-cache']).optional(),
51+
52+
/**
53+
* The preferred region for the page.
54+
*/
55+
preferredRegion: z.union([z.string(), z.array(z.string())]).optional(),
56+
57+
/**
58+
* Whether the page supports partial prerendering. When true, the page will be
59+
* served using partial prerendering. This setting will only take affect if
60+
* it's enabled via the `experimental.ppr = "incremental"` option.
61+
*/
62+
experimental_ppr: z.boolean().optional(),
63+
64+
/**
65+
* The runtime to use for the page.
66+
*/
67+
runtime: z.enum(['edge', 'nodejs']).optional(),
68+
69+
/**
70+
* The maximum duration for the page in seconds.
71+
*/
72+
maxDuration: z.number().int().nonnegative().optional(),
73+
})
74+
75+
/**
76+
* The configuration for a page.
77+
*
78+
* @internal
79+
*/
80+
export type AppSegmentConfig = z.infer<typeof AppSegmentConfigSchema>
81+
82+
/**
83+
* The keys of the configuration for a page.
84+
*
85+
* @internal
86+
*/
87+
export const AppSegmentConfigSchemaKeys = AppSegmentConfigSchema.keyof().options

packages/next/src/build/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ import {
130130
collectMeta,
131131
// getSupportedBrowsers,
132132
} from './utils'
133-
import type { PageInfo, PageInfos, AppConfig, PrerenderedRoute } from './utils'
133+
import type { PageInfo, PageInfos, PrerenderedRoute } from './utils'
134+
import type { AppSegmentConfig } from './app-segment-config'
134135
import { writeBuildId } from './write-build-id'
135136
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
136137
import isError from '../lib/is-error'
@@ -1815,7 +1816,7 @@ export default async function build(
18151816
const staticPaths = new Map<string, PrerenderedRoute[]>()
18161817
const appNormalizedPaths = new Map<string, string>()
18171818
const fallbackModes = new Map<string, FallbackMode>()
1818-
const appDefaultConfigs = new Map<string, AppConfig>()
1819+
const appDefaultConfigs = new Map<string, AppSegmentConfig>()
18191820
const pageInfos: PageInfos = new Map<string, PageInfo>()
18201821
let pagesManifest = await readManifest<PagesManifest>(pagesManifestPath)
18211822
const buildManifest = await readManifest<BuildManifest>(buildManifestPath)

0 commit comments

Comments
 (0)