From 05a23ad9f622d39f3e68934150e25de9016a78df Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Tue, 14 May 2024 09:43:45 -0700 Subject: [PATCH 1/9] wip --- .../transformers/readme-components.test.ts | 15 +++++++++++ index.tsx | 7 +++-- processor/transform/index.ts | 3 +++ processor/transform/readme-components.ts | 26 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 __tests__/transformers/readme-components.test.ts create mode 100644 processor/transform/readme-components.ts diff --git a/__tests__/transformers/readme-components.test.ts b/__tests__/transformers/readme-components.test.ts new file mode 100644 index 000000000..4ac19c711 --- /dev/null +++ b/__tests__/transformers/readme-components.test.ts @@ -0,0 +1,15 @@ +import { mdast } from '../../index'; + +describe('Readme Components Transformer', () => { + const nodes = [ + { md: '', type: 'table' }, + { md: '', type: 'image' }, + { md: '', type: 'code-tabs' }, + ]; + + it.each(nodes)('transformss $md into a(n) $type node', ({ md, type }) => { + const tree = mdast(md); + + expect(tree.children[0].type).toBe(type); + }); +}); diff --git a/index.tsx b/index.tsx index bb8d8db63..f283b4725 100644 --- a/index.tsx +++ b/index.tsx @@ -16,7 +16,7 @@ import { GlossaryContext } from './components/GlossaryItem'; import BaseUrlContext from './contexts/BaseUrl'; import { options } from './options'; -import transformers from './processor/transform'; +import transformers, { readmeComponentsTransformer } from './processor/transform'; import compilers from './processor/compile'; import MdxSyntaxError from './errors/mdx-syntax-error'; @@ -103,7 +103,10 @@ export const html = (text: string, opts = {}) => { unimplemented('html export'); }; -const astProcessor = (opts = {}) => remark().use(remarkMdx).use(remarkFrontmatter).use(remarkPlugins); +const astProcessor = (opts = {}) => + remark().use(remarkMdx).use(remarkFrontmatter).use(remarkPlugins).use(readmeComponentsTransformer, { + readmeComponents: Components, + }); export const mdast: any = (text: string, opts = {}) => { const processor = astProcessor(opts); diff --git a/processor/transform/index.ts b/processor/transform/index.ts index acbbe71a0..4e9b24613 100644 --- a/processor/transform/index.ts +++ b/processor/transform/index.ts @@ -1,5 +1,8 @@ import calloutTransformer from './callouts'; import codeTabsTransfromer from './code-tabs'; import gemojiTransformer from './gemoji+'; +import readmeComponentsTransformer from './readme-components'; + +export { readmeComponentsTransformer }; export default [calloutTransformer, codeTabsTransfromer, gemojiTransformer]; diff --git a/processor/transform/readme-components.ts b/processor/transform/readme-components.ts new file mode 100644 index 000000000..27bb3ce85 --- /dev/null +++ b/processor/transform/readme-components.ts @@ -0,0 +1,26 @@ +import { Root } from 'mdast'; +import { visit } from 'unist-util-visit'; + +const types = { + CodeTabs: 'code-tabs', + Image: 'image', + Table: 'table', +}; + +const readmeComponents = + ({ components, readmeComponents }) => + (tree: Root) => { + visit(tree, 'mdxJsxFlowElement', (node, index, parent) => { + if (node.name in readmeComponents) { + const newNode = { + type: types[node.name], + }; + + parent.children[index] = newNode; + } + }); + + return tree; + }; + +export default readmeComponents; From 8ec73d7ce710710a43b1c660fd2cfe749f9a6fcf Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 13:57:03 -0700 Subject: [PATCH 2/9] wip --- __tests__/matchers.ts | 19 +++++ .../transformers/readme-components.test.ts | 76 +++++++++++++++++- components/Callout/index.tsx | 21 ++++- index.tsx | 5 +- package-lock.json | 14 ++++ package.json | 1 + processor/transform/callouts.ts | 22 ----- processor/transform/code-tabs.ts | 9 --- processor/transform/readme-components.ts | 80 +++++++++++++++++-- processor/transform/single-code-tabs.js | 26 ------ tsconfig.json | 22 ++--- vitest-setup.js | 3 +- vitest.d.ts | 10 +++ 13 files changed, 219 insertions(+), 89 deletions(-) create mode 100644 __tests__/matchers.ts delete mode 100644 processor/transform/single-code-tabs.js create mode 100644 vitest.d.ts diff --git a/__tests__/matchers.ts b/__tests__/matchers.ts new file mode 100644 index 000000000..f812446f1 --- /dev/null +++ b/__tests__/matchers.ts @@ -0,0 +1,19 @@ +import { expect } from 'vitest'; +import { map } from 'unist-util-map'; + +const removePosition = ({ position, ...node }) => node; + +function toStrictEqualExceptPosition(received, expected) { + const { equals } = this; + const receivedTrimmed = map(received, removePosition); + const expectedTrimmed = map(expected, removePosition); + + return { + pass: equals(receivedTrimmed, expectedTrimmed), + message: () => 'Expected two trees to be equal!', + actual: receivedTrimmed, + expected: expectedTrimmed, + }; +} + +expect.extend({ toStrictEqualExceptPosition }); diff --git a/__tests__/transformers/readme-components.test.ts b/__tests__/transformers/readme-components.test.ts index 4ac19c711..cf35cb384 100644 --- a/__tests__/transformers/readme-components.test.ts +++ b/__tests__/transformers/readme-components.test.ts @@ -2,14 +2,84 @@ import { mdast } from '../../index'; describe('Readme Components Transformer', () => { const nodes = [ - { md: '
', type: 'table' }, - { md: '', type: 'image' }, + { md: '', type: 'rdme-callout' }, + { md: '', type: 'code' }, { md: '', type: 'code-tabs' }, + { md: '', type: 'image' }, + { md: '
', type: 'table' }, ]; - it.each(nodes)('transformss $md into a(n) $type node', ({ md, type }) => { + it.each(nodes)('transforms $md into a(n) $type node', ({ md, type }) => { const tree = mdast(md); expect(tree.children[0].type).toBe(type); }); + + const docs = { + ['rdme-callout']: { + md: `> 📘 It works!`, + mdx: ``, + }, + code: { + md: ` +~~~ +This is a code block +~~~ + `, + mdx: ``, + }, + ['code-tabs']: { + md: ` +~~~ +First +~~~ +~~~ +Second +~~~ + `, + mdx: ` + + + + + `, + }, + image: { + md: `![](http://placekitten.com/600/200)`, + mdx: ``, + }, + table: { + md: ` +| h1 | h2 | +| --- | --- | +| a1 | a2 | + `, + // @todo there's text nodes that get inserted between the td's. Pretty sure + // they'd get filtered out by rehype, but lets keep the tests easy. + mdx: ` +
+ + + + + + +
h1h2
a1a2
+ `, + }, + }; + it.each(Object.entries(docs))('matches the equivalent markdown for %s', (type, { md, mdx }) => { + let mdTree = mdast(md); + const mdxTree = mdast(mdx); + + if (type === 'image') { + // @todo something about these dang paragraphs! + mdTree = { + type: 'root', + children: mdTree.children[0].children, + }; + } + + expect(mdxTree).toStrictEqualExceptPosition(mdTree); + }); }); diff --git a/components/Callout/index.tsx b/components/Callout/index.tsx index 10c8b0d23..79d460eb0 100644 --- a/components/Callout/index.tsx +++ b/components/Callout/index.tsx @@ -3,12 +3,29 @@ import * as React from 'react'; interface Props extends React.PropsWithChildren> { attributes: {}; icon: string; - theme: string; + theme?: string; heading?: React.ReactElement; } +const themes: Record = { + '\uD83D\uDCD8': 'info', + '\uD83D\uDEA7': 'warn', + '\u26A0\uFE0F': 'warn', + '\uD83D\uDC4D': 'okay', + '\u2705': 'okay', + '\u2757\uFE0F': 'error', + '\u2757': 'error', + '\uD83D\uDED1': 'error', + '\u2049\uFE0F': 'error', + '\u203C\uFE0F': 'error', + '\u2139\uFE0F': 'info', + '\u26A0': 'warn', +}; + const Callout = (props: Props) => { - const { attributes, children, theme, icon, heading } = props; + const { attributes, children, icon, heading } = props; + + let theme = props.theme || themes[icon] || 'default'; return ( // @ts-ignore diff --git a/index.tsx b/index.tsx index f283b4725..8bbe8e45f 100644 --- a/index.tsx +++ b/index.tsx @@ -46,6 +46,7 @@ const makeUseMDXComponents = (more: RunOpts['components']) => { ...more, ...Components, Variable, + code: Components.Code, 'code-tabs': Components.CodeTabs, img: Components.Image, table: Components.Table, @@ -104,9 +105,7 @@ export const html = (text: string, opts = {}) => { }; const astProcessor = (opts = {}) => - remark().use(remarkMdx).use(remarkFrontmatter).use(remarkPlugins).use(readmeComponentsTransformer, { - readmeComponents: Components, - }); + remark().use(remarkMdx).use(remarkFrontmatter).use(remarkPlugins).use(readmeComponentsTransformer, {}); export const mdast: any = (text: string, opts = {}) => { const processor = astProcessor(opts); diff --git a/package-lock.json b/package-lock.json index 02b78b8cc..73caf2646 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,6 +89,7 @@ "terser-webpack-plugin": "^5.3.7", "ts-loader": "^9.4.2", "typescript": "^5.4.5", + "unist-util-map": "^4.0.0", "vitest": "^1.4.0", "webpack": "^5.56.0", "webpack-cli": "^5.0.1", @@ -29159,6 +29160,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-map/-/unist-util-map-4.0.0.tgz", + "integrity": "sha512-HJs1tpkSmRJUzj6fskQrS5oYhBYlmtcvy4SepdDEEsL04FjBrgF0Mgggvxc1/qGBGgW7hRh9+UBK1aqTEnBpIA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", diff --git a/package.json b/package.json index b11a94051..44525b484 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "terser-webpack-plugin": "^5.3.7", "ts-loader": "^9.4.2", "typescript": "^5.4.5", + "unist-util-map": "^4.0.0", "vitest": "^1.4.0", "webpack": "^5.56.0", "webpack-cli": "^5.0.1", diff --git a/processor/transform/callouts.ts b/processor/transform/callouts.ts index f72af65f6..b4d018245 100644 --- a/processor/transform/callouts.ts +++ b/processor/transform/callouts.ts @@ -4,27 +4,6 @@ import { Blockquote, BlockContent, Parent, DefinitionContent } from 'mdast'; const regex = `^(${emojiRegex().source}|⚠)(\\s+|$)`; -const themes: Record = { - '\uD83D\uDCD8': 'info', - '\uD83D\uDEA7': 'warn', - '\u26A0\uFE0F': 'warn', - '\uD83D\uDC4D': 'okay', - '\u2705': 'okay', - '\u2757\uFE0F': 'error', - '\u2757': 'error', - '\uD83D\uDED1': 'error', - '\u2049\uFE0F': 'error', - '\u203C\uFE0F': 'error', - '\u2139\uFE0F': 'info', - '\u26A0': 'warn', -}; - -const toString = (node: Node): string => { - if ('value' in node && node.value) return node.value as string; - if ('children' in node && node.children) return (node.children as Node[]).map(child => toString(child)).join(''); - return ''; -}; - interface Callout extends Parent { type: 'rdme-callout'; children: Array; @@ -49,7 +28,6 @@ const calloutTransformer = () => { hProperties: { heading, icon, - theme: themes[icon] || 'default', }, }; } diff --git a/processor/transform/code-tabs.ts b/processor/transform/code-tabs.ts index 9ae3b44ef..d1bec6757 100644 --- a/processor/transform/code-tabs.ts +++ b/processor/transform/code-tabs.ts @@ -4,15 +4,6 @@ import { visit } from 'unist-util-visit'; import { NodeTypes } from '../../enums'; const codeTabs = () => tree => { - visit(tree, 'code', (node: Code) => { - const { lang, meta, value } = node; - - node.data = { - hName: 'Code', - hProperties: { lang, meta, value }, - }; - }); - visit(tree, 'code', (node: Code, index: number, parent: BlockContent) => { if (parent.type === 'code-tabs' || !('children' in parent)) return; diff --git a/processor/transform/readme-components.ts b/processor/transform/readme-components.ts index 27bb3ce85..ba60d013a 100644 --- a/processor/transform/readme-components.ts +++ b/processor/transform/readme-components.ts @@ -1,25 +1,93 @@ -import { Root } from 'mdast'; +import { BlockContent, Paragraph, Root, TableRow } from 'mdast'; +import { MdxJsxFlowElement } from 'mdast-util-mdx'; import { visit } from 'unist-util-visit'; const types = { + Callout: 'rdme-callout', + Code: 'code', CodeTabs: 'code-tabs', Image: 'image', Table: 'table', + tr: 'tableRow', + td: 'tableCell', }; +const attributes = (jsx: MdxJsxFlowElement) => + jsx.attributes.reduce((memo, attr) => { + memo[attr.name] = attr.value; + return memo; + }, {}); + const readmeComponents = - ({ components, readmeComponents }) => + ({ components }) => (tree: Root) => { - visit(tree, 'mdxJsxFlowElement', (node, index, parent) => { - if (node.name in readmeComponents) { - const newNode = { + visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => { + if (node.name === 'Code') { + const { position } = node; + const { value, lang = null, meta = null } = attributes(node); + + const mdNode = { + lang, + meta, + position, + type: 'code', + value, + }; + + parent.children[index] = mdNode; + } else if (node.name === 'Image') { + const { position } = node; + const { alt = '', src, title = null } = attributes(node); + + const mdNode = { + alt, + position, + title, + type: 'image', + url: src, + }; + + parent.children[index] = mdNode; + } else if (node.name === 'Table') { + const { children, position } = node; + const { align = [...new Array(node.children.length)].map(() => null) } = attributes(node); + + const mdNode = { + align, + type: 'table', + position, + children, + }; + + parent.children[index] = mdNode; + } else if (node.name in types) { + const hProperties = attributes(node); + + const mdNode = { + children: node.children, type: types[node.name], + ...(['tr', 'td'].includes(node.name) + ? {} + : { + data: { + hName: node.name, + ...(Object.keys(hProperties).length ? { hProperties } : {}), + }, + }), + position: node.position, }; - parent.children[index] = newNode; + parent.children[index] = mdNode; } }); + visit(tree, 'paragraph', (node: Paragraph, index: number, parent: BlockContent | TableRow) => { + if (parent.type !== 'tableRow') return; + + // @ts-ignore + parent.children.splice(index, 1, ...node.children); + }); + return tree; }; diff --git a/processor/transform/single-code-tabs.js b/processor/transform/single-code-tabs.js deleted file mode 100644 index 136f317e8..000000000 --- a/processor/transform/single-code-tabs.js +++ /dev/null @@ -1,26 +0,0 @@ -import { visit } from 'unist-util-visit'; - -const singleCodeTabs = () => tree => { - visit(tree, (node, index, parent) => { - if (node.type === 'code' && (node.lang || node.meta) && parent.type !== 'code-tabs') { - Object.assign(node, { - className: 'tab-panel', - data: { - hName: 'code', - hProperties: { meta: node.meta, lang: node.lang }, - }, - }); - - parent.children[index] = { - type: 'code-tabs', - className: 'tabs', - data: { hName: 'div', hProperties: { className: ['code-tabs'] } }, - children: [node], - }; - } - }); - - return tree; -}; - -export default singleCodeTabs; diff --git a/tsconfig.json b/tsconfig.json index f2dafcb78..51b05843d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,21 +15,9 @@ "moduleResolution": "Bundler", "outDir": "dist", "sourceMap": true, - "types": [ - "vitest/globals", - ] + "types": ["vitest/globals"] }, - "include": [ - "./index.ts", - "./options.js", - "./components", - "./contexts", - "./example", - "./lib", - "./processor" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "include": ["./index.ts", "./options.js", "./components", "./contexts", "./example", "./lib", "./processor"], + "exclude": ["node_modules", "dist"] +} + diff --git a/vitest-setup.js b/vitest-setup.js index 0453352ef..50aeb9dcd 100644 --- a/vitest-setup.js +++ b/vitest-setup.js @@ -1,2 +1,3 @@ -import '@testing-library/jest-dom' +import '@testing-library/jest-dom'; import '@testing-library/jest-dom/vitest'; +import './__tests__/matchers'; diff --git a/vitest.d.ts b/vitest.d.ts new file mode 100644 index 000000000..67dd018b6 --- /dev/null +++ b/vitest.d.ts @@ -0,0 +1,10 @@ +import type { Assertion, AsymmetricMatchersContaining } from 'vitest'; + +interface CustomMatchers { + toStrictEqualExceptPosition: () => R; +} + +declare module 'vitest' { + interface Assertion extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +} From 6cc622a39f1f24fc1c598df4d7d9b756ff3e3d06 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 14:03:26 -0700 Subject: [PATCH 3/9] fix test --- __tests__/transformers/code-tabs.test.ts | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/__tests__/transformers/code-tabs.test.ts b/__tests__/transformers/code-tabs.test.ts index df75d4d34..595cf2614 100644 --- a/__tests__/transformers/code-tabs.test.ts +++ b/__tests__/transformers/code-tabs.test.ts @@ -44,26 +44,10 @@ Second code block `; const ast = mdast(md); - expect(ast.children[0].children[0].data).toMatchInlineSnapshot(` - { - "hName": "Code", - "hProperties": { - "lang": "javascript", - "meta": "First Title", - "value": "First code block", - }, - } - `); - expect(ast.children[0].children[1].data).toMatchInlineSnapshot(` - { - "hName": "Code", - "hProperties": { - "lang": "text", - "meta": null, - "value": "Second code block", - }, - } - `); + expect(ast.children[0].children[0]).toStrictEqual( + expect.objectContaining({ lang: 'javascript', meta: 'First Title' }), + ); + expect(ast.children[0].children[1]).toStrictEqual(expect.objectContaining({ lang: 'text', meta: null })); }); it('wraps single code blocks with tabs if they have a lang set', () => { From 4ee8964ceed4fb507eaa3707528500cea8065841 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 14:43:57 -0700 Subject: [PATCH 4/9] wat --- components/Code/index.tsx | 4 +++- package.json | 2 +- tsconfig.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/Code/index.tsx b/components/Code/index.tsx index 650fc249d..a82cb13d3 100644 --- a/components/Code/index.tsx +++ b/components/Code/index.tsx @@ -52,9 +52,11 @@ const Code = (props: Props) => { dark: theme === 'dark', }; - const code = value ?? children?.[0] ?? children ?? ''; + const code = value ?? (Array.isArray(children) ? children[0] : children) ?? ''; const highlightedCode = syntaxHighlighter && code ? syntaxHighlighter(code, language, codeOpts) : code; + console.log(JSON.stringify({ props }, null, 2)); + return ( <> {copyButtons && } diff --git a/package.json b/package.json index 44525b484..2148a408d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@readme/markdown", + "name": "@readme/mdx", "description": "ReadMe's React-based Markdown parser", "author": "Rafe Goldberg ", "version": "6.75.0-beta.30", diff --git a/tsconfig.json b/tsconfig.json index 51b05843d..fbc52a6d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,9 +15,9 @@ "moduleResolution": "Bundler", "outDir": "dist", "sourceMap": true, + "target": "ES2015", "types": ["vitest/globals"] }, "include": ["./index.ts", "./options.js", "./components", "./contexts", "./example", "./lib", "./processor"], "exclude": ["node_modules", "dist"] } - From 84a83479d8a044bd6e92da5bbebdf9d95c5e781d Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 15:02:31 -0700 Subject: [PATCH 5/9] fix code highlighting --- components/Code/index.tsx | 2 -- processor/transform/code-tabs.ts | 8 ++++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/Code/index.tsx b/components/Code/index.tsx index a82cb13d3..43ed0772b 100644 --- a/components/Code/index.tsx +++ b/components/Code/index.tsx @@ -55,8 +55,6 @@ const Code = (props: Props) => { const code = value ?? (Array.isArray(children) ? children[0] : children) ?? ''; const highlightedCode = syntaxHighlighter && code ? syntaxHighlighter(code, language, codeOpts) : code; - console.log(JSON.stringify({ props }, null, 2)); - return ( <> {copyButtons && } diff --git a/processor/transform/code-tabs.ts b/processor/transform/code-tabs.ts index d1bec6757..8bd6d1bc3 100644 --- a/processor/transform/code-tabs.ts +++ b/processor/transform/code-tabs.ts @@ -4,6 +4,14 @@ import { visit } from 'unist-util-visit'; import { NodeTypes } from '../../enums'; const codeTabs = () => tree => { + visit(tree, 'code', (node: Code) => { + const { lang, meta, value } = node; + + node.data = { + hProperties: { lang, meta, value }, + }; + }); + visit(tree, 'code', (node: Code, index: number, parent: BlockContent) => { if (parent.type === 'code-tabs' || !('children' in parent)) return; From 361862cc2fa2e4864f6ff8f00b118a0aa8e85f59 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 15:06:51 -0700 Subject: [PATCH 6/9] fix rdme transformer --- processor/transform/readme-components.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/processor/transform/readme-components.ts b/processor/transform/readme-components.ts index ba60d013a..bab425f10 100644 --- a/processor/transform/readme-components.ts +++ b/processor/transform/readme-components.ts @@ -32,6 +32,9 @@ const readmeComponents = position, type: 'code', value, + data: { + hProperties: { value, lang, meta }, + }, }; parent.children[index] = mdNode; From ec4d710adb46bd8027d45de191288bc749560cd8 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 17:21:36 -0700 Subject: [PATCH 7/9] Update __tests__/matchers.ts Co-authored-by: Jon Ursenbach --- __tests__/matchers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/__tests__/matchers.ts b/__tests__/matchers.ts index f812446f1..23f7902b1 100644 --- a/__tests__/matchers.ts +++ b/__tests__/matchers.ts @@ -3,7 +3,9 @@ import { map } from 'unist-util-map'; const removePosition = ({ position, ...node }) => node; -function toStrictEqualExceptPosition(received, expected) { +import type { ExpectationResult } from '@vitest/expect'; + +function toStrictEqualExceptPosition(received, expected): ExpectationResult { const { equals } = this; const receivedTrimmed = map(received, removePosition); const expectedTrimmed = map(expected, removePosition); From 05cc6c1ccc7ddae61c043bf185bff1fd26f4ca8a Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Wed, 15 May 2024 17:44:13 -0700 Subject: [PATCH 8/9] oops...I did it again --- __tests__/matchers.ts | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/__tests__/matchers.ts b/__tests__/matchers.ts index 23f7902b1..31767de42 100644 --- a/__tests__/matchers.ts +++ b/__tests__/matchers.ts @@ -1,11 +1,12 @@ import { expect } from 'vitest'; import { map } from 'unist-util-map'; -const removePosition = ({ position, ...node }) => node; - import type { ExpectationResult } from '@vitest/expect'; +import { Root, Node } from 'mdast'; + +const removePosition = ({ position, ...node }: Node) => node; -function toStrictEqualExceptPosition(received, expected): ExpectationResult { +function toStrictEqualExceptPosition(received: Root, expected: Root): ExpectationResult { const { equals } = this; const receivedTrimmed = map(received, removePosition); const expectedTrimmed = map(expected, removePosition); diff --git a/package.json b/package.json index 2148a408d..44525b484 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@readme/mdx", + "name": "@readme/markdown", "description": "ReadMe's React-based Markdown parser", "author": "Rafe Goldberg ", "version": "6.75.0-beta.30", From 2ac2967b239004b5d9400de913b940bab5009b78 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Thu, 16 May 2024 12:50:11 -0700 Subject: [PATCH 9/9] ignore custom components --- .../transformers/readme-components.test.ts | 15 +++++++++++++++ index.tsx | 19 +++++++++++++++---- processor/transform/readme-components.ts | 4 +++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/__tests__/transformers/readme-components.test.ts b/__tests__/transformers/readme-components.test.ts index cf35cb384..2adb51da0 100644 --- a/__tests__/transformers/readme-components.test.ts +++ b/__tests__/transformers/readme-components.test.ts @@ -82,4 +82,19 @@ Second expect(mdxTree).toStrictEqualExceptPosition(mdTree); }); + + it('does not convert components that have custom implementations', () => { + const mdx = ` + +`; + + const tree = mdast(mdx, { + components: { + Callout: () => null, + }, + }); + + expect(tree.children[0].type).toBe('mdxJsxFlowElement'); + expect(tree.children[0].name).toBe('Callout'); + }); }); diff --git a/index.tsx b/index.tsx index 8bbe8e45f..c8d38d0c5 100644 --- a/index.tsx +++ b/index.tsx @@ -22,11 +22,17 @@ import MdxSyntaxError from './errors/mdx-syntax-error'; const unimplemented = debug('mdx:unimplemented'); +type ComponentOpts = Record React.ReactNode>; + type RunOpts = Omit & { - components?: Record React.ReactNode>; + components?: ComponentOpts; imports?: Record; }; +type MdastOpts = { + components?: ComponentOpts; +}; + export { Components }; export const utils = { @@ -72,7 +78,8 @@ export const compile = (text: string, opts = {}) => { }), ).replace(/await import\(_resolveDynamicMdxSpecifier\('react'\)\)/, 'arguments[0].imports.React'); } catch (error) { - throw new MdxSyntaxError(error, text); + console.error(error); + throw error.line ? new MdxSyntaxError(error, text) : error; } }; @@ -105,9 +112,13 @@ export const html = (text: string, opts = {}) => { }; const astProcessor = (opts = {}) => - remark().use(remarkMdx).use(remarkFrontmatter).use(remarkPlugins).use(readmeComponentsTransformer, {}); + remark() + .use(remarkMdx) + .use(remarkFrontmatter) + .use(remarkPlugins) + .use(readmeComponentsTransformer, { components: opts.components }); -export const mdast: any = (text: string, opts = {}) => { +export const mdast: any = (text: string, opts: MdastOpts = {}) => { const processor = astProcessor(opts); const tree = processor.parse(text); diff --git a/processor/transform/readme-components.ts b/processor/transform/readme-components.ts index bab425f10..b0f5255b8 100644 --- a/processor/transform/readme-components.ts +++ b/processor/transform/readme-components.ts @@ -19,9 +19,11 @@ const attributes = (jsx: MdxJsxFlowElement) => }, {}); const readmeComponents = - ({ components }) => + ({ components = {} } = {}) => (tree: Root) => { visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => { + if (node.name in components) return; + if (node.name === 'Code') { const { position } = node; const { value, lang = null, meta = null } = attributes(node);