diff --git a/__tests__/lib/exports/index.test.ts b/__tests__/lib/exports/index.test.ts index df73fb068..f56d24361 100644 --- a/__tests__/lib/exports/index.test.ts +++ b/__tests__/lib/exports/index.test.ts @@ -14,6 +14,6 @@ describe('export tags', () => { }); it('returns different types of export names', () => { - expect(exports(weirdExportsMdx)).toStrictEqual(['Foo', 'bar', 'doSomethingFunction', 'YELLING']); + expect(exports(weirdExportsMdx)).toStrictEqual(['Foo', 'bar', 'doSomethingFunction', 'YELLING', 'SingleNewlinesAreAnnoying', 'x', 'MyClass']); }); }); \ No newline at end of file diff --git a/__tests__/lib/exports/input/weirdExports.mdx b/__tests__/lib/exports/input/weirdExports.mdx index 3953dd587..8656cd25e 100644 --- a/__tests__/lib/exports/input/weirdExports.mdx +++ b/__tests__/lib/exports/input/weirdExports.mdx @@ -7,13 +7,16 @@ export function Foo() { export const bar = () => { return ; } - export function doSomethingFunction(input) { return input.trim(); } export const YELLING = () => {} +export const SingleNewlinesAreAnnoying = () => "hey"; +export let x = 2; +export class MyClass { +} ## Hey there \ No newline at end of file diff --git a/lib/exports.ts b/lib/exports.ts index ff072028b..ec9c1bc9d 100644 --- a/lib/exports.ts +++ b/lib/exports.ts @@ -3,19 +3,75 @@ import mdast from './mdast'; import { isMDXEsm } from '../processor/utils'; import { MdxjsEsm } from 'mdast-util-mdx'; -const EXPORT_NAME_REGEX = /export\s+(?:const|let|var|function)\s+(\w+)/; +/* Example mdast structures to find first export name in a mdxjsEsm node: +There are three types of export declarations that we need to consider: +1. VARIABLE DECLARATION + "type": "mdxjsEsm", + "value": "export const Foo = () =>
Hello world
\nexport const Bar = () =>
hello darkness my old friend
", + "data": { + "estree": { + "type": "Program", + "body": [ + { + "type": "ExportNamedDeclaration", + "declaration": { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "Foo" // --------> This is the export name + }, + ... + +2/3. FUNCTION DECLARATION & CLASS DECLARATION + "estree": { + "type": "Program", + "body": [ + { + "type": "ExportNamedDeclaration", + "declaration": { + "type": "ClassDeclaration" | "FunctionDeclaration", + "id": { + "type": "Identifier", + "name": "Foo" // --------> This is the export name + }, +*/ const exports = (doc: string) => { const set = new Set(); visit(mdast(doc), isMDXEsm, (node: MdxjsEsm) => { - if (node.value?.match(EXPORT_NAME_REGEX)) { - const [, name] = node.value.match(EXPORT_NAME_REGEX); - set.add(name); + // Once inside an mdxjsEsm node, we need to check for one or more declared exports within data.estree.body + // This is because single newlines \n are not considered as a new block, so there may be more than one export statement in a single mdxjsEsm node + const body = node.data?.estree.body; + if (!body) return; + + for (const child of body) { + if (child.type === 'ExportNamedDeclaration') { + // There are three types of ExportNamedDeclaration that we need to consider: VariableDeclaration, FunctionDeclaration, ClassDeclaration + const declaration = child.declaration; + // FunctionDeclaration and ClassDeclaration have the same structure + if (declaration.type !== 'VariableDeclaration') { + // Note: declaration.id.type is always 'Identifier' for FunctionDeclarations and ClassDeclarations + set.add(declaration.id.name); + } + else { + const declarations = declaration.declarations; + for (const declaration of declarations) { + const id = declaration.id; + if (id.type === 'Identifier') { + set.add(id.name); + } + } + } + } } + }); return Array.from(set); }; -export default exports; +export default exports; \ No newline at end of file