diff --git a/packages/gatsby-transformer-json/README.md b/packages/gatsby-transformer-json/README.md index fe952bf245f09..f842ba02380e7 100644 --- a/packages/gatsby-transformer-json/README.md +++ b/packages/gatsby-transformer-json/README.md @@ -138,6 +138,88 @@ Which would return: } ``` +## Configuration options + +**`typeName`** [string|function][optional] + +The default naming convention documented above can be changed with +either a static string value (e.g. to be able to query all json with a +simple query): + +```javascript +module.exports = { + plugins: [ + { + resolve: `gatsby-transformer-json`, + options: { + typeName: `Json`, // a fixed string + }, + }, + ], +} +``` + +```graphql +{ + allJson { + edges { + node { + value + } + } + } +} +``` + +or a function that receives the following arguments: + +- `node`: the graphql node that is being processed, e.g. a File node with + json content +- `object`: a single object (either an item from an array or the whole json content) +- `isArray`: boolean, true if `object` is part of an array + +```json +[ + { + "level": "info", + "message": "hurray", + }, + { + "level": "info", + "message": "it works", + }, + { + "level": "warning", + "message": "look out", + } +] +``` + +```javascript +module.exports = { + plugins: [ + { + resolve: `gatsby-transformer-json`, + options: { + typeName: (({ node, object, isArray }) => object.level), + }, + }, + ], +} +``` + +```graphql +{ + allInfo { + edges { + node { + message + } + } + } +} +``` + ## Examples The [gatsbygram example site](https://github.com/gatsbyjs/gatsby/blob/master/examples/gatsbygram/gatsby-node.js) uses this plugin. diff --git a/packages/gatsby-transformer-json/src/__tests__/gatsby-node.js b/packages/gatsby-transformer-json/src/__tests__/gatsby-node.js index e593975f2b84b..6d4ba82dd16b9 100644 --- a/packages/gatsby-transformer-json/src/__tests__/gatsby-node.js +++ b/packages/gatsby-transformer-json/src/__tests__/gatsby-node.js @@ -67,4 +67,101 @@ describe(`Process JSON nodes correctly`, () => { expect(createParentChildLink).toHaveBeenCalledTimes(1) }) }) + + it(`correctly sets node type for array of objects`, async () => { + ;[ + { + typeName: null, + expectedNodeTypes: [`NodeNameJson`, `NodeNameJson`], + }, + { + typeName: `fixed`, + expectedNodeTypes: [`fixed`, `fixed`], + }, + { + typeName: (({ node, object }) => object.funny), + expectedNodeTypes: [`yup`, `nope`], + }, + ].forEach(async ({ typeName, expectedNodeTypes: [expectedOne, expectedTwo] }) => { + const data = [ + { id: `foo`, blue: true, funny: `yup` }, + { blue: false, funny: `nope` }, + ] + node.content = JSON.stringify(data) + + const createNode = jest.fn() + const createParentChildLink = jest.fn() + const actions = { createNode, createParentChildLink } + const createNodeId = jest.fn() + createNodeId.mockReturnValue(`uuid-from-gatsby`) + + await onCreateNode({ + node, + loadNodeContent, + actions, + createNodeId, + }, { + typeName, + }).then(() => { + expect(createNode).toBeCalledWith( + expect.objectContaining({ + internal: expect.objectContaining({ + type: expectedOne, + }), + }) + ) + expect(createNode).toBeCalledWith( + expect.objectContaining({ + internal: expect.objectContaining({ + type: expectedTwo, + }), + }) + ) + }) + }) + }) + + it(`correctly sets node type for single object`, async () => { + ;[ + { + typeName: null, + expectedNodeType: `FooJson`, + }, + { + typeName: `fixed`, + expectedNodeType: `fixed`, + }, + { + typeName: (({ node, object }) => object.funny), + expectedNodeType: `yup`, + }, + ].forEach(async ({ typeName, expectedNodeType }) => { + const data = { id: `foo`, blue: true, funny: `yup` } + node.content = JSON.stringify(data) + node.dir = `/tmp/foo/` + + const createNode = jest.fn() + const createParentChildLink = jest.fn() + const actions = { createNode, createParentChildLink } + const createNodeId = jest.fn() + createNodeId.mockReturnValue(`uuid-from-gatsby`) + + await onCreateNode({ + node, + loadNodeContent, + actions, + createNodeId, + }, { + typeName, + }).then(() => { + expect(createNode).toBeCalledWith( + expect.objectContaining({ + internal: expect.objectContaining({ + type: expectedNodeType, + }), + }) + ) + }) + }) + }) }) diff --git a/packages/gatsby-transformer-json/src/gatsby-node.js b/packages/gatsby-transformer-json/src/gatsby-node.js index a5be138b70bf8..d7d55243d53f3 100644 --- a/packages/gatsby-transformer-json/src/gatsby-node.js +++ b/packages/gatsby-transformer-json/src/gatsby-node.js @@ -2,7 +2,19 @@ const _ = require(`lodash`) const crypto = require(`crypto`) const path = require(`path`) -async function onCreateNode({ node, actions, loadNodeContent, createNodeId }) { +async function onCreateNode({ node, actions, loadNodeContent, createNodeId }, pluginOptions) { + function getType({ node, object, isArray }) { + if (pluginOptions && _.isFunction(pluginOptions.typeName)) { + return pluginOptions.typeName({ node, object, isArray }) + } else if (pluginOptions && _.isString(pluginOptions.typeName)) { + return pluginOptions.typeName + } else if (isArray) { + return _.upperFirst(_.camelCase(`${node.name} Json`)) + } else { + return _.upperFirst(_.camelCase(`${path.basename(node.dir)} Json`)) + } + } + function transformObject(obj, id, type) { const objStr = JSON.stringify(obj) const contentDigest = crypto @@ -38,14 +50,14 @@ async function onCreateNode({ node, actions, loadNodeContent, createNodeId }) { transformObject( obj, obj.id ? obj.id : createNodeId(`${node.id} [${i}] >>> JSON`), - _.upperFirst(_.camelCase(`${node.name} Json`)) + getType({ node, object: obj, isArray: true }) ) }) } else if (_.isPlainObject(parsedContent)) { transformObject( parsedContent, parsedContent.id ? parsedContent.id : createNodeId(`${node.id} >>> JSON`), - _.upperFirst(_.camelCase(`${path.basename(node.dir)} Json`)) + getType({ node, object: parsedContent, isArray: false }) ) } }