Skip to content

Commit

Permalink
perf(gatsby-plugin-mdx): performance changes (#26004)
Browse files Browse the repository at this point in the history
Re-submit of #24595 
Drops a babel step

This Babel step was used to get exports and remark header but we can use the mdast for this purpose, just like for imports.

> This cuts off 20 to 30% of the total runtime of a baseline mdx benchmark!

Co-authored-by: Laurie Barth <laurie@LauriesrkLaptop.fios-router.home>
Co-authored-by: Peter van der Zee <github-public@qfox.nl>
  • Loading branch information
3 people committed Jul 30, 2020
1 parent 33aff39 commit 9e02abe
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 73 deletions.
21 changes: 10 additions & 11 deletions packages/gatsby-plugin-mdx/gatsby/on-create-node.js
Expand Up @@ -4,9 +4,8 @@ const babel = require(`@babel/core`)
const { createContentDigest } = require(`gatsby-core-utils`)

const defaultOptions = require(`../utils/default-options`)
const createMDXNode = require(`../utils/create-mdx-node`)
const createMDXNodeWithScope = require(`../utils/mdx-node-with-scope`)
const { MDX_SCOPES_LOCATION } = require(`../constants`)
const { findImports } = require(`../utils/gen-mdx`)

const contentDigest = val => createContentDigest(val)

Expand Down Expand Up @@ -46,18 +45,14 @@ module.exports = async (

const content = await loadNodeContent(node)

const mdxNode = await createMDXNode({
const {
mdxNode,
scopeIdentifiers,
scopeImports,
} = await createMDXNodeWithScope({
id: createNodeId(`${node.id} >>> Mdx`),
node,
content,
})

createNode(mdxNode)
createParentChildLink({ parent: node, child: mdxNode })

// write scope files into .cache for later consumption
const { scopeImports, scopeIdentifiers } = await findImports({
node: mdxNode,
getNode,
getNodes,
reporter,
Expand All @@ -69,6 +64,10 @@ module.exports = async (
createNodeId,
...helpers,
})

createNode(mdxNode)
createParentChildLink({ parent: node, child: mdxNode })

await cacheScope({
cache,
scopeIdentifiers,
Expand Down
12 changes: 9 additions & 3 deletions packages/gatsby-plugin-mdx/loaders/mdx-loader.js
Expand Up @@ -25,7 +25,7 @@ const debugMore = require(`debug`)(`gatsby-plugin-mdx-info:mdx-loader`)

const genMdx = require(`../utils/gen-mdx`)
const withDefaultOptions = require(`../utils/default-options`)
const createMDXNode = require(`../utils/create-mdx-node`)
const createMDXNodeWithScope = require(`../utils/mdx-node-with-scope`)
const { createFileNode } = require(`../utils/create-fake-file-node`)

const DEFAULT_OPTIONS = {
Expand Down Expand Up @@ -94,6 +94,7 @@ module.exports = async function (content) {
const {
getNode: rawGetNode,
getNodes,
getNodesByType,
reporter,
cache,
pathPrefix,
Expand Down Expand Up @@ -121,11 +122,15 @@ module.exports = async function (content) {

let mdxNode
try {
mdxNode = await createMDXNode({
// This node attempts to break the chicken-egg problem, where parsing mdx
// allows for custom plugins, which can receive a mdx node
;({ mdxNode } = await createMDXNodeWithScope({
id: `fakeNodeIdMDXFileABugIfYouSeeThis`,
node: fileNode,
content,
})
options,
getNodesByType,
}))
} catch (e) {
return callback(e)
}
Expand Down Expand Up @@ -192,6 +197,7 @@ ${contentWithoutFrontmatter}`
node: { ...mdxNode, rawBody: code },
getNode,
getNodes,
getNodesByType,
reporter,
cache,
pathPrefix,
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-mdx/loaders/mdx-loader.test.js
Expand Up @@ -16,9 +16,9 @@ array: [1,2,3]
)`,
namedExports: `export const meta = {author: "chris"}`,
body: `# Some title
a bit of a paragraph
some content`,
}

Expand Down
57 changes: 14 additions & 43 deletions packages/gatsby-plugin-mdx/utils/create-mdx-node.js
@@ -1,46 +1,17 @@
const { createContentDigest } = require(`gatsby-core-utils`)
const withDefaultOptions = require(`../utils/default-options`)
const { getNodesByType } = require(`gatsby/dist/redux/nodes.js`)
const createMdxNodeWithScope = require(`../utils/mdx-node-with-scope`)

const mdx = require(`../utils/mdx`)
const extractExports = require(`../utils/extract-exports`)

module.exports = async ({ id, node, content }) => {
let code
try {
code = await mdx(content)
} catch (e) {
// add the path of the file to simplify debugging error messages
e.message += `${node.absolutePath}: ${e.message}`
throw e
}

// extract all the exports
const { frontmatter, ...nodeExports } = extractExports(code)

const mdxNode = {
async function createMdxNodeLegacy({ id, node, content } = {}) {
const nodeWithScope = await createMdxNodeWithScope({
id,
children: [],
parent: node.id,
internal: {
content: content,
type: `Mdx`,
},
}

mdxNode.frontmatter = {
title: ``, // always include a title
...frontmatter,
}

mdxNode.excerpt = frontmatter.excerpt
mdxNode.exports = nodeExports
mdxNode.rawBody = content

// Add path to the markdown file path
if (node.internal.type === `File`) {
mdxNode.fileAbsolutePath = node.absolutePath
}

mdxNode.internal.contentDigest = createContentDigest(mdxNode)

return mdxNode
node,
content,
getNodesByType,
options: withDefaultOptions({ plugins: [] }),
})
return nodeWithScope.mdxNode
}

// This function is deprecated in favor of createMDXNodeWithScope and slated to be dropped in v3
module.exports = createMdxNodeLegacy
33 changes: 19 additions & 14 deletions packages/gatsby-plugin-mdx/utils/gen-mdx.js
Expand Up @@ -194,8 +194,10 @@ ${code}`
module.exports = genMDX // Legacy API, drop in v3 in favor of named export
module.exports.genMDX = genMDX

async function findImports({
async function findImportsExports({
node,
rawInput,
absolutePath = null,
options,
getNode,
getNodes,
Expand All @@ -205,7 +207,7 @@ async function findImports({
pathPrefix,
...helpers
}) {
const { content } = grayMatter(node.rawBody)
const { data: frontmatter, content } = grayMatter(rawInput)

const gatsbyRemarkPluginsAsremarkPlugins = await getSourcePluginsAsRemarkPlugins(
{
Expand All @@ -226,7 +228,7 @@ async function findImports({
)

const compilerOptions = {
filepath: node.fileAbsolutePath,
filepath: absolutePath,
...options,
remarkPlugins: [
...options.remarkPlugins,
Expand All @@ -236,8 +238,8 @@ async function findImports({
const compiler = mdx.createCompiler(compilerOptions)

const fileOpts = { contents: content }
if (node.fileAbsolutePath) {
fileOpts.path = node.fileAbsolutePath
if (absolutePath) {
fileOpts.path = absolutePath
}

const mdast = await compiler.parse(fileOpts)
Expand All @@ -246,16 +248,17 @@ async function findImports({
// we don't need to dedupe the symbols here.
const identifiers = []
const imports = []
const exports = []

mdast.children.forEach(node => {
if (node.type !== `import`) return

const importCode = node.value

imports.push(importCode)

const bindings = parseImportBindings(importCode)
identifiers.push(...bindings)
if (node.type === `import`) {
const importCode = node.value
imports.push(importCode)
const bindings = parseImportBindings(importCode)
identifiers.push(...bindings)
} else if (node.type === `export`) {
exports.push(node.value)
}
})

if (!identifiers.includes(`React`)) {
Expand All @@ -264,9 +267,11 @@ async function findImports({
}

return {
frontmatter,
scopeImports: imports,
scopeExports: exports,
scopeIdentifiers: identifiers,
}
}

module.exports.findImports = findImports
module.exports.findImportsExports = findImportsExports
45 changes: 45 additions & 0 deletions packages/gatsby-plugin-mdx/utils/mdx-node-with-scope.js
@@ -0,0 +1,45 @@
const { createContentDigest } = require(`gatsby-core-utils`)

const { findImportsExports } = require(`../utils/gen-mdx`)

async function createMdxNodeWithScope({ id, node, content, ...helpers }) {
const {
frontmatter,
scopeImports,
scopeExports,
scopeIdentifiers,
} = await findImportsExports({
node,
rawInput: content,
absolutePath: node.absolutePath,
...helpers,
})

const mdxNode = {
id,
children: [],
parent: node.id,
internal: {
content,
type: `Mdx`,
},
excerpt: frontmatter.excerpt,
exports: scopeExports,
rawBody: content,
frontmatter: {
title: ``, // always include a title
...frontmatter,
},
}

// Add path to the markdown file path
if (node.internal.type === `File`) {
mdxNode.fileAbsolutePath = node.absolutePath
}

mdxNode.internal.contentDigest = createContentDigest(mdxNode)

return { mdxNode, scopeIdentifiers, scopeImports }
}

module.exports = createMdxNodeWithScope

0 comments on commit 9e02abe

Please sign in to comment.