Skip to content

Commit

Permalink
feat(hiccup-markdown): add & refactor markdown parser (from example),…
Browse files Browse the repository at this point in the history
… update docs
  • Loading branch information
postspectacular committed Jan 4, 2019
1 parent 5f09d3e commit 35db07f
Show file tree
Hide file tree
Showing 7 changed files with 752 additions and 182 deletions.
196 changes: 170 additions & 26 deletions packages/hiccup-markdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,180 @@ This project is part of the
<!-- TOC depthFrom:2 depthTo:3 -->

- [About](#about)
- [Behaviors](#behaviors)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Usage examples](#usage-examples)
- [Parser](#parser)
- [Features](#features)
- [Limitations](#limitations)
- [Other parser features](#other-parser-features)
- [Serializing to HTML](#serializing-to-html)
- [Customizing tags](#customizing-tags)
- [Serializer](#serializer)
- [Features](#features-1)
- [Behaviors](#behaviors)
- [Usage examples](#usage-examples)
- [Authors](#authors)
- [License](#license)

<!-- /TOC -->

## About

This package provides an extensible serializer of HTML-ish
[@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)
trees to Markdown format. Currently supports most standard (applicable)
Markdown features:
This package provides both a customizable
[Markdown](https://en.wikipedia.org/wiki/Markdown)-to-[Hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)
parser and an extensible Hiccup-to-Markdown serializer.

## Installation

```bash
yarn add @thi.ng/hiccup-markdown
```

## Dependencies

- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks)
- [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/master/packages/defmulti)
- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors)
- [@thi.ng/fsm](https://github.com/thi-ng/umbrella/tree/master/packages/fsm)
- [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)
- [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/master/packages/strings)

## Parser

### Features

The parser itself is not aimed at supporting **all** of Markdown's
quirky syntax features, but restricts itself to a sane subset of
features:

| Feature | Comments |
|-------------|-----------------------------------------------------------------------------------------------------|
| Heading | ATX only (`#` line prefix), levels 1-6, then downgrade to paragraph |
| Paragraph | no support for `\` line breaks |
| Blockquote | Respects newlines |
| Format | **bold**, _emphasis_, `code`, ~~strikethrough~~ in paragraphs, headings, lists, blockquotes, tables |
| Link | no support for inline formats in label |
| Image | no image links |
| List | only unordered (`- ` line prefix), no nesting, supports line breaks |
| Table | no support for column alignment |
| Code block | GFM only (triple backtick prefix), w/ optional language hint |
| Horiz. Rule | only dash supported (e.g. `---`), min 3 chars required |

Note: Currently, the last heading, paragraph, blockquote, list or table requires an additional newline.

### Limitations

These MD features (and probably many more) are **not** supported:

- inline HTML
- nested inline formats (e.g. **bold** inside _italic_)
- inline formats within link labels
- image links
- footnotes
- link references
- nested / ordered / numbered / todo lists

Some of these are considered, though currently not high priority... Pull
requests are welcome, though!

### Other parser features

- **Functional:** parser entirely built using
[transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers)
& function composition. Use the parser in a transducer pipeline to
easily apply post-processing of the emitted results
- **Declarative:** parsing rules defined declaratively with only minimal
state/context handling needed
- **No regex:** consumes input character-wise and produces an iterator
of hiccup-style tree nodes, ready to be used with
[@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/master/packages/hdom),
[@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)
or the serializer of this package for back conversion to MD
- **Customizable:** supports custom tag factory functions to override
default behavior / representation of each parsed result element
- **Fast (enough):** parses this markdown file (5.8KB) in ~5ms on MBP2016 / Chrome 71
- **Small:** minified + gzipped ~2.6KB (parser sub-module incl. deps)

### Serializing to HTML

```ts
import { iterator } from "@thi.ng/transducers";
import { serialize } from "@thi.ng/hiccup";

import { parse } from "@thi.ng/hiccup-markdown/parse";

const src = `
# Hello world
[This](http://example.com) is a _test_.
`;

// convert to hiccup tree
[...iterator(parse(), src)]
// [ [ 'h1', ' Hello world ' ],
// [ 'p',
// [ 'a', { href: 'http://example.com' }, 'This' ],
// ' is a ',
// [ 'em', 'test' ],
// '. ' ] ]

// or serialize to HTML
serialize(iterator(parse(), src));

// <h1>Hello world</h1><p>
// <a href="http://example.com">This</a> is a <em>test</em>. </p>
```

### Customizing tags

- Headings (level 1-6)
The following interface defines factory functions for all supported
elements. User implementations / overrides can be given to the
`parse()` transducer to customize output.

```ts
interface TagFactories {
blockquote(...children: any[]): any[];
code(body: string): any[];
codeblock(lang: string, body: string): any[];
em(body: string): any[];
heading(level, children: any[]): any[];
hr(): any[];
img(src: string, alt: string): any[];
li(children: any[]): any[];
link(href: string, body: string): any[];
list(type: string, items: any[]): any[];
paragraph(children: any[]): any[];
strike(body: string): any[];
strong(body: string): any[];
table(rows: any[]): any[];
td(i: number, children: any[]): any[];
tr(i: number, cells: any[]): any[];
}
```

Example with custom link elements:

```ts
const tags = {
link: (href, body) => ["a.link.blue", { href }, body]
};

serialize(iterator(parse(tags), src));

// <h1>Hello world</h1>
// <p><a href="http://example.com" class="link blue">This</a> is a <em>test</em>. </p>
```

## Serializer

For the reverse operation, the `serialize()` function can be used to
convert an hiccup component tree into Markdown. Currently supports most
standard (applicable) Markdown features:

### Features

- ATX-style headings (level 1-6)
- Paragraphs
- Forced line breaks
- Inline styles: strong, italic, code
Expand All @@ -47,9 +204,10 @@ Not (yet) supported:

### Behaviors

- Unless needed for serialization, all other element attribs are ignored
- Code blocks are always output in GFM w/ optional language hint (via
`lang` attrib)
- Unless needed for serialization, all other hiccup element attribs are
ignored
- Code blocks are always output in GFM flavor w/ optional language hint
(via `lang` attrib)
- Images use the optional `alt` attrib as label
- Forced line breaks are realized via `["br"]` elements in the hiccup
tree
Expand All @@ -61,24 +219,10 @@ implementation to the exported `serializeElement`
[multi-method](https://github.com/thi-ng/umbrella/tree/master/packages/defmulti).
See source code for reference.

## Installation

```bash
yarn add @thi.ng/hiccup-markdown
```

## Dependencies

- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks)
- [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/master/packages/defmulti)
- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors)
- [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)
- [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/master/packages/strings)

## Usage examples
### Usage examples

```ts
import { serialize } from "@thi.ng/hiccup-markdown";
import { serialize } from "@thi.ng/hiccup-markdown/serialize";

// list component
// the 1st arg is the optional user context object
Expand Down
9 changes: 7 additions & 2 deletions packages/hiccup-markdown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,24 @@
"@thi.ng/checks": "^1.5.14",
"@thi.ng/defmulti": "^0.7.0",
"@thi.ng/errors": "^0.1.12",
"@thi.ng/fsm": "^0.1.0",
"@thi.ng/hiccup": "^2.7.2",
"@thi.ng/strings": "^0.7.1"
"@thi.ng/strings": "^0.7.1",
"@thi.ng/transducers": "^2.3.2"
},
"keywords": [
"ES6",
"ast",
"converter",
"DOM",
"hiccup",
"markdown",
"parser",
"serialize",
"transducers",
"typescript"
],
"publishConfig": {
"access": "public"
}
}
}
18 changes: 18 additions & 0 deletions packages/hiccup-markdown/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface TagFactories {
blockquote(...children: any[]): any[];
code(body: string): any[];
codeblock(lang: string, body: string): any[];
em(body: string): any[];
heading(level, children: any[]): any[];
hr(): any[];
img(src: string, alt: string): any[];
li(children: any[]): any[];
link(href: string, body: string): any[];
list(type: string, items: any[]): any[];
paragraph(children: any[]): any[];
strike(body: string): any[];
strong(body: string): any[];
table(rows: any[]): any[];
td(i: number, children: any[]): any[];
tr(i: number, cells: any[]): any[];
}
Loading

0 comments on commit 35db07f

Please sign in to comment.