From c42671b269b75cdaac261b1d239e9fed2c41420f Mon Sep 17 00:00:00 2001 From: Alexandre Kirszenberg Date: Tue, 21 Aug 2018 10:33:19 +0200 Subject: [PATCH 1/3] Allow for multiple different remark sources --- packages/gatsby-transformer-remark/README.md | 5 + .../src/__tests__/extend-node.js | 51 +++++- .../src/__tests__/gatsby-node.js | 44 ++++++ .../src/extend-node-type.js | 146 +++++++++--------- .../src/on-node-create.js | 18 ++- 5 files changed, 184 insertions(+), 80 deletions(-) diff --git a/packages/gatsby-transformer-remark/README.md b/packages/gatsby-transformer-remark/README.md index ffea63c965858..6bfff15147284 100644 --- a/packages/gatsby-transformer-remark/README.md +++ b/packages/gatsby-transformer-remark/README.md @@ -14,6 +14,11 @@ plugins: [ { resolve: `gatsby-transformer-remark`, options: { + + // Defaults to `() => true` + filter: node => node.sourceInstanceName === `blog`, + // Defaults to `MarkdownRemark` + type: `BlogPost`, // CommonMark mode (default: true) commonmark: true, // Footnotes mode (default: true) diff --git a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js index d46f821579d44..1317c59da26b4 100644 --- a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js +++ b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js @@ -4,7 +4,8 @@ const { GraphQLList, GraphQLSchema, } = require(`gatsby/graphql`) -const { onCreateNode } = require(`../gatsby-node`) +const { Promise } = require(`bluebird`) +const { onCreateNode, setFieldsOnGraphQLNodeType } = require(`../gatsby-node`) const { inferObjectStructureFromNodes, } = require(`../../../gatsby/src/schema/infer-graphql-type`) @@ -14,7 +15,7 @@ const extendNodeType = require(`../extend-node-type`) async function queryResult( nodes, fragment, - { types = [] } = {}, + { typeName = `MarkdownRemark`, types = [] } = {}, { additionalParameters = {}, pluginOptions = {} } ) { const inferredFields = inferObjectStructureFromNodes({ @@ -51,7 +52,7 @@ async function queryResult( name: `LISTNODE`, type: new GraphQLList( new GraphQLObjectType({ - name: `MarkdownRemark`, + name: typeName, fields: markdownRemarkFields, }) ), @@ -559,3 +560,47 @@ This is [a reference] { additionalParameters: { pathPrefix: `/prefix` } } ) }) + +describe(`Adding fields to the GraphQL schema`, () => { + it(`only adds fields when the GraphQL type matches the provided type`, async () => { + const getNode = jest.fn() + const getNodesByType = jest.fn() + + expect( + setFieldsOnGraphQLNodeType({ + type: { name: `MarkdownRemark` }, + getNode, + getNodesByType, + }) + ).toBeInstanceOf(Promise) + + expect( + setFieldsOnGraphQLNodeType( + { type: { name: `MarkdownRemark` }, getNode, getNodesByType }, + { type: `MarkdownRemark` } + ) + ).toBeInstanceOf(Promise) + + expect( + setFieldsOnGraphQLNodeType( + { type: { name: `MarkdownRemark` }, getNode, getNodesByType }, + { type: `GatsbyTestType` } + ) + ).toEqual({}) + + expect( + setFieldsOnGraphQLNodeType( + { type: { name: `GatsbyTestType` }, getNode, getNodesByType }, + { type: `GatsbyTestType` } + ) + ).toBeInstanceOf(Promise) + + expect( + setFieldsOnGraphQLNodeType({ + type: { name: `GatsbyTestType` }, + getNode, + getNodesByType, + }) + ).toEqual({}) + }) +}) diff --git a/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js b/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js index 70709a35f3c90..58fa687101294 100644 --- a/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js +++ b/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js @@ -101,6 +101,50 @@ Sed bibendum sem iaculis, pellentesque leo sed, imperdiet ante. Sed consequat ma expect(createParentChildLink).toHaveBeenCalledTimes(1) }) }) + + it(`Filters nodes with the given filter function, if provided`, async () => { + const content = `` + + node.content = content + node.sourceInstanceName = `gatsby-test-source` + + 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, + }, + { + filter: node => + node.sourceInstanceName === `gatsby-other-test-source`, + } + ).then(() => { + expect(createNode).toHaveBeenCalledTimes(0) + expect(createParentChildLink).toHaveBeenCalledTimes(0) + }) + + await onCreateNode( + { + node, + loadNodeContent, + actions, + createNodeId, + }, + { + filter: node => node.sourceInstanceName === `gatsby-test-source`, + } + ).then(() => { + expect(createNode).toHaveBeenCalledTimes(1) + expect(createParentChildLink).toHaveBeenCalledTimes(1) + }) + }) }) describe(`process graphql correctly`, () => { diff --git a/packages/gatsby-transformer-remark/src/extend-node-type.js b/packages/gatsby-transformer-remark/src/extend-node-type.js index 1d478be20503e..fdcf9cf823804 100644 --- a/packages/gatsby-transformer-remark/src/extend-node-type.js +++ b/packages/gatsby-transformer-remark/src/extend-node-type.js @@ -68,6 +68,59 @@ const safeGetCache = ({ getCache, cache }) => id => { return getCache(id) } +const HeadingType = new GraphQLObjectType({ + name: `MarkdownHeading`, + fields: { + value: { + type: GraphQLString, + resolve(heading) { + return heading.value + }, + }, + depth: { + type: GraphQLInt, + resolve(heading) { + return heading.depth + }, + }, + }, +}) + +const HeadingLevels = new GraphQLEnumType({ + name: `HeadingLevels`, + values: { + h1: { value: 1 }, + h2: { value: 2 }, + h3: { value: 3 }, + h4: { value: 4 }, + h5: { value: 5 }, + h6: { value: 6 }, + }, +}) + +const ExcerptFormats = new GraphQLEnumType({ + name: `ExcerptFormats`, + values: { + PLAIN: { value: `plain` }, + HTML: { value: `html` }, + }, +}) + +const wordCountType = new GraphQLObjectType({ + name: `wordCount`, + fields: { + paragraphs: { + type: GraphQLInt, + }, + sentences: { + type: GraphQLInt, + }, + words: { + type: GraphQLInt, + }, + }, +}) + /** * Map that keeps track of generation of AST to not generate it multiple * times in parallel. @@ -87,29 +140,31 @@ module.exports = ( reporter, ...rest }, - pluginOptions + { + type: typeName = `MarkdownRemark`, + plugins = [], + blocks, + commonmark = true, + footnotes = true, + gfm = true, + pedantic = true, + tableOfContents = { + heading: null, + maxDepth: 6, + }, + ...grayMatterOptions + } = {} ) => { - if (type.name !== `MarkdownRemark`) { + if (type.name !== typeName) { return {} } - pluginsCacheStr = pluginOptions.plugins.map(p => p.name).join(``) + pluginsCacheStr = plugins.map(p => p.name).join(``) pathPrefixCacheStr = pathPrefix || `` const getCache = safeGetCache({ cache, getCache: possibleGetCache }) return new Promise((resolve, reject) => { // Setup Remark. - const { - blocks, - commonmark = true, - footnotes = true, - gfm = true, - pedantic = true, - tableOfContents = { - heading: null, - maxDepth: 6, - }, - } = pluginOptions const tocOptions = tableOfContents const remarkOptions = { commonmark, @@ -122,7 +177,7 @@ module.exports = ( } let remark = new Remark().data(`settings`, remarkOptions) - for (let plugin of pluginOptions.plugins) { + for (let plugin of plugins) { const requiredPlugin = require(plugin.resolve) if (_.isFunction(requiredPlugin.setParserPlugins)) { for (let parserPlugin of requiredPlugin.setParserPlugins( @@ -167,7 +222,7 @@ module.exports = ( fileNodes = getNodesByType(`File`) } // Use Bluebird's Promise function "each" to run remark plugins serially. - await Promise.each(pluginOptions.plugins, plugin => { + await Promise.each(plugins, plugin => { const requiredPlugin = require(plugin.resolve) if (_.isFunction(requiredPlugin.mutateSource)) { return requiredPlugin.mutateSource( @@ -235,7 +290,7 @@ module.exports = ( fileNodes = getNodesByType(`File`) } // Use Bluebird's Promise function "each" to run remark plugins serially. - await Promise.each(pluginOptions.plugins, plugin => { + await Promise.each(plugins, plugin => { const requiredPlugin = require(plugin.resolve) if (_.isFunction(requiredPlugin)) { return requiredPlugin( @@ -362,44 +417,6 @@ module.exports = ( } } - const HeadingType = new GraphQLObjectType({ - name: `MarkdownHeading`, - fields: { - value: { - type: GraphQLString, - resolve(heading) { - return heading.value - }, - }, - depth: { - type: GraphQLInt, - resolve(heading) { - return heading.depth - }, - }, - }, - }) - - const HeadingLevels = new GraphQLEnumType({ - name: `HeadingLevels`, - values: { - h1: { value: 1 }, - h2: { value: 2 }, - h3: { value: 3 }, - h4: { value: 4 }, - h5: { value: 5 }, - h6: { value: 6 }, - }, - }) - - const ExcerptFormats = new GraphQLEnumType({ - name: `ExcerptFormats`, - values: { - PLAIN: { value: `plain` }, - HTML: { value: `html` }, - }, - }) - return resolve({ html: { type: GraphQLString, @@ -434,13 +451,13 @@ module.exports = ( }, async resolve(markdownNode, { format, pruneLength, truncate }) { if (format === `html`) { - if (pluginOptions.excerpt_separator) { + if (grayMatterOptions.excerpt_separator) { const fullAST = await getHTMLAst(markdownNode) const excerptAST = cloneTreeUntil( fullAST, ({ nextNode }) => nextNode.type === `raw` && - nextNode.value === pluginOptions.excerpt_separator + nextNode.value === grayMatterOptions.excerpt_separator ) return hastToHTML(excerptAST, { allowDangerousHTML: true, @@ -558,20 +575,7 @@ module.exports = ( }, // TODO add support for non-latin languages https://github.com/wooorm/remark/issues/251#issuecomment-296731071 wordCount: { - type: new GraphQLObjectType({ - name: `wordCount`, - fields: { - paragraphs: { - type: GraphQLInt, - }, - sentences: { - type: GraphQLInt, - }, - words: { - type: GraphQLInt, - }, - }, - }), + type: wordCountType, resolve(markdownNode) { let counts = {} diff --git a/packages/gatsby-transformer-remark/src/on-node-create.js b/packages/gatsby-transformer-remark/src/on-node-create.js index 80bbb2575b814..0a02574fe248f 100644 --- a/packages/gatsby-transformer-remark/src/on-node-create.js +++ b/packages/gatsby-transformer-remark/src/on-node-create.js @@ -3,14 +3,20 @@ const crypto = require(`crypto`) module.exports = async function onCreateNode( { node, loadNodeContent, actions, createNodeId, reporter }, - pluginOptions + { + plugins = null, + filter = () => true, + type = `MarkdownRemark`, + ...grayMatterOptions + } = {} ) { const { createNode, createParentChildLink } = actions // We only care about markdown content. if ( - node.internal.mediaType !== `text/markdown` && - node.internal.mediaType !== `text/x-markdown` + (node.internal.mediaType !== `text/markdown` && + node.internal.mediaType !== `text/x-markdown`) || + !filter(node) ) { return } @@ -18,15 +24,15 @@ module.exports = async function onCreateNode( const content = await loadNodeContent(node) try { - const data = grayMatter(content, pluginOptions) + const data = grayMatter(content, grayMatterOptions) const markdownNode = { - id: createNodeId(`${node.id} >>> MarkdownRemark`), + id: createNodeId(`${node.id} >>> ${type}`), children: [], parent: node.id, internal: { content: data.content, - type: `MarkdownRemark`, + type, }, } From f68f05cb42a1ab6fd809532fef48776085fae3e4 Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Wed, 13 Mar 2019 15:11:21 +0100 Subject: [PATCH 2/3] update tests --- .../gatsby-transformer-remark/package.json | 1 - .../src/__tests__/extend-node.js | 1 - .../src/__tests__/gatsby-node.js | 1 - .../src/extend-node-type.js | 22 ++++++++++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/gatsby-transformer-remark/package.json b/packages/gatsby-transformer-remark/package.json index 7255f72d59012..5262b6d12b1ab 100644 --- a/packages/gatsby-transformer-remark/package.json +++ b/packages/gatsby-transformer-remark/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@babel/runtime": "^7.0.0", - "bluebird": "^3.5.0", "gray-matter": "^4.0.0", "hast-util-raw": "^4.0.0", "hast-util-to-html": "^4.0.0", diff --git a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js index 1317c59da26b4..fa9705f5a084b 100644 --- a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js +++ b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js @@ -4,7 +4,6 @@ const { GraphQLList, GraphQLSchema, } = require(`gatsby/graphql`) -const { Promise } = require(`bluebird`) const { onCreateNode, setFieldsOnGraphQLNodeType } = require(`../gatsby-node`) const { inferObjectStructureFromNodes, diff --git a/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js b/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js index 58fa687101294..c6d061741917e 100644 --- a/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js +++ b/packages/gatsby-transformer-remark/src/__tests__/gatsby-node.js @@ -1,4 +1,3 @@ -const Promise = require(`bluebird`) const _ = require(`lodash`) const { onCreateNode } = require(`../gatsby-node`) diff --git a/packages/gatsby-transformer-remark/src/extend-node-type.js b/packages/gatsby-transformer-remark/src/extend-node-type.js index fdcf9cf823804..6b4f2e1c39a2e 100644 --- a/packages/gatsby-transformer-remark/src/extend-node-type.js +++ b/packages/gatsby-transformer-remark/src/extend-node-type.js @@ -15,7 +15,6 @@ const visit = require(`unist-util-visit`) const toHAST = require(`mdast-util-to-hast`) const hastToHTML = require(`hast-util-to-html`) const mdastToToc = require(`mdast-util-toc`) -const Promise = require(`bluebird`) const unified = require(`unified`) const parse = require(`remark-parse`) const stringify = require(`remark-stringify`) @@ -68,6 +67,19 @@ const safeGetCache = ({ getCache, cache }) => id => { return getCache(id) } +/** + * @template T + * @param {Array} input + * @param {(input: T) => Promise} iterator + * @return Promise + */ +const eachPromise = (input, iterator) => + input.reduce( + (accumulatorPromise, nextValue) => + accumulatorPromise.then(() => void iterator(nextValue)), + Promise.resolve() + ) + const HeadingType = new GraphQLObjectType({ name: `MarkdownHeading`, fields: { @@ -221,8 +233,8 @@ module.exports = ( if (process.env.NODE_ENV !== `production` || !fileNodes) { fileNodes = getNodesByType(`File`) } - // Use Bluebird's Promise function "each" to run remark plugins serially. - await Promise.each(plugins, plugin => { + + await eachPromise(plugins, plugin => { const requiredPlugin = require(plugin.resolve) if (_.isFunction(requiredPlugin.mutateSource)) { return requiredPlugin.mutateSource( @@ -289,8 +301,8 @@ module.exports = ( if (process.env.NODE_ENV !== `production` || !fileNodes) { fileNodes = getNodesByType(`File`) } - // Use Bluebird's Promise function "each" to run remark plugins serially. - await Promise.each(plugins, plugin => { + + await eachPromise(plugins, plugin => { const requiredPlugin = require(plugin.resolve) if (_.isFunction(requiredPlugin)) { return requiredPlugin( From 9c14034961811f8a07a8b44988d690424ccff477 Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Wed, 13 Mar 2019 15:28:12 +0100 Subject: [PATCH 3/3] capitalize wordcount type --- packages/gatsby-transformer-remark/src/extend-node-type.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gatsby-transformer-remark/src/extend-node-type.js b/packages/gatsby-transformer-remark/src/extend-node-type.js index 3f5bfdc046f04..e314ce2a7c488 100644 --- a/packages/gatsby-transformer-remark/src/extend-node-type.js +++ b/packages/gatsby-transformer-remark/src/extend-node-type.js @@ -119,7 +119,7 @@ const ExcerptFormats = new GraphQLEnumType({ }, }) -const wordCountType = new GraphQLObjectType({ +const WordCountType = new GraphQLObjectType({ name: `wordCount`, fields: { paragraphs: { @@ -631,7 +631,7 @@ module.exports = ( }, // TODO add support for non-latin languages https://github.com/wooorm/remark/issues/251#issuecomment-296731071 wordCount: { - type: wordCountType, + type: WordCountType, resolve(markdownNode) { let counts = {}