diff --git a/packages/gatsby-transformer-remark/README.md b/packages/gatsby-transformer-remark/README.md
index 423329b668d06..ffea63c965858 100644
--- a/packages/gatsby-transformer-remark/README.md
+++ b/packages/gatsby-transformer-remark/README.md
@@ -93,7 +93,28 @@ Using the following GraphQL query you'll be able to get the table of contents
edges {
node {
html
- tableOfContents(pathToSlugField: "frontmatter.path")
+ tableOfContents
+ }
+ }
+ }
+}
+```
+
+### Configuring the tableOfContents
+
+By default the tableOfContents is using the field `slug` to generate URLs. You can however provide another field using the pathToSlugField parameter. **Note** that providing a non existing field will cause the result to be null. To alter the default values for tableOfContents generation, include values for `heading` (string) and/or `maxDepth` (number 1 to 6) in graphQL query. If a value for `heading` is given, the first heading that matches will be ommitted and the toc is generated from the next heading of the same depth onwards. Value for `maxDepth` sets the maximum depth of the toc (i.e. if a maxDepth of 3 is set, only h1 to h3 headings will appear in the toc).
+
+```graphql
+{
+ allMarkdownRemark {
+ edges {
+ node {
+ html
+ tableOfContents(
+ pathToSlugField: "frontmatter.path"
+ heading: "only show toc from this heading onwards"
+ maxDepth: 2
+ )
frontmatter {
# Assumes you're using path in your frontmatter.
path
@@ -104,7 +125,22 @@ Using the following GraphQL query you'll be able to get the table of contents
}
```
-By default the tableOfContents is using the field `slug` to generate URLs. You can however provide another field using the pathToSlugField parameter. **Note** that providing a non existing field will cause the result to be null.
+To pass default options to the plugin generating the tableOfContents, configure it in gatsby-config.js as shown below. The options shown below are the defaults used by the plugin.
+
+```javascript
+// In your gatsby-config.js
+plugins: [
+ {
+ resolve: `gatsby-transformer-remark`,
+ options: {
+ tableOfContents: {
+ heading: null,
+ maxDepth: 6,
+ },
+ },
+ },
+]
+```
### Excerpts
diff --git a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js
index 8fc3ead8756e0..d46f821579d44 100644
--- a/packages/gatsby-transformer-remark/src/__tests__/extend-node.js
+++ b/packages/gatsby-transformer-remark/src/__tests__/extend-node.js
@@ -455,6 +455,89 @@ final text
expect(node).toMatchSnapshot()
}
)
+
+ bootstrapTest(
+ `table of contents is generated with correct depth (graphql option)`,
+ `---
+title: "my little pony"
+date: "2017-09-18T23:19:51.246Z"
+---
+# first title
+
+some text
+
+## second title
+
+some other text`,
+ `tableOfContents(pathToSlugField: "frontmatter.title", maxDepth: 1)
+ frontmatter {
+ title
+ }`,
+ node => {
+ expect(node.tableOfContents).toBe(`
`)
+ }
+ )
+
+ bootstrapTest(
+ `table of contents is generated with correct depth (plugin option)`,
+ `---
+title: "my little pony"
+date: "2017-09-18T23:19:51.246Z"
+---
+# first title
+
+some text
+
+## second title
+
+some other text`,
+ `tableOfContents(pathToSlugField: "frontmatter.title")
+ frontmatter {
+ title
+ }`,
+ node => {
+ expect(node.tableOfContents).toBe(``)
+ },
+ {
+ pluginOptions: {
+ tableOfContents: {
+ maxDepth: 1,
+ },
+ },
+ }
+ )
+
+ bootstrapTest(
+ `table of contents is generated from given heading onwards`,
+ `---
+title: "my little pony"
+date: "2017-09-18T23:19:51.246Z"
+---
+# first title
+
+some text
+
+## second title
+
+some other text
+
+# third title
+
+final text`,
+ `tableOfContents(pathToSlugField: "frontmatter.title", heading: "first title")
+ frontmatter {
+ title
+ }`,
+ node => {
+ expect(node.tableOfContents).toBe(``)
+ }
+ )
})
describe(`Links are correctly prefixed`, () => {
diff --git a/packages/gatsby-transformer-remark/src/extend-node-type.js b/packages/gatsby-transformer-remark/src/extend-node-type.js
index c4054ba1722aa..1d478be20503e 100644
--- a/packages/gatsby-transformer-remark/src/extend-node-type.js
+++ b/packages/gatsby-transformer-remark/src/extend-node-type.js
@@ -49,10 +49,12 @@ const headingsCacheKey = node =>
`transformer-remark-markdown-headings-${
node.internal.contentDigest
}-${pluginsCacheStr}-${pathPrefixCacheStr}`
-const tableOfContentsCacheKey = node =>
+const tableOfContentsCacheKey = (node, appliedTocOptions) =>
`transformer-remark-markdown-toc-${
node.internal.contentDigest
- }-${pluginsCacheStr}-${pathPrefixCacheStr}`
+ }-${pluginsCacheStr}-${JSON.stringify(
+ appliedTocOptions
+ )}-${pathPrefixCacheStr}`
// ensure only one `/` in new url
const withPathPrefix = (url, pathPrefix) =>
@@ -98,16 +100,21 @@ module.exports = (
return new Promise((resolve, reject) => {
// Setup Remark.
const {
+ blocks,
commonmark = true,
footnotes = true,
- pedantic = true,
gfm = true,
- blocks,
+ pedantic = true,
+ tableOfContents = {
+ heading: null,
+ maxDepth: 6,
+ },
} = pluginOptions
+ const tocOptions = tableOfContents
const remarkOptions = {
- gfm,
commonmark,
footnotes,
+ gfm,
pedantic,
}
if (_.isArray(blocks)) {
@@ -271,27 +278,37 @@ module.exports = (
}
}
- async function getTableOfContents(markdownNode, pathToSlugField) {
- const cachedToc = await cache.get(tableOfContentsCacheKey(markdownNode))
+ async function getTableOfContents(markdownNode, gqlTocOptions) {
+ // fetch defaults
+ let appliedTocOptions = { ...tocOptions, ...gqlTocOptions }
+ // get cached toc
+ const cachedToc = await cache.get(
+ tableOfContentsCacheKey(markdownNode, appliedTocOptions)
+ )
if (cachedToc) {
return cachedToc
} else {
const ast = await getAST(markdownNode)
- const tocAst = mdastToToc(ast)
+ const tocAst = mdastToToc(ast, appliedTocOptions)
let toc
if (tocAst.map) {
const addSlugToUrl = function(node) {
if (node.url) {
- if (_.get(markdownNode, pathToSlugField) === undefined) {
+ if (
+ _.get(markdownNode, appliedTocOptions.pathToSlugField) ===
+ undefined
+ ) {
console.warn(
- `Skipping TableOfContents. Field '${pathToSlugField}' missing from markdown node`
+ `Skipping TableOfContents. Field '${
+ appliedTocOptions.pathToSlugField
+ }' missing from markdown node`
)
return null
}
node.url = [
pathPrefix,
- _.get(markdownNode, pathToSlugField),
+ _.get(markdownNode, appliedTocOptions.pathToSlugField),
node.url,
]
.join(`/`)
@@ -309,7 +326,7 @@ module.exports = (
} else {
toc = ``
}
- cache.set(tableOfContentsCacheKey(markdownNode), toc)
+ cache.set(tableOfContentsCacheKey(markdownNode, appliedTocOptions), toc)
return toc
}
}
@@ -528,9 +545,15 @@ module.exports = (
type: GraphQLString,
defaultValue: `fields.slug`,
},
+ maxDepth: {
+ type: GraphQLInt,
+ },
+ heading: {
+ type: GraphQLString,
+ },
},
- resolve(markdownNode, { pathToSlugField }) {
- return getTableOfContents(markdownNode, pathToSlugField)
+ resolve(markdownNode, args) {
+ return getTableOfContents(markdownNode, args)
},
},
// TODO add support for non-latin languages https://github.com/wooorm/remark/issues/251#issuecomment-296731071