diff --git a/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-html-tests-without-surprises-1-snap.png b/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-html-tests-without-surprises-1-snap.png new file mode 100644 index 000000000..458ebc6d9 Binary files /dev/null and b/__tests__/browser/__image_snapshots__/markdown-test-js-visual-regression-tests-rdmd-syntax-renders-html-tests-without-surprises-1-snap.png differ diff --git a/__tests__/browser/markdown.test.js b/__tests__/browser/markdown.test.js index 31b75892c..5c3536d16 100644 --- a/__tests__/browser/markdown.test.js +++ b/__tests__/browser/markdown.test.js @@ -18,6 +18,7 @@ describe('visual regression tests', () => { //'features', // 'headings', 'images', + 'htmlTests', // 'lists', 'mdxComponents', 'tables', diff --git a/__tests__/components/HTMLBlock.test.jsx b/__tests__/components/HTMLBlock.test.jsx deleted file mode 100644 index 308cae104..000000000 --- a/__tests__/components/HTMLBlock.test.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; -import { renderToString } from 'react-dom/server'; -import { vi } from 'vitest'; - -import createHTMLBlock from '../../components/HTMLBlock'; -import { compile, run } from '../../index'; -import createSchema from '../../sanitize.schema'; - -const HTMLBlock = createHTMLBlock(createSchema(), {}); - -describe.skip('HTML Block', () => { - beforeEach(() => { - global.window = true; - global.mockFn = vi.fn(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('runs user scripts in compat mode', () => { - render(); - expect(global.mockFn).toHaveBeenCalledTimes(1); - }); - - it("doesn't run user scripts by default", () => { - render(); - expect(global.mockFn).toHaveBeenCalledTimes(0); - }); - - it("doesn't render user scripts by default", () => { - render(); - - expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); - }); - - it("doesn't render user scripts with weird endings", () => { - render(); - - expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); - }); - - it("doesn't render user scripts with a malicious string", () => { - render(); - - expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); - }); - - it("doesn't run scripts on the server (even in compat mode)", () => { - const html = ` -

Hello World

- - `; - const elem = ; - const view = renderToString(elem); - expect(elem.props.runScripts).toBe(true); - expect(view.indexOf('`}); + expect(global.mockFn).toHaveBeenCalledTimes(1); + }); + + it("doesn't run user scripts by default", () => { + render({``}); + expect(global.mockFn).toHaveBeenCalledTimes(0); + }); + + it("doesn't render user scripts by default", () => { + render({``}); + expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); + }); + + it("doesn't render user scripts with weird endings", () => { + render({``}); + expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); + }); + + it("doesn't render user scripts with a malicious string", () => { + render({`t>mockFn()cript>`}); + expect(screen.queryByText('mockFn()')).not.toBeInTheDocument(); + }); + + it("doesn't run scripts on the server (even in compat mode)", () => { + const html = ` +

Hello World

+ + `; + const elem = {html}; + const view = renderToString(elem); + expect(elem.props.runScripts).toBe(true); + expect(view.indexOf('\n\nHello!\n\n', -})} -[/block] - -[block:parameters] -${JSON.stringify({ - data: { - '0-0': "```js Tab Zed\nconsole.log('tab zed');\n```\n```js Tab One\nconsole.log('tab one')\n```", - '0-1': - '> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime repellat placeat expedita voluptatum fugiat rerum, accusamus eius dolorum sequi eveniet esse, adipisci soluta quia mollitia? Dolorem minus, dolores, rerum, pariatur sit quia eum esse voluptatibus ea veritatis non.', - '0-2': '', - 'h-0': 'Hello', - 'h-1': 'Bonjour', - 'h-2': 'Willkommen', - }, - cols: 2, - rows: 1, -})} -[/block]`; - - let rdmd; - let container; - beforeEach(() => { - rdmd = markdown.react(tabs, { compatibilityMode: true }); - // eslint-disable-next-line testing-library/no-render-in-setup - ({ container } = render(rdmd)); - }); - - it('Should use h1 tags for magic heading blocks.', () => expect(container.querySelectorAll('h1')).toHaveLength(1)); - - it('Should allow block-level RDMD compoonents in tables.', () => { - const table = container.querySelector('table'); - expect(table.querySelectorAll('.CodeTabs')).toHaveLength(1); - expect(table.querySelectorAll('blockquote')).toHaveLength(1); - }); -}); diff --git a/components/HTMLBlock/index.jsx b/components/HTMLBlock/index.jsx deleted file mode 100644 index 102a721f2..000000000 --- a/components/HTMLBlock/index.jsx +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable no-eval - */ -const escape = require('lodash.escape'); -const PropTypes = require('prop-types'); -const React = require('react'); - -const MATCH_SCRIPT_TAGS = /]*>([\s\S]*?)<\/script *>\n?/gim; - -const extractScripts = (html = '') => { - const scripts = []; - let match; - while ((match = MATCH_SCRIPT_TAGS.exec(html)) !== null) { - scripts.push(match[1]); - } - const cleaned = html.replace(MATCH_SCRIPT_TAGS, ''); - return [cleaned, () => scripts.map(js => window.eval(js))]; -}; - -class HTMLBlock extends React.PureComponent { - constructor(props) { - super(props); - [this.html, this.exec] = extractScripts(this.props.html); - } - - componentDidMount() { - const { runScripts } = this.props; - if (typeof window !== 'undefined' && (runScripts === '' || runScripts)) this.exec(); - } - - render() { - const { html, safeMode } = this.props; - - if (safeMode) { - return ( -
-          
-        
- ); - } - - return
; - } -} - -HTMLBlock.defaultProps = { - runScripts: false, - safeMode: false, -}; - -HTMLBlock.propTypes = { - html: PropTypes.string, - runScripts: PropTypes.any, - safeMode: PropTypes.bool, -}; - -const CreateHtmlBlock = - ({ safeMode }) => - // eslint-disable-next-line react/display-name - props => - ; - -CreateHtmlBlock.sanitize = schema => { - schema.tagNames.push('html-block'); - schema.attributes['html-block'] = ['html', 'runScripts']; - - return schema; -}; - -module.exports = CreateHtmlBlock; diff --git a/components/HTMLBlock/index.tsx b/components/HTMLBlock/index.tsx new file mode 100644 index 000000000..e4e891204 --- /dev/null +++ b/components/HTMLBlock/index.tsx @@ -0,0 +1,36 @@ +import React, { useEffect } from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; + +const MATCH_SCRIPT_TAGS = /]*>([\s\S]*?)<\/script *>\n?/gim; + +const extractScripts = (html: string = ''): [string, () => void] => { + const scripts: string[] = []; + let match: RegExpExecArray | null; + while ((match = MATCH_SCRIPT_TAGS.exec(html)) !== null) { + scripts.push(match[1]); + } + const cleaned = html.replace(MATCH_SCRIPT_TAGS, ''); + return [cleaned, () => scripts.map(js => window.eval(js))]; +}; + +const HTMLBlock = ({ children = '', runScripts = false, safeMode = false }) => { + let html = children; + if (typeof html !== 'string') html = renderToStaticMarkup(html); + const [cleanedHtml, exec] = extractScripts(html); + + useEffect(() => { + if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts) exec(); + }, [runScripts, exec]); + + if (safeMode) { + return ( +
+        {html}
+      
+ ); + } + + return
; +}; + +export default HTMLBlock; diff --git a/components/Image/index.tsx b/components/Image/index.tsx index 14faac973..7dd90af6b 100644 --- a/components/Image/index.tsx +++ b/components/Image/index.tsx @@ -1,34 +1,19 @@ import * as React from 'react'; -interface Props extends React.PropsWithChildren> { - align: string; - alt: string; - border: boolean; - caption: string; - className: string; - height: string; - lazy: boolean; - src: string; - title: string; - width: string; -} - -const Image = (props: Props) => { +const Image = ({ + align = '', + alt = '', + border = false, + caption, + className = '', + height = 'auto', + src = '', + title = '', + width = 'auto', + lazy = false, +}) => { const [lightbox, setLightbox] = React.useState(false); - const { - align = '', - alt = '', - border = false, - caption, - className = '', - height = 'auto', - lazy, - src = '', - title = '', - width = 'auto', - } = props; - if (className === 'emoji') { return {alt}; } diff --git a/docs/html-tests.md b/docs/html-tests.md new file mode 100644 index 000000000..0c6884379 --- /dev/null +++ b/docs/html-tests.md @@ -0,0 +1,71 @@ +--- +title: 'HTML Blocks' +--- + +## JSX (with no HTML attributes) + + + +

Header 0

+

Paragraph with italics and bold stuff.

+
I am actually a div!
+
+ + +## JSX + + + +

Header 1

+

Paragraph with italics and bold stuff.

+
+ Behold, I am blue and indented! +
+
+ + +## HTML as a prop + + + + +## HTML in template literal + + + {` + +

Header 3

+

Paragraph with italics and bold stuff.

+
+ Behold, I am blue and indented! +
+ `} +
+ + +## JSX in safe mode + + + +

Header 4

+

Paragraph with italics and bold stuff.

+
+ Behold, I am blue and indented! +
+
+ + +## HTML in safe mode + + +{` + +

Header 5

+

Paragraph with italics and bold stuff.

+
+ Behold, I am blue and indented! +
+`} +
\ No newline at end of file diff --git a/enums.ts b/enums.ts index aa398912f..a8e849ef0 100644 --- a/enums.ts +++ b/enums.ts @@ -3,4 +3,5 @@ export enum NodeTypes { emoji = 'emoji', i = 'i', image = 'image', + htmlBlock = 'html-block', } diff --git a/example/docs.ts b/example/docs.ts index 1d7956eae..20ba4a3d6 100644 --- a/example/docs.ts +++ b/example/docs.ts @@ -15,6 +15,8 @@ import gettingStarted from '../docs/getting-started.md'; // @ts-ignore import headings from '../docs/headings.md'; // @ts-ignore +import htmlTests from '../docs/html-tests.md'; +// @ts-ignore import images from '../docs/images.md'; // @ts-ignore import lists from '../docs/lists.md'; @@ -41,6 +43,7 @@ const fixtures = Object.entries({ features, gettingStarted, headings, + htmlTests, images, lists, mdxComponents, diff --git a/index.tsx b/index.tsx index bb8d8db63..d54c7354d 100644 --- a/index.tsx +++ b/index.tsx @@ -47,6 +47,7 @@ const makeUseMDXComponents = (more: RunOpts['components']) => { ...Components, Variable, 'code-tabs': Components.CodeTabs, + 'html-block': Components.HTMLBlock, img: Components.Image, table: Components.Table, }; diff --git a/package-lock.json b/package-lock.json index 02b78b8cc..849b49002 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,7 @@ "@types/mdast": "^4.0.3", "@types/mdx": "^2.0.12", "@vitejs/plugin-react": "^4.2.1", - "@vitest/ui": "^1.5.0", + "@vitest/ui": "^1.6.0", "babel-jest": "^29.5.0", "babel-loader": "^9.1.2", "browserify-fs": "^1.0.0", @@ -7532,13 +7532,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", - "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.0", - "@vitest/utils": "1.5.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -7546,12 +7546,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", - "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.0", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -7587,9 +7587,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", - "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -7627,15 +7627,15 @@ } }, "node_modules/@vitest/snapshot/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/@vitest/spy": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", - "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -7645,12 +7645,12 @@ } }, "node_modules/@vitest/ui": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.5.0.tgz", - "integrity": "sha512-ETcToK2TzICf/Oartvt19IH7yR4oCs8GrQk5hRhZ5oZFaSdDHTh6o3EdzyxOaY24NZ20cXYYNGjj1se/5vHfFg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.0", + "@vitest/utils": "1.6.0", "fast-glob": "^3.3.2", "fflate": "^0.8.1", "flatted": "^3.2.9", @@ -7662,13 +7662,13 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.5.0" + "vitest": "1.6.0" } }, "node_modules/@vitest/utils": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", - "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -7707,9 +7707,9 @@ } }, "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/@webassemblyjs/ast": { @@ -18735,15 +18735,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", - "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/make-dir": { @@ -29580,9 +29577,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", - "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -29602,16 +29599,16 @@ } }, "node_modules/vitest": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", - "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.0", - "@vitest/runner": "1.5.0", - "@vitest/snapshot": "1.5.0", - "@vitest/spy": "1.5.0", - "@vitest/utils": "1.5.0", + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -29625,7 +29622,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.5.0", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -29640,8 +29637,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.0", - "@vitest/ui": "1.5.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index b11a94051..98e58bfb4 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@types/mdast": "^4.0.3", "@types/mdx": "^2.0.12", "@vitejs/plugin-react": "^4.2.1", - "@vitest/ui": "^1.5.0", + "@vitest/ui": "^1.6.0", "babel-jest": "^29.5.0", "babel-loader": "^9.1.2", "browserify-fs": "^1.0.0", diff --git a/processor/compile/html-block.js b/processor/compile/html-block.js deleted file mode 100644 index 64e393e3c..000000000 --- a/processor/compile/html-block.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = function () { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors['html-block'] = node => { - const html = node.data.hProperties.html; - return `[block:html]\n${JSON.stringify({ html }, null, 2)}\n[/block]`; - }; -}; diff --git a/processor/compile/html-block.ts b/processor/compile/html-block.ts new file mode 100644 index 000000000..e9f4db9a3 --- /dev/null +++ b/processor/compile/html-block.ts @@ -0,0 +1,8 @@ +import { HTMLBlock } from '../../types'; + +const htmlBlock = (node: HTMLBlock) => { + const html = node.data.hProperties.html; + return `${JSON.stringify({ html }, null, 2)}`; +} + +export default htmlBlock; diff --git a/processor/compile/index.ts b/processor/compile/index.ts index 983543e46..81cbf5377 100644 --- a/processor/compile/index.ts +++ b/processor/compile/index.ts @@ -1,6 +1,7 @@ import gemoji from './gemoji'; import codeTabs from './code-tabs'; import image from './image'; +import htmlBlock from './html-block'; import { NodeTypes } from '../../enums'; function compilers() { @@ -12,9 +13,10 @@ function compilers() { [NodeTypes.emoji]: gemoji, [NodeTypes.codeTabs]: codeTabs, [NodeTypes.image]: image, + [NodeTypes.htmlBlock]: htmlBlock, }; toMarkdownExtensions.push({ extensions: [{ handlers }] }); -} +}; export default compilers; diff --git a/types.d.ts b/types.d.ts index 2fa54c023..f890fc74d 100644 --- a/types.d.ts +++ b/types.d.ts @@ -9,6 +9,16 @@ interface CodeTabs extends Parent { }; } +interface HTMLBlock extends Parent { + type: NodeTypes.htmlBlock; + data: Data & { + hName: 'html-block'; + hProperties: { + html: string; + }; + }; +} + interface Gemoji extends Literal { type: NodeTypes.emoji; name: string;