diff --git a/packages/gatsby-plugin-mdx/mdx-loader.js b/packages/gatsby-plugin-mdx/mdx-loader.js index 697c6f10edee7..a3f20b5ecbfd1 100644 --- a/packages/gatsby-plugin-mdx/mdx-loader.js +++ b/packages/gatsby-plugin-mdx/mdx-loader.js @@ -1,11 +1,81 @@ const _ = require("lodash"); const { getOptions } = require("loader-utils"); const grayMatter = require("gray-matter"); +const unified = require("unified"); +const { + isImport, + isExport, + isExportDefault, + BLOCKS_REGEX, + EMPTY_NEWLINE, + ...props +} = require("@mdx-js/mdx/util"); + +const toMDAST = require("remark-parse"); +const squeeze = require("remark-squeeze-paragraphs"); const mdx = require("./utils/mdx"); const debug = require("debug")("gatsby-mdx:mdx-loader"); -const hasDefaultExport = str => /\nexport default/.test(str); +const DEFAULT_OPTIONS = { + footnotes: true, + mdPlugins: [], + hastPlugins: [], + compilers: [], + blocks: [BLOCKS_REGEX] +}; + +/** + * TODO: Find a way to PR all of this code that was lifted + * from @mdx-js/mdx back into mdx with the modifications. We + * don't want to maintain subtly different parsing code if we + * can avoid it. + */ +const hasDefaultExport = (str, options) => { + let hasDefaultExportBool = false; + + function getDefaultExportBlock(subvalue) { + const isDefault = isExportDefault(subvalue); + hasDefaultExportBool = hasDefaultExportBool || isDefault; + return isDefault; + } + const tokenizeEsSyntax = (eat, value) => { + const index = value.indexOf(EMPTY_NEWLINE); + const subvalue = value.slice(0, index); + + if (isExport(subvalue) || isImport(subvalue)) { + return eat(subvalue)({ + type: isExport(subvalue) ? "export" : "import", + default: getDefaultExportBlock(subvalue), + value: subvalue + }); + } + }; + + tokenizeEsSyntax.locator = (value, fromIndex) => { + return isExport(value) || isImport(value) ? -1 : 1; + }; + + function esSyntax() { + var Parser = this.Parser; + var tokenizers = Parser.prototype.blockTokenizers; + var methods = Parser.prototype.blockMethods; + + tokenizers.esSyntax = tokenizeEsSyntax; + + methods.splice(methods.indexOf("paragraph"), 0, "esSyntax"); + } + + const { content } = grayMatter(str); + const fn = unified() + .use(toMDAST, options) + .use(esSyntax) + .use(squeeze, options) + .parse(content) + .toString(); + + return hasDefaultExportBool; +}; module.exports = async function(content) { const callback = this.async(); @@ -29,7 +99,7 @@ module.exports = async function(content) { let code = content; // after running mdx, the code *always* has a default export, so this // check needs to happen first. - if (!hasDefaultExport(content) && !!defaultLayout) { + if (!hasDefaultExport(content, DEFAULT_OPTIONS) && !!defaultLayout) { debug("inserting default layout", defaultLayout); const { content: contentWithoutFrontmatter } = grayMatter(content);