Skip to content

Commit

Permalink
Improve export name extraction for shortcode generation (#1160)
Browse files Browse the repository at this point in the history
* Improve export name extraction

Many different types of export syntax wasn't supported,
primarily destructuring for objects and arrays. This adds
export extraction to the import name extraction step so
we can more accurately determine what shortcodes to
generate.

* Place gatsby-plugin-mdx in the root for now, seems to not be resolving properly

* Fix linting

* Make null handling more clear

* Fix handling of null specifiers

* Move dep to proper place
  • Loading branch information
johno committed Jul 17, 2020
1 parent ea9970a commit db93304
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 22 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -63,6 +63,7 @@
"eslint-plugin-prettier": "3.1.4",
"eslint-plugin-react": "7.20.3",
"gatsby": "2.24.3",
"gatsby-plugin-mdx": "^1.2.27",
"hast-util-select": "4.0.0",
"husky": "4.2.5",
"jest": "26.1.0",
Expand Down Expand Up @@ -134,5 +135,6 @@
"bracketSpacing": false,
"semi": false,
"trailingComma": "none"
}
},
"dependencies": {}
}
68 changes: 68 additions & 0 deletions packages/babel-plugin-extract-export-names/index.js
@@ -0,0 +1,68 @@
const {declare} = require('@babel/helper-plugin-utils')

class BabelPluginExtractExportNames {
constructor() {
const names = []
this.state = {names}

this.plugin = declare(api => {
api.assertVersion(7)
const {types: t} = api

const handleDeclarations = node => {
if (!node.declaration) {
return
}

const {declarations} = node.declaration
if (!declarations) {
return
}

declarations.forEach(declaration => {
if (t.isIdentifier(declaration.id)) {
// Export const foo = 'bar'
names.push(declaration.id.name)
} else if (t.isArrayPattern(declaration.id)) {
// Export const [ a, b ] = []
declaration.id.elements.forEach(decl => {
names.push(decl.name)
})
} else if (t.isObjectPattern(declaration.id)) {
// Export const { a, b } = {}
declaration.id.properties.forEach(decl => {
names.push(decl.key.name)
})
}
})
}

const handleSpecifiers = node => {
const {specifiers} = node

if (!specifiers) {
return
}

specifiers.forEach(specifier => {
if (t.isExportDefaultSpecifier(specifier)) {
names.push(specifier.exported.name)
} else {
names.push(specifier.local.name)
}
})
}

return {
visitor: {
ExportNamedDeclaration(path) {
handleDeclarations(path.node)
handleSpecifiers(path.node)
}
}
}
})
}
}

module.exports = BabelPluginExtractExportNames
28 changes: 28 additions & 0 deletions packages/babel-plugin-extract-export-names/package.json
@@ -0,0 +1,28 @@
{
"name": "babel-plugin-extract-export-names",
"version": "2.0.0-next.1",
"description": "Extract export names",
"repository": "mdx-js/mdx",
"homepage": "https://mdxjs.com",
"bugs": "https://github.com/mdx-js/mdx/issues",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
},
"author": "John Otander <johnotander@gmail.com> (http://johnotander.com)",
"license": "MIT",
"files": [
"index.js"
],
"keywords": [
"mdx",
"markdown",
"react",
"jsx",
"remark",
"babel"
],
"dependencies": {
"@babel/helper-plugin-utils": "7.10.4"
}
}
37 changes: 37 additions & 0 deletions packages/babel-plugin-extract-export-names/readme.md
@@ -0,0 +1,37 @@
# `babel-plugin-extract-export-names`

Babel plugin that extracts all variable names from
export statements.Used by the [MDX](https://mdxjs.com)
pragma.

## Installation

```sh
yarn add babel-plugin-extract-export-names
```

## Usage

```js
const babel = require('@babel/core')

const BabelPluginExtractExportNames = require('babel-plugin-extract-export-names')

const jsx = `
export const foo = 'bar'
export const [A] = [1]
`

const plugin = new BabelPluginExtractExportNames()

const result = babel.transform(jsx, {
configFile: false,
plugins: [plugin.plugin]
})

console.log(plugin.state.names)
```

## License

MIT
56 changes: 56 additions & 0 deletions packages/babel-plugin-extract-export-names/test/index.test.js
@@ -0,0 +1,56 @@
const babel = require('@babel/core')

const BabelPluginExtractExportNames = require('..')

const FIXTURE = `
export const foo = 'bar'
export const foo2 = {
bar: 'baze',
hi: \`Hello! \${foo.toJSON() || 'hrm'}\`,
abc: {
def: 123
}
}
export const foo3 = [1, 2, 3, { a: 'b' }]
export const A = 'baz'
export const [B] = [1]
export const [C, D, E] = [a, { b: 'c' }]
export const { F } = { foo: 'bar' }
export const { G, H } = {}
export { Super } from './super'
`

const transform = str => {
const plugin = new BabelPluginExtractExportNames()

const result = babel.transform(str, {
configFile: false,
plugins: [plugin.plugin]
})

return {
...result,
state: plugin.state
}
}

describe('babel-plugin-extract-export-names', () => {
test('adds export names to state', () => {
const result = transform(FIXTURE)

expect(result.state.names).toEqual([
'foo',
'foo2',
'foo3',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'Super'
])
})
})
Expand Up @@ -21,7 +21,7 @@ const transform = str => {
}
}

