From 41ae1c07ad9919655782ef17feed8cf4f14f12d8 Mon Sep 17 00:00:00 2001 From: Max Stoiber Date: Mon, 2 Nov 2020 14:26:44 +0100 Subject: [PATCH] feat(gatsby): release plugin option validation (#27437) * chore(gatsby): add @gatsbyVersion pragma to pluginOptionsSchema Node API This will make Gatsby prompt users on old versions of gatsby that don't support the API to upgrade to the new version. * 2.25.0! * Remove GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION feature flag * Fix incorrect option in gatsby-admin; * Fix tests * Remove obsolete snapshots * Trigger Build * Remove flag from remark-autolink-headers * Fix gatsby-plugin-mdx default --- .jestSetup.js | 1 - packages/gatsby-admin/gatsby-config.js | 7 +- .../__snapshots__/gatsby-node.js.snap | 4 - .../src/__tests__/gatsby-node.js | 104 +------- .../gatsby-plugin-feed/src/gatsby-node.js | 61 +---- .../src/gatsby-node.js | 82 +++---- .../src/gatsby-node.js | 52 ++-- .../gatsby-plugin-manifest/src/gatsby-node.js | 74 +++--- packages/gatsby-plugin-mdx/gatsby-node.js | 102 ++++---- .../gatsby-plugin-netlify/src/gatsby-node.js | 4 +- .../gatsby-plugin-offline/src/gatsby-node.js | 76 +++--- .../gatsby-plugin-sass/src/gatsby-node.js | 222 +++++++++--------- .../gatsby-plugin-sharp/src/gatsby-node.js | 34 ++- .../gatsby-plugin-sitemap/src/gatsby-node.js | 68 +++--- .../src/gatsby-node.js | 26 +- .../gatsby-node.js | 28 +-- .../src/gatsby-node.js | 64 +++-- .../gatsby-remark-images/src/gatsby-node.js | 136 ++++++----- .../src/gatsby-node.js | 29 +-- .../src/gatsby-node.js | 14 +- .../src/gatsby-node.js | 52 ++-- packages/gatsby/scripts/__tests__/api.js | 4 +- .../src/bootstrap/load-plugins/index.ts | 4 +- packages/gatsby/src/utils/api-node-docs.ts | 1 + 24 files changed, 505 insertions(+), 744 deletions(-) diff --git a/.jestSetup.js b/.jestSetup.js index 01abb9c9bad71..715a57338921b 100644 --- a/.jestSetup.js +++ b/.jestSetup.js @@ -1,2 +1 @@ process.env.GATSBY_RECIPES_NO_COLOR = "true" -process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION = "true" diff --git a/packages/gatsby-admin/gatsby-config.js b/packages/gatsby-admin/gatsby-config.js index 7aea547984595..0ad8b00f1ad26 100644 --- a/packages/gatsby-admin/gatsby-config.js +++ b/packages/gatsby-admin/gatsby-config.js @@ -1,11 +1,6 @@ module.exports = { plugins: [ - { - resolve: "gatsby-plugin-react-helmet", - options: { - test: false, - }, - }, + "gatsby-plugin-react-helmet", { resolve: "gatsby-plugin-webfonts", options: { diff --git a/packages/gatsby-plugin-feed/src/__tests__/__snapshots__/gatsby-node.js.snap b/packages/gatsby-plugin-feed/src/__tests__/__snapshots__/gatsby-node.js.snap index b2348acc47be0..f3119e341c259 100644 --- a/packages/gatsby-plugin-feed/src/__tests__/__snapshots__/gatsby-node.js.snap +++ b/packages/gatsby-plugin-feed/src/__tests__/__snapshots__/gatsby-node.js.snap @@ -5,7 +5,3 @@ exports[`Test plugin feed custom properties work properly 1`] = `"<![CDATA[my feed]]>http://github.com/dylang/node-rssGatsbyJSMon, 01 Jan 2018 00:00:00 GMT<![CDATA[No title]]>http://dummy.url/a-custom-pathhttp://dummy.url/a-custom-path<![CDATA[No title]]>http://dummy.url/another-custom-pathhttp://dummy.url/another-custom-path"`; exports[`Test plugin feed default settings work properly 1`] = `"<![CDATA[a sample title]]>http://github.com/dylang/node-rssGatsbyJSMon, 01 Jan 2018 00:00:00 GMT<![CDATA[No title]]>http://dummy.url/a-slughttp://dummy.url/a-slug"`; - -exports[`Test plugin feed options validation throws when invalid plugin options 1`] = `[Error: [Config Validation]: "feeds[0].output" is required]`; - -exports[`Test plugin feed options validation throws when invalid plugin options 2`] = `[Error: [Config Validation]: "feeds[0].query" is required]`; diff --git a/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js index 461f56bbd6bc2..a669f5f662d79 100644 --- a/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js @@ -1,7 +1,7 @@ jest.mock(`fs-extra`) const fs = require(`fs-extra`) const path = require(`path`) -const { onPreBootstrap, onPostBuild } = require(`../gatsby-node`) +const { onPostBuild } = require(`../gatsby-node`) const DATE_TO_USE = new Date(`2018`) const _Date = Date global.Date = jest.fn(() => DATE_TO_USE) @@ -15,108 +15,6 @@ describe(`Test plugin feed`, () => { fs.mkdirp = jest.fn().mockResolvedValue() }) - describe(`options validation`, () => { - const setup = async options => { - const reporter = { - stripIndent: jest.fn(value => value.trim()), - warn: jest.fn(), - } - await onPreBootstrap({ reporter }, options) - - return [reporter, options] - } - - const deprecationNotice = `This behavior will be removed in the next major release of gatsby-plugin-feed` - - it(`removes plugins`, async () => { - const options = { plugins: [] } - - await setup(options) - - expect(options.plugins).toBeUndefined() - }) - - it(`warns when feeds is not supplied`, async () => { - const options = {} - - const [reporter] = await setup(options) - - expect(reporter.warn).toHaveBeenCalledTimes(1) - expect(reporter.warn).toHaveBeenCalledWith( - expect.stringContaining(deprecationNotice) - ) - }) - - it(`warns when individual feed does not have title`, async () => { - const options = { - feeds: [ - { - output: `rss.xml`, - query: `{}`, - serialize: () => {}, - }, - ], - } - - const [reporter] = await setup(options) - - expect(reporter.warn).toHaveBeenCalledTimes(1) - expect(reporter.warn).toHaveBeenCalledWith( - expect.stringContaining(`title`) - ) - }) - - it(`warns when individual feed does not have serialize function`, async () => { - const options = { - feeds: [ - { - output: `rss.xml`, - query: `{}`, - title: `my feed`, - }, - ], - } - - const [reporter] = await setup(options) - - expect(reporter.warn).toHaveBeenCalledTimes(1) - expect(reporter.warn).toHaveBeenCalledWith( - expect.stringContaining(deprecationNotice) - ) - }) - - it(`throws when invalid plugin options`, async () => { - const invalidOptions = [ - { - feeds: [ - { - // output is missing - query: `{}`, - }, - ], - }, - { - feeds: [ - { - output: `rss.xml`, - // query is missing - }, - ], - }, - ] - - for (let options of invalidOptions) { - try { - await setup(options) - } catch (e) { - expect(e).toMatchSnapshot() - } - } - - expect.assertions(invalidOptions.length) - }) - }) - it(`default settings work properly`, async () => { fs.writeFile = jest.fn() fs.writeFile.mockResolvedValue(true) diff --git a/packages/gatsby-plugin-feed/src/gatsby-node.js b/packages/gatsby-plugin-feed/src/gatsby-node.js index 6f154cfa0d491..8b2fab7277f1d 100644 --- a/packages/gatsby-plugin-feed/src/gatsby-node.js +++ b/packages/gatsby-plugin-feed/src/gatsby-node.js @@ -2,23 +2,13 @@ import fs from "fs-extra" import path from "path" import RSS from "rss" import merge from "lodash.merge" -import { Joi } from "gatsby-plugin-utils" import { defaultOptions, runQuery } from "./internals" import pluginOptionsSchema from "./plugin-options" const publicPath = `./public` -const warnMessage = (error, behavior) => ` - gatsby-plugin-feed was initialized in gatsby-config.js without a ${error}. - This means that the plugin will use ${behavior}, which may not match your use case. - This behavior will be removed in the next major release of gatsby-plugin-feed. - For more info, check out: https://gatsby.dev/adding-rss-feed -` - -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = pluginOptionsSchema -} +exports.pluginOptionsSchema = pluginOptionsSchema // TODO: remove in the next major release // A default function to transform query data into feed entries. @@ -33,55 +23,6 @@ const serialize = ({ query: { site, allMarkdownRemark } }) => } }) -exports.onPreBootstrap = async function onPreBootstrap( - { reporter }, - pluginOptions -) { - delete pluginOptions.plugins - - try { - // TODO: remove this once pluginOptionsSchema is stable - const { value: normalized, error } = await pluginOptionsSchema({ - Joi, - }).validate(pluginOptions, { - externals: false, - }) - - if (error) throw error - - if (!normalized.feeds) { - reporter.warn( - reporter.stripIndent( - warnMessage(`feeds option`, `the internal RSS feed creation`) - ) - ) - } else if (normalized.feeds.some(feed => typeof feed.title !== `string`)) { - reporter.warn( - reporter.stripIndent( - warnMessage(`title in a feed`, `the default feed title`) - ) - ) - } else if ( - normalized.feeds.some(feed => typeof feed.serialize !== `function`) - ) { - reporter.warn( - reporter.stripIndent( - warnMessage( - `serialize function in a feed`, - `the internal serialize function` - ) - ) - ) - } - } catch (e) { - throw new Error( - e.details - .map(detail => `[Config Validation]: ${detail.message}`) - .join(`\n`) - ) - } -} - exports.onPostBuild = async ({ graphql }, pluginOptions) => { /* * Run the site settings query to gather context, then diff --git a/packages/gatsby-plugin-google-analytics/src/gatsby-node.js b/packages/gatsby-plugin-google-analytics/src/gatsby-node.js index fb470d49f8cfd..8666677330e60 100644 --- a/packages/gatsby-plugin-google-analytics/src/gatsby-node.js +++ b/packages/gatsby-plugin-google-analytics/src/gatsby-node.js @@ -1,48 +1,38 @@ -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - // TODO: make sure that trackingId gets required() when releasing a major version - Joi.object({ - trackingId: Joi.string().description( - `The property ID; the tracking code won't be generated without it` +exports.pluginOptionsSchema = ({ Joi }) => + // TODO: make sure that trackingId gets required() when releasing a major version + Joi.object({ + trackingId: Joi.string().description( + `The property ID; the tracking code won't be generated without it` + ), + head: Joi.boolean() + .default(false) + .description( + `Defines where to place the tracking script - \`true\` in the head and \`false\` in the body` ), - head: Joi.boolean() - .default(false) - .description( - `Defines where to place the tracking script - \`true\` in the head and \`false\` in the body` - ), - anonymize: Joi.boolean().default(false), - respectDNT: Joi.boolean().default(false), - exclude: Joi.array() - .items(Joi.string()) - .default([]) - .description(`Avoids sending pageview hits from custom paths`), - pageTransitionDelay: Joi.number() - .default(0) - .description( - `Delays sending pageview hits on route update (in milliseconds)` - ), - optimizeId: Joi.string().description( - `Enables Google Optimize using your container Id` + anonymize: Joi.boolean().default(false), + respectDNT: Joi.boolean().default(false), + exclude: Joi.array() + .items(Joi.string()) + .default([]) + .description(`Avoids sending pageview hits from custom paths`), + pageTransitionDelay: Joi.number() + .default(0) + .description( + `Delays sending pageview hits on route update (in milliseconds)` ), - experimentId: Joi.string().description( - `Enables Google Optimize Experiment ID` - ), - variationId: Joi.string().description( - `Set Variation ID. 0 for original 1,2,3....` - ), - defer: Joi.boolean().description( - `Defers execution of google analytics script after page load` - ), - sampleRate: Joi.number(), - siteSpeedSampleRate: Joi.number(), - cookieDomain: Joi.string(), - }) -} else { - exports.onPreInit = ({ reporter }, { trackingId } = {}) => { - if (!trackingId) { - reporter.warn( - `The Google Analytics plugin requires a tracking ID. Did you mean to add it?` - ) - } - } -} + optimizeId: Joi.string().description( + `Enables Google Optimize using your container Id` + ), + experimentId: Joi.string().description( + `Enables Google Optimize Experiment ID` + ), + variationId: Joi.string().description( + `Set Variation ID. 0 for original 1,2,3....` + ), + defer: Joi.boolean().description( + `Defers execution of google analytics script after page load` + ), + sampleRate: Joi.number(), + siteSpeedSampleRate: Joi.number(), + cookieDomain: Joi.string(), + }) diff --git a/packages/gatsby-plugin-google-tagmanager/src/gatsby-node.js b/packages/gatsby-plugin-google-tagmanager/src/gatsby-node.js index ab8eff74b13a2..daa6532e1b3f6 100644 --- a/packages/gatsby-plugin-google-tagmanager/src/gatsby-node.js +++ b/packages/gatsby-plugin-google-tagmanager/src/gatsby-node.js @@ -12,33 +12,31 @@ exports.onPreInit = (args, options) => { } } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - id: Joi.string().description( - `Google Tag Manager ID that can be found in your Tag Manager dashboard.` +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + id: Joi.string().description( + `Google Tag Manager ID that can be found in your Tag Manager dashboard.` + ), + includeInDevelopment: Joi.boolean() + .default(false) + .description( + `Include Google Tag Manager when running in development mode.` ), - includeInDevelopment: Joi.boolean() - .default(false) - .description( - `Include Google Tag Manager when running in development mode.` - ), - defaultDataLayer: Joi.object() - .default(null) - .description( - `Data layer to be set before Google Tag Manager is loaded. Should be an object or a function.` - ), - gtmAuth: Joi.string().description( - `Google Tag Manager environment auth string.` + defaultDataLayer: Joi.object() + .default(null) + .description( + `Data layer to be set before Google Tag Manager is loaded. Should be an object or a function.` ), - gtmPreview: Joi.string().description( - `Google Tag Manager environment preview name.` + gtmAuth: Joi.string().description( + `Google Tag Manager environment auth string.` + ), + gtmPreview: Joi.string().description( + `Google Tag Manager environment preview name.` + ), + dataLayerName: Joi.string().description(`Data layer name.`), + routeChangeEventName: Joi.string() + .default(`gatsby-route-change`) + .description( + `Name of the event that is triggered on every Gatsby route change.` ), - dataLayerName: Joi.string().description(`Data layer name.`), - routeChangeEventName: Joi.string() - .default(`gatsby-route-change`) - .description( - `Name of the event that is triggered on every Gatsby route change.` - ), - }) -} + }) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index 9b07663fc3007..c079e42fa934c 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -58,45 +58,43 @@ async function checkCache(cache, icon, srcIcon, srcIconDigest, callback) { } } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - name: Joi.string(), - short_name: Joi.string(), - description: Joi.string(), - lang: Joi.string(), - localize: Joi.array().items( - Joi.object({ - start_url: Joi.string(), - name: Joi.string(), - short_name: Joi.string(), - description: Joi.string(), - lang: Joi.string(), - }) - ), - start_url: Joi.string(), - background_color: Joi.string(), - theme_color: Joi.string(), - display: Joi.string(), - legacy: Joi.boolean(), - include_favicon: Joi.boolean(), - icon: Joi.string(), - theme_color_in_head: Joi.boolean(), - crossOrigin: Joi.string().valid(`use-credentials`, `anonymous`), - cache_busting_mode: Joi.string().valid(`query`, `name`, `none`), - icons: Joi.array().items( - Joi.object({ - src: Joi.string(), - sizes: Joi.string(), - type: Joi.string(), - purpose: Joi.string(), - }) - ), - icon_options: Joi.object({ +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + name: Joi.string(), + short_name: Joi.string(), + description: Joi.string(), + lang: Joi.string(), + localize: Joi.array().items( + Joi.object({ + start_url: Joi.string(), + name: Joi.string(), + short_name: Joi.string(), + description: Joi.string(), + lang: Joi.string(), + }) + ), + start_url: Joi.string(), + background_color: Joi.string(), + theme_color: Joi.string(), + display: Joi.string(), + legacy: Joi.boolean(), + include_favicon: Joi.boolean(), + icon: Joi.string(), + theme_color_in_head: Joi.boolean(), + crossOrigin: Joi.string().valid(`use-credentials`, `anonymous`), + cache_busting_mode: Joi.string().valid(`query`, `name`, `none`), + icons: Joi.array().items( + Joi.object({ + src: Joi.string(), + sizes: Joi.string(), + type: Joi.string(), purpose: Joi.string(), - }), - }) -} + }) + ), + icon_options: Joi.object({ + purpose: Joi.string(), + }), + }) /** * Setup pluginOption defaults diff --git a/packages/gatsby-plugin-mdx/gatsby-node.js b/packages/gatsby-plugin-mdx/gatsby-node.js index 0b5b4bed74daf..96911aedff5aa 100644 --- a/packages/gatsby-plugin-mdx/gatsby-node.js +++ b/packages/gatsby-plugin-mdx/gatsby-node.js @@ -68,56 +68,54 @@ exports.onPostBootstrap = ({ cache }, pluginOptions) => { } } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = function ({ Joi }) { - return Joi.object({ - extensions: Joi.array() - .items(Joi.string()) - .default([".mdx"]) - .description( - `Configure the file extensions that gatsby-plugin-mdx will process` - ), - defaultLayouts: Joi.object({}) - .unknown(true) - .default({}) - .description(`Set the layout components for MDX source types`), - gatsbyRemarkPlugins: Joi.array() - .items( - Joi.string(), - Joi.object({ - resolve: Joi.string(), - options: Joi.object({}).unknown(true), - }) - ) - .default([]) - .description(`Use Gatsby-specific remark plugins`), - remarkPlugins: Joi.array() - .items( - Joi.function(), - Joi.object({}).unknown(true), - Joi.array().items(Joi.object({}).unknown(true), Joi.function()) - ) - .default([]) - .description(`Specify remark plugins`), - rehypePlugins: Joi.array() - .items( - Joi.function(), - Joi.object({}).unknown(true), - Joi.array().items(Joi.object({}).unknown(true), Joi.function()) - ) - .default([]) - .description(`Specify rehype plugins`), - plugins: Joi.array().items(Joi.string(), Joi.object({}).unknown(true)), - mediaTypes: Joi.array() - .items(Joi.string()) - .default(["text/markdown", "text/x-markdown"]) - .description(`Determine which media types are processed by MDX`), - shouldBlockNodeFromTransformation: Joi.function() - .maxArity(1) - .default(() => false) - .description( - `Disable MDX transformation for nodes where this function returns true` - ), - }) - } +exports.pluginOptionsSchema = function ({ Joi }) { + return Joi.object({ + extensions: Joi.array() + .items(Joi.string()) + .default([".mdx"]) + .description( + `Configure the file extensions that gatsby-plugin-mdx will process` + ), + defaultLayouts: Joi.object({}) + .unknown(true) + .default({}) + .description(`Set the layout components for MDX source types`), + gatsbyRemarkPlugins: Joi.array() + .items( + Joi.string(), + Joi.object({ + resolve: Joi.string(), + options: Joi.object({}).unknown(true), + }) + ) + .default([]) + .description(`Use Gatsby-specific remark plugins`), + remarkPlugins: Joi.array() + .items( + Joi.function(), + Joi.object({}).unknown(true), + Joi.array().items(Joi.object({}).unknown(true), Joi.function()) + ) + .default([]) + .description(`Specify remark plugins`), + rehypePlugins: Joi.array() + .items( + Joi.function(), + Joi.object({}).unknown(true), + Joi.array().items(Joi.object({}).unknown(true), Joi.function()) + ) + .default([]) + .description(`Specify rehype plugins`), + plugins: Joi.array().items(Joi.string(), Joi.object({}).unknown(true)), + mediaTypes: Joi.array() + .items(Joi.string()) + .default(["text/markdown", "text/x-markdown"]) + .description(`Determine which media types are processed by MDX`), + shouldBlockNodeFromTransformation: Joi.function() + .maxArity(1) + .default(() => () => false) + .description( + `Disable MDX transformation for nodes where this function returns true` + ), + }) } diff --git a/packages/gatsby-plugin-netlify/src/gatsby-node.js b/packages/gatsby-plugin-netlify/src/gatsby-node.js index a4ec931517676..1f8d1762e354b 100644 --- a/packages/gatsby-plugin-netlify/src/gatsby-node.js +++ b/packages/gatsby-plugin-netlify/src/gatsby-node.js @@ -85,6 +85,4 @@ const pluginOptionsSchema = function ({ Joi }) { }) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = pluginOptionsSchema -} +exports.pluginOptionsSchema = pluginOptionsSchema diff --git a/packages/gatsby-plugin-offline/src/gatsby-node.js b/packages/gatsby-plugin-offline/src/gatsby-node.js index ebe549777ec7f..3d87d1b57426c 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-node.js +++ b/packages/gatsby-plugin-offline/src/gatsby-node.js @@ -212,46 +212,44 @@ exports.onPostBuild = ( }) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - const MATH_ALL_KEYS = /^/ - exports.pluginOptionsSchema = function ({ Joi }) { - // These are the options of the v3: https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/#available-options - return Joi.object({ - precachePages: Joi.array() - .items(Joi.string()) - .description( - `An array of pages whose resources should be precached by the service worker, using an array of globs` - ), - appendScript: Joi.string().description( - `A file (path) to be appended at the end of the generated service worker` +const MATCH_ALL_KEYS = /^/ +exports.pluginOptionsSchema = function ({ Joi }) { + // These are the options of the v3: https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/#available-options + return Joi.object({ + precachePages: Joi.array() + .items(Joi.string()) + .description( + `An array of pages whose resources should be precached by the service worker, using an array of globs` ), - debug: Joi.boolean().description( - `Specifies whether Workbox should show debugging output in the browser console at runtime. When undefined, defaults to showing debug messages on localhost only` + appendScript: Joi.string().description( + `A file (path) to be appended at the end of the generated service worker` + ), + debug: Joi.boolean().description( + `Specifies whether Workbox should show debugging output in the browser console at runtime. When undefined, defaults to showing debug messages on localhost only` + ), + workboxConfig: Joi.object({ + importWorkboxFrom: Joi.string(), + globDirectory: Joi.string(), + globPatterns: Joi.array().items(Joi.string()), + modifyURLPrefix: Joi.object().pattern(MATCH_ALL_KEYS, Joi.string()), + cacheId: Joi.string(), + dontCacheBustURLsMatching: Joi.object().instance(RegExp), + runtimeCaching: Joi.array().items( + Joi.object({ + urlPattern: Joi.object().instance(RegExp), + handler: Joi.string().valid( + `StaleWhileRevalidate`, + `CacheFirst`, + `NetworkFirst`, + `NetworkOnly`, + `CacheOnly` + ), + }) ), - workboxConfig: Joi.object({ - importWorkboxFrom: Joi.string(), - globDirectory: Joi.string(), - globPatterns: Joi.array().items(Joi.string()), - modifyURLPrefix: Joi.object().pattern(MATH_ALL_KEYS, Joi.string()), - cacheId: Joi.string(), - dontCacheBustURLsMatching: Joi.object().instance(RegExp), - runtimeCaching: Joi.array().items( - Joi.object({ - urlPattern: Joi.object().instance(RegExp), - handler: Joi.string().valid( - `StaleWhileRevalidate`, - `CacheFirst`, - `NetworkFirst`, - `NetworkOnly`, - `CacheOnly` - ), - }) - ), - skipWaiting: Joi.boolean(), - clientsClaim: Joi.boolean(), - }) - .description(`Overrides workbox configuration. Helpful documentation: https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/#overriding-workbox-configuration - `), + skipWaiting: Joi.boolean(), + clientsClaim: Joi.boolean(), }) - } + .description(`Overrides workbox configuration. Helpful documentation: https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/#overriding-workbox-configuration + `), + }) } diff --git a/packages/gatsby-plugin-sass/src/gatsby-node.js b/packages/gatsby-plugin-sass/src/gatsby-node.js index 0907cd70c8723..450d9c7a98649 100644 --- a/packages/gatsby-plugin-sass/src/gatsby-node.js +++ b/packages/gatsby-plugin-sass/src/gatsby-node.js @@ -76,123 +76,117 @@ exports.onCreateWebpackConfig = ( }) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - const MATH_ALL_KEYS = /^/ - exports.pluginOptionsSchema = function ({ Joi }) { - return Joi.object({ - implementation: Joi.object({}) - .unknown(true) - .description( - `By default the node implementation of Sass (node-sass) is used. To use the implementation written in Dart (dart-sass), you can install sass instead of node-sass and pass it into the options as the implementation` - ), - postCssPlugins: Joi.array() - .items(Joi.object({}).unknown(true)) - .description(`An array of postCss plugins`), - sassRuleTest: Joi.object() - .instance(RegExp) - .description(`Override the file regex for SASS`), - sassRuleModulesTest: Joi.object() - .instance(RegExp) - .description(`Override the file regex for SASS`), - useResolveUrlLoader: Joi.alternatives().try( - Joi.boolean(), - Joi.object({}).unknown(true) - ) - .description(`This plugin resolves url() paths relative to the entry SCSS/Sass file not – as might be expected – the location relative to the declaration. Under the hood, it makes use of sass-loader and this is documented in the readme. +const MATCH_ALL_KEYS = /^/ +exports.pluginOptionsSchema = function ({ Joi }) { + return Joi.object({ + implementation: Joi.object({}) + .unknown(true) + .description( + `By default the node implementation of Sass (node-sass) is used. To use the implementation written in Dart (dart-sass), you can install sass instead of node-sass and pass it into the options as the implementation` + ), + postCssPlugins: Joi.array() + .items(Joi.object({}).unknown(true)) + .description(`An array of postCss plugins`), + sassRuleTest: Joi.object() + .instance(RegExp) + .description(`Override the file regex for SASS`), + sassRuleModulesTest: Joi.object() + .instance(RegExp) + .description(`Override the file regex for SASS`), + useResolveUrlLoader: Joi.alternatives().try( + Joi.boolean(), + Joi.object({}).unknown(true) + ) + .description(`This plugin resolves url() paths relative to the entry SCSS/Sass file not – as might be expected – the location relative to the declaration. Under the hood, it makes use of sass-loader and this is documented in the readme. Using resolve-url-loader provides a workaround, if you want to use relative url just install the plugin and then add it to your sass plugin options configuration.`), - file: Joi.string() - .allow(null) - .description(`Path to a file for LibSass to compile.`) - .default(null), - data: Joi.string() - .allow(null) - .description( - `A string to pass to LibSass to compile. It is recommended that you use includePaths in conjunction with this so that LibSass can find files when using the @import directive.` - ) - .default(null), - importer: Joi.function() - .maxArity(3) - .description( - `Handles when LibSass encounters the @import directive. A custom importer allows extension of the LibSass engine in both a synchronous and asynchronous manner. In both cases, the goal is to either return or call done() with an object literal. (https://github.com/sass/node-sass#importer--v200---experimental)` - ), - functions: Joi.object() - .pattern(MATH_ALL_KEYS, Joi.function().maxArity(2)) - .description( - `functions is an Object that holds a collection of custom functions that may be invoked by the sass files being compiled.` - ), - includePaths: Joi.array() - .items(Joi.string()) - .default([]) - .description( - `An array of paths that LibSass can look in to attempt to resolve your @import declarations. When using data, it is recommended that you use this.` - ), - indentedSyntax: Joi.boolean() - .default(false) - .description( - `true values enable Sass Indented Syntax for parsing the data string or file.` - ), - indentType: Joi.string() - .default(`space`) - .description( - `Used to determine whether to use space or tab character for indentation.` - ), - indentWidth: Joi.number() - .default(2) - .max(10) - .description( - `Used to determine the number of spaces or tabs to be used for indentation.` - ), - linefeed: Joi.string() - .default(`lf`) - .valid(`cr`, `crlf`, `lf`, `lfcr`) - .description( - `Used to determine whether to use cr, crlf, lf or lfcr sequence for line break.` - ), - omitSourceMapUrl: Joi.boolean() - .default(false) - .description( - `true values disable the inclusion of source map information in the output file.` - ), - outFile: Joi.string() - .allow(null) - .default(null) - .description( - `Specify the intended location of the output file. Strongly recommended when outputting source maps so that they can properly refer back to their intended files.` - ), - outputStyle: Joi.string() - .valid(`nested`, `expanded`, `compact`, `compressed`) - .default(`nested`) - .description(`Determines the output format of the final CSS style.`), - precision: Joi.number() - .default(5) - .description( - `Used to determine how many digits after the decimal will be allowed. For instance, if you had a decimal number of 1.23456789 and a precision of 5, the result will be 1.23457 in the final CSS.` - ), - sourceComments: Joi.boolean() - .default(false) - .description( - `true Enables the line number and file where a selector is defined to be emitted into the compiled CSS as a comment. Useful for debugging, especially when using imports and mixins.` - ), - sourceMap: Joi.alternatives() - .try(Joi.boolean(), Joi.string()) - .description( - `Enables source map generation during render and renderSync. + file: Joi.string() + .allow(null) + .description(`Path to a file for LibSass to compile.`) + .default(null), + data: Joi.string() + .allow(null) + .description( + `A string to pass to LibSass to compile. It is recommended that you use includePaths in conjunction with this so that LibSass can find files when using the @import directive.` + ) + .default(null), + importer: Joi.function() + .maxArity(3) + .description( + `Handles when LibSass encounters the @import directive. A custom importer allows extension of the LibSass engine in both a synchronous and asynchronous manner. In both cases, the goal is to either return or call done() with an object literal. (https://github.com/sass/node-sass#importer--v200---experimental)` + ), + functions: Joi.object() + .pattern(MATCH_ALL_KEYS, Joi.function().maxArity(2)) + .description( + `functions is an Object that holds a collection of custom functions that may be invoked by the sass files being compiled.` + ), + includePaths: Joi.array() + .items(Joi.string()) + .default([]) + .description( + `An array of paths that LibSass can look in to attempt to resolve your @import declarations. When using data, it is recommended that you use this.` + ), + indentedSyntax: Joi.boolean() + .default(false) + .description( + `true values enable Sass Indented Syntax for parsing the data string or file.` + ), + indentType: Joi.string() + .default(`space`) + .description( + `Used to determine whether to use space or tab character for indentation.` + ), + indentWidth: Joi.number() + .default(2) + .max(10) + .description( + `Used to determine the number of spaces or tabs to be used for indentation.` + ), + linefeed: Joi.string() + .default(`lf`) + .valid(`cr`, `crlf`, `lf`, `lfcr`) + .description( + `Used to determine whether to use cr, crlf, lf or lfcr sequence for line break.` + ), + omitSourceMapUrl: Joi.boolean() + .default(false) + .description( + `true values disable the inclusion of source map information in the output file.` + ), + outFile: Joi.string() + .allow(null) + .default(null) + .description( + `Specify the intended location of the output file. Strongly recommended when outputting source maps so that they can properly refer back to their intended files.` + ), + outputStyle: Joi.string() + .valid(`nested`, `expanded`, `compact`, `compressed`) + .default(`nested`) + .description(`Determines the output format of the final CSS style.`), + precision: Joi.number() + .default(5) + .description( + `Used to determine how many digits after the decimal will be allowed. For instance, if you had a decimal number of 1.23456789 and a precision of 5, the result will be 1.23457 in the final CSS.` + ), + sourceComments: Joi.boolean() + .default(false) + .description( + `true Enables the line number and file where a selector is defined to be emitted into the compiled CSS as a comment. Useful for debugging, especially when using imports and mixins.` + ), + sourceMap: Joi.alternatives().try(Joi.boolean(), Joi.string()).description( + `Enables source map generation during render and renderSync. When sourceMap === true, the value of outFile is used as the target output location for the source map with the suffix .map appended. If no outFile is set, sourceMap parameter is ignored. When typeof sourceMap === "string", the value of sourceMap will be used as the writing location for the file` - ), - sourceMapContents: Joi.boolean() - .default(false) - .description( - `true includes the contents in the source map information` - ), - sourceMapEmbed: Joi.boolean() - .default(false) - .description(`true embeds the source map as a data URI`), - sourceMapRoot: Joi.string().description( - `the value will be emitted as sourceRoot in the source map information` - ), - }) - } + ), + sourceMapContents: Joi.boolean() + .default(false) + .description(`true includes the contents in the source map information`), + sourceMapEmbed: Joi.boolean() + .default(false) + .description(`true embeds the source map as a data URI`), + sourceMapRoot: Joi.string().description( + `the value will be emitted as sourceRoot in the source map information` + ), + }) } diff --git a/packages/gatsby-plugin-sharp/src/gatsby-node.js b/packages/gatsby-plugin-sharp/src/gatsby-node.js index b2f9db348ef89..65a6ea7394bcc 100644 --- a/packages/gatsby-plugin-sharp/src/gatsby-node.js +++ b/packages/gatsby-plugin-sharp/src/gatsby-node.js @@ -62,22 +62,20 @@ exports.onPreBootstrap = ({ actions, emitter, reporter }, pluginOptions) => { // normalizedOptions = setPluginOptions(pluginOptions) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - base64Width: Joi.number() - .default(20) - .description(`The width of the generated base64 preview image`), - forceBase64Format: Joi.any() - .valid(`png`, `jpg`, `webp`) - .description( - `Force a different format for the generated base64 image. Defaults to the same format as the input image` - ), - useMozJpeg: Joi.boolean().description( - `The the mozJpeg library for encoding. Defaults to false, unless \`process.env.GATSBY_JPEG_ENCODER\` === \`MOZJPEG\`` +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + base64Width: Joi.number() + .default(20) + .description(`The width of the generated base64 preview image`), + forceBase64Format: Joi.any() + .valid(`png`, `jpg`, `webp`) + .description( + `Force a different format for the generated base64 image. Defaults to the same format as the input image` ), - stripMetadata: Joi.boolean().default(true), - defaultQuality: Joi.number().default(50), - failOnError: Joi.boolean().default(true), - }) -} + useMozJpeg: Joi.boolean().description( + `The the mozJpeg library for encoding. Defaults to false, unless \`process.env.GATSBY_JPEG_ENCODER\` === \`MOZJPEG\`` + ), + stripMetadata: Joi.boolean().default(true), + defaultQuality: Joi.number().default(50), + failOnError: Joi.boolean().default(true), + }) diff --git a/packages/gatsby-plugin-sitemap/src/gatsby-node.js b/packages/gatsby-plugin-sitemap/src/gatsby-node.js index ce418c1bc32da..055b90787c9ed 100644 --- a/packages/gatsby-plugin-sitemap/src/gatsby-node.js +++ b/packages/gatsby-plugin-sitemap/src/gatsby-node.js @@ -81,47 +81,45 @@ exports.onPostBuild = async ( }) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object() - .keys({ - output: Joi.string() - .default(`/sitemap.xml`) - .description(`The filepath and name`), - exclude: Joi.array() - .items(Joi.string()) - .description(`An array of paths to exclude from the sitemap`), - createLinkInHead: Joi.boolean() - .default(true) - .description( - `Whether to populate the \`\` of your site with a link to the sitemap.` - ), - serialize: Joi.function().description( - `Takes the output of the data query and lets you return an array of sitemap entries.` +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object() + .keys({ + output: Joi.string() + .default(`/sitemap.xml`) + .description(`The filepath and name`), + exclude: Joi.array() + .items(Joi.string()) + .description(`An array of paths to exclude from the sitemap`), + createLinkInHead: Joi.boolean() + .default(true) + .description( + `Whether to populate the \`\` of your site with a link to the sitemap.` ), - resolveSiteUrl: Joi.function().description( - `Takes the output of the data query and lets you return the site URL.` - ), - query: Joi.string().description( - stripIndent` + serialize: Joi.function().description( + `Takes the output of the data query and lets you return an array of sitemap entries.` + ), + resolveSiteUrl: Joi.function().description( + `Takes the output of the data query and lets you return the site URL.` + ), + query: Joi.string().description( + stripIndent` The query for the data you need to generate the sitemap. It’s required to get the site’s URL, if you are not fetching it from site.siteMetadata.siteUrl, you will need to set a custom resolveSiteUrl function. If you override the query, you probably will also need to set a serializer to return the correct data for the sitemap. Due to how this plugin was built it is currently expected/required to fetch the page paths from allSitePage, but you may use the allSitePage.edges.node or allSitePage.nodes query structure.` - ), - }) - .external(({ query }) => { - if (query) { - try { - parse(query) - } catch (e) { - throw new Error( - stripIndent` + ), + }) + .external(({ query }) => { + if (query) { + try { + parse(query) + } catch (e) { + throw new Error( + stripIndent` Invalid plugin options for "gatsby-plugin-sitemap": "query" must be a valid GraphQL query. Received the error "${e.message}"` - ) - } + ) } - }) -} + } + }) diff --git a/packages/gatsby-plugin-typescript/src/gatsby-node.js b/packages/gatsby-plugin-typescript/src/gatsby-node.js index f168344546ee4..3c60f83a40363 100644 --- a/packages/gatsby-plugin-typescript/src/gatsby-node.js +++ b/packages/gatsby-plugin-typescript/src/gatsby-node.js @@ -35,21 +35,17 @@ function onCreateWebpackConfig({ actions, loaders }) { }) } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - isTSX: Joi.boolean().description(`Enables jsx parsing.`).default(false), - jsxPragma: Joi.string() - .description( - `Replace the function used when compiling JSX expressions.` - ) - .default(`React`), - allExtensions: Joi.boolean() - .description(`Indicates that every file should be parsed as TS or TSX.`) - .default(false) - .when(`isTSX`, { is: true, then: Joi.valid(true) }), - }) -} +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + isTSX: Joi.boolean().description(`Enables jsx parsing.`).default(false), + jsxPragma: Joi.string() + .description(`Replace the function used when compiling JSX expressions.`) + .default(`React`), + allExtensions: Joi.boolean() + .description(`Indicates that every file should be parsed as TS or TSX.`) + .default(false) + .when(`isTSX`, { is: true, then: Joi.valid(true) }), + }) exports.resolvableExtensions = resolvableExtensions exports.onCreateBabelConfig = onCreateBabelConfig diff --git a/packages/gatsby-remark-autolink-headers/gatsby-node.js b/packages/gatsby-remark-autolink-headers/gatsby-node.js index 7880e62e02534..45c5a20d5c427 100644 --- a/packages/gatsby-remark-autolink-headers/gatsby-node.js +++ b/packages/gatsby-remark-autolink-headers/gatsby-node.js @@ -1,17 +1,15 @@ "use strict"; -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = function (_ref) { - var Joi = _ref.Joi; - return Joi.object({ - offsetY: Joi.number().integer().description("Signed integer. Vertical offset value in pixels.").default(0), - icon: Joi.alternatives().try(Joi.string(), Joi.boolean()).description("SVG shape inside a template literal or boolean 'false'. Set your own svg or disable icon.").default(true), - className: Joi.string().description("Set your own class for the anchor.").default("anchor"), - maintainCase: Joi.boolean().description("Maintains the case for markdown header."), - removeAccents: Joi.boolean().description("Remove accents from generated headings IDs."), - enableCustomId: Joi.boolean().description("Enable custom header IDs with `{#id}`"), - isIconAfterHeader: Joi.boolean().description("Enable the anchor icon to be inline at the end of the header text."), - elements: Joi.array().items(Joi.string()).description("Specify which type of header tags to link.") - }); - }; -} \ No newline at end of file +exports.pluginOptionsSchema = function (_ref) { + var Joi = _ref.Joi; + return Joi.object({ + offsetY: Joi.number().integer().description("Signed integer. Vertical offset value in pixels.").default(0), + icon: Joi.alternatives().try(Joi.string(), Joi.boolean()).description("SVG shape inside a template literal or boolean 'false'. Set your own svg or disable icon.").default(true), + className: Joi.string().description("Set your own class for the anchor.").default("anchor"), + maintainCase: Joi.boolean().description("Maintains the case for markdown header."), + removeAccents: Joi.boolean().description("Remove accents from generated headings IDs."), + enableCustomId: Joi.boolean().description("Enable custom header IDs with `{#id}`"), + isIconAfterHeader: Joi.boolean().description("Enable the anchor icon to be inline at the end of the header text."), + elements: Joi.array().items(Joi.string()).description("Specify which type of header tags to link.") + }); +}; \ No newline at end of file diff --git a/packages/gatsby-remark-autolink-headers/src/gatsby-node.js b/packages/gatsby-remark-autolink-headers/src/gatsby-node.js index 6cc4760a5d341..e3c63ea70e3c4 100644 --- a/packages/gatsby-remark-autolink-headers/src/gatsby-node.js +++ b/packages/gatsby-remark-autolink-headers/src/gatsby-node.js @@ -1,33 +1,31 @@ -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - offsetY: Joi.number() - .integer() - .description(`Signed integer. Vertical offset value in pixels.`) - .default(0), - icon: Joi.alternatives() - .try(Joi.string(), Joi.boolean()) - .description( - `SVG shape inside a template literal or boolean 'false'. Set your own svg or disable icon.` - ) - .default(true), - className: Joi.string() - .description(`Set your own class for the anchor.`) - .default(`anchor`), - maintainCase: Joi.boolean().description( - `Maintains the case for markdown header.` - ), - removeAccents: Joi.boolean().description( - `Remove accents from generated headings IDs.` - ), - enableCustomId: Joi.boolean().description( - `Enable custom header IDs with \`{#id}\`` - ), - isIconAfterHeader: Joi.boolean().description( - `Enable the anchor icon to be inline at the end of the header text.` - ), - elements: Joi.array() - .items(Joi.string()) - .description(`Specify which type of header tags to link.`), - }) -} +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + offsetY: Joi.number() + .integer() + .description(`Signed integer. Vertical offset value in pixels.`) + .default(0), + icon: Joi.alternatives() + .try(Joi.string(), Joi.boolean()) + .description( + `SVG shape inside a template literal or boolean 'false'. Set your own svg or disable icon.` + ) + .default(true), + className: Joi.string() + .description(`Set your own class for the anchor.`) + .default(`anchor`), + maintainCase: Joi.boolean().description( + `Maintains the case for markdown header.` + ), + removeAccents: Joi.boolean().description( + `Remove accents from generated headings IDs.` + ), + enableCustomId: Joi.boolean().description( + `Enable custom header IDs with \`{#id}\`` + ), + isIconAfterHeader: Joi.boolean().description( + `Enable the anchor icon to be inline at the end of the header text.` + ), + elements: Joi.array() + .items(Joi.string()) + .description(`Specify which type of header tags to link.`), + }) diff --git a/packages/gatsby-remark-images/src/gatsby-node.js b/packages/gatsby-remark-images/src/gatsby-node.js index 647ec4d4a8a76..fa437b99d2112 100644 --- a/packages/gatsby-remark-images/src/gatsby-node.js +++ b/packages/gatsby-remark-images/src/gatsby-node.js @@ -1,75 +1,73 @@ -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = function ({ Joi }) { - return Joi.object({ - maxWidth: Joi.number() - .default(650) - .description( - `The maxWidth in pixels of the div where the markdown will be displayed. This value is used when deciding what the width of the various responsive thumbnails should be.` - ), - linkImagesToOriginal: Joi.boolean() - .default(true) - .description( - `Add a link to each image to the original image. Sometimes people want to see a full-sized version of an image e.g. to see extra detail on a part of the image and this is a convenient and common pattern for enabling this. Set this option to false to disable this behavior.` - ), - showCaptions: Joi.boolean() - .default(false) - .description( - `Add a caption to each image with the contents of the title attribute, when this is not empty. If the title attribute is empty but the alt attribute is not, it will be used instead. Set this option to true to enable this behavior. You can also pass an array instead to specify which value should be used for the caption — for example, passing ['alt', 'title'] would use the alt attribute first, and then the title. When this is set to true it is the same as passing ['title', 'alt']. If you just want to use the title (and omit captions for images that have alt attributes but no title), pass ['title'].` - ), - markdownCaptions: Joi.boolean() - .default(false) - .description( - `Parse the caption as markdown instead of raw text. Ignored if showCaptions is false.` - ), - sizeByPixelDensity: Joi.boolean() - .default(false) - .description( - `[deprecated] Pixel density is only used in vector images, which Gatsby’s implementation of Sharp doesn’t support. This option is currently a no-op and will be removed in the next major version of Gatsby.` - ), - wrapperStyle: Joi.alternatives().try( - Joi.object({}).unknown(true), - Joi.function().maxArity(1) +exports.pluginOptionsSchema = function ({ Joi }) { + return Joi.object({ + maxWidth: Joi.number() + .default(650) + .description( + `The maxWidth in pixels of the div where the markdown will be displayed. This value is used when deciding what the width of the various responsive thumbnails should be.` ), - backgroundColor: Joi.string().default(`white`) - .description(`Set the background color of the image to match the background image of your design. + linkImagesToOriginal: Joi.boolean() + .default(true) + .description( + `Add a link to each image to the original image. Sometimes people want to see a full-sized version of an image e.g. to see extra detail on a part of the image and this is a convenient and common pattern for enabling this. Set this option to false to disable this behavior.` + ), + showCaptions: Joi.boolean() + .default(false) + .description( + `Add a caption to each image with the contents of the title attribute, when this is not empty. If the title attribute is empty but the alt attribute is not, it will be used instead. Set this option to true to enable this behavior. You can also pass an array instead to specify which value should be used for the caption — for example, passing ['alt', 'title'] would use the alt attribute first, and then the title. When this is set to true it is the same as passing ['title', 'alt']. If you just want to use the title (and omit captions for images that have alt attributes but no title), pass ['title'].` + ), + markdownCaptions: Joi.boolean() + .default(false) + .description( + `Parse the caption as markdown instead of raw text. Ignored if showCaptions is false.` + ), + sizeByPixelDensity: Joi.boolean() + .default(false) + .description( + `[deprecated] Pixel density is only used in vector images, which Gatsby’s implementation of Sharp doesn’t support. This option is currently a no-op and will be removed in the next major version of Gatsby.` + ), + wrapperStyle: Joi.alternatives().try( + Joi.object({}).unknown(true), + Joi.function().maxArity(1) + ), + backgroundColor: Joi.string().default(`white`) + .description(`Set the background color of the image to match the background image of your design. Note: - set this option to transparent for a transparent image background. - set this option to none to completely remove the image background.`), - quality: Joi.number() - .default(50) - .description(`The quality level of the generated files.`), - withWebp: Joi.boolean() - .default(false) - .description( - `Additionally generate WebP versions alongside your chosen file format. They are added as a srcset with the appropriate mimetype and will be loaded in browsers that support the format. Pass true for default support, or an object of options to specifically override those for the WebP files. For example, pass { quality: 80 } to have the WebP images be at quality level 80.` - ), - tracedSVG: Joi.boolean() - .default(false) - .description( - `Use traced SVGs for placeholder images instead of the “blur up” effect. Pass true for traced SVGs with the default settings (seen here), or an object of options to override the default. For example, pass { color: "#F00", turnPolicy: "TURNPOLICY_MAJORITY" } to change the color of the trace to red and the turn policy to TURNPOLICY_MAJORITY. See node-potrace parameter documentation for a full listing and explanation of the available options.` - ), - loading: Joi.string() - .valid(`lazy`, `eager`, `auto`) - .default(`lazy`) - .description( - `Set the browser’s native lazy loading attribute. One of lazy, eager or auto.` - ), - disableBgImageOnAlpha: Joi.boolean() - .default(false) - .description( - `Images containing transparent pixels around the edges results in images with blurry edges. As a result, these images do not work well with the “blur up” technique used in this plugin. As a workaround to disable background images with blurry edges on images containing transparent pixels, enable this setting.` - ), - disableBgImage: Joi.boolean() - .default(false) - .description( - `Remove background image and its’ inline style. Useful to prevent Stylesheet too long error on AMP.` - ), - srcSetBreakpoints: Joi.array() - .items(Joi.number()) - .description( - `By default gatsby generates 0.25x, 0.5x, 1x, 1.5x, 2x, and 3x sizes of thumbnails. If you want more control over which sizes are output you can use the srcSetBreakpoints parameter. For example, if you want images that are 200, 340, 520, and 890 wide you can add srcSetBreakpoints: [ 200, 340, 520, 890 ] as a parameter. You will also get maxWidth as a breakpoint (which is 650 by default), so you will actually get [ 200, 340, 520, 650, 890 ] as breakpoints.` - ), - }) - } + quality: Joi.number() + .default(50) + .description(`The quality level of the generated files.`), + withWebp: Joi.boolean() + .default(false) + .description( + `Additionally generate WebP versions alongside your chosen file format. They are added as a srcset with the appropriate mimetype and will be loaded in browsers that support the format. Pass true for default support, or an object of options to specifically override those for the WebP files. For example, pass { quality: 80 } to have the WebP images be at quality level 80.` + ), + tracedSVG: Joi.boolean() + .default(false) + .description( + `Use traced SVGs for placeholder images instead of the “blur up” effect. Pass true for traced SVGs with the default settings (seen here), or an object of options to override the default. For example, pass { color: "#F00", turnPolicy: "TURNPOLICY_MAJORITY" } to change the color of the trace to red and the turn policy to TURNPOLICY_MAJORITY. See node-potrace parameter documentation for a full listing and explanation of the available options.` + ), + loading: Joi.string() + .valid(`lazy`, `eager`, `auto`) + .default(`lazy`) + .description( + `Set the browser’s native lazy loading attribute. One of lazy, eager or auto.` + ), + disableBgImageOnAlpha: Joi.boolean() + .default(false) + .description( + `Images containing transparent pixels around the edges results in images with blurry edges. As a result, these images do not work well with the “blur up” technique used in this plugin. As a workaround to disable background images with blurry edges on images containing transparent pixels, enable this setting.` + ), + disableBgImage: Joi.boolean() + .default(false) + .description( + `Remove background image and its’ inline style. Useful to prevent Stylesheet too long error on AMP.` + ), + srcSetBreakpoints: Joi.array() + .items(Joi.number()) + .description( + `By default gatsby generates 0.25x, 0.5x, 1x, 1.5x, 2x, and 3x sizes of thumbnails. If you want more control over which sizes are output you can use the srcSetBreakpoints parameter. For example, if you want images that are 200, 340, 520, and 890 wide you can add srcSetBreakpoints: [ 200, 340, 520, 890 ] as a parameter. You will also get maxWidth as a breakpoint (which is 650 by default), so you will actually get [ 200, 340, 520, 650, 890 ] as breakpoints.` + ), + }) } diff --git a/packages/gatsby-source-contentful/src/gatsby-node.js b/packages/gatsby-source-contentful/src/gatsby-node.js index b3e2406170899..6121621fbcccf 100644 --- a/packages/gatsby-source-contentful/src/gatsby-node.js +++ b/packages/gatsby-source-contentful/src/gatsby-node.js @@ -5,15 +5,10 @@ const fs = require(`fs-extra`) const { createClient } = require(`contentful`) const v8 = require(`v8`) const fetch = require(`node-fetch`) -const { Joi } = require(`gatsby-plugin-utils`) const normalize = require(`./normalize`) const fetchData = require(`./fetch`) -const { - createPluginConfig, - maskText, - formatPluginOptionsForCLI, -} = require(`./plugin-options`) +const { createPluginConfig, maskText } = require(`./plugin-options`) const { downloadContentfulAssets } = require(`./download-contentful-assets`) const conflictFieldPrefix = `contentful` @@ -30,24 +25,6 @@ const restrictedNodeFields = [ exports.setFieldsOnGraphQLNodeType = require(`./extend-node-type`).extendNodeType -// TODO: Remove once pluginOptionsSchema is stable -exports.onPreInit = ({ reporter }, options) => { - const result = pluginOptionsSchema({ Joi }).validate(options, { - abortEarly: false, - externals: false, - }) - if (result.error) { - const errors = {} - result.error.details.forEach(detail => { - errors[detail.path[0]] = detail.message - }) - reporter.panic(`Problems with gatsby-source-contentful plugin options: -${formatPluginOptionsForCLI(options, errors)}`) - } - - options = result.value -} - const validateContentfulAccess = async pluginOptions => { if (process.env.NODE_ENV === `test`) return undefined @@ -164,9 +141,7 @@ List of locales and their codes can be found in Contentful app -> Settings -> Lo }) .external(validateContentfulAccess) -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = pluginOptionsSchema -} +exports.pluginOptionsSchema = pluginOptionsSchema /*** * Localization algorithm diff --git a/packages/gatsby-source-filesystem/src/gatsby-node.js b/packages/gatsby-source-filesystem/src/gatsby-node.js index cb00044756f96..642180713ab45 100644 --- a/packages/gatsby-source-filesystem/src/gatsby-node.js +++ b/packages/gatsby-source-filesystem/src/gatsby-node.js @@ -153,14 +153,12 @@ const createFSMachine = ( return interpret(fsMachine).start() } -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = ({ Joi }) => - Joi.object({ - name: Joi.string(), - path: Joi.string(), - ignore: Joi.array().items(Joi.string()), - }) -} +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + name: Joi.string(), + path: Joi.string(), + ignore: Joi.array().items(Joi.string()), + }) exports.sourceNodes = (api, pluginOptions) => { // Validate that the path exists. diff --git a/packages/gatsby-transformer-remark/src/gatsby-node.js b/packages/gatsby-transformer-remark/src/gatsby-node.js index 7330ef19ea39c..4160b7b833c38 100644 --- a/packages/gatsby-transformer-remark/src/gatsby-node.js +++ b/packages/gatsby-transformer-remark/src/gatsby-node.js @@ -7,32 +7,30 @@ exports.unstable_shouldOnCreateNode = unstable_shouldOnCreateNode exports.createSchemaCustomization = require(`./create-schema-customization`) exports.setFieldsOnGraphQLNodeType = require(`./extend-node-type`) -if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - exports.pluginOptionsSchema = function ({ Joi }) { - return Joi.object({ - commonmark: Joi.boolean().description( - `Activates CommonMark mode (default: true)` +exports.pluginOptionsSchema = function ({ Joi }) { + return Joi.object({ + commonmark: Joi.boolean().description( + `Activates CommonMark mode (default: true)` + ), + footnotes: Joi.boolean().description( + `Activates Footnotes mode (default: true)` + ), + pedantic: Joi.boolean().description( + `Activates pedantic mode (default: true)` + ), + gfm: Joi.boolean().description( + `Activates GitHub Flavored Markdown mode (default: true)` + ), + plugins: Joi.array() + .items( + Joi.string(), + Joi.object({ + resolve: Joi.string(), + options: Joi.object({}).unknown(true), + }) + ) + .description( + `A list of remark plugins. See also: https://github.com/gatsbyjs/gatsby/tree/master/examples/using-remark for examples` ), - footnotes: Joi.boolean().description( - `Activates Footnotes mode (default: true)` - ), - pedantic: Joi.boolean().description( - `Activates pedantic mode (default: true)` - ), - gfm: Joi.boolean().description( - `Activates GitHub Flavored Markdown mode (default: true)` - ), - plugins: Joi.array() - .items( - Joi.string(), - Joi.object({ - resolve: Joi.string(), - options: Joi.object({}).unknown(true), - }) - ) - .description( - `A list of remark plugins. See also: https://github.com/gatsbyjs/gatsby/tree/master/examples/using-remark for examples` - ), - }) - } + }) } diff --git a/packages/gatsby/scripts/__tests__/api.js b/packages/gatsby/scripts/__tests__/api.js index 105f3c9ab04f9..c815492d55ae0 100644 --- a/packages/gatsby/scripts/__tests__/api.js +++ b/packages/gatsby/scripts/__tests__/api.js @@ -54,7 +54,9 @@ it("generates the expected api output", done => { "onPreBuild": Object {}, "onPreExtractQueries": Object {}, "onPreInit": Object {}, - "pluginOptionsSchema": Object {}, + "pluginOptionsSchema": Object { + "version": "2.25.0", + }, "preprocessSource": Object {}, "resolvableExtensions": Object {}, "setFieldsOnGraphQLNodeType": Object {}, diff --git a/packages/gatsby/src/bootstrap/load-plugins/index.ts b/packages/gatsby/src/bootstrap/load-plugins/index.ts index 296f1e9eeb708..7bc1d0b4200ec 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/index.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/index.ts @@ -88,9 +88,7 @@ export async function loadPlugins( const config = normalizeConfig(rawConfig) // Show errors for invalid plugin configuration - if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) { - await validateConfigPluginsOptions(config, rootDir) - } + await validateConfigPluginsOptions(config, rootDir) const currentAPIs = getAPI({ browser: browserAPIs, diff --git a/packages/gatsby/src/utils/api-node-docs.ts b/packages/gatsby/src/utils/api-node-docs.ts index 8de789cb8db2a..e7b40e0198f83 100644 --- a/packages/gatsby/src/utils/api-node-docs.ts +++ b/packages/gatsby/src/utils/api-node-docs.ts @@ -443,6 +443,7 @@ export const onCreateDevServer = true /** * Run during the bootstrap phase. Plugins can use this to define a schema for their options using * [Joi](https://joi.dev) to validate the options users pass to the plugin. + * @gatsbyVersion 2.25.0 * @param {object} $0 * @param {Joi} $0.Joi The instance of [Joi](https://joi.dev) to define the schema * @example