Vitaly Puzrin
10 years ago
3 changed files with 151 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||
|
This folder contains info mostly for plugins developers. |
||||
|
|
||||
|
If you just use `markdown-it` in your app, see |
||||
|
[README](https://github.com/markdown-it/markdown-it#markdown-it) and |
||||
|
[API docs](https://markdown-it.github.io/markdown-it/). |
@ -0,0 +1,96 @@ |
|||||
|
# markdown-it design principles |
||||
|
|
||||
|
## Data flow |
||||
|
|
||||
|
Parse process is unified as much as possible. Input data is piped via nestesd |
||||
|
chains of rules. There are 3 "main" chains (core / block / inline): |
||||
|
|
||||
|
``` |
||||
|
core |
||||
|
core.rule1 |
||||
|
... (none yet, you can patch input string here) |
||||
|
|
||||
|
block |
||||
|
block.rule1 |
||||
|
... |
||||
|
block.ruleX |
||||
|
|
||||
|
core.ruleXX |
||||
|
... (references, abbreviations, footnotes) |
||||
|
|
||||
|
inline (applyed to each block token with "inline type") |
||||
|
inline.rule1 |
||||
|
... |
||||
|
inline.ruleX |
||||
|
|
||||
|
core.ruleYY |
||||
|
... (typographer, linkifier) |
||||
|
|
||||
|
``` |
||||
|
|
||||
|
Mutable data are: |
||||
|
|
||||
|
- array of tokens |
||||
|
- `env` sandbox |
||||
|
|
||||
|
Tokens are the "main" data, but some rules can be "splitted" to several chains, |
||||
|
and need sandbox for exchange. Also, `env` can be used to inject per-render |
||||
|
variables for your custom parse and render rules. |
||||
|
|
||||
|
Each chain (core / block / inline) has independent `state` object, to isolate |
||||
|
data and protect code from clutter. |
||||
|
|
||||
|
|
||||
|
## Token stream |
||||
|
|
||||
|
Instead of traditional AST we use more low-level data representation - tokens. |
||||
|
Difference is very simple. |
||||
|
|
||||
|
- tokens are sequence (Array) |
||||
|
- opening and closing tags are separate tokens |
||||
|
- there are special token object, "inline containers", having nested token |
||||
|
sequences with inline markup (bold, italic, text) |
||||
|
|
||||
|
Each token has 2 mandatory fields: |
||||
|
|
||||
|
- __type__ - token name. |
||||
|
- __level__ - nesting level, useful to seek matched pair. |
||||
|
- __lines__ - [begin, end], for block tokens only. Range of input lines, |
||||
|
compiled to this token |
||||
|
|
||||
|
Inline container (`type === "inline"`) has additional properties: |
||||
|
|
||||
|
- __content__ - raw text, unparsed inline content. |
||||
|
- __children__ - token stream for parsed content. |
||||
|
|
||||
|
See [renderer source](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js) |
||||
|
for available tokens and those properties. Currently there are no special |
||||
|
requirements on tokens naming and additional fields. |
||||
|
|
||||
|
In total, token stream is: |
||||
|
|
||||
|
- Array of paired or single "block" tokens, on top level: |
||||
|
- open/close for headers, lists, blockquotes paragraphs |
||||
|
- codes, fenced blocks, horisontal rules, html blocks, inlines containers |
||||
|
- Inline containers have "substream" Array with inline tags: |
||||
|
- open/close for strong, em, link, code, ... |
||||
|
- text, line breaks |
||||
|
|
||||
|
Why not AST? Because it's not needed for our tasks. We follow KISS principle. |
||||
|
If you whish - you can call parser withour renderer and convert token stream |
||||
|
to AST. |
||||
|
|
||||
|
## Parse process |
||||
|
|
||||
|
This was mentioned in [Data flow](#data-flow), but let's repeat sequence again: |
||||
|
|
||||
|
1. Blocks are parsed, and top level of token stream filled with block tokens. |
||||
|
2. Content on inline containers is parsed, filling `.children` properties. |
||||
|
3. Rendering happens. |
||||
|
|
||||
|
And somewhere between you can apply addtional transformations :) . Full content |
||||
|
of each chain can be seen on the top of |
||||
|
[parser_core.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_core.js), |
||||
|
[parser_block.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_block.js) and |
||||
|
[parser_inline.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_inline.js) |
||||
|
files. |
@ -0,0 +1,50 @@ |
|||||
|
# Development recommendations |
||||
|
|
||||
|
Prior to continue, make sure you've readed: |
||||
|
|
||||
|
1. [README](https://github.com/markdown-it/markdown-it#markdown-it) |
||||
|
2. [API documentation](https://markdown-it.github.io/markdown-it/) |
||||
|
3. [Architecture description](architecture.md) |
||||
|
|
||||
|
|
||||
|
## General considerations for plugins. |
||||
|
|
||||
|
1. Try to understand, where your plugin rule sould be located |
||||
|
- Will it conflict with existing markup (by priority)? |
||||
|
- If yes - you need to write inline or block rule. |
||||
|
- If no - you can morth tokens in core chain. |
||||
|
- Remember, that tokens morphing in core is always more simple than writing |
||||
|
block / inline rules. However, block / inline rules are usually faster |
||||
|
- Sometime it's enougth to modify renderer only (for example, to add |
||||
|
header IDs or target=_blank for the links) |
||||
|
2. Search existing [plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin) |
||||
|
or [rules](https://github.com/markdown-it/markdown-it/tree/master/lib), |
||||
|
doing something similar. It can me more simple to modify existing code, |
||||
|
instead of writing from scratch. |
||||
|
3. If you did all steps above, but still has questions - ask in |
||||
|
[tracker](https://github.com/markdown-it/markdown-it/issues). But, please: |
||||
|
- Be specific. Generic questions like "how to do plugins" and |
||||
|
"how to learn programming" are not accepted. |
||||
|
- Don't ask us to break [CommonMark](http://commonmark.org/) specification. |
||||
|
Such things should be discussed first on [CommonMark forum](http://talk.commonmark.org/). |
||||
|
|
||||
|
|
||||
|
## Notes for NPM packages |
||||
|
|
||||
|
To simplify search: |
||||
|
|
||||
|
- add to `package.json` keyswords `markdown-it` and `markdown-it-plugin` for plugins |
||||
|
- add keyword `markdown-it` for any other related packages. |
||||
|
|
||||
|
|
||||
|
## FAQ |
||||
|
|
||||
|
#### I need async rule, how to do it? |
||||
|
|
||||
|
Sorry. You can't do it directly. All complex parsers are sync by nature. But you |
||||
|
can use workarounds: |
||||
|
|
||||
|
1. On parse phase, replace content by random number and store it in `env`. |
||||
|
2. Do async processing over collected data. |
||||
|
3. Render content and replace those random numbers with text |
||||
|
(or replace first, then render) |
Loading…
Reference in new issue