Skip to content

Commit 252f50d

Browse files
authored
fix(gatsby): don't throw on warnings in pluginOptionsSchema (#34182)
* fix: don't throw on warnings * fix: typings * fix: more type fixes * fix: tests and add correct test * tests: fix tests based on cahnges * fix: resolve final test issues * cleanup: remove test console.log * add https to github link * test: fix incorect snapshot
1 parent d061b1c commit 252f50d

File tree

13 files changed

+173
-106
lines changed

13 files changed

+173
-106
lines changed

packages/gatsby-plugin-cxs/src/__tests__/gatsby-node.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import { testPluginOptionsSchema } from "gatsby-plugin-utils"
33
import { pluginOptionsSchema } from "../gatsby-node"
44

55
it(`should provide meaningful errors when fields are invalid`, async () => {
6-
const expectedErrors = [`"optionA" is not allowed`]
6+
const expectedWarnings = [`"optionA" is not allowed`]
77

8-
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
9-
optionA: `This options shouldn't exist`,
10-
})
11-
12-
expect(errors).toEqual(expectedErrors)
8+
const { warnings, isValid, hasWarnings } = await testPluginOptionsSchema(
9+
pluginOptionsSchema,
10+
{
11+
optionA: `This options shouldn't exist`,
12+
}
13+
)
14+
expect(isValid).toBe(true)
15+
expect(hasWarnings).toBe(true)
16+
expect(warnings).toEqual(expectedWarnings)
1317
})
1418

1519
it.each`

packages/gatsby-plugin-flow/src/__tests__/gatsby-node.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ describe(`onCreateBabelConfig`, () => {
1919

2020
describe(`pluginOptionsSchema`, () => {
2121
it(`should provide meaningful errors when fields are invalid`, async () => {
22-
const expectedErrors = [`"optionA" is not allowed`]
22+
const expectedWarnings = [`"optionA" is not allowed`]
2323

24-
const { isValid, errors } = await testPluginOptionsSchema(
24+
const { isValid, warnings, hasWarnings } = await testPluginOptionsSchema(
2525
pluginOptionsSchema,
2626
{
2727
optionA: `This option shouldn't exist`,
2828
}
2929
)
3030

31-
expect(isValid).toBe(false)
32-
expect(errors).toEqual(expectedErrors)
31+
expect(isValid).toBe(true)
32+
expect(hasWarnings).toBe(true)
33+
expect(warnings).toEqual(expectedWarnings)
3334
})
3435

3536
it.each`

packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,11 @@ describe(`Test plugin manifest options`, () => {
532532

533533
describe(`pluginOptionsSchema`, () => {
534534
it(`validates options correctly`, async () => {
535-
expect(await testPluginOptionsSchema(pluginOptionsSchema, manifestOptions))
536-
.toMatchInlineSnapshot(`
537-
Object {
538-
"errors": Array [],
539-
"isValid": true,
540-
}
541-
`)
535+
const { isValid } = await testPluginOptionsSchema(
536+
pluginOptionsSchema,
537+
manifestOptions
538+
)
539+
540+
expect(isValid).toBe(true)
542541
})
543542
})

packages/gatsby-plugin-sass/src/__tests__/gatsby-node.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,14 @@ describe(`pluginOptionsSchema`, () => {
170170
})
171171

172172
it(`should allow unknown options`, async () => {
173-
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
174-
webpackImporter: `unknown option`,
175-
})
173+
const { isValid, hasWarnings } = await testPluginOptionsSchema(
174+
pluginOptionsSchema,
175+
{
176+
webpackImporter: `unknown option`,
177+
}
178+
)
176179

177180
expect(isValid).toBe(true)
181+
expect(hasWarnings).toBe(true)
178182
})
179183
})

packages/gatsby-plugin-sitemap/src/__tests__/options-validation.js

+19-13
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,31 @@ import { testPluginOptionsSchema, Joi } from "gatsby-plugin-utils"
33

44
describe(`pluginOptionsSchema`, () => {
55
it(`should provide meaningful errors when fields are invalid`, async () => {
6-
const expectedErrors = [`"wrong" is not allowed`]
6+
const expectedErrors = [`"output" must be a string`]
77

8-
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
9-
wrong: `test`,
10-
})
8+
const { isValid, errors } = await testPluginOptionsSchema(
9+
pluginOptionsSchema,
10+
{
11+
output: 123,
12+
}
13+
)
1114

15+
expect(isValid).toBe(false)
1216
expect(errors).toEqual(expectedErrors)
1317
})
1418

15-
it(`should provide error for deprecated "exclude" option`, async () => {
16-
const expectedErrors = [
17-
`As of v4 the \`exclude\` option was renamed to \`excludes\``,
18-
]
19+
it(`should provide warning for deprecated "exclude" option`, async () => {
20+
const expectedWarnings = [`"exclude" is not allowed`]
1921

20-
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
21-
exclude: [`test`],
22-
})
22+
const { warnings, hasWarnings } = await testPluginOptionsSchema(
23+
pluginOptionsSchema,
24+
{
25+
exclude: [`test`],
26+
}
27+
)
2328

24-
expect(errors).toEqual(expectedErrors)
29+
expect(hasWarnings).toBe(true)
30+
expect(warnings).toEqual(expectedWarnings)
2531
})
2632

2733
it(`creates correct defaults`, async () => {
@@ -48,7 +54,7 @@ describe(`pluginOptionsSchema`, () => {
4854
${undefined}
4955
${{}}
5056
`(`should validate the schema: $options`, async ({ options }) => {
51-
const { isValid } = await testPluginOptionsSchema(
57+
const { isValid, errors } = await testPluginOptionsSchema(
5258
pluginOptionsSchema,
5359
options
5460
)

packages/gatsby-plugin-sitemap/src/options-validation.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export const pluginOptionsSchema = ({ Joi }) =>
4141
}
4242
}`
4343
)
44-
.external(({ query }) => {
44+
.external(pluginOptions => {
45+
const query = pluginOptions?.query
4546
if (query) {
4647
try {
4748
parseGraphql(query)
@@ -74,9 +75,6 @@ export const pluginOptionsSchema = ({ Joi }) =>
7475
enter other data types into this array for custom filtering.
7576
Doing so will require customization of the \`filterPages\` function.`
7677
),
77-
exclude: Joi.forbidden().messages({
78-
"any.unknown": `As of v4 the \`exclude\` option was renamed to \`excludes\``,
79-
}),
8078
resolveSiteUrl: Joi.function()
8179
.default(() => resolveSiteUrl)
8280
.description(

packages/gatsby-plugin-twitter/src/__tests__/gatsby-node.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ import { testPluginOptionsSchema } from "gatsby-plugin-utils"
33
import { pluginOptionsSchema } from "../gatsby-node"
44

55
it(`should provide meaningful errors when fields are invalid`, async () => {
6-
const expectedErrors = [`"optionA" is not allowed`]
6+
const expectedWarnings = [`"optionA" is not allowed`]
77

8-
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
9-
optionA: `This options shouldn't exist`,
10-
})
8+
const { warnings, isValid, hasWarnings } = await testPluginOptionsSchema(
9+
pluginOptionsSchema,
10+
{
11+
optionA: `This options shouldn't exist`,
12+
}
13+
)
1114

12-
expect(errors).toEqual(expectedErrors)
15+
expect(isValid).toBe(true)
16+
expect(hasWarnings).toBe(true)
17+
expect(warnings).toEqual(expectedWarnings)
1318
})
1419

1520
it.each`

packages/gatsby-plugin-utils/src/__tests__/index.ts

+31-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @typescript-eslint/explicit-function-return-type */
22
import { validateOptionsSchema, Joi } from "../"
3+
import { testPluginOptionsSchema } from "../test-plugin-options-schema"
34

45
it(`validates a basic schema`, async () => {
56
const pluginSchema = Joi.object({
@@ -9,9 +10,9 @@ it(`validates a basic schema`, async () => {
910
const validOptions = {
1011
str: `is a string`,
1112
}
12-
expect(await validateOptionsSchema(pluginSchema, validOptions)).toEqual(
13-
validOptions
14-
)
13+
14+
const { value } = await validateOptionsSchema(pluginSchema, validOptions)
15+
expect(value).toEqual(validOptions)
1516

1617
const invalid = () =>
1718
validateOptionsSchema(pluginSchema, {
@@ -56,18 +57,38 @@ it(`does not validate async external validation rules when validateExternalRules
5657
expect(invalid).not.toThrowError()
5758
})
5859

59-
it(`throws an error on unknown values`, async () => {
60+
it(`throws an warning on unknown values`, async () => {
6061
const schema = Joi.object({
6162
str: Joi.string(),
6263
})
6364

64-
const invalid = () =>
65-
validateOptionsSchema(schema, {
65+
const validWarnings = [`"notInSchema" is not allowed`]
66+
67+
const { hasWarnings, warnings } = await testPluginOptionsSchema(
68+
() => schema,
69+
{
6670
str: `bla`,
6771
notInSchema: true,
68-
})
69-
70-
expect(invalid()).rejects.toThrowErrorMatchingInlineSnapshot(
71-
`"\\"notInSchema\\" is not allowed"`
72+
}
7273
)
74+
75+
expect(hasWarnings).toBe(true)
76+
expect(warnings).toEqual(validWarnings)
77+
})
78+
79+
it(`populates default values`, async () => {
80+
const pluginSchema = Joi.object({
81+
str: Joi.string(),
82+
default: Joi.string().default(`default`),
83+
})
84+
85+
const validOptions = {
86+
str: `is a string`,
87+
}
88+
89+
const { value } = await validateOptionsSchema(pluginSchema, validOptions)
90+
expect(value).toEqual({
91+
...validOptions,
92+
default: `default`,
93+
})
7394
})

packages/gatsby-plugin-utils/src/test-plugin-options-schema.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { IPluginInfoOptions } from "./types"
55

66
interface ITestPluginOptionsSchemaReturnType {
77
errors: Array<string>
8+
warnings: Array<string>
89
isValid: boolean
10+
hasWarnings: boolean
911
}
1012

1113
export async function testPluginOptionsSchema(
@@ -44,11 +46,22 @@ export async function testPluginOptionsSchema(
4446
})
4547

4648
try {
47-
await validateOptionsSchema(pluginSchema, pluginOptions)
49+
const { warning } = await validateOptionsSchema(pluginSchema, pluginOptions)
50+
51+
const warnings = warning?.details?.map(detail => detail.message) ?? []
52+
53+
if (warnings?.length > 0) {
54+
return {
55+
isValid: true,
56+
errors: [],
57+
hasWarnings: true,
58+
warnings,
59+
}
60+
}
4861
} catch (e) {
49-
const errors = e.details.map(detail => detail.message)
50-
return { isValid: false, errors }
62+
const errors = e?.details?.map(detail => detail.message) ?? []
63+
return { isValid: false, errors, hasWarnings: false, warnings: [] }
5164
}
5265

53-
return { isValid: true, errors: [] }
66+
return { isValid: true, errors: [], hasWarnings: false, warnings: [] }
5467
}

packages/gatsby-plugin-utils/src/utils/plugin-options-schema-joi-type.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ interface AnySchema extends SchemaInternals {
11601160
* Warnings are reported separately from errors alongside the result value via the warning key (i.e. `{ value, warning }`).
11611161
* Warning are always included when calling `any.validate()`.
11621162
*/
1163-
warning(code: string, context: Context): this
1163+
warning(code: string, context?: Context): this
11641164

11651165
/**
11661166
* Converts the type into an alternatives type where the conditions are merged into the type definition where:
+26-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ValidationOptions } from "joi"
2-
import { ObjectSchema } from "./joi"
2+
import { ObjectSchema, Joi } from "./joi"
33
import { IPluginInfoOptions } from "./types"
44

55
const validationOptions: ValidationOptions = {
@@ -10,21 +10,40 @@ const validationOptions: ValidationOptions = {
1010

1111
interface IOptions {
1212
validateExternalRules?: boolean
13+
returnWarnings?: boolean
14+
}
15+
16+
interface IValidateAsyncResult {
17+
value: IPluginInfoOptions
18+
warning: {
19+
message: string
20+
details: Array<{
21+
message: string
22+
path: Array<string>
23+
type: string
24+
context: Array<Record<string, unknown>>
25+
}>
26+
}
1327
}
1428

1529
export async function validateOptionsSchema(
1630
pluginSchema: ObjectSchema,
1731
pluginOptions: IPluginInfoOptions,
1832
options: IOptions = {
1933
validateExternalRules: true,
34+
returnWarnings: true,
2035
}
21-
): Promise<IPluginInfoOptions> {
22-
const { validateExternalRules } = options
36+
): Promise<IValidateAsyncResult> {
37+
const { validateExternalRules, returnWarnings } = options
2338

24-
const value = await pluginSchema.validateAsync(pluginOptions, {
39+
const warnOnUnknownSchema = pluginSchema.pattern(
40+
/.*/,
41+
Joi.any().warning(`any.unknown`)
42+
)
43+
44+
return (await warnOnUnknownSchema.validateAsync(pluginOptions, {
2545
...validationOptions,
2646
externals: validateExternalRules,
27-
})
28-
29-
return value
47+
warnings: returnWarnings,
48+
})) as Promise<IValidateAsyncResult>
3049
}

packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ describe(`Load plugins`, () => {
454454
expect((reporter.warn as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(`
455455
Array [
456456
"Warning: there are unknown plugin options for \\"gatsby-plugin-google-analytics\\": doesThisExistInTheSchema
457-
Please open an issue at ghub.io/gatsby-plugin-google-analytics if you believe this option is valid.",
457+
Please open an issue at https://ghub.io/gatsby-plugin-google-analytics if you believe this option is valid.",
458458
]
459459
`)
460460
expect(mockProcessExit).not.toHaveBeenCalled()

0 commit comments

Comments
 (0)