Skip to content

Commit

Permalink
chore(contented-pipeline-md): restrict what remark can be parsed
Browse files Browse the repository at this point in the history
  • Loading branch information
fuxingloh committed Sep 28, 2023
1 parent 4300e42 commit 305138e
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 2 deletions.
45 changes: 45 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions packages/contented-pipeline-md/fixtures/RemarkDirective.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# RemarkDirective

::::codeblock-group

:::codeblock-header{filename="example.ts" language="TypeScript"}

```ts
const a: number = 1;
const b: number = 2;
```

:::

:::codeblock-header{filename="example.js" language="JavaScript"}

```js
const a = 1;
const b = 2;
```

:::

::::

```txt
case 'div':
case 'table':
case 'tr':
case 'td':
case 'th':
case 'tbody':
case 'thead':
case 'tfoot':
```

Should only parse the tags above.

:::div{onClick="alert('clicked')"}
Should not parse onClick.
:::

:::script
Should not parse script tags.
:::

:::div{.admonitions.green}
This is `div{.admonitions.green}`.
:::
2 changes: 1 addition & 1 deletion packages/contented-pipeline-md/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@jsdevtools/rehype-toc": "^3.0.2",
"@sindresorhus/slugify": "^2.2.1",
"hast-util-to-string": "^2.0.0",
"hastscript": "^7.0.0",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"minimatch": "^5.1.6",
Expand All @@ -55,7 +56,6 @@
"rehype-slug": "^5.1.0",
"rehype-stringify": "^9.0.4",
"remark-directive": "^2.0.1",
"remark-directive-rehype": "^0.4.2",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"remark-parse": "^10.0.2",
Expand Down
35 changes: 35 additions & 0 deletions packages/contented-pipeline-md/src/MarkdownPipeline.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,39 @@ describe('Without Config', () => {
},
]);
});

it('should process RemarkDirective.md', async () => {
const content = await pipeline.process(rootPath, 'RemarkDirective.md');
expect(content).toStrictEqual([
{
type: 'Markdown',
fields: {
description: expect.any(String),
title: 'RemarkDirective',
},
headings: expect.any(Array),
path: '/remark-directive',
sections: [],
fileId: expect.stringMatching(/[0-f]{64}/),
modifiedDate: expect.any(Number),
html:
'<nav class="toc"><ol class="toc-level toc-level-1"><li class="toc-item toc-item-h1"><a class="toc-link toc-link-h1" href="#remarkdirective">RemarkDirective</a></li></ol></nav><h1 id="remarkdirective"><a aria-hidden="true" tabindex="-1" href="#remarkdirective"><span class="icon icon-link"></span></a>RemarkDirective</h1>\n' +
'<div class="codeblock-group"><nav data-groups="[{&#x22;filename&#x22;:&#x22;example.ts&#x22;,&#x22;language&#x22;:&#x22;TypeScript&#x22;},{&#x22;filename&#x22;:&#x22;example.js&#x22;,&#x22;language&#x22;:&#x22;JavaScript&#x22;}]"><span class="codeblock-filename">example.ts</span><span class="codeblock-language"><button class="codeblock-language-selected">TypeScript</button><div class="codeblock-language-options"><div><button>TypeScript</button><button>JavaScript</button></div></div></span></nav><div class="codeblock-header" data-language="TypeScript" style=""><header><span class="codeblock-filename">example.ts</span><span class="codeblock-language">TypeScript</span></header><div class="rehype-shiki"><pre class="shiki css-variable" style="background-color: var(--shiki-color-background)"><code><span class="line"><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">a</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">number</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span><span style="color: var(--shiki-color-text)">;</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">b</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">number</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">2</span><span style="color: var(--shiki-color-text)">;</span></span></code></pre></div></div><div class="codeblock-header" data-language="JavaScript" style="display: none;"><header><span class="codeblock-filename">example.js</span><span class="codeblock-language">JavaScript</span></header><div class="rehype-shiki"><pre class="shiki css-variable" style="background-color: var(--shiki-color-background)"><code><span class="line"><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">a</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span><span style="color: var(--shiki-color-text)">;</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">b</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">2</span><span style="color: var(--shiki-color-text)">;</span></span></code></pre></div></div></div>\n' +
'<div class="rehype-shiki"><pre class="shiki css-variable" style="background-color: var(--shiki-color-background)"><code><span class="line"><span style="color: var(--shiki-color-text)">case \'div\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'table\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'tr\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'td\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'th\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'tbody\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'thead\':</span></span>\n' +
'<span class="line"><span style="color: var(--shiki-color-text)">case \'tfoot\':</span></span></code></pre></div>\n' +
'<p>Should only parse the tags above.</p>\n' +
'<div><p>Should not parse onClick.</p></div>\n' +
'<div><p>Should not parse script tags.</p></div>\n' +
'<div class="admonitions green"><p>This is <code>div{.admonitions.green}</code>.</p></div>',
},
]);
});
});
2 changes: 1 addition & 1 deletion packages/contented-pipeline-md/src/UnifiedProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import rehypeExternalLinks from 'rehype-external-links';
import rehypeSlug from 'rehype-slug';
import rehypeStringify from 'rehype-stringify';
import remarkDirective from 'remark-directive';
import remarkDirectiveRehype from 'remark-directive-rehype';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkParse from 'remark-parse';
Expand All @@ -18,6 +17,7 @@ import {
remarkDirectiveRehypeCodeblockGroup,
remarkDirectiveRehypeCodeblockHeader,
} from './plugins/RemarkCodeblock.js';
import { remarkDirectiveRehype } from './plugins/RemarkDirectiveRehype.js';
import { collectFields, resolveFields, validateFields } from './plugins/RemarkFrontmatter.js';
import { remarkLink } from './plugins/RemarkLink.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { h } from 'hastscript';
import type { Directive as DirectiveNode } from 'mdast-util-directive';
import type { Plugin } from 'unified';
import type { Node } from 'unist';
import type { MapFunction } from 'unist-util-map';
import { map } from 'unist-util-map';

const isDirectiveNode = (node: Node): node is DirectiveNode => {
const { type } = node;
return type === 'textDirective' || type === 'leafDirective' || type === 'containerDirective';
};

function parseHastProperties(node: DirectiveNode): { tagName: string; properties: Record<string, string> } {
// @ts-ignore
return h(node.name, node.attributes);
}

function isTagAllowed(tagName: string): boolean {
switch (tagName) {
case 'div':
case 'table':
case 'tr':
case 'td':
case 'th':
case 'tbody':
case 'thead':
case 'tfoot':
return true;
default:
return false;
}
}

const mapDirectiveNode: MapFunction = (node) => {
if (isDirectiveNode(node)) {
const { tagName, properties } = parseHastProperties(node);

if (!isTagAllowed(tagName)) {
return node;
}

return {
...node,
data: {
hName: tagName,
hProperties: {
className: properties?.className,
},
},
};
}

return node;
};

export function remarkDirectiveRehype(): Plugin {
return (nodeTree) => map(nodeTree, mapDirectiveNode);
}

0 comments on commit 305138e

Please sign in to comment.