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..5d4a40b25cc17 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", @@ -4596,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 4781add6f5153..864b078667a3a 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, @@ -66,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 new file mode 100644 index 0000000000000..48f1091452b0c --- /dev/null +++ b/integration-tests/gatsby-source-wordpress/gatsby-node.js @@ -0,0 +1,12 @@ +exports.createSchemaCustomization = ({ actions }) => { + const { createTypes } = actions + const typeDefs = ` + type WpPage { + beforeChangeNodeTest: String + } + type WpPost { + 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..6696ca4e164ad 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,38 @@ 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 */ ` + { + # 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 + } + } + } + `, + }) + + 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 () => { 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..aee923ce17d57 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,14 @@ describe(`pluginOptionsSchema`, () => { MenuItem: { beforeChangeNode: null, }, + 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 7515da603af36..88db9af9844f0 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 | 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 cb3b85ea2a1d5..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 @@ -88,10 +88,10 @@ const pluginOptionsSchema = ({ Joi }) => { .allow(null) .allow(false) .meta({ - trueType: `function`, + 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.` + `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..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 @@ -1,7 +1,9 @@ +import path from "path" 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 @@ -46,6 +48,60 @@ 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 ( + 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 + } + + 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 }, }, 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`)