From 5bd9fde2034ef39b18795aa943ba19d9470a7934 Mon Sep 17 00:00:00 2001 From: Tyler Barnes Date: Tue, 24 Aug 2021 17:11:48 -0700 Subject: [PATCH 1/4] Allow path to js file for beforeChangeNode option --- .../__tests__/__snapshots__/index.js.snap | 1 + .../gatsby-source-wordpress/gatsby-config.js | 1 + .../gatsby-source-wordpress/gatsby-node.js | 9 ++++ .../src/before-change-page.js | 5 ++ .../test-fns/data-resolution.js | 20 ++++++++ .../__tests__/plugin-options-schema.test.js | 3 ++ .../docs/plugin-options.md | 4 +- .../steps/declare-plugin-options-schema.js | 7 +-- .../process-and-validate-plugin-options.ts | 46 +++++++++++++++++++ 9 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 integration-tests/gatsby-source-wordpress/gatsby-node.js create mode 100644 integration-tests/gatsby-source-wordpress/src/before-change-page.js diff --git a/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap b/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap index 6e881dfbf2e90..ba823b56a6c95 100644 --- a/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap +++ b/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap @@ -4380,6 +4380,7 @@ Array [ "title", "uri", "nodeType", + "beforeChangeNodeTest", "parent", "children", "internal", diff --git a/integration-tests/gatsby-source-wordpress/gatsby-config.js b/integration-tests/gatsby-source-wordpress/gatsby-config.js index 4781add6f5153..10a1e94c6fb8d 100644 --- a/integration-tests/gatsby-source-wordpress/gatsby-config.js +++ b/integration-tests/gatsby-source-wordpress/gatsby-config.js @@ -40,6 +40,7 @@ const wpPluginOptions = !process.env.DEFAULT_PLUGIN_OPTIONS }, Page: { excludeFieldNames: [`enclosure`], + beforeChangeNode: `./src/before-change-page.js`, }, DatabaseIdentifier: { exclude: true, diff --git a/integration-tests/gatsby-source-wordpress/gatsby-node.js b/integration-tests/gatsby-source-wordpress/gatsby-node.js new file mode 100644 index 0000000000000..711a0ed59c074 --- /dev/null +++ b/integration-tests/gatsby-source-wordpress/gatsby-node.js @@ -0,0 +1,9 @@ +exports.createSchemaCustomization = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + type WpPage { + beforeChangeNodeTest: String + } + ` + createTypes(typeDefs) +} diff --git a/integration-tests/gatsby-source-wordpress/src/before-change-page.js b/integration-tests/gatsby-source-wordpress/src/before-change-page.js new file mode 100644 index 0000000000000..35ceb0c7ee919 --- /dev/null +++ b/integration-tests/gatsby-source-wordpress/src/before-change-page.js @@ -0,0 +1,5 @@ +module.exports = ({ remoteNode }) => { + remoteNode.beforeChangeNodeTest = `TEST-${remoteNode.id}` + + return remoteNode +} diff --git a/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js b/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js index 4fed3b045ec88..eee517e484204 100644 --- a/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js +++ b/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js @@ -219,6 +219,26 @@ describe(`data resolution`, () => { expect(result.data.testUser.name).toEqual(`admin`) }) + it(`resolves data added via a fn file in onBeforeChangeNode type option`, async () => { + const result = await fetchGraphql({ + url, + query: /* GraphQL */ ` + { + allWpPage { + nodes { + id + beforeChangeNodeTest + } + } + } + `, + }) + + result.data.allWpPage.nodes.forEach(node => { + expect(node.beforeChangeNodeTest).toBe(`TEST-${node.id}`) + }) + }) + it(`resolves root fields`, async () => { const result = await fetchGraphql({ url, diff --git a/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js b/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js index 6335466205e09..45c9d70c3c5ea 100644 --- a/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js +++ b/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js @@ -96,6 +96,9 @@ describe(`pluginOptionsSchema`, () => { MenuItem: { beforeChangeNode: null, }, + Page: { + beforeChangeNode: `./docs-generation.test.js`, + }, EnqueuedScript: { exclude: true, }, diff --git a/packages/gatsby-source-wordpress/docs/plugin-options.md b/packages/gatsby-source-wordpress/docs/plugin-options.md index 7515da603af36..7db58d2126ddd 100644 --- a/packages/gatsby-source-wordpress/docs/plugin-options.md +++ b/packages/gatsby-source-wordpress/docs/plugin-options.md @@ -1107,9 +1107,9 @@ Determines whether or not this type will be treated as an interface comprised en #### type.\_\_all.beforeChangeNode -A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it. +A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it. This option should be a path to a JS file where the default export is the beforeChangeNode function. The path can be relative to your gatsby-node.js or absolute. Currently you can inline a function by writing it out directly in this option but starting from Gatsby v4 only a path to a function file will work. -**Field type**: `Function` +**Field type**: `String` ### type.RootQuery diff --git a/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js b/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js index cb3b85ea2a1d5..4f3033d10c0a4 100644 --- a/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js +++ b/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js @@ -84,14 +84,11 @@ const pluginOptionsSchema = ({ Joi }) => { } `), }), - beforeChangeNode: Joi.any() + beforeChangeNode: Joi.string() .allow(null) .allow(false) - .meta({ - trueType: `function`, - }) .description( - `A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it.` + `A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it. This option should be a path to a JS file where the default export is the beforeChangeNode function. The path can be relative to your gatsby-node.js or absolute. Currently you can inline a function by writing it out directly in this option but starting from Gatsby v4 only a path to a function file will work.` ), }) diff --git a/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts b/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts index cc93a2b9242d5..cee808c8c239b 100644 --- a/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts +++ b/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts @@ -1,3 +1,4 @@ +import path from "path" import { formatLogMessage } from "~/utils/format-log-message" import isInteger from "lodash/isInteger" import { IPluginOptions } from "~/models/gatsby-api" @@ -46,6 +47,51 @@ const optionsProcessors: Array = [ delete userPluginOptions.schema.queryDepth + return userPluginOptions + }, + }, + { + name: `Require beforeChangeNode type setting functions by absolute or relative path`, + test: ({ userPluginOptions }: IProcessorOptions): boolean => + !!userPluginOptions?.type, + processor: ({ + helpers, + userPluginOptions, + }: IProcessorOptions): IPluginOptions => { + const gatsbyStore = helpers.store.getState() + const typeSettings = Object.entries(userPluginOptions.type) + + typeSettings.forEach(([typeName, settings]) => { + const beforeChangeNodePath = settings?.beforeChangeNode + + if (!beforeChangeNodePath || typeof beforeChangeNodePath !== `string`) { + return + } + + try { + const absoluteRequirePath: string | undefined = path.isAbsolute( + beforeChangeNodePath + ) + ? beforeChangeNodePath + : require.resolve( + path.join(gatsbyStore.program.directory, beforeChangeNodePath) + ) + + const beforeChangeNodeFn = require(absoluteRequirePath) + + if (beforeChangeNodeFn) { + userPluginOptions.type[typeName].beforeChangeNode = + beforeChangeNodeFn + } + } catch (e) { + helpers.reporter.panic( + formatLogMessage( + `beforeChangeNode type setting for ${typeName} threw error:\n${e.message}` + ) + ) + } + }) + return userPluginOptions }, }, From 64a9230e764334bd1c0ee6fe4d72a0d934915097 Mon Sep 17 00:00:00 2001 From: Tyler Barnes Date: Wed, 25 Aug 2021 09:13:41 -0700 Subject: [PATCH 2/4] ensure Joi doesn't fail the build for inline beforeChangeNode fn's --- .../__tests__/__snapshots__/index.js.snap | 1 + .../gatsby-source-wordpress/gatsby-config.js | 7 +++++++ .../gatsby-source-wordpress/gatsby-node.js | 3 +++ .../test-fns/data-resolution.js | 12 ++++++++++++ .../__tests__/plugin-options-schema.test.js | 5 +++++ .../gatsby-source-wordpress/docs/plugin-options.md | 2 +- .../generate-plugin-options-docs.js | 5 ++++- .../src/steps/declare-plugin-options-schema.js | 5 ++++- 8 files changed, 37 insertions(+), 3 deletions(-) diff --git a/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap b/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap index ba823b56a6c95..5d4a40b25cc17 100644 --- a/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap +++ b/integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap @@ -4597,6 +4597,7 @@ Array [ "toPing", "uri", "nodeType", + "beforeChangeNodeTest", "parent", "children", "internal", diff --git a/integration-tests/gatsby-source-wordpress/gatsby-config.js b/integration-tests/gatsby-source-wordpress/gatsby-config.js index 10a1e94c6fb8d..864b078667a3a 100644 --- a/integration-tests/gatsby-source-wordpress/gatsby-config.js +++ b/integration-tests/gatsby-source-wordpress/gatsby-config.js @@ -67,6 +67,13 @@ const wpPluginOptions = !process.env.DEFAULT_PLUGIN_OPTIONS 50 : // and we don't actually need more than 1000 in production 1000, + + beforeChangeNode: ({ remoteNode }) => { + console.log(`Hi from an inline fn!`) + remoteNode.beforeChangeNodeTest = `TEST-${remoteNode.id}` + + return remoteNode + }, }, // excluding this because it causes Gatsby to throw errors BlockEditorContentNode: { exclude: true }, diff --git a/integration-tests/gatsby-source-wordpress/gatsby-node.js b/integration-tests/gatsby-source-wordpress/gatsby-node.js index 711a0ed59c074..48f1091452b0c 100644 --- a/integration-tests/gatsby-source-wordpress/gatsby-node.js +++ b/integration-tests/gatsby-source-wordpress/gatsby-node.js @@ -4,6 +4,9 @@ exports.createSchemaCustomization = ({ actions }) => { type WpPage { beforeChangeNodeTest: String } + type WpPost { + beforeChangeNodeTest: String + } ` createTypes(typeDefs) } diff --git a/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js b/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js index eee517e484204..6696ca4e164ad 100644 --- a/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js +++ b/integration-tests/gatsby-source-wordpress/test-fns/data-resolution.js @@ -224,12 +224,21 @@ describe(`data resolution`, () => { url, query: /* GraphQL */ ` { + # fn as a file path allWpPage { nodes { id beforeChangeNodeTest } } + # inline fn in gatsby-config.js + # support for this will be removed in future versions + allWpPost { + nodes { + id + beforeChangeNodeTest + } + } } `, }) @@ -237,6 +246,9 @@ describe(`data resolution`, () => { result.data.allWpPage.nodes.forEach(node => { expect(node.beforeChangeNodeTest).toBe(`TEST-${node.id}`) }) + result.data.allWpPost.nodes.forEach(node => { + expect(node.beforeChangeNodeTest).toBe(`TEST-${node.id}`) + }) }) it(`resolves root fields`, async () => { diff --git a/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js b/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js index 45c9d70c3c5ea..aee923ce17d57 100644 --- a/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js +++ b/packages/gatsby-source-wordpress/__tests__/plugin-options-schema.test.js @@ -99,6 +99,11 @@ describe(`pluginOptionsSchema`, () => { Page: { beforeChangeNode: `./docs-generation.test.js`, }, + Post: { + beforeChangeNode: () => { + console.log(`Hi from an inline fn!`) + }, + }, EnqueuedScript: { exclude: true, }, diff --git a/packages/gatsby-source-wordpress/docs/plugin-options.md b/packages/gatsby-source-wordpress/docs/plugin-options.md index 7db58d2126ddd..88db9af9844f0 100644 --- a/packages/gatsby-source-wordpress/docs/plugin-options.md +++ b/packages/gatsby-source-wordpress/docs/plugin-options.md @@ -1109,7 +1109,7 @@ Determines whether or not this type will be treated as an interface comprised en A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it. This option should be a path to a JS file where the default export is the beforeChangeNode function. The path can be relative to your gatsby-node.js or absolute. Currently you can inline a function by writing it out directly in this option but starting from Gatsby v4 only a path to a function file will work. -**Field type**: `String` +**Field type**: `String | Function` ### type.RootQuery diff --git a/packages/gatsby-source-wordpress/generate-plugin-options-docs.js b/packages/gatsby-source-wordpress/generate-plugin-options-docs.js index 28212330df198..e26e06ad4a304 100644 --- a/packages/gatsby-source-wordpress/generate-plugin-options-docs.js +++ b/packages/gatsby-source-wordpress/generate-plugin-options-docs.js @@ -60,7 +60,10 @@ function joiKeysToMD({ (value.meta && value.meta.find(meta => `trueType` in meta)) || {} mdString += `\n\n` - mdString += `**Field type**: \`${_.startCase(trueType || value.type)}\`` + mdString += `**Field type**: \`${(trueType || value.type) + .split(`|`) + .map(typename => _.startCase(typename)) + .join(` | `)}\`` } if ( diff --git a/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js b/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js index 4f3033d10c0a4..8998221e3caa0 100644 --- a/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js +++ b/packages/gatsby-source-wordpress/src/steps/declare-plugin-options-schema.js @@ -84,9 +84,12 @@ const pluginOptionsSchema = ({ Joi }) => { } `), }), - beforeChangeNode: Joi.string() + beforeChangeNode: Joi.any() .allow(null) .allow(false) + .meta({ + trueType: `string|function`, + }) .description( `A function which is invoked before a node is created, updated, or deleted. This is a hook in point to modify the node or perform side-effects related to it. This option should be a path to a JS file where the default export is the beforeChangeNode function. The path can be relative to your gatsby-node.js or absolute. Currently you can inline a function by writing it out directly in this option but starting from Gatsby v4 only a path to a function file will work.` ), From 4607a8f65c1f546cacccccd87794b9be09c9ba59 Mon Sep 17 00:00:00 2001 From: Tyler Barnes Date: Wed, 25 Aug 2021 11:35:30 -0700 Subject: [PATCH 3/4] Create gatsby-version.ts --- packages/gatsby-source-wordpress/src/utils/gatsby-version.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/gatsby-source-wordpress/src/utils/gatsby-version.ts diff --git a/packages/gatsby-source-wordpress/src/utils/gatsby-version.ts b/packages/gatsby-source-wordpress/src/utils/gatsby-version.ts new file mode 100644 index 0000000000000..f0ac40c31eb78 --- /dev/null +++ b/packages/gatsby-source-wordpress/src/utils/gatsby-version.ts @@ -0,0 +1,5 @@ +import semver from "semver" +const gatsbyVersion = require(`gatsby/package.json`)?.version + +// gt = greater than +export const usingGatsbyV4OrGreater = semver.gt(gatsbyVersion, `4.0.0`) From ea6352e1811ad75745028252c3d62d1a18ec2b0e Mon Sep 17 00:00:00 2001 From: Tyler Barnes Date: Wed, 25 Aug 2021 11:43:20 -0700 Subject: [PATCH 4/4] Panic on Gatsby v4+ if beforeChangeNode is a function. --- .../src/steps/process-and-validate-plugin-options.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts b/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts index cee808c8c239b..1c028240009da 100644 --- a/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts +++ b/packages/gatsby-source-wordpress/src/steps/process-and-validate-plugin-options.ts @@ -3,6 +3,7 @@ import { formatLogMessage } from "~/utils/format-log-message" import isInteger from "lodash/isInteger" import { IPluginOptions } from "~/models/gatsby-api" import { GatsbyNodeApiHelpers } from "~/utils/gatsby-types" +import { usingGatsbyV4OrGreater } from "~/utils/gatsby-version" interface IProcessorOptions { userPluginOptions: IPluginOptions helpers: GatsbyNodeApiHelpers @@ -64,6 +65,15 @@ const optionsProcessors: Array = [ typeSettings.forEach(([typeName, settings]) => { const beforeChangeNodePath = settings?.beforeChangeNode + if ( + usingGatsbyV4OrGreater && + typeof beforeChangeNodePath === `function` + ) { + helpers.reporter.panic( + `Since Gatsby v4+ you cannot use the ${typeName}.beforeChangeNode option as a function. Please make the option a relative or absolute path to a JS file where the beforeChangeNode fn is the default export.` + ) + } + if (!beforeChangeNodePath || typeof beforeChangeNodePath !== `string`) { return }