Skip to content

Commit 9b01ca7

Browse files
authored
feat(gatsby): plugin option validation (#27242)
* Add pluginOptionsSchema to google analytics for testing * Add structured error for plugin option validation errors * Validate gatsbyNode.pluginOptionsSchema when defined * Add first test, fix some bugs discovered based on it * process.exit on invalid plugin options * Much nicer structured error logging for invalid plugin options * Move TODO comment to correct place * Remove unused import * Add new API to gatsby-node API tests * Warn on invalid plugin schema * Gate plugin option validation behind feature flag for now * less formal wording * Clean up based on code review * Type GatsbyNode.pluginOptionsSchema * Properly type Joi including our .dotenv() extension * Move custom joi + types to gatsby-plugin-utils * Disable the .dotenv extension for now
1 parent cc1bbfc commit 9b01ca7

File tree

15 files changed

+2554
-11
lines changed

15 files changed

+2554
-11
lines changed

.jestSetup.js

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
process.env.GATSBY_RECIPES_NO_COLOR = "true"
2+
process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION = "true"

packages/gatsby-cli/src/structured-errors/error-map.ts

+16
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,22 @@ const errors = {
420420
type: Type.PLUGIN,
421421
level: Level.ERROR,
422422
},
423+
// Invalid plugin options
424+
"11331": {
425+
text: (context): string =>
426+
[
427+
stripIndent(`
428+
Invalid plugin options for "${context.pluginName}":
429+
`),
430+
]
431+
.concat([``])
432+
.concat(
433+
context.validationErrors.map(error => `- ${error.message}`).join(`\n`)
434+
)
435+
.join(`\n`),
436+
type: Type.PLUGIN,
437+
level: Level.ERROR,
438+
},
423439
// node object didn't pass validation
424440
"11467": {
425441
text: (context): string =>
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
1-
exports.onPreInit = ({ reporter }, options) => {
2-
if (!options.trackingId) {
3-
reporter.warn(
4-
`The Google Analytics plugin requires a tracking ID. Did you mean to add it?`
5-
)
6-
}
7-
}
1+
exports.pluginOptionsSchema = ({ Joi }) =>
2+
Joi.object({
3+
trackingId: Joi.string()
4+
.required()
5+
.description(
6+
`The property ID; the tracking code won't be generated without it`
7+
),
8+
head: Joi.boolean()
9+
.default(false)
10+
.description(
11+
`Defines where to place the tracking script - \`true\` in the head and \`false\` in the body`
12+
),
13+
anonymize: Joi.boolean().default(false),
14+
respectDNT: Joi.boolean().default(false),
15+
exclude: Joi.array()
16+
.items(Joi.string())
17+
.default([])
18+
.description(`Avoids sending pageview hits from custom paths`),
19+
pageTransitionDelay: Joi.number()
20+
.default(0)
21+
.description(
22+
`Delays sending pageview hits on route update (in milliseconds)`
23+
),
24+
optimizeId: Joi.string().description(
25+
`Enables Google Optimize using your container Id`
26+
),
27+
experimentId: Joi.string().description(
28+
`Enables Google Optimize Experiment ID`
29+
),
30+
variationId: Joi.string().description(
31+
`Set Variation ID. 0 for original 1,2,3....`
32+
),
33+
defer: Joi.boolean().description(
34+
`Defers execution of google analytics script after page load`
35+
),
36+
sampleRate: Joi.number(),
37+
siteSpeedSampleRate: Joi.number(),
38+
cookieDomain: Joi.string(),
39+
})

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable @typescript-eslint/explicit-function-return-type */
2-
import Joi from "joi"
3-
import { validateOptionsSchema } from "../"
2+
import { validateOptionsSchema, Joi } from "../"
43

54
it(`validates a basic schema`, async () => {
65
const pluginSchema = Joi.object({
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./validate"
2+
export * from "./joi"
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import joi from "joi"
2+
import { PluginOptionsSchemaJoi } from "./utils/plugin-options-schema-joi-type"
3+
4+
export * from "./utils/plugin-options-schema-joi-type"
5+
export const Joi: PluginOptionsSchemaJoi = joi.extend({
6+
// This tells Joi to extend _all_ types with .dotenv(), see
7+
// https://github.com/sideway/joi/commit/03adf22eb1f06c47d1583617093edee3a96b3873
8+
// @ts-ignore Joi types weren't updated with that commit, PR: https://github.com/sideway/joi/pull/2477
9+
type: /^s/,
10+
rules: {
11+
// NOTE(@mxstbr): Disabled until we decide on the necessity for this API.
12+
// dotenv: {
13+
// args: [`name`],
14+
// validate(value, helpers, args): void {
15+
// if (!args.name) {
16+
// return helpers.error(
17+
// `any.dotenv requires the environment variable name`
18+
// )
19+
// }
20+
// return value
21+
// },
22+
// method(name): Schema {
23+
// return this.$_addRule({ name: `dotenv`, args: { name } })
24+
// },
25+
// },
26+
},
27+
})

0 commit comments

Comments
 (0)