Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(gatsby-plugin-mdx): performance changes #26004

Merged
merged 7 commits into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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