Giter VIP home page Giter VIP logo

markdown-to-jsx's Introduction

markdown-to-jsx

The most lightweight, customizable React markdown component.

npm version downloads


markdown-to-jsx uses a heavily-modified fork of simple-markdown as its parsing engine and extends it in a number of ways to make your life easier. Notably, this package offers the following additional benefits:

  • Arbitrary HTML is supported and parsed into the appropriate JSX representation without dangerouslySetInnerHTML

  • Any HTML tags rendered by the compiler and/or <Markdown> component can be overridden to include additional props or even a different HTML representation entirely.

  • GFM task list support.

  • Fenced code blocks with highlight.js support; see Syntax highlighting for instructions on setting up highlight.js.

All this clocks in at around 6 kB gzipped, which is a fraction of the size of most other React markdown components.

Requires React >= 0.14.

Installation

Install markdown-to-jsx with your favorite package manager.

npm i markdown-to-jsx

Usage

markdown-to-jsx exports a React component by default for easy JSX composition:

ES6-style usage*:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

render(<Markdown># Hello world!</Markdown>, document.body)

/*
    renders:

    <h1>Hello world!</h1>
 */

* NOTE: JSX does not natively preserve newlines in multiline text. In general, writing markdown directly in JSX is discouraged and it's a better idea to keep your content in separate .md files and require them, perhaps using webpack's raw-loader.

Parsing Options

options.forceBlock

By default, the compiler will try to make an intelligent guess about the content passed and wrap it in a <div>, <p>, or <span> as needed to satisfy the "inline"-ness of the markdown. For instance, this string would be considered "inline":

Hello. _Beautiful_ day isn't it?

But this string would be considered "block" due to the existence of a header tag, which is a block-level HTML element:

# Whaddup?

However, if you really want all input strings to be treated as "block" layout, simply pass options.forceBlock = true like this:

;<Markdown options={{ forceBlock: true }}>Hello there old chap!</Markdown>

// or

compiler('Hello there old chap!', { forceBlock: true })

// renders
;<p>Hello there old chap!</p>

options.forceInline

The inverse is also available by passing options.forceInline = true:

;<Markdown options={{ forceInline: true }}># You got it babe!</Markdown>

// or

compiler('# You got it babe!', { forceInline: true })

// renders
;<span># You got it babe!</span>

options.wrapper

When there are multiple children to be rendered, the compiler will wrap the output in a div by default. You can override this default by setting the wrapper option to either a string (React Element) or a component.

const str = '# Heck Yes\n\nThis is great!'

<Markdown options={{ wrapper: 'article' }}>
  {str}
</Markdown>;

// or

compiler(str, { wrapper: 'article' });

// renders

<article>
  <h1>Heck Yes</h1>
  <p>This is great!</p>
</article>
Other useful recipes

To get an array of children back without a wrapper, set wrapper to null. This is particularly useful when using compiler(…) directly.

compiler('One\n\nTwo\n\nThree', { wrapper: null })

// returns
;[<p>One</p>, <p>Two</p>, <p>Three</p>]

To render children at the same DOM level as <Markdown> with no HTML wrapper, set wrapper to React.Fragment. This will still wrap your children in a React node for the purposes of rendering, but the wrapper element won't show up in the DOM.

options.forceWrapper

By default, the compiler does not wrap the rendered contents if there is only a single child. You can change this by setting forceWrapper to true. If the child is inline, it will not necessarily be wrapped in a span.

// Using `forceWrapper` with a single, inline child…
<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
  Mumble, mumble…
</Markdown>

// renders

<aside>Mumble, mumble…</aside>

options.overrides - Override Any HTML Tag's Representation

Pass the options.overrides prop to the compiler or <Markdown> component to seamlessly revise the rendered representation of any HTML tag. You can choose to change the component itself, add/change props, or both.

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

// surprise, it's a div instead!
const MyParagraph = ({ children, ...props }) => <div {...props}>{children}</div>

render(
  <Markdown
    options={{
      overrides: {
        h1: {
          component: MyParagraph,
          props: {
            className: 'foo',
          },
        },
      },
    }}
  >
    # Hello world!
  </Markdown>,
  document.body
)

/*
    renders:

    <div class="foo">
        Hello World
    </div>
 */

If you only wish to provide a component override, a simplified syntax is available:

{
    overrides: {
        h1: MyParagraph,
    },
}

Depending on the type of element, there are some props that must be preserved to ensure the markdown is converted as intended. They are:

  • a: title, href
  • img: title, alt, src
  • input[type="checkbox"]: checked, readonly (specifically, the one rendered by a GFM task list)
  • ol: start
  • td: style
  • th: style

Any conflicts between passed props and the specific properties above will be resolved in favor of markdown-to-jsx's code.

Some element mappings are a bit different from other libraries, in particular:

  • span: Used for inline text.
  • code: Used for inline code.
  • pre > code: Code blocks are a code element with a pre as its direct ancestor.

options.overrides - Rendering Arbitrary React Components

One of the most interesting use cases enabled by the HTML syntax processing in markdown-to-jsx is the ability to use any kind of element, even ones that aren't real HTML tags like React component classes.

By adding an override for the components you plan to use in markdown documents, it's possible to dynamically render almost anything. One possible scenario could be writing documentation:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

import DatePicker from './date-picker'

const md = `
# DatePicker

The DatePicker works by supplying a date to bias towards,
as well as a default timezone.

<DatePicker biasTowardDateTime="2017-12-05T07:39:36.091Z" timezone="UTC+5" />
`

render(
  <Markdown
    children={md}
    options={{
      overrides: {
        DatePicker: {
          component: DatePicker,
        },
      },
    }}
  />,
  document.body
)

markdown-to-jsx also handles JSX interpolation syntax, but in a minimal way to not introduce a potential attack vector. Interpolations are sent to the component as their raw string, which the consumer can then eval() or process as desired to their security needs.

In the following case, DatePicker could simply run parseInt() on the passed startTime for example:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

import DatePicker from './date-picker'

const md = `
# DatePicker

The DatePicker works by supplying a date to bias towards,
as well as a default timezone.

<DatePicker
  biasTowardDateTime="2017-12-05T07:39:36.091Z"
  timezone="UTC+5"
  startTime={1514579720511}
/>
`

render(
  <Markdown
    children={md}
    options={{
      overrides: {
        DatePicker: {
          component: DatePicker,
        },
      },
    }}
  />,
  document.body
)

Another possibility is to use something like recompose's withProps() HOC to create various pregenerated scenarios and then reference them by name in the markdown:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'
import withProps from 'recompose/withProps'

import DatePicker from './date-picker'

const DecemberDatePicker = withProps({
  range: {
    start: new Date('2017-12-01'),
    end: new Date('2017-12-31'),
  },
  timezone: 'UTC+5',
})(DatePicker)

const md = `
# DatePicker

The DatePicker works by supplying a date to bias towards,
as well as a default timezone.

<DatePicker
  biasTowardDateTime="2017-12-05T07:39:36.091Z"
  timezone="UTC+5"
  startTime={1514579720511}
/>

Here's an example of a DatePicker pre-set to only the month of December:

<DecemberDatePicker />
`

render(
  <Markdown
    children={md}
    options={{
      overrides: {
        DatePicker,
        DecemberDatePicker,
      },
    }}
  />,
  document.body
)

options.createElement - Custom React.createElement behavior

Sometimes, you might want to override the React.createElement default behavior to hook into the rendering process before the JSX gets rendered. This might be useful to add extra children or modify some props based on runtime conditions. The function mirrors the React.createElement function, so the params are type, [props], [...children]:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

const md = `
# Hello world
`

render(
  <Markdown
    children={md}
    options={{
      createElement(type, props, children) {
        return (
          <div className="parent">
            {React.createElement(type, props, children)}
          </div>
        )
      },
    }}
  />,
  document.body
)

options.enforceAtxHeadings

Forces the compiler to have space between hash sign # and the header text which is explicitly stated in the most of the markdown specs.

The opening sequence of # characters must be followed by a space or by the end of line.

options.renderRule

Supply your own rendering function that can selectively override how rules are rendered (note, this is different than options.overrides which operates at the HTML tag level and is more general). You can use this functionality to do pretty much anything with an established AST node; here's an example of selectively overriding the "codeBlock" rule to process LaTeX syntax using the @matejmazur/react-katex library:

import Markdown, { RuleType } from 'markdown-to-jsx'
import TeX from '@matejmazur/react-katex'

const exampleContent =
  'Some important formula:\n\n```latex\nmathbb{N} = { a in mathbb{Z} : a > 0 }\n```\n'

function App() {
  return (
    <Markdown
      children={exampleContent}
      options={{
        renderRule(next, node, renderChildren, state) {
          if (node.type === RuleType.codeBlock && node.lang === 'latex') {
            return (
              <TeX as="div" key={state.key}>{String.raw`${node.text}`}</TeX>
            )
          }

          return next()
        },
      }}
    />
  )
}

options.slugify

By default, a lightweight deburring function is used to generate an HTML id from headings. You can override this by passing a function to options.slugify. This is helpful when you are using non-alphanumeric characters (e.g. Chinese or Japanese characters) in headings. For example:

;<Markdown options={{ slugify: str => str }}># 中文</Markdown>

// or

compiler('# 中文', { slugify: str => str })

// renders:
;<h1 id="中文">中文</h1>

options.namedCodesToUnicode

By default only a couple of named html codes are converted to unicode characters:

  • & (&amp;)
  • ' (&apos;)
  • > (&gt;)
  • < (&lt;)
  • (&nbsp;)
  • " (&quot;)

Some projects require to extend this map of named codes and unicode characters. To customize this list with additional html codes pass the option namedCodesToUnicode as object with the code names needed as in the example below:

<Markdown options={{ namedCodesToUnicode: {
    le: '\u2264',
    ge: '\u2265',
    '#39': '\u0027',
} }}>This text is &le; than this text.</Markdown>;

// or

compiler('This text is &le; than this text.', namedCodesToUnicode: {
    le: '\u2264',
    ge: '\u2265',
    '#39': '\u0027',
});

// renders:

<p>This text is ≤ than this text.</p>

options.disableParsingRawHTML

By default, raw HTML is parsed to JSX. This behavior can be disabled with this option.

<Markdown options={{ disableParsingRawHTML: true }}>
    This text has <span>html</span> in it but it won't be rendered
</Markdown>;

// or

compiler('This text has <span>html</span> in it but it won't be rendered', { disableParsingRawHTML: true });

// renders:

<span>This text has &lt;span&gt;html&lt;/span&gt; in it but it won't be rendered</span>

Syntax highlighting

When using fenced code blocks with language annotation, that language will be added to the <code> element as class="lang-${language}". For best results, you can use options.overrides to provide an appropriate syntax highlighting integration like this one using highlight.js:

import { Markdown, RuleType } from 'markdown-to-jsx'

const mdContainingFencedCodeBlock = '```js\nconsole.log("Hello world!");\n```\n'

function App() {
  return (
    <Markdown
      children={mdContainingFencedCodeBlock}
      options={{
        overrides: {
          code: SyntaxHighlightedCode,
        },
      }}
    />
  )
}

/**
 * Add the following tags to your page <head> to automatically load hljs and styles:

  <link
    rel="stylesheet"
    href="https://unpkg.com/@highlightjs/[email protected]/styles/nord.min.css"
  />

  * NOTE: for best performance, load individual languages you need instead of all
          of them. See their docs for more info: https://highlightjs.org/

  <script
    crossorigin
    src="https://unpkg.com/@highlightjs/[email protected]/highlight.min.js"
  ></script>
 */

function SyntaxHighlightedCode(props) {
  const ref = (React.useRef < HTMLElement) | (null > null)

  React.useEffect(() => {
    if (ref.current && props.className?.includes('lang-') && window.hljs) {
      window.hljs.highlightElement(ref.current)

      // hljs won't reprocess the element unless this attribute is removed
      ref.current.removeAttribute('data-highlighted')
    }
  }, [props.className, props.children])

  return <code {...props} ref={ref} />
}

Getting the smallest possible bundle size

Many development conveniences are placed behind process.env.NODE_ENV !== "production" conditionals. When bundling your app, it's a good idea to replace these code snippets such that a minifier (like uglify) can sweep them away and leave a smaller overall bundle.

Here are instructions for some of the popular bundlers:

Usage with Preact

Everything will work just fine! Simply Alias react to preact/compat like you probably already are doing.

Gotchas

Significant indentation inside arbitrary HTML

People usually write HTML like this:

<div>Hey, how are you?</div>

Note the leading spaces before the inner content. This sort of thing unfortunately clashes with existing markdown syntaxes since 4 spaces === a code block and other similar collisions.

To get around this, markdown-to-jsx left-trims approximately as much whitespace as the first line inside the HTML block. So for example:

<div># Hello How are you?</div>

The two leading spaces in front of "# Hello" would be left-trimmed from all lines inside the HTML block. In the event that there are varying amounts of indentation, only the amount of the first line is trimmed.

NOTE! These syntaxes work just fine when you aren't writing arbitrary HTML wrappers inside your markdown. This is very much an edge case of an edge case. 🙃

Code blocks

⛔️

<div>
    var some = code();
</div>

<div>
```js
var some = code();
``\`
</div>

Using The Compiler Directly

If desired, the compiler function is a "named" export on the markdown-to-jsx module:

import { compiler } from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

render(compiler('# Hello world!'), document.body)

/*
    renders:

    <h1>Hello world!</h1>
 */

It accepts the following arguments:

compiler(markdown: string, options: object?)

Changelog

See Github Releases.

Donate

Like this library? It's developed entirely on a volunteer basis; chip in a few bucks if you can via the Sponsor link!

MIT

markdown-to-jsx's People

Contributors

andarist avatar ariabuckles avatar coreyward avatar dependabot[bot] avatar djskinner avatar fubhy avatar github-actions[bot] avatar greenkeeperio-bot avatar ikonst avatar insanicly avatar jackyef avatar jakelazaroff avatar jonarod avatar ldabiralai avatar m-architek avatar majman avatar marwahaha avatar mjfwebb avatar monkeywithacupcake avatar mstruebing avatar pravdomil avatar prestaul avatar quantizor avatar rschristian avatar sapegin avatar stephan-noel avatar tanglemesh avatar themightypenguin avatar tupton avatar zegl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

markdown-to-jsx's Issues

Support for custom JSX components and props

First of all, very good job in this library. I really like the way you structured your code, it is easy to read.

Well, I'm working in a framework called Grommet (grommet.io) and I'm planning to use your library to parse a body of a blog post (markdown) to JSX. I've quickly tested your engine and generally it works as expected.

Although I was hoping to have a little bit of control over the JSX components that are being created.

For example, my framework provides an Anchor component that I would like to use instead of the default a. Also I would like to send custom props to whatever JSX component inside the AST. In the context of the anchor, I would like to send an onClick prop so that I can avoid page refresh for my single page app.

As an example this is what I hoping to get:

Markdown

This is a general text with a [link](http://grommet.io) on it

JSX

<Paragraph size="large">
  This is a general text with a <Anchor href="http://grommet.io" onClick={...}>link</Anchor> on it
</Paragraph>

So, I thought about augmenting your options to receive these properties, for example:

import Paragraph from 'grommet/component/Paragraph';
import Anchor from 'grommet/component/Anchor';

converter(post.content, {
  nodeType: {
    paragraph: {
      component: Paragraph,
      props: {
        size="large"
      }
    },
    link: {
      component: Anchor,
      props: {
        onClick: function (event) {
          event.preventDefault();
          history.push('/somewhere');
        }
      }
    }
  }
})

Although initially it seems something needed only for my project, I believe it could be an extension of your library that others can leverage. So, if you agree with this, I can send a Pull Request with these changes. If not I can fork your project and do that on my end.

Looking forward to hearing back from you...

Issue rendering nested HTML

Some nested HTML tags don't get rendered correctly. One example is <div></div><div><div></div></div>, which gets compiled as <div> &lt;div&gt; <div></div><div></div></div>.
It seems to be related to the detection of HTML, since entering <div>#a</div> compiles to <div><h1>a</h1></div>.
A few versions back markdown-to-jsx compiled those strings correctly.
Just a sidenode: entering "<span>#a</span>" crashes the compiler.

Module build failed: Error: Couldn't find preset "es2015" relative to directory "/node_modules/markdown-to-jsx"

Here is the error:

Uncaught Error: Module build failed: Error: Couldn't find preset "es2015" relative to directory "/var/www/kcrypt/frontend/node_modules/markdown-to-jsx"
    at :3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:293
    at Array.map (<anonymous>)
    at OptionManager.resolvePresets (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:275)
    at OptionManager.mergePresets (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:264)
    at OptionManager.mergeOptions (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:249)
    at OptionManager.init (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368)
    at File.initOptions (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/index.js:212)
    at new File (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/index.js:135)
    at Pipeline.transform (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/pipeline.js:46)
    at transpile (:3000/var/www/kcrypt/frontend/node_modules/babel-loader/lib/index.js:50)
    at Object.module.exports (:3000/var/www/kcrypt/frontend/node_modules/babel-loader/lib/index.js:175)
    at :3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:293
    at Array.map (<anonymous>)
    at OptionManager.resolvePresets (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:275)
    at OptionManager.mergePresets (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:264)
    at OptionManager.mergeOptions (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:249)
    at OptionManager.init (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368)
    at File.initOptions (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/index.js:212)
    at new File (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/file/index.js:135)
    at Pipeline.transform (:3000/var/www/kcrypt/frontend/node_modules/babel-core/lib/transformation/pipeline.js:46)
    at transpile (:3000/var/www/kcrypt/frontend/node_modules/babel-loader/lib/index.js:50)
    at Object.module.exports (:3000/var/www/kcrypt/frontend/node_modules/babel-loader/lib/index.js:175)
    at Object.<anonymous> (ViewPostComponent.js:65)
    at __webpack_require__ (bootstrap c145be9b1a2adf02a477:19)
    at Object.<anonymous> (ViewPostComponent.js:1)
    at __webpack_require__ (bootstrap c145be9b1a2adf02a477:19)
    at Object.defineProperty.value (PostsRoutes.js:16)
    at __webpack_require__ (bootstrap c145be9b1a2adf02a477:19)
    at Object._typeof (index.js:28)
    at __webpack_require__ (bootstrap c145be9b1a2adf02a477:19)
    at Object.<anonymous> (main.bundle.js:27759)
    at __webpack_require__ (bootstrap c145be9b1a2adf02a477:19)
(anonymous) @ ViewPostComponent.js:65
__webpack_require__ @ bootstrap c145be9b1a2adf02a477:19
(anonymous) @ ViewPostComponent.js:1
__webpack_require__ @ bootstrap c145be9b1a2adf02a477:19
Object.defineProperty.value @ PostsRoutes.js:16
__webpack_require__ @ bootstrap c145be9b1a2adf02a477:19
_typeof @ index.js:28
__webpack_require__ @ bootstrap c145be9b1a2adf02a477:19
(anonymous) @ main.bundle.js:27759
__webpack_require__ @ bootstrap c145be9b1a2adf02a477:19
Object.defineProperty.value @ bootstrap c145be9b1a2adf02a477:62
(anonymous) @ bootstrap c145be9b1a2adf02a477:62

I'm using babel-env and don't want to install preset es2015. Can I still use markdown-to-jsx? What about babel 7?

The presence of two or more same-name html elements inside another html element will produce borked output

I couldn’t determine precisely what the trigger is — I’m not certain it’s really specific to these element names — but strange results can be reliably produced with dl, dt or dd elements and seemingly not with other made-up two-letter element names, e.g. "aa", "bb", "cc".

> console.log(JSON.stringify(require('markdown-to-jsx').compiler('<dl><dt>foo</dt><dd>bar</dd><dt>baz</dt><dd>qux</dd></dl>'), undefined, 2))
{
  "type": "dl",
  "key": "0",
  "ref": null,
  "props": {
    "children": [
      {
        "type": "dt",
        "key": "0",
        "ref": null,
        "props": {
          "children": [
            "foo",
            {
              "type": "/dt><dd>bar</dd><dt",
              "key": "1",
              "ref": null,
              "props": {},
              "_owner": null,
              "_store": {}
            },
            "baz"
          ]
        },
        "_owner": null,
        "_store": {}
      },
      {
        "type": "dd",
        "key": "1",
        "ref": null,
        "props": {
          "children": [
            "qux"
          ]
        },
        "_owner": null,
        "_store": {}
      }
    ]
  },
  "_owner": null,
  "_store": {}
}

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

malformed HTML causes a regex backtracking lockup

Try execute following code in node.js v8.7.0 repl:

const a =`<g>
<g>
<path fill="#ffffff"/>
</g>
<path fill="#ffffff"/>`

var HTML_BLOCK_ELEMENT_R = /^ *<([^ >/]+) ?([^>]*)\/{0}>(?=[\s\S]*<\/\1>)((?:[\s\S]*?(?:<\1[^>]*>[\s\S]*?<\/\1>)*[\s\S]*?)*?)<\/\1>\n*/

HTML_BLOCK_ELEMENT_R.exec(a)

node.js will hang at last command

Error parsing < and >

Hi,

When I use angle brackets in certain situations I get
InvalidCharacterError: The string contains invalid characters.

In the following if I use 1<2 (with no space between the < and 2) then I get the InvalidCharacterError:
<Markdown> {'1<2 or 2>1'} </Markdown>

If I put a space after the < it will work.
<Markdown> {'1< 2 or 2>1'} </Markdown>

This is what happens when the first character after the < is a number.

If it was another character then the output is displayed wrong. For example
<Markdown> {'1<a or 2>1'} </Markdown>
Will display:
11
missing everything between and including the <>

Now I understand I should be passing
&lt; or &gt; but when I pass those values they aren't translated to < or >, they just display as &lt; or &gt;.

Is there a problem here, or being stupid and not doing something?

thanks,

Package not installing properly

I think there is something wrong when installing markdown-to-jsx. Maybe I'm missing something, but all I get is this:

image

I'm only using this package as a dependency of https://github.com/styleguidist/react-styleguidist
I was expecting to find the main file index.es5.js in the node_modules directory but all I found is what you see in the picture above.

I'm on react 15.5.0, Ubuntu 16.04

Whitespace prevents random component to be interpreted

Say my render logic is like this:

render((
    <Markdown
        children={md}
        options={{
            overrides: {
                MyComponent: {
                    component: MyComponent,
                },
            },
        }} />
), document.body);

MyComponent being as simple as possible like:

const MyComponent = props => <h1>{props.say}</h1>

Now, the problem occurs depending on the markdown input syntax. Observe those two similar inputs:

Input 1 (equal sign with spaces):

Say hello: <MyComponent say = "hello" />

Input 2 (equal sign withOUT spaces):

Say hello: <MyComponent say="hello" />

Input 1 outputs nothing, while Input 2 correctly prints the component's content.
Is there any way to make markdown-to-jsx more resilient to whitespaces so that both syntax works ?
I have been struggling like 3 hours just to understand where was my mistake while it was just a space messing... :/
Anyway, nice library :)

html does not get custom tags

<Markdown
  options={{
    overrides: {
      img: {
        component: GitHubImage,
      },
      a: {
        component: GitHubLink,
      },
    },
  }}
>
  {this.state.text}
</Markdown>

These overrides aren’t called when a literal html element is used, or if it’s part of another html element.

Inlined anchors

https://codesandbox.io/s/xjr1lx4j5q

import Markdown from "markdown-to-jsx";
import React from "react";
import { render } from "react-dom";

render(
  <Markdown options={{ forceBlock: true }}>{`

<a href="/!10px">10px</a>

<a href="/!20px">20px</a>

<a href="/!30px">30px</a>

<a href="/!40px">40px</a>

<a href="/!50px">50px</a>

<a href="/!60px">60px</a>

    `}</Markdown>,
  document.body
);

renders

<div><a href="/!10px">10px</a><a href="/!20px">20px</a><a href="/!30px">30px</a><a href="/!40px">40px</a><a href="/!50px">50px</a><a href="/!60px">60px</a></div>

should render

<p><a href="/!10px">10px</a></p>
<p><a href="/!20px">20px</a></p>
<p><a href="/!30px">30px</a></p>
<p><a href="/!40px">40px</a></p>
<p><a href="/!50px">50px</a></p>
<p><a href="/!60px">60px</a></p>

try it in https://stackedit.io/app

Allow non alphanumeric characters id for headings

Currently, markdown-to-jsx generates HTML with ID for headings. like:

# This is Markdown

This markdown is converted to following HTML.

<h1 id="this-is-markdown" class="">This is Markdown</h1>

But, non alphanumeric characters like Japanese headings are converted to HTML tags with empty id.

# これはマークダウンです
<h1 id="" class="">これはマークダウンです</h1>

But it's inconvenient and I want heading tag with Japanese characters to have id.
I think this can be achievable by changing the logic to replace accented characters:

https://github.com/probablyup/markdown-to-jsx/blob/3a1e9d27db4f96ff017e15265a5dedc6a2f9baa5/index.js#L226

To be more specific, you can encode non alphanumeric characters and set its value to id.

You can encode Japanese using encodeURIComponent() funcation and (encodeURIComponent("これはマークダウンです") returns "%E3%81%93%E3%82%8C%E3%81%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%81%A7%E3%81%99" (ref: https://stackoverflow.com/questions/18656089/japanese-characters-escape-and-decoding-in-js)

So, example above(html tag with empty id) can be:

<h1 id="%E3%81%93%E3%82%8C%E3%81%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%81%A7%E3%81%99" class="">これはマークダウンです</h1>

This would be useful for non-alphanumeric language speakers like me. For example, I would need this to implement TOC.

New markdown -> AST parser engine

Not super happy with the current one, so very likely going to whip up a new one using jison or something of the like. It'll be a major semver change, but with it will we be able to natively handle real HTML, so 🎉 (block and inline.)

Please comment here if any flavors beyond GFM are important to you.

`<figure>` children wrapped in `<p>`

When I use markdown like the following, markdown-to-jsx wraps the children of the <figure> element in a paragraph tag:

<figure>
  ![](//placehold.it/300x200)
  <figcaption>This is a placeholder image</figcaption>
</figure>

Output (formatted for readability):

<figure>
  <p>
    <img src="//placehold.it/300x200">
    <figcaption>This is a placeholder image</figcaption>
  </p>
</figure>

This is invalid and React complains about it:

Warning: validateDOMNesting(...): <figcaption> cannot appear as a descendant of <p>.
    in figcaption (created by Markdown)

documentation link is down

the link https://yaycmyk.com/markdown-to-jsx/ which is mentioned in the GitHub description gives a 404 for me.

Support custom JSX components?

E.g. in a markdown blog post you want to add some sort of interactive widget.

# My sweet post

Some interesting content.

<MyInteractiveWidget
  sound={false}
/>

My thought is that these custom components would be automatically resolved e.g. look first in the same directory and then next in an optional global component directory you setup in the converter. I need the auto resolving for Gatsby (where I want to add support for this) so that people can just drop this into a project and it just kinda works.

Thoughts?

Also... I'm investigating this for Gatsby — so I'd want the conversion to happen at build time not run time. I don't see any easy way to convert things? Perhaps you do? Perhaps I need to fork this project to convert it to building a string of React.createElements.

Parsing of JSX attributes

It would be great if attributes were correctly parsed not only for just simple HTML attributes, but JSX attributes such as those in the following examples:

<MyComponent times={12}>Isn't that cool?</MyComponent>

<MyComponent arr={[1,2,3]}>Isn't that just as cool?</MyComponent>

<MyComponent onChange={function(){
    // Handle changes...
}}>Isn't that very cool?</MyComponent>

<MyComponent obj={{
  'firstName': 'Evan'
  'lastName': 'Scott'
}}>Isn't that the coolest?</MyComponent>

Unexpected token after 5.3.1 and 5.3.2

After the recent update I cannot use markdown-to-jsx anymore. It complains about a loader missing. I'm assuming you guys forgot to run babel before release it?

grommet/grommet#1330

Previous versions used index.es5.js, which does not seem to be present in the latest release from 6 hours ago.

Line of text w/o line break wraps with span instead of P

Having an issue where a string of text within the Markdown component that does not contain a line break gets wrapped with a <span> tag instead of what I would expect a <p> tag.

I took some screen shots highlighting the text from your demo site, you'll notice the one with the line break produces a P tag, the other does not.

screen shot 2018-03-26 at 9 59 30 am

screen shot 2018-03-26 at 9 59 42 am

I would expect that any string, even if it doesn't contain a line break would be wrapped in a P tag unless a markdown or html modifier is supplied. I believe this is how Markdown is supposed to behave.

Support for self-closing tags

Thank you for this promising library, very useful indeed!

I have noticed that self-closing tags for React components are currently not parsed correctly when they span multiple lines.

While

<DatePicker biasTowardDateTime="2017-12-05T07:39:36.091Z" timezone="UTC+5" />

is correctly parsed,

<DatePicker 
    biasTowardDateTime="2017-12-05T07:39:36.091Z" 
    timezone="UTC+5" 
/>

is not. This is the case no matter how the JSX attributes are indented.

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

code blocks with highlight.js support !!

Hi thank you for your amazing work !

I have just try to use this lib to render markdown but my jsx code block inside my markdown is rendered without highlight.

it will work out of the box or I need some kind of configurations ??

Incorrect table rendering

Hi there, markdown tables is rendered incorrect even on your demo here.
image

Parser add extra children in table's children. How it can be resolved?

Rendering HTML Tables with nested lists parses incorrectly

Hi there!

I have come upon a use case where we need to render tables, with lists nested within them. I have tested on prior versions of the component (v6.4.1) and the issue is not present. It seems to have started since 6.5.0.

This is an example using 6.4.1 - https://stackblitz.com/edit/react-qugqd6
This is an example using 6.6.1 - https://stackblitz.com/edit/react-6nelfr

You'll notice when using 6.6.1, it renders trailing </li> tags for some reason.

screen shot 2018-04-02 at 9 10 37 am

This is the HTML i'm using

<table border="1">
  <tr>
    <td>a</td>
    <td>b</td>
    <td>c</td>
  </tr>
  <tr>
    <td>left</td>
    <td>
      <p>Start of table</p>
      <ul>
        <li>List 1</li>
        <li>
          <ul>
            <li>Nested List 1</li>
          </ul>
        </li>
        <li>
          <ul>
            <li>list 2</li>
          </ul>
        </li>
      </ul>
    </td>
    <td>right</td>
  </tr>
</table>

Override code block

This is pretty cool 👍
I am trying to override the code blocks in markdown, but i need the value of that code block to be passed in a prop for a component. is that possible ?

<Markdown 
    options={{
        overrides: {
            code: {
                  component: AceEditor,
                      props: {
                          height: '6em',
                          mode:'jsx',
                          value: 'CODE VALUE GOES HERE'
                      }
                  }
             }
         }}>
    {zComponents['readme']}
</Markdown>

Bold formatting is causing output to be wrapped in <p> tags

Context

  • Visual bug?
  • Runtime bug?
  • Build bug?

package name(s) and versions

v6.2.2

Issue

What are you trying to achieve or the steps to reproduce ?

When passing relatively basic Markdown, it wrapped the output in a <p> tag.

Input:

This text is **bold**.

What result did you expect ?

Output:

<span>This text is <strong>bold</strong>.</span>

What result did you observe ?

Output:

<p>This text is <strong>bold</strong>.</p>

Potential enhancement - Allow className prop directly on Markdown component

First, thanks for this library, the docs & api are excellent 👍.

The feature

Allow a className prop to be added to the <Markdown /> component directly.

e.g.

<Markdown className="myClass">
  {markdownText}
</Markdown>

Reasons / use-case

I have a use-case where i need to apply specific styling to anchors which are telephone links e.g.

<a href="tel:+123456789" />

My ideal approach would be to create a styled component of the <Markdown /> component, e.g.

const modifiedMarkdown = styled(Markdown)`
  a[href^=tel] {
    myStyles
  }
`;

This would allow me to retain the default styling of non-telephone anchors.

Unfortunately for styled-components to style the Markdown component, it needs to pass a className prop down directly;

Tackling the use-case without the className prop

There are ways to handle this use-case where it happens, although they require a lot of abstraction, I've made a codesandbox to show this - https://codesandbox.io/s/mzo4mw6q89

Conclusion

There are a number of ways to handle this use-case with the component as-is (including adding a div wrapper with this css attached) so i'll leave this at the maintainers' discretion.

Thanks for your time 👍

options.blocks passed to remark-parser is in an invalid format

Update below, tldr; my webpack config did not properly load json files.

Hello there, I just installed markdown-to-jsx version 5.3.3 and remark-parse 3.0.1 but I am encountering the following trying to display Markup content:

Uncaught Error: Invalid value `[
  "address",
  "article",
  "aside",
  "base",
  "basefont",
  "blockquote",
  "body",
  "caption",
  "center",
  "col",
  "colgroup",
  "dd",
  "details",
  "dialog",
  "dir",
  "div",
  "dl",
  "dt",
  "fieldset",
  "figcaption",
  "figure",
  "footer",
  "form",
  "frame",
  "frameset",
  "h1",
  "h2",
  "h3",
  "h4",
  "h5",
  "h6",
  "head",
  "header",
  "hgroup",
  "hr",
  "html",
  "iframe",
  "legend",
  "li",
  "link",
  "main",
  "menu",
  "menuitem",
  "meta",
  "nav",
  "noframes",
  "ol",
  "optgroup",
  "option",
  "p",
  "param",
  "pre",
  "section",
  "source",
  "title",
  "summary",
  "table",
  "tbody",
  "td",
  "tfoot",
  "th",
  "thead",
  "title",
  "tr",
  "track",
  "ul"
]
` for setting `options.blocks`
    at Of.setOptions (bundle.js:35588)
    at Of.Parser (bundle.js:33996)
    at new Of (bundle.js:33909)
    at Function.parse (bundle.js:32745)
    at compiler (bundle.js:31477)
    at Component (bundle.js:31518)
    at bundle.js:16674
    at measureLifeCyclePerf (bundle.js:16444)
    at ReactCompositeComponentWrapper._constructComponentWithoutOwner (bundle.js:16673)
    at ReactCompositeComponentWrapper._constructComponent (bundle.js:16648)

I've tried simplifiying my markdown using component as much as possible and as close to the provided examples as possible. My code currently looks like this:

import React, { Component } from 'react'
import Markdown from 'markdown-to-jsx'

export default class MarkdownContentArea extends Component {
  render() {
    content = `
      # Hello world!
    `.trim();
    return (
      <Markdown>
        {content}
      </Markdown>
    )
  }
}

After reading through the error a bit more carefully and reading through the remark-parse code a bit I've found the following check in their package (github link to the file):

    if (
      (key !== 'blocks' && typeof value !== 'boolean') ||
      (key === 'blocks' && typeof value !== 'object')
    ) {
      throw new Error(
        'Invalid value `' + value + '` ' +
        'for setting `options.' + key + '`'
      );
    }

Which indicates that something might be going on with the passing of options.blocks being an array instead of an object. However I couldnt quite figure out why this is, or why I am the first to be having this problem since it doesn't appear like the file has changed much.

closing tag recognised as an opening tag

see in this codesandbox:

<details>
<summary><strong>Table of Contents</strong></summary>

- [Contributing](#contributing)
</details>

is rendered with two details tags

<details>

<summary><strong>Table of Contents</strong></summary>

- [Contributing](#contributing)

</details>

Renders better, but still has issues with the summary tag.

This can also be tested in the demo site 😄

Subtle rendering magic

const compiler = require('markdown-to-jsx').compiler;

compiler('a b')
/* { '$$typeof': Symbol(react.element),
  type: 'span',
  key: null,
  ref: null,
  props: { className: undefined, children: 'a b' },
  _owner: null,
  _store: {} } */

compiler('a b\n')
/* { '$$typeof': Symbol(react.element),
  type: 'p',
  key: '0',
  ref: null,
  props: { className: undefined, children: [ 'a b' ] },
  _owner: null,
  _store: {} } */

compiler('a *b*')
/* { '$$typeof': Symbol(react.element),
  type: 'p',
  key: null,
  ref: null,
  props: { className: undefined, children: [ 'a ', [Object] ] },
  _owner: null,
  _store: {} } */

compiler('*a*')
/* { '$$typeof': Symbol(react.element),
  type: 'em',
  key: '0',
  ref: null,
  props: { className: undefined, children: [ 'a' ] },
  _owner: null,
  _store: {} } */

For me this looks like a bug or at lest something unexpected and too much magic, that probably shouldn’t be enabled by default.

ESM module contains call to require

The file index.esm.js looks like this

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

/* @jsx h */
/**
 * markdown-to-jsx@6 is a fork of [simple-markdown v0.2.2](https://github.com/Khan/simple-markdown)
 * from Khan Academy. Thank you Khan devs for making such an awesome and extensible
 * parsing infra... without it, half of the optimizations here wouldn't be feasible. 🙏🏼
 */
var React = require('react');
var unquote = require('unquote');

This breaks rollup bundles because the module version should read

import React from 'react';

Use special chars like &gt; , \u00b7 or <

Is there anyway to embed special chars into the markdown text outside of a code block?

I've tried a few things, wrapping in brackets, using the < char directly, wrapping in quotes, etc, but I can't find anyway to make it render correctly.

Thanks

New approach to markdown-to-jsx

Try it here

import { transform } from "@babel/core";
import TransformReactJsx from "@babel/plugin-transform-react-jsx";

import React from "react";
import { render } from "react-dom";

import markdownIt from "markdown-it";
const md = markdownIt({ html: true });

const App = () => (
  <div>
    {myCompiler(`

<div style="float: right">

# Header

[a **a** a](aaa)

<a href="bbb">b <b>b</b> b</a>

</div>

  `)}
  </div>
);

render(<App />, document.getElementById("root"));

function myCompiler(text) {
  const html = md.render(text);

  const jsx = "<div>" + html.replace(/{/g, "{'{'}") + "</div>";

  const plugins = [[TransformReactJsx, { pragma: "h" }]];
  const { code } = transform(jsx, { plugins });

  const func = new Function("h", "return " + code);

  return func(createElement);
}

function createElement(type, props, ...children) {
  if (!props) {
    props = {};
  }

  if (props.style) {
    props.style = props.style.split(/;\s?/).reduce(function(styles, kvPair) {
      // taken from https://github.com/probablyup/markdown-to-jsx
      const key = kvPair.slice(0, kvPair.indexOf(":"));

      // snake-case to camelCase
      // also handles PascalCasing vendor prefixes
      const camelCasedKey = key.replace(/(-[a-z])/g, function toUpper(substr) {
        return substr[1].toUpperCase();
      });

      // key.length + 1 to skip over the colon
      styles[camelCasedKey] = kvPair.slice(key.length + 1).trim();

      return styles;
    }, {});
  }

  if (props.class) {
    props.className = props.class;
    delete props.class;
  }

  return React.createElement(type, props, children);
}

Override nested html elements with single custom component?

Wondering if it's possible to override groups of html elements? For instance, right now I have:

  p: {
       component: P,
      },
  img: {
       component: Image,
      },

Because everything seems to get wrapped by <p>, this outputs <P><Image></P>
Can I define an override for <p><img></p> to produce a single component <Image>?
Something like:

  p: {
       component: P,
       img: {
               component: Image,
               },
      }

html comment causes "Error: Invalid tag: --"

When parsing the following markdown:

foo

<!-- comment -->

bar

The generated JSX fails to render, with the error: Error: Invalid tag: -- (an Invariant Violation in within React). Removing the HTML comment (<!-- comment -->) makes this go away, but it really should be allowed.

Tables containing UL tag throws error

Version: Latest 6.1.4

Error is not thrown in Version 5.4.2

Example:

<Markdown>
<table>
    <tbody>
      <tr>
        <td>Time</td>
        <td>Payment Criteria</td>
        <td>Payment</td>
      </tr>
      <tr>
        <td>Office Visit </td>
        <td>
          <ul>
            <li>
              Complete full visit and enroll 
              <ul>
                <li>Enrolling is fun!</li>
              </ul>
            </li>
          </ul>
        </td>
        <td>$20</td>
      </tr>
    </tbody>
</table>
</Markdown>

Error displayed: Invariant Violation: Invalid tag: ul>

Table produces thead and tbody elements with "null" keys

I have a Markdown document with the following table:

| ID  | Name               | Rank              |
| --- | ------------------ | ----------------- |
| 1   | Tom Preston-Werner | Awesome           |
| 2   | Albert Einstein    | Nearly as awesome |

And it's producing the following React output:

Chrome console output

As you can see instead of having an integer key (like all the other elements), both elements have a "null" key. Versions: same as #144

Line breaks / paragraphs?

Hello, does this library support paragraphs? I'm using it like:

<Markdown>
# Title

Hello, world

Foo bar
</Markdown>

And it all renders on one line (in fact, as an <h1>)

Render Markdown in HTML

Hello, is there any chance to get Markdown syntax working inside HTML elements?

Now this

<div style="float: right">

# Hello

</div>

renders as <div style="float: right;"># Hello</div>

I know that some md compilers does it and some not, but can we do that with passing some option to compiler?

Indent makes `<style>` tag render incorrectly

I've tried to add <style /> html tag to markdown

I supposed codes below

<style>
.bar {
  font-size: 12px;
}
</style>

should render properly

but turns out it will add <p /> between lines, which results in

<style>
  <p>
    .bar {
      font-size: 12px;
    }
  </p>
</style>

but if I remove indent before font-size: 12px, it works again.

any ideas? using v6.6.1

More gracefully handle arbitrary HTML

I'm thinking about detecting the tag name of the block and trying to do a little magic to reparent the logical contents of the HTML in JSX so we don't have to use dangerouslySetInnerHTML after all. The places where it could potentially break down are inline styles and inline JS listeners. Most other HTML attributes could be easily detected and converted into JSX/props with some regexes.

So HTML like <a href="/foo" class="bar">_hello_ there</a> could potentially be converted into this JSX:

<a href='foo' className='bar'>
     <em>hello</em> there
</a>

That's the dream anyway.

Prettier-generated tables are not supported

This code renders fine:

Foo | Bar
-|-
1 | 2

But if you run Prettier, it will generate this code, and it doesn’t work:

| Foo | Bar |
| --- | --- |
| 1   | 2   |

Actually, this still works:

| Foo | Bar |
- | -
| 1 | 2 |

But this doesn’t:

| Foo | Bar |
| - | - |
| 1 | 2 |

Code blocks produce null children prop

I have a Markdown file which contains the following:

['a', 'b'].map { |x| x.uppercase }

By using require('markdown-to-jsx').compiler('<String above>', {}) I'm getting the following JS output:

Chrome console output

As you can see, the second children in that array has a null children instead of the code above. Even though I haven't installed highlight.js, it is being packaged by Webpack.

Single quoute fences also have empty outputs. I'm using version 6.4.0 and React 16.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.