describe('babel-plugin-add-mdx-type-prop', () => {
describe('babel-plugin-extract-import-names', () => {
test('adds import names to state', () => {
const result = transform(FIXTURE)

Expand Down
33 changes: 16 additions & 17 deletions packages/mdx/mdx-hast-to-jsx.js
Expand Up @@ -6,6 +6,7 @@ const toH = require('hast-to-hyperscript')
const {toTemplateLiteral} = require('@mdx-js/util')
const BabelPluginApplyMdxProp = require('babel-plugin-apply-mdx-type-prop')
const BabelPluginExtractImportNames = require('babel-plugin-extract-import-names')
const BabelPluginExtractExportNames = require('babel-plugin-extract-export-names')

function toJSX(node, parentNode = {}, options = {}) {
if (node.type === 'root') {
Expand Down Expand Up @@ -79,13 +80,6 @@ function serializeRoot(node, options) {
return true
})

const exportNames = groups.export
.map(node =>
node.value.match(/^export\s*(var|const|let|class|function)?\s*(\w+)/)
)
.map(match => (Array.isArray(match) ? match[2] : null))
.filter(Boolean)

const importStatements = groups.import
.map(childNode => toJSX(childNode, node))
.join('\n')
Expand All @@ -94,14 +88,6 @@ function serializeRoot(node, options) {
.map(childNode => toJSX(childNode, node))
.join('\n')

let layoutProps = 'const layoutProps = {'

if (exportNames.length !== 0) {
layoutProps += '\n ' + exportNames.join(',\n ') + '\n'
}

layoutProps += '};'

const mdxLayout = `const MDXLayout = ${layout ? layout : '"wrapper"'}`

const doc = groups.rest
Expand All @@ -120,16 +106,20 @@ MDXContent.isMDXComponent = true`

// Check JSX nodes against imports
const babelPluginExtractImportNamesInstance = new BabelPluginExtractImportNames()
transformSync(importStatements, {
const babelPluginExtractExportNamesInstance = new BabelPluginExtractExportNames()
const importsAndExports = [importStatements, exportStatements].join('\n')
transformSync(importsAndExports, {
configFile: false,
babelrc: false,
plugins: [
require('@babel/plugin-syntax-jsx'),
require('@babel/plugin-syntax-object-rest-spread'),
babelPluginExtractImportNamesInstance.plugin
babelPluginExtractImportNamesInstance.plugin,
babelPluginExtractExportNamesInstance.plugin
]
})
const importNames = babelPluginExtractImportNamesInstance.state.names
const exportNames = babelPluginExtractExportNamesInstance.state.names

const babelPluginApplyMdxPropInstance = new BabelPluginApplyMdxProp()
const babelPluginApplyMdxPropToExportsInstance = new BabelPluginApplyMdxProp()
Expand All @@ -154,6 +144,15 @@ MDXContent.isMDXComponent = true`
]
}).code

// TODO: Remove layout props entirely
let layoutProps = 'const layoutProps = {'

if (exportNames.length !== 0) {
layoutProps += '\n ' + exportNames.join(',\n ') + '\n'
}

layoutProps += '};'

const allJsxNames = [
...babelPluginApplyMdxPropInstance.state.names,
...babelPluginApplyMdxPropToExportsInstance.state.names
Expand Down
1 change: 1 addition & 0 deletions packages/mdx/package.json
Expand Up @@ -45,6 +45,7 @@
"@mdx-js/util": "^2.0.0-next.1",
"babel-plugin-apply-mdx-type-prop": "^2.0.0-next.1",
"babel-plugin-extract-import-names": "^2.0.0-next.1",
"babel-plugin-extract-export-names": "^2.0.0-next.1",
"camelcase-css": "2.0.1",
"detab": "2.0.3",
"hast-to-hyperscript": "9.0.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/remark-mdxjs/test/__snapshots__/test.js.snap
Expand Up @@ -8,10 +8,11 @@ const makeShortcode = name => function MDXDefaultShortcode(props) {
console.warn(\\"Component \\" + name + \\" was not imported, exported, or provided by MDXProvider as global scope\\")
return <div {...props}/>
};
const Baz = makeShortcode(\\"Baz\\");
const Paragraph = makeShortcode(\\"Paragraph\\");
const Button = makeShortcode(\\"Button\\");
const layoutProps = {};
const layoutProps = {
Baz
};
const MDXLayout = Foo
export default function MDXContent({
components,
Expand Down
44 changes: 43 additions & 1 deletion yarn.lock
Expand Up @@ -12362,7 +12362,7 @@ gatsby-plugin-google-fonts@1.0.1:
resolved "https://registry.yarnpkg.com/gatsby-plugin-google-fonts/-/gatsby-plugin-google-fonts-1.0.1.tgz#d71054f7bf207b9a8da227380369e18e6f4e0201"
integrity sha512-p1NVkn27GUnDA5qHM+Z4cCcLCJIardzZXMon3640sT4xuL/AZJbsx3HEt2KY/5oZu0UXIkytkxzV2Da4rQeUIg==

gatsby-plugin-mdx@1.2.26, gatsby-plugin-mdx@^1.2.26:
gatsby-plugin-mdx@1.2.26:
version "1.2.26"
resolved "https://registry.yarnpkg.com/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.2.26.tgz#3f5f2c929769cf0094eb78e0a052b916e40515d4"
integrity sha512-1KZEBzRp69H6faiBHeTy0NmWr56Qa5MfLNhi3UqQdZDLRdVxCzgXML7TafBBXuBAsJJBCvQi0Df437fodkdpvQ==
Expand Down Expand Up @@ -12403,6 +12403,48 @@ gatsby-plugin-mdx@1.2.26, gatsby-plugin-mdx@^1.2.26:
unist-util-remove "^1.0.3"
unist-util-visit "^1.4.1"

gatsby-plugin-mdx@^1.2.26, gatsby-plugin-mdx@^1.2.27:
version "1.2.27"
resolved "https://registry.yarnpkg.com/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.2.27.tgz#dadc9ce6e874b1f181c12b5cc019bb7217d4e457"
integrity sha512-2j5voALrvJ14JR9UzY9NygatVHRpGcm15jjJRDYtnT9k5ptKkqYLuiPcQ3eBxoqwjvAamLJ8WYRhc/poP/Ezuw==
dependencies:
"@babel/core" "^7.10.3"
"@babel/generator" "^7.10.3"
"@babel/helper-plugin-utils" "^7.10.3"
"@babel/plugin-proposal-object-rest-spread" "^7.10.3"
"@babel/preset-env" "^7.10.3"
"@babel/preset-react" "^7.10.1"
"@babel/types" "^7.10.3"
camelcase-css "^2.0.1"
change-case "^3.1.0"
core-js "^3.6.5"
dataloader "^1.4.0"
debug "^4.1.1"
escape-string-regexp "^1.0.5"
eval "^0.1.4"
fs-extra "^8.1.0"
gatsby-core-utils "^1.3.12"
gray-matter "^4.0.2"
json5 "^2.1.3"
loader-utils "^1.4.0"
lodash "^4.17.15"
mdast-util-to-string "^1.1.0"
mdast-util-toc "^3.1.0"
mime "^2.4.6"
p-queue "^5.0.0"
pretty-bytes "^5.3.0"
remark "^10.0.1"
remark-retext "^3.1.3"
retext-english "^3.0.4"
slugify "^1.4.4"
static-site-generator-webpack-plugin "^3.4.2"
style-to-object "^0.3.0"
underscore.string "^3.3.5"
unified "^8.4.2"
unist-util-map "^1.0.5"
unist-util-remove "^1.0.3"
unist-util-visit "^1.4.1"

gatsby-plugin-page-creator@2.3.17, gatsby-plugin-page-creator@^2.3.17:
version "2.3.17"
resolved "https://registry.yarnpkg.com/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-2.3.17.tgz#9e63af4bb78342fbdd7a0c0f01c9f13409274c5b"
Expand Down

0 comments on commit db93304

Please sign in to comment.