Skip to content

Commit

Permalink
enable plugins with transforms (#305)
Browse files Browse the repository at this point in the history
* enable transform plugins

* remove old comment

* update test and snapshot, fix variable scope issue

* fix list rendering. add node position fallback

* minor changes and formatting

* update raw source position snapshot

* minor refactor, add dev comments

* add table of contents plugin to demo & test suite

* use simpler way of accessing parent node
  • Loading branch information
frankieali authored and rexxars committed Sep 2, 2019
1 parent c727761 commit c63dccb
Show file tree
Hide file tree
Showing 9 changed files with 2,358 additions and 24 deletions.
2,211 changes: 2,197 additions & 14 deletions demo/dist/js/demo.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions demo/src/demo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const React = require('react')
const ReactDOM = require('react-dom')
const toc = require('remark-toc')
const Markdown = require('../../src/with-html')
const Editor = require('./editor')
const CodeBlock = require('./code-block')
Expand All @@ -10,6 +11,8 @@ const initialSource = `
Changes are automatically rendered as you type.
## Table of Contents
* Implements [GitHub Flavored Markdown](https://github.github.com/gfm/)
* Renders actual, "native" React DOM elements
* Allows you to escape or skip HTML (try toggling the checkboxes above)
Expand Down Expand Up @@ -87,6 +90,7 @@ class Demo extends React.PureComponent {
skipHtml={this.state.htmlMode === 'skip'}
escapeHtml={this.state.htmlMode === 'escape'}
renderers={{code: CodeBlock}}
plugins={[toc]}
/>
</div>
</div>
Expand Down
56 changes: 56 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"react-test-renderer": "^16.8.6",
"remark-breaks": "^1.0.2",
"remark-shortcodes": "^0.2.1",
"remark-toc": "^5.1.1",
"rimraf": "^2.6.3",
"uglify-js": "^3.5.4",
"webpack": "^4.30.0",
Expand Down
14 changes: 13 additions & 1 deletion src/ast-to-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@
const React = require('react')
const xtend = require('xtend')

const defaultNodePosition = {
start: {line: 1, column: 1, offset: 0},
end: {line: 1, column: 1, offset: 0}
}

function astToReact(node, options, parent = {}, index = 0) {
const renderer = options.renderers[node.type]

// nodes generated by plugins may not have position data
// much of the code after this point will attempt to access properties of the node.position
// this will set the node position to the parent node's position to prevent errors
if (node.position === undefined) {
node.position = (parent.node && parent.node.position) || defaultNodePosition
}

const pos = node.position.start
const key = [node.type, pos.line, pos.column].join('-')
const key = [node.type, pos.line, pos.column, index].join('-')

if (
typeof renderer !== 'function' &&
Expand Down
6 changes: 4 additions & 2 deletions src/react-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const ReactMarkdown = function ReactMarkdown(props) {
renderers: renderers,
definitions: getDefinitions(rawAst)
})

const astPlugins = determineAstPlugins(props)
const ast = astPlugins.reduce((node, plugin) => plugin(node, renderProps), rawAst)
// eslint-disable-next-line no-sync
const transformedAst = parser.runSync(rawAst)
const ast = astPlugins.reduce((node, plugin) => plugin(node, renderProps), transformedAst)

return astToReact(ast, renderProps)
}
Expand Down
4 changes: 2 additions & 2 deletions src/renderers.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function Heading(props) {

function List(props) {
const attrs = getCoreProps(props)
if (props.start !== null && props.start !== 1) {
if (props.start !== null && props.start !== 1 && props.start !== undefined) {
attrs.start = props.start.toString()
}

Expand All @@ -77,7 +77,7 @@ function List(props) {

function ListItem(props) {
let checkbox = null
if (props.checked !== null) {
if (props.checked !== null && props.checked !== undefined) {
const checked = props.checked
checkbox = createElement('input', {type: 'checkbox', checked, readOnly: true})
}
Expand Down
67 changes: 64 additions & 3 deletions test/__snapshots__/react-markdown.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1851,7 +1851,7 @@ Object {
"children": Array [
<TextRenderer
index={0}
nodeKey="text-1-1"
nodeKey="text-1-1-0"
parentChildCount={1}
value="Foo"
>
Expand All @@ -1868,7 +1868,7 @@ Object {
"children": Array [
<TextRenderer
index={0}
nodeKey="text-3-1"
nodeKey="text-3-1-0"
parentChildCount={1}
value="Bar"
>
Expand All @@ -1885,7 +1885,7 @@ Object {
"children": Array [
<TextRenderer
index={0}
nodeKey="text-5-1"
nodeKey="text-5-1-0"
parentChildCount={1}
value="Baz"
>
Expand Down Expand Up @@ -1953,6 +1953,67 @@ exports[`should render link references 1`] = `"<p>Stuff were changed in <a href=
exports[`should render partial tables 1`] = `"<p>User is writing a table by hand</p><table><thead><tr><th>Test</th><th>Test</th></tr></thead></table>"`;
exports[`should render table of contents plugin 1`] = `
Array [
<h1>
Header
</h1>,
<h2>
Table of Contents
</h2>,
<ul>
<li>
<p>
<a
href="#first-section"
>
First Section
</a>
</p>
</li>
<li>
<p>
<a
href="#second-section"
>
Second Section
</a>
</p>
<ul>
<li>
<a
href="#subsection"
>
Subsection
</a>
</li>
</ul>
</li>
<li>
<p>
<a
href="#third-section"
>
Third Section
</a>
</p>
</li>
</ul>,
<h2>
First Section
</h2>,
<h2>
Second Section
</h2>,
<h3>
Subsection
</h3>,
<h2>
Third Section
</h2>,
]
`;

exports[`should render tables 1`] = `"<p>Languages are fun, right?</p><table><thead><tr><th style=\\"text-align:left\\">ID</th><th style=\\"text-align:center\\">English</th><th style=\\"text-align:right\\">Norwegian</th><th>Italian</th></tr></thead><tbody><tr><td style=\\"text-align:left\\">1</td><td style=\\"text-align:center\\">one</td><td style=\\"text-align:right\\">en</td><td>uno</td></tr><tr><td style=\\"text-align:left\\">2</td><td style=\\"text-align:center\\">two</td><td style=\\"text-align:right\\">to</td><td>due</td></tr><tr><td style=\\"text-align:left\\">3</td><td style=\\"text-align:center\\">three</td><td style=\\"text-align:right\\">tre</td><td>tre</td></tr></tbody></table>"`;
exports[`should sanititize language strings in code blocks 1`] = `
Expand Down
19 changes: 17 additions & 2 deletions test/react-markdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const shortcodes = require('remark-shortcodes')
const htmlParser = require('../src/plugins/html-parser')
const Markdown = require('../src/react-markdown')
const MarkdownWithHtml = require('../src/with-html')
const toc = require('remark-toc')

Enzyme.configure({adapter: new Adapter()})

Expand Down Expand Up @@ -754,8 +755,8 @@ test('should be able to override text renderer', () => {
})

test('should pass the key to an overriden text renderer', () => {
const textRenderer = props => {
expect(props.nodeKey).toEqual('text-1-1')
const textRenderer = (props) => {
expect(props.nodeKey).toEqual('text-1-1-0')
return <marquee key={props.nodeKey}>{props.children}</marquee>
}

Expand Down Expand Up @@ -785,3 +786,17 @@ test('should be able to override remark-parse plugin options', () => {
expect(pedantic.toJSON()).toMatchSnapshot()
expect(unscholarly.toJSON()).not.toBe(pedantic.toJSON())
})

test('should render table of contents plugin', () => {
const input = [
'# Header',
'## Table of Contents',
'## First Section',
'## Second Section',
'### Subsection',
'## Third Section'
].join('\n')

const component = renderer.create(<Markdown source={input} plugins={[toc]} />)
expect(component.toJSON()).toMatchSnapshot()
})

0 comments on commit c63dccb

Please sign in to comment.