From a3b7f5035193fe271ccf8dfb546bed2189dbb3e2 Mon Sep 17 00:00:00 2001 From: Kelly Joseph Price Date: Mon, 9 Dec 2024 10:20:24 -0800 Subject: [PATCH] feat: add title props to plain (#1036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![PR App][icn]][demo] | Ref RM-11201 :-------------------:|:----------: ## 🧰 Changes Adds the `title` prop from our built ins to `plain` This will make content in the `title` indexable in the main app. ## 🧬 QA & Testing - [Broken on production][prod]. - [Working in this PR app][demo]. [demo]: https://markdown-pr-PR_NUMBER.herokuapp.com [prod]: https://SUBDOMAIN.readme.io [icn]: https://user-images.githubusercontent.com/886627/160426047-1bee9488-305a-4145-bb2b-09d8b757d38a.svg --- __tests__/lib/plain/custom-components.test.ts | 33 +++++++++++ lib/hast.ts | 8 ++- lib/plain.ts | 59 +++++++++++-------- processor/transform/index.ts | 14 +++-- processor/transform/mdx-to-hast.ts | 27 +++++++++ 5 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 __tests__/lib/plain/custom-components.test.ts create mode 100644 processor/transform/mdx-to-hast.ts diff --git a/__tests__/lib/plain/custom-components.test.ts b/__tests__/lib/plain/custom-components.test.ts new file mode 100644 index 000000000..34a5a5958 --- /dev/null +++ b/__tests__/lib/plain/custom-components.test.ts @@ -0,0 +1,33 @@ +import { hast, plain } from '../../../index'; + +describe('plain compiler', () => { + it('should include the title of Accordion', () => { + const mdx = ` + + Body + +`; + + expect(plain(hast(mdx))).toContain('Title Body'); + }); + + it('should include the title of Card', () => { + const mdx = ` + + Body + +`; + + expect(plain(hast(mdx))).toContain('Title Body'); + }); + + it('should include the title of Tab', () => { + const mdx = ` + + Body + +`; + + expect(plain(hast(mdx))).toContain('Title Body'); + }); +}); diff --git a/lib/hast.ts b/lib/hast.ts index 92c864166..9ec5ccec9 100644 --- a/lib/hast.ts +++ b/lib/hast.ts @@ -1,6 +1,6 @@ import astProcessor, { rehypePlugins, MdastOpts } from './ast-processor'; import remarkRehype from 'remark-rehype'; -import { injectComponents } from '../processor/transform'; +import { injectComponents, mdxToHast } from '../processor/transform'; import { MdastComponents } from '../types'; import mdast from './mdast'; @@ -10,7 +10,11 @@ const hast = (text: string, opts: MdastOpts = {}) => { return memo; }, {}); - const processor = astProcessor(opts).use(injectComponents({ components })).use(remarkRehype).use(rehypePlugins); + const processor = astProcessor(opts) + .use(injectComponents({ components })) + .use(mdxToHast) + .use(remarkRehype) + .use(rehypePlugins); return processor.runSync(processor.parse(text)); }; diff --git a/lib/plain.ts b/lib/plain.ts index 220814f93..9f775870b 100644 --- a/lib/plain.ts +++ b/lib/plain.ts @@ -21,32 +21,39 @@ function one(node: Nodes, opts: Options) { if ('tagName' in node) { if (STRIP_TAGS.includes(node.tagName)) return ''; - if (node.tagName === 'html-block') { - if (!node.properties.html) return ''; - return all(hast(node.properties.html.toString()), opts); - } - - if (node.tagName === 'rdme-callout') { - const { icon, title } = node.properties; - - const children = node?.children?.slice(title ? 1 : 0); - const body = children ? all({ type: 'root', children }, opts) : ''; - - return [icon, ' ', title, title && body && ': ', body].filter(Boolean).join(''); - } - - if (node.tagName === 'readme-glossary-item') { - return node.properties.term; - } - - if (node.tagName === 'readme-variable') { - const key = node.properties.variable.toString(); - const val = opts.variables[key]; - return val || `<<${key}>>`; - } - - if (node.tagName === 'img') { - return node.properties?.title || ''; + switch (node.tagName) { + case 'html-block': { + if (!node.properties.html) return ''; + return all(hast(node.properties.html.toString()), opts); + } + case 'rdme-callout': { + const { icon, title } = node.properties; + + const children = node?.children?.slice(title ? 1 : 0); + const body = children ? all({ type: 'root', children }, opts) : ''; + + return [icon, ' ', title, title && body && ': ', body].filter(Boolean).join(''); + } + case 'readme-glossary-item': { + return node.properties.term; + } + case 'readme-variable': { + const key = node.properties.variable.toString(); + const val = opts.variables[key]; + return val || `<<${key}>>`; + } + case 'img': { + return node.properties?.title || ''; + } + case 'Accordion': + case 'Card': + case 'Tab': { + const title = node.properties?.title || ''; + const children = node?.children; + const body = children ? all({ type: 'root', children }, opts) : ''; + + return [title, body].filter(Boolean).join(' '); + } } } diff --git a/processor/transform/index.ts b/processor/transform/index.ts index 7a9bc5898..a21491da0 100644 --- a/processor/transform/index.ts +++ b/processor/transform/index.ts @@ -4,24 +4,26 @@ import embedTransformer from './embeds'; import imageTransformer from './images'; import gemojiTransformer from './gemoji+'; +import compatabilityTransfomer from './compatability'; import divTransformer from './div'; import injectComponents from './inject-components'; +import mdxToHast from './mdx-to-hast'; +import mermaidTransformer from './mermaid'; import readmeComponentsTransformer from './readme-components'; import readmeToMdx from './readme-to-mdx'; -import variablesTransformer from './variables'; import tablesToJsx from './tables-to-jsx'; -import compatabilityTransfomer from './compatability'; -import mermaidTransformer from './mermaid'; +import variablesTransformer from './variables'; export { compatabilityTransfomer, divTransformer, + injectComponents, + mdxToHast, + mermaidTransformer, readmeComponentsTransformer, readmeToMdx, - injectComponents, - variablesTransformer, tablesToJsx, - mermaidTransformer, + variablesTransformer, }; export const defaultTransforms = { diff --git a/processor/transform/mdx-to-hast.ts b/processor/transform/mdx-to-hast.ts new file mode 100644 index 000000000..f6a6c1544 --- /dev/null +++ b/processor/transform/mdx-to-hast.ts @@ -0,0 +1,27 @@ +import { visit } from 'unist-util-visit'; +import { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx'; +import { Transform } from 'mdast-util-from-markdown'; +import { Parents } from 'mdast'; +import { getAttrs, isMDXElement } from '../utils'; +import * as Components from '../../components'; + +const setData = (node: MdxJsxFlowElement | MdxJsxTextElement, index: number, parent: Parents) => { + if (!node.name) return; + if (!(node.name in Components)) return; + + parent.children[index] = { + ...node, + data: { + hName: node.name, + hProperties: getAttrs(node), + }, + }; +}; + +const mdxToHast = (): Transform => tree => { + visit(tree, isMDXElement, setData); + + return tree; +}; + +export default mdxToHast;