├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── tester.yml ├── .gitignore ├── .mocharc.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── anchors.js ├── images.js └── renderer.js ├── package.json └── test ├── .eslintrc ├── fixtures ├── markdownit.md └── outputs │ ├── anchors.html │ ├── commonmark-deprecated.html │ ├── commonmark.html │ ├── custom.html │ ├── default-disable_rules.html │ ├── default.html │ ├── plugins.html │ ├── zero-enable_rules.html │ └── zero.html ├── index.js └── scripts └── enable_unsafe_link.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | tmp/ 4 | 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "hexo", 3 | "root": true 4 | } 5 | 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | *.cs text diff=csharp 5 | *.java text diff=java 6 | *.html text diff=html 7 | *.css text 8 | *.js text 9 | 10 | 11 | # absolute paths are ok, as are globs 12 | test/**/* text eol=lf 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | ignore: 8 | - dependency-name: "*" 9 | update-types: ["version-update:semver-patch"] 10 | open-pull-requests-limit: 3 11 | -------------------------------------------------------------------------------- /.github/workflows/tester.yml: -------------------------------------------------------------------------------- 1 | name: Tester 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tester: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, windows-latest, macos-latest] 11 | node-version: ['14.x', '16.x', '18.x'] 12 | fail-fast: false 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - name: Cache NPM dependencies 20 | uses: actions/cache@v1 21 | with: 22 | path: node_modules 23 | key: ${{ runner.os }}-npm-cache 24 | restore-keys: ${{ runner.os }}-npm-cache 25 | - name: Install Dependencies 26 | run: npm install 27 | - name: Test 28 | run: npm run test 29 | env: 30 | CI: true 31 | coverage: 32 | runs-on: ${{ matrix.os }} 33 | strategy: 34 | matrix: 35 | os: [ubuntu-latest] 36 | node-version: ['14.x'] 37 | steps: 38 | - uses: actions/checkout@v3 39 | - name: Use Node.js ${{ matrix.node-version }} 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: ${{ matrix.node-version }} 43 | - name: Cache NPM dependencies 44 | uses: actions/cache@v1 45 | with: 46 | path: node_modules 47 | key: ${{ runner.os }}-npm-cache 48 | restore-keys: ${{ runner.os }}-npm-cache 49 | - name: Install Dependencies 50 | run: npm install 51 | - name: Coverage 52 | run: npm run test-cov 53 | env: 54 | CI: true 55 | - name: Coveralls 56 | uses: coverallsapp/github-action@master 57 | with: 58 | github-token: ${{ secrets.github_token }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | tmp/ 4 | *.log 5 | .idea/ 6 | coverage/ 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /.mocharc.yml: -------------------------------------------------------------------------------- 1 | color: true 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Celso Miranda 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hexo-renderer-markdown-it 2 | 3 | [![Build Status](https://github.com/hexojs/hexo-renderer-markdown-it/workflows/Tester/badge.svg)](https://github.com/hexojs/hexo-renderer-markdown-it/actions?query=workflow%3ATester) 4 | [![npm version](https://badge.fury.io/js/hexo-renderer-markdown-it.svg)](https://www.npmjs.com/package/hexo-renderer-markdown-it) 5 | [![Coverage Status](https://coveralls.io/repos/github/hexojs/hexo-renderer-markdown-it/badge.svg?branch=master)](https://coveralls.io/github/hexojs/hexo-renderer-markdown-it?branch=master) 6 | 7 | This renderer plugin uses [Markdown-it] as a render engine on [Hexo]. Adds support for [Markdown] and [CommonMark]. 8 | 9 | ## Main Features 10 | 11 | - Support for [Markdown], [GFM] and [CommonMark] 12 | - Extensive configuration 13 | - Faster than the default renderer | `hexo-renderer-marked` 14 | - Safe ID for headings 15 | - Anchors for headings with ID 16 | - Footnotes 17 | - `` H2O 18 | - `` x2 19 | - `` Inserted 20 | 21 | ## Installation 22 | 23 | **Warning:** make sure you're inside the main hexo directory before starting this guide. 24 | 25 | A default Hexo installation will include a markdown renderer plugin which uses `marked`, so you will have to remove it if you want to use `hexo-renderer-markdown-it`. 26 | 27 | ``` sh 28 | $ npm un hexo-renderer-marked --save 29 | ``` 30 | 31 | If you have already removed the default renderer, and others you might of added, you can now safely install `hexo-renderer-markdown-it` 32 | 33 | ``` sh 34 | $ npm i hexo-renderer-markdown-it --save 35 | ``` 36 | 37 | ## Options 38 | 39 | ``` yml 40 | markdown: 41 | preset: 'default' 42 | render: 43 | html: true 44 | xhtmlOut: false 45 | langPrefix: 'language-' 46 | breaks: true 47 | linkify: true 48 | typographer: true 49 | quotes: '“”‘’' 50 | enable_rules: 51 | disable_rules: 52 | plugins: 53 | anchors: 54 | level: 2 55 | collisionSuffix: '' 56 | permalink: false 57 | permalinkClass: 'header-anchor' 58 | permalinkSide: 'left' 59 | permalinkSymbol: '¶' 60 | case: 0 61 | separator: '-' 62 | images: 63 | lazyload: false 64 | prepend_root: false 65 | post_asset: false 66 | inline: false # https://markdown-it.github.io/markdown-it/#MarkdownIt.renderInline 67 | ``` 68 | 69 | See below for more details. 70 | 71 | ## Advanced Configuration 72 | 73 | ### [Preset](https://markdown-it.github.io/markdown-it/#MarkdownIt.new) options 74 | 75 | ``` yml 76 | markdown: 77 | preset: 'default' 78 | ``` 79 | 80 | - **"commonmark"** - configures parser to strict [CommonMark](https://commonmark.org/) mode. 81 | - **"default"** - similar to [GFM](https://github.github.com/gfm/), used when no preset name given. Enables all available rules, but still without html, typographer & autolinker. 82 | - **"zero"** - all rules disabled. Useful to quickly setup your config via `.enable()`. For example, when you need only `bold` and `italic` markup and nothing else. 83 | 84 | Note that the [default](https://github.com/hexojs/hexo-renderer-markdown-it#options) render and anchor options override some options in the preset. If you prefer to have the preset only: 85 | 86 | ``` yml 87 | markdown: 88 | preset: 'default' 89 | render: 90 | anchors: 91 | ``` 92 | 93 | ### Render options 94 | 95 | #### html 96 | 97 | The `html` setting defines whether or not HTML content inside the document should be escaped or passed to the final result. 98 | 99 | ``` yaml 100 | html: true # Doesn't escape HTML content 101 | ## OR 102 | html: false # Escapes HTML content so the tags will appear as text. 103 | ``` 104 | 105 | #### xhtmlOut 106 | 107 | The `xhtmlOut` setting defines whether the parser will export fully XHTML compatible tags. This only needs to be `true` for complete [CommonMark] support. 108 | 109 | ``` yaml 110 | xhtmlOut: true # Parser produces fully XHTML compliant code. 111 | # Ex: A line breaks will be
112 | ## OR 113 | xhtmlOut: false # Parser will not produce XHTML compliant code. 114 | # Ex: A line break will be
115 | ``` 116 | 117 | #### breaks 118 | 119 | Makes line breaks in the source file will be parsed into `
` tags. So every time you press the `Enter` key you will create a line break, which is not the default Markdown, CommonMark, or GFM behaviour. 120 | 121 | ``` yaml 122 | breaks: true # Parser produces `
` tags every time there is a line break in the source document. 123 | ## OR 124 | breaks: false # Parser will ignore line breaks in the source document. 125 | #Default double line break creates paragraph is still supported 126 | ``` 127 | 128 | #### langPrefix 129 | 130 | Add a prefix to the class name of code blocks (when a language is specified). 131 | 132 | ``` yaml 133 | langPrefix: 'language-' # default 134 | ``` 135 | 136 | _This option only applies when both Hexo's built-in highlighters are [**disabled**](https://hexo.io/docs/syntax-highlight#Disabled)._ 137 | 138 | **Example:** 139 | 140 | ``` yml 141 | langPrefix: 'lang-' 142 | ``` 143 | 144 | Source: 145 | ```` 146 | ``` js 147 | example 148 | ``` 149 | ```` 150 | 151 | HTML: 152 | 153 | ```html 154 |
155 | example
156 | 
157 | ``` 158 | 159 | #### linkify 160 | 161 | The parser has the ability to inline links pasted directly into the text. If you write a piece of text that looks like a link it will be rendered as `http://example.com`. 162 | 163 | ``` yaml 164 | linkify: true # Returns text links as proper links inlined with the paragraph. 165 | ## OR 166 | linkify: false # Returns text links as text. 167 | ``` 168 | 169 | #### typographer 170 | 171 | This is enables the substitution for common typography elements like ©, curly quotes, dashes, etc. 172 | 173 | ``` yaml 174 | typographer: true # Substitution of common typographical elements will take place. 175 | ## OR 176 | typographer: false # No substitution, so dumb quotes will remain dumb quotes, etc. 177 | ``` 178 | 179 | #### quotes 180 | 181 | Defines the double and single quotes used for substituting dumb quotes if typographer is set to `true`. 182 | 183 | ``` yaml 184 | quotes: '“”‘’' # "double" will be turned into “single” 185 | # 'single' will be turned into ‘single’ 186 | ## OR 187 | quotes: '«»“”' # "double" will be turned into «single» 188 | # 'single' will be turned into “single” 189 | ``` 190 | 191 | #### Example configuration 192 | 193 | ``` yaml 194 | markdown: 195 | render: 196 | html: true 197 | xhtmlOut: false 198 | breaks: false 199 | linkify: true 200 | typographer: true 201 | quotes: '“”‘’' 202 | ``` 203 | 204 | ### Manage rules 205 | 206 | Certain rules are enabled or disabled depending on the [preset](#preset-options). For example, "zero" preset disables all rules, to enable specific rules: 207 | 208 | ``` yml 209 | markdown: 210 | preset: 'zero' 211 | 212 | # Single rule 213 | enable_rules: 'link' 214 | 215 | # Multiple rules 216 | enable_rules: 217 | - 'link' 218 | - 'image' 219 | ``` 220 | 221 | "default" preset enables all rules, to disable specific rules: 222 | 223 | ``` yml 224 | markdown: 225 | preset: 'default' 226 | 227 | # Single rule 228 | disable_rules: 'link' 229 | 230 | # Multiple rules 231 | disable_rules: 232 | - 'link' 233 | - 'image' 234 | ``` 235 | 236 | #### Available rules 237 | 238 | Since the rules are subject to change, it's better to check the Markdown-it's source code for up-to-date rules. Look for the `_rules` variable in the following files: 239 | - [parser_block.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_block.js) 240 | - [parser_core.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_core.js) 241 | - [parser_inline.js](https://github.com/markdown-it/markdown-it/blob/master/lib/parser_inline.js) 242 | 243 | ### Automatic Headline ID's 244 | 245 | Enables you to automatically create ID's for the headings so you can link back to them. A valid html document cannot have two elements with duplicate id value, for example if `title` id is already used, subsequent `title` values will be updated to `title-2`, `title-3` and so on. 246 | 247 | Default options: 248 | 249 | ``` yaml 250 | markdown: 251 | anchors: 252 | # Minimum level for ID creation. (Ex. h2 to h6) 253 | level: 2 254 | # A suffix that is prepended to the number given if the ID is repeated. 255 | collisionSuffix: '' 256 | # If `true`, creates an anchor tag with a permalink besides the heading. 257 | permalink: false 258 | # Class used for the permalink anchor tag. 259 | permalinkClass: header-anchor 260 | # Set to 'right' to add permalink after heading 261 | permalinkSide: 'left' 262 | # The symbol used to make the permalink 263 | permalinkSymbol: ¶ 264 | # Transform anchor to (1) lower case; (2) upper case 265 | case: 0 266 | # Replace space with a character 267 | separator: '-' 268 | ``` 269 | 270 | ### Plugins 271 | 272 | Included plugins: 273 | - markdown-it-abbr 274 | - markdown-it-attrs 275 | - markdown-it-cjk-breaks 276 | - markdown-it-container 277 | - markdown-it-deflist 278 | - markdown-it-emoji 279 | - markdown-it-footnote 280 | - markdown-it-ins 281 | - markdown-it-mark 282 | - markdown-it-sub 283 | - markdown-it-sup 284 | 285 | Plugins are not enabled by default, to enable: 286 | 287 | ``` yaml 288 | markdown: 289 | plugins: 290 | - markdown-it-abbr 291 | # installed external plugins also can be enabled 292 | - markdown-it-table-of-contents 293 | ``` 294 | 295 | #### Plugin option 296 | 297 | ``` yaml 298 | markdown: 299 | plugins: 300 | - name: 'markdown-it-emoji' 301 | options: 302 | shortcuts: 303 | laughing: ':D' 304 | - name: 'other-plugin' 305 | options: ... 306 | ``` 307 | 308 | ## Extensibility 309 | 310 | This plugin overrides some default behaviors of how markdown-it plugin renders the markdown into html, to integrate with the Hexo ecosystem. It is possible to override this plugin too, without resorting to forking the whole thing. 311 | 312 | For example, to enable [unsafe links](https://markdown-it.github.io/markdown-it/#MarkdownIt.prototype.validateLink) (which is disabled by default): 313 | 314 | ``` js 315 | hexo.extend.filter.register('markdown-it:renderer', function(md) { 316 | const { config } = this; // Optional, parse user config from _config.yml 317 | md.validateLink = function() { return true; }; 318 | }); 319 | 320 | // Specify custom function in plugin option 321 | const { slugize } = require('hexo-util'); 322 | const opts = hexo.config.markdown.anchors; 323 | const mdSlugize = (str) => { 324 | return slugize(str, { transform: opts.case, ...opts }); 325 | }; 326 | 327 | hexo.extend.filter.register('markdown-it:renderer', function(md) { 328 | md.use(require('markdown-it-table-of-contents'), { 329 | includeLevel: [2,3,4], 330 | slugify: mdSlugize 331 | }); 332 | }); 333 | ``` 334 | 335 | Save the file in "scripts/" folder and run Hexo as usual. 336 | 337 | Refer to markdown-it [API documentation](https://markdown-it.github.io/markdown-it/#MarkdownIt). 338 | 339 | ## Frequently Asked Questions 340 | 341 | ### Missing Styles of GFM Task Lists 342 | 343 | In general, adding the following styles to the theme can solve the problem. 344 | 345 | ```css 346 | li.task-list-item { 347 | list-style-type: none; 348 | } 349 | 350 | li.task-list-item .task-list-item-checkbox { 351 | margin: 0 0.2em 0.25em -1.6em; 352 | } 353 | ``` 354 | 355 | ### How can I add math support? 356 | 357 | First, install KaTeX plugin for markdown-it. 358 | 359 | ```bash 360 | npm install katex @renbaoshuo/markdown-it-katex 361 | ``` 362 | 363 | Then add [`@renbaoshuo/markdown-it-katex`](https://github.com/renbaoshuo/markdown-it-katex) to plugins list. 364 | 365 | ```yaml 366 | plugins: 367 | - '@renbaoshuo/markdown-it-katex' 368 | # Other plugins... 369 | ``` 370 | 371 | If you need to allow spaces before and after delimiters (e.g. `$ 1 + 1 = 2 $`), set the `skipDelimitersCheck` option to `true`: 372 | 373 | ```yaml 374 | plugins: 375 | - name: '@renbaoshuo/markdown-it-katex' 376 | options: 377 | skipDelimitersCheck: true 378 | ``` 379 | 380 | Don't forget to include the KaTeX stylesheet in your html: 381 | 382 | ```html 383 | 387 | ``` 388 | 389 | ### How can I merge table cells with the same content? 390 | 391 | Install the [`markdown-it-merge-cells`](https://github.com/Menci/markdown-it-merge-cells) plugin. 392 | 393 | ```bash 394 | npm install markdown-it-merge-cells 395 | ``` 396 | 397 | Then add the plugin to plugins list. 398 | 399 | ```yaml 400 | plugins: 401 | - markdown-it-merge-cells 402 | # Other plugins... 403 | ``` 404 | 405 | ## Requests and bug reports 406 | 407 | If you have any feature requests or bugs to report, you're welcome to [file an issue](https://github.com/hexojs/hexo-renderer-markdown-it/issues). 408 | 409 | 410 | [CommonMark]: http://commonmark.org/ 411 | [Markdown]: http://daringfireball.net/projects/markdown/ 412 | [GFM]: https://help.github.com/articles/github-flavored-markdown/ 413 | [Markdown-it]: https://github.com/markdown-it/markdown-it 414 | [Hexo]: http://hexo.io/ 415 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 3 | 'use strict'; 4 | 5 | hexo.config.markdown = Object.assign({ 6 | preset: 'default', 7 | render: {}, 8 | anchors: {} 9 | }, hexo.config.markdown); 10 | 11 | hexo.config.markdown.render = Object.assign({ 12 | html: true, 13 | xhtmlOut: false, 14 | breaks: true, 15 | linkify: true, 16 | typographer: true, 17 | quotes: '“”‘’' 18 | }, hexo.config.markdown.render); 19 | 20 | hexo.config.markdown.anchors = Object.assign({ 21 | level: 2, 22 | collisionSuffix: '', 23 | permalink: false, 24 | permalinkClass: 'header-anchor', 25 | permalinkSide: 'left', 26 | permalinkSymbol: '¶', 27 | case: 0, 28 | separator: '-' 29 | }, hexo.config.markdown.anchors); 30 | 31 | const Renderer = require('./lib/renderer'); 32 | const renderer = new Renderer(hexo); 33 | 34 | renderer.disableNunjucks = Boolean(hexo.config.markdown.disableNunjucks); 35 | 36 | function render(data, options) { 37 | return renderer.render(data, options); 38 | } 39 | 40 | hexo.extend.renderer.register('md', 'html', render, true); 41 | hexo.extend.renderer.register('markdown', 'html', render, true); 42 | hexo.extend.renderer.register('mkd', 'html', render, true); 43 | hexo.extend.renderer.register('mkdn', 'html', render, true); 44 | hexo.extend.renderer.register('mdwn', 'html', render, true); 45 | hexo.extend.renderer.register('mdtxt', 'html', render, true); 46 | hexo.extend.renderer.register('mdtext', 'html', render, true); 47 | -------------------------------------------------------------------------------- /lib/anchors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Token = require('markdown-it/lib/token'); 4 | const { slugize } = require('hexo-util'); 5 | 6 | const renderPermalink = function(slug, opts, tokens, idx) { 7 | const permalink = [Object.assign(new Token('link_open', 'a', 1), { 8 | attrs: [['class', opts.permalinkClass], ['href', '#' + slug]] 9 | }), Object.assign(new Token('text', '', 0), { 10 | content: opts.permalinkSymbol 11 | }), new Token('link_close', 'a', -1), Object.assign(new Token('text', '', 0), { 12 | content: '' 13 | })]; 14 | 15 | if (opts.permalinkSide === 'right') { 16 | return tokens[idx + 1].children.push(...permalink); 17 | } 18 | 19 | return tokens[idx + 1].children.unshift(...permalink); 20 | }; 21 | 22 | const anchor = function(md, opts) { 23 | Object.assign(opts, { renderPermalink }); 24 | 25 | let titleStore = {}; 26 | const originalHeadingOpen = md.renderer.rules.heading_open; 27 | const slugOpts = { transform: opts.case, ...opts }; 28 | 29 | md.renderer.rules.heading_open = function(...args) { 30 | const [tokens, idx, something, somethingelse, self] = args; // eslint-disable-line no-unused-vars 31 | 32 | if (tokens[idx].tag.substr(1) >= opts.level) { 33 | let _tokens$idx; 34 | 35 | const title = tokens[idx + 1].children.reduce((acc, t) => { 36 | return acc + t.content; 37 | }, ''); 38 | 39 | let slug = slugize(title, slugOpts); 40 | 41 | if (Object.prototype.hasOwnProperty.call(titleStore, slug)) { 42 | titleStore[slug] = titleStore[slug] + 1; 43 | slug = slug + '-' + opts.collisionSuffix + titleStore[slug].toString(); 44 | } else { 45 | titleStore[slug] = 1; 46 | } 47 | 48 | 49 | (_tokens$idx = tokens[idx], !_tokens$idx.attrs && (_tokens$idx.attrs = []), _tokens$idx.attrs) 50 | .push(['id', slug]); 51 | 52 | if (opts.permalink) { 53 | opts.renderPermalink.apply(opts, [slug, opts].concat(args)); 54 | } 55 | } 56 | 57 | return originalHeadingOpen 58 | ? originalHeadingOpen.apply(this, args) 59 | : self.renderToken.apply(self, args); 60 | }; 61 | 62 | md.core.ruler.push('clear_anchor_title_store', () => { 63 | titleStore = {}; 64 | }); 65 | }; 66 | 67 | module.exports = anchor; 68 | -------------------------------------------------------------------------------- /lib/images.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { join, relative: relativePosix } = require('path').posix; 4 | const { relative, basename, extname, dirname, isAbsolute } = require('path'); 5 | const { url_for } = require('hexo-util'); 6 | 7 | function images(md, opts) { 8 | const { hexo, images } = opts; 9 | const { lazyload, prepend_root: prependRoot, post_asset: postAsset } = images; 10 | const { relative_link, model, base_dir, source_dir } = hexo; 11 | 12 | md.renderer.rules.image = function(tokens, idx, options, env, self) { 13 | const token = tokens[idx]; 14 | const { postPath } = env; 15 | 16 | token.attrSet('alt', token.content); 17 | 18 | if (lazyload) { 19 | token.attrSet('loading', 'lazy'); 20 | } 21 | 22 | if (!prependRoot && !postAsset) { 23 | return self.renderToken(tokens, idx, options); 24 | } 25 | 26 | const srcIdx = token.attrs.findIndex(attr => attr[0] === 'src'); 27 | let src = token.attrs[srcIdx][1]; 28 | if (!/^(#|\/\/|http(s)?:)/.test(src) && !relative_link) { 29 | if (!(src.startsWith('/') || src.startsWith('\\')) && postAsset) { 30 | const PostAsset = model.call(hexo, 'PostAsset'); 31 | let assetDirBasePath = join(basename(source_dir), dirname(relativePosix(source_dir, postPath)), basename(postPath, extname(postPath))); 32 | if (isAbsolute(assetDirBasePath)) assetDirBasePath = relative(base_dir, assetDirBasePath); 33 | assetDirBasePath = assetDirBasePath.replace(/\\/g, '/'); 34 | 35 | const asset = [ 36 | join(assetDirBasePath, src), 37 | join(assetDirBasePath, src.replace(new RegExp('^' + basename(postPath, extname(postPath)) + '/'), '')) 38 | ] 39 | .map(id => PostAsset.findById(id)) 40 | .filter(Boolean); 41 | 42 | if (asset.length) { 43 | src = asset[0].path.replace(/\\/g, '/'); 44 | } 45 | } 46 | 47 | token.attrSet('src', url_for.call(hexo, src)); 48 | } 49 | 50 | return self.renderToken(tokens, idx, options); 51 | }; 52 | } 53 | 54 | module.exports = images; 55 | -------------------------------------------------------------------------------- /lib/renderer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MarkdownIt = require('markdown-it'); 4 | const path = require('path'); 5 | 6 | class Renderer { 7 | /** 8 | * constructor 9 | * 10 | * @param {*} hexo context of hexo 11 | */ 12 | constructor(hexo) { 13 | this.hexo = hexo; 14 | 15 | let { markdown } = hexo.config; 16 | 17 | // Temporary backward compatibility 18 | if (typeof markdown === 'string') { 19 | markdown = { 20 | preset: markdown, 21 | }; 22 | hexo.log.warn( 23 | `Deprecated config detected. Please use\n\nmarkdown:\n preset: ${markdown.preset}\n\nSee https://github.com/hexojs/hexo-renderer-markdown-it#options` 24 | ); 25 | } 26 | 27 | const { preset, render, enable_rules, disable_rules, plugins, anchors, images } = markdown; 28 | this.parser = new MarkdownIt(preset, render); 29 | 30 | if (enable_rules) { 31 | this.parser.enable(enable_rules); 32 | } 33 | 34 | if (disable_rules) { 35 | this.parser.disable(disable_rules); 36 | } 37 | 38 | if (plugins) { 39 | this.parser = plugins.reduce((parser, pugs) => { 40 | if (pugs instanceof Object && pugs.name) { 41 | const resolved = require.resolve(pugs.name, { 42 | paths: [ 43 | // find from root hexo base directory node_modules 44 | path.join(hexo.base_dir, 'node_modules'), 45 | // find from current installed library node_modules 46 | path.join(__dirname, '../node_modules'), 47 | // find from root hexo base directory 48 | hexo.base_dir, 49 | // find from current library directory 50 | path.join(__dirname, '../'), 51 | ], 52 | }); 53 | return parser.use(require(resolved), pugs.options); 54 | } 55 | return parser.use(require(pugs)); 56 | }, this.parser); 57 | } 58 | 59 | if (anchors) { 60 | this.parser.use(require('./anchors'), anchors); 61 | } 62 | 63 | if (images) { 64 | this.parser.use(require('./images'), { 65 | images, 66 | hexo: this.hexo, 67 | }); 68 | } 69 | } 70 | 71 | render(data, options) { 72 | this.hexo.execFilterSync('markdown-it:renderer', this.parser, { context: this }); 73 | 74 | if (options != null && options.inline === true) { 75 | return this.parser.renderInline(data.text, { 76 | postPath: data.path 77 | }); 78 | } 79 | return this.parser.render(data.text, { 80 | postPath: data.path, 81 | }); 82 | } 83 | } 84 | 85 | module.exports = Renderer; 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-renderer-markdown-it", 3 | "version": "7.1.1", 4 | "description": "Markdown-it is a Markdown parser, done right. A faster and CommonMark compliant alternative for Hexo.", 5 | "main": "index.js", 6 | "scripts": { 7 | "eslint": "eslint .", 8 | "test": "mocha test/index.js", 9 | "test-cov": "c8 --reporter=lcovonly npm run test" 10 | }, 11 | "repository": "hexojs/hexo-renderer-markdown-it", 12 | "keywords": [ 13 | "hexo", 14 | "renderer", 15 | "markdown", 16 | "markdown-it", 17 | "hexo-renderer" 18 | ], 19 | "directories": { 20 | "lib": "./lib" 21 | }, 22 | "files": [ 23 | "index.js", 24 | "lib/" 25 | ], 26 | "author": "Celso Miranda (http://celsomiranda.net/)", 27 | "maintainers": [ 28 | "Hexo Core Team (https://github.com/orgs/hexojs/teams/core)" 29 | ], 30 | "license": "MIT", 31 | "bugs": "https://github.com/hexojs/hexo-renderer-markdown-it/issues", 32 | "homepage": "https://github.com/hexojs/hexo-renderer-markdown-it", 33 | "dependencies": { 34 | "hexo-util": "^3.0.1", 35 | "markdown-it": "^13.0.1", 36 | "markdown-it-abbr": "^1.0.4", 37 | "markdown-it-attrs": "^4.1.3", 38 | "markdown-it-cjk-breaks": "^1.1.2", 39 | "markdown-it-container": "^3.0.0", 40 | "markdown-it-deflist": "^2.0.3", 41 | "markdown-it-emoji": "^2.0.0", 42 | "markdown-it-footnote": "^3.0.1", 43 | "markdown-it-ins": "^3.0.0", 44 | "markdown-it-mark": "^3.0.0", 45 | "markdown-it-sub": "^1.0.0", 46 | "markdown-it-sup": "^1.0.0" 47 | }, 48 | "devDependencies": { 49 | "chai": "^4.3.7", 50 | "eslint": "^8.41.0", 51 | "eslint-config-hexo": "^5.0.0", 52 | "hexo": "^7.0.0", 53 | "mocha": "^10.2.0", 54 | "c8": "^8.0.1" 55 | }, 56 | "engines": { 57 | "node": ">=14" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "hexo/test" 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/fixtures/markdownit.md: -------------------------------------------------------------------------------- 1 | # h1 Heading em português 2 | ## h2 Heading :P 3 | ### h3 Heading 4 | #### h4 Heading 5 | ##### h5 Heading 6 | ###### h6 Heading 7 | 8 | 9 | ## Horizontal Rule 10 | 11 | ___ 12 | 13 | ## Horizontal Rule 14 | 15 | --- 16 | 17 | ## Horizontal Rule 18 | 19 | *** 20 | 21 | 22 | ## Typographic replacements 23 | 24 | Enable typographer option to see result. 25 | 26 | (c) (C) (r) (R) (tm) (TM) +- 27 | 28 | test.. test... test..... test?..... test!.... 29 | 30 | !!!!!! ???? ,, -- --- 31 | 32 | "Smartypants, double quotes" and 'single quotes' 33 | 34 | 35 | ## Emphasis 36 | 37 | **This is bold text** 38 | 39 | __This is bold text__ 40 | 41 | *This is italic text* 42 | 43 | _This is italic text_ 44 | 45 | ~~Strikethrough~~ 46 | 47 | 48 | ## Blockquotes 49 | 50 | 51 | > Blockquotes can also be nested... 52 | >> ...by using additional greater-than signs right next to each other... 53 | > > > ...or with spaces between arrows. 54 | 55 | 56 | ## Lists 57 | 58 | Unordered 59 | 60 | + Create a list by starting a line with `+`, `-`, or `*` 61 | + Sub-lists are made by indenting 2 spaces: 62 | - Marker character change forces new list start: 63 | * Ac tristique libero volutpat at 64 | + Facilisis in pretium nisl aliquet 65 | - Nulla volutpat aliquam velit 66 | + Very easy! 67 | 68 | Ordered 69 | 70 | 1. Lorem ipsum dolor sit amet 71 | 2. Consectetur adipiscing elit 72 | 3. Integer molestie lorem at massa 73 | 74 | 75 | 76 | ## Code 77 | 78 | Inline `code` 79 | 80 | Indented code 81 | 82 | // Some comments 83 | line 1 of code 84 | line 2 of code 85 | line 3 of code 86 | 87 | 88 | Block code "fences" 89 | 90 | ``` 91 | Sample text here... 92 | ``` 93 | 94 | Syntax highlighting 95 | 96 | ``` js 97 | var foo = function (bar) { 98 | return bar++; 99 | }; 100 | 101 | console.log(foo(5)); 102 | ``` 103 | 104 | ## Tables 105 | 106 | | Option | Description | 107 | | ------ | ----------- | 108 | | data | path to data files to supply the data that will be passed into templates. | 109 | | engine | engine to be used for processing templates. Handlebars is the default. | 110 | | ext | extension to be used for dest files. | 111 | 112 | Right aligned columns 113 | 114 | | Option | Description | 115 | | ------:| -----------:| 116 | | data | path to data files to supply the data that will be passed into templates. | 117 | | engine | engine to be used for processing templates. Handlebars is the default. | 118 | | ext | extension to be used for dest files. | 119 | 120 | 121 | ## Links 122 | 123 | [link text](http://dev.nodeca.com) 124 | 125 | Autoconverted link https://github.com/nodeca/pica (enable linkify to see) 126 | 127 | 128 | ## Images 129 | 130 | ![Minion](https://octodex.github.com/images/minion.png) 131 | ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") 132 | 133 | ![Alt text][id] 134 | 135 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" 136 | 137 | 138 | ## Plugins 139 | 140 | The killer feature of `markdown-it` is very effective support of 141 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). 142 | 143 | 144 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji) 145 | 146 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 147 | > 148 | > Shortcuts (emoticons): :-) :-( 8-) ;) 149 | 150 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. 151 | 152 | 153 | ### [Subscipt](https://github.com/markdown-it/markdown-it-sub) / [Superscirpt](https://github.com/markdown-it/markdown-it-sup) 154 | 155 | - 19^th^ 156 | - H~2~O 157 | 158 | 159 | ### [\](https://github.com/markdown-it/markdown-it-ins) 160 | 161 | ++Inserted text++ 162 | 163 | 164 | ### [\](https://github.com/markdown-it/markdown-it-mark) 165 | 166 | ==Marked text== 167 | 168 | 169 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) 170 | 171 | Footnote 1 link[^first]. 172 | 173 | Footnote 2 link[^second]. 174 | 175 | Inline footnote^[Text of inline footnote] definition. 176 | 177 | Duplicated footnote reference[^second]. 178 | 179 | [^first]: Footnote **can have markup** 180 | 181 | and multiple paragraphs. 182 | 183 | [^second]: Footnote text. 184 | 185 | 186 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) 187 | 188 | Term 1 189 | 190 | : Definition 1 191 | with lazy continuation. 192 | 193 | Term 2 with *inline markup* 194 | 195 | : Definition 2 196 | 197 | { some code, part of Definition 2 } 198 | 199 | Third paragraph of definition 2. 200 | 201 | _Compact style:_ 202 | 203 | Term 1 204 | ~ Definition 1 205 | 206 | Term 2 207 | ~ Definition 2a 208 | ~ Definition 2b 209 | 210 | 211 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) 212 | 213 | This is HTML abbreviation example. 214 | 215 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. 216 | 217 | *[HTML]: Hyper Text Markup Language 218 | 219 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container) 220 | 221 | ::: warning 222 | *here be dragons* 223 | ::: 224 | 225 | ### [Attributes](https://github.com/arve0/markdown-it-attrs) { data-attr=true } 226 | 227 | This is an attribute example. 228 | -------------------------------------------------------------------------------- /test/fixtures/outputs/anchors.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

© © ® ® ™ ™ ±

16 |

test… test… test… test?.. test!..

17 |

!!! ??? , – —

18 |

“Smartypants, double quotes” and ‘single quotes’

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

Strikethrough

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested…

28 |
29 |

…by using additional greater-than signs right next to each other…

30 |
31 |

…or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code “fences”

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
103 |

Right aligned columns

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
126 | 127 |

link text

128 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

129 |

Images

130 |

Minion
131 | Stormtroopocat

132 |

Alt text

133 |

Plugins

134 |

The killer feature of markdown-it is very effective support of
135 | syntax plugins.

136 |

Emojies

137 |
138 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

139 |

Shortcuts (emoticons): :-) :-( 8-) ;)

140 |
141 |

see how to change output with twemoji.

142 |

Subscipt / Superscirpt

143 |
    144 |
  • 19^th^
  • 145 |
  • H~2~O
  • 146 |
147 |

<ins>

148 |

++Inserted text++

149 |

<mark>

150 |

==Marked text==

151 |

Footnotes

152 |

Footnote 1 link[^first].

153 |

Footnote 2 link[^second].

154 |

Inline footnote^[Text of inline footnote] definition.

155 |

Duplicated footnote reference[^second].

156 |

[^first]: Footnote can have markup

157 |
and multiple paragraphs.
158 | 
159 |

[^second]: Footnote text.

160 |

Definition lists

161 |

Term 1

162 |

: Definition 1
163 | with lazy continuation.

164 |

Term 2 with inline markup

165 |

: Definition 2

166 |
    { some code, part of Definition 2 }
167 | 
168 | Third paragraph of definition 2.
169 | 
170 |

Compact style:

171 |

Term 1
172 | ~ Definition 1

173 |

Term 2
174 | ~ Definition 2a
175 | ~ Definition 2b

176 |

Abbreviations

177 |

This is HTML abbreviation example.

178 |

It converts “HTML”, but keep intact partial entries like “xxxHTMLyyy” and so on.

179 |

*[HTML]: Hyper Text Markup Language

180 |

Custom containers

181 |

::: warning
182 | here be dragons
183 | :::

184 |

Attributes { data-attr=true }

185 |

This is an attribute example.

186 | -------------------------------------------------------------------------------- /test/fixtures/outputs/commonmark-deprecated.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

(c) (C) (r) (R) (tm) (TM) +-

16 |

test.. test... test..... test?..... test!....

17 |

!!!!!! ???? ,, -- ---

18 |

"Smartypants, double quotes" and 'single quotes'

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

~~Strikethrough~~

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested...

28 |
29 |

...by using additional greater-than signs right next to each other...

30 |
31 |

...or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code "fences"

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 |

| Option | Description | 82 | | ------ | ----------- | 83 | | data | path to data files to supply the data that will be passed into templates. | 84 | | engine | engine to be used for processing templates. Handlebars is the default. | 85 | | ext | extension to be used for dest files. |

86 |

Right aligned columns

87 |

| Option | Description | 88 | | ------:| -----------:| 89 | | data | path to data files to supply the data that will be passed into templates. | 90 | | engine | engine to be used for processing templates. Handlebars is the default. | 91 | | ext | extension to be used for dest files. |

92 |

Links

93 |

link text

94 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

95 |

Images

96 |

Minion 97 | Stormtroopocat

98 |

Alt text

99 |

Plugins

100 |

The killer feature of markdown-it is very effective support of 101 | syntax plugins.

102 |

Emojies

103 |
104 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

105 |

Shortcuts (emoticons): :-) :-( 8-) ;)

106 |
107 |

see how to change output with twemoji.

108 |

Subscipt / Superscirpt

109 |
    110 |
  • 19^th^
  • 111 |
  • H~2~O
  • 112 |
113 |

<ins>

114 |

++Inserted text++

115 |

<mark>

116 |

==Marked text==

117 |

Footnotes

118 |

Footnote 1 link[^first].

119 |

Footnote 2 link[^second].

120 |

Inline footnote^[Text of inline footnote] definition.

121 |

Duplicated footnote reference[^second].

122 |

[^first]: Footnote can have markup

123 |
and multiple paragraphs.
124 | 
125 |

[^second]: Footnote text.

126 |

Definition lists

127 |

Term 1

128 |

: Definition 1 129 | with lazy continuation.

130 |

Term 2 with inline markup

131 |

: Definition 2

132 |
    { some code, part of Definition 2 }
133 | 
134 | Third paragraph of definition 2.
135 | 
136 |

Compact style:

137 |

Term 1 138 | ~ Definition 1

139 |

Term 2 140 | ~ Definition 2a 141 | ~ Definition 2b

142 |

Abbreviations

143 |

This is HTML abbreviation example.

144 |

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

145 |

*[HTML]: Hyper Text Markup Language

146 |

Custom containers

147 |

::: warning 148 | here be dragons 149 | :::

150 |

Attributes { data-attr=true }

151 |

This is an attribute example.

152 | -------------------------------------------------------------------------------- /test/fixtures/outputs/commonmark.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

(c) (C) (r) (R) (tm) (TM) +-

16 |

test.. test... test..... test?..... test!....

17 |

!!!!!! ???? ,, -- ---

18 |

"Smartypants, double quotes" and 'single quotes'

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

~~Strikethrough~~

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested...

28 |
29 |

...by using additional greater-than signs right next to each other...

30 |
31 |

...or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code "fences"

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 |

| Option | Description |
82 | | ------ | ----------- |
83 | | data | path to data files to supply the data that will be passed into templates. |
84 | | engine | engine to be used for processing templates. Handlebars is the default. |
85 | | ext | extension to be used for dest files. |

86 |

Right aligned columns

87 |

| Option | Description |
88 | | ------:| -----------:|
89 | | data | path to data files to supply the data that will be passed into templates. |
90 | | engine | engine to be used for processing templates. Handlebars is the default. |
91 | | ext | extension to be used for dest files. |

92 | 93 |

link text

94 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

95 |

Images

96 |

Minion
97 | Stormtroopocat

98 |

Alt text

99 |

Plugins

100 |

The killer feature of markdown-it is very effective support of
101 | syntax plugins.

102 |

Emojies

103 |
104 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

105 |

Shortcuts (emoticons): :-) :-( 8-) ;)

106 |
107 |

see how to change output with twemoji.

108 |

Subscipt / Superscirpt

109 |
    110 |
  • 19^th^
  • 111 |
  • H~2~O
  • 112 |
113 |

<ins>

114 |

++Inserted text++

115 |

<mark>

116 |

==Marked text==

117 |

Footnotes

118 |

Footnote 1 link[^first].

119 |

Footnote 2 link[^second].

120 |

Inline footnote^[Text of inline footnote] definition.

121 |

Duplicated footnote reference[^second].

122 |

[^first]: Footnote can have markup

123 |
and multiple paragraphs.
124 | 
125 |

[^second]: Footnote text.

126 |

Definition lists

127 |

Term 1

128 |

: Definition 1
129 | with lazy continuation.

130 |

Term 2 with inline markup

131 |

: Definition 2

132 |
    { some code, part of Definition 2 }
133 | 
134 | Third paragraph of definition 2.
135 | 
136 |

Compact style:

137 |

Term 1
138 | ~ Definition 1

139 |

Term 2
140 | ~ Definition 2a
141 | ~ Definition 2b

142 |

Abbreviations

143 |

This is HTML abbreviation example.

144 |

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

145 |

*[HTML]: Hyper Text Markup Language

146 |

Custom containers

147 |

::: warning
148 | here be dragons
149 | :::

150 |

Attributes { data-attr=true }

151 |

This is an attribute example.

152 | -------------------------------------------------------------------------------- /test/fixtures/outputs/custom.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

© © ® ® ™ ™ ±

16 |

test… test… test… test?.. test!..

17 |

!!! ??? , – —

18 |

«Smartypants, double quotes» and “single quotes”

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

Strikethrough

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested…

28 |
29 |

…by using additional greater-than signs right next to each other…

30 |
31 |

…or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code «fences»

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
103 |

Right aligned columns

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
126 | 127 |

link text

128 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

129 |

Images

130 |

Minion
131 | Stormtroopocat

132 |

Alt text

133 |

Plugins

134 |

The killer feature of markdown-it is very effective support of
135 | syntax plugins.

136 |

Emojies

137 |
138 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

139 |

Shortcuts (emoticons): :-) :-( 8-) ;)

140 |
141 |

see how to change output with twemoji.

142 |

Subscipt / Superscirpt

143 |
    144 |
  • 19^th^
  • 145 |
  • H~2~O
  • 146 |
147 |

<ins>

148 |

++Inserted text++

149 |

<mark>

150 |

==Marked text==

151 |

Footnotes

152 |

Footnote 1 link[^first].

153 |

Footnote 2 link[^second].

154 |

Inline footnote^[Text of inline footnote] definition.

155 |

Duplicated footnote reference[^second].

156 |

[^first]: Footnote can have markup

157 |
and multiple paragraphs.
158 | 
159 |

[^second]: Footnote text.

160 |

Definition lists

161 |

Term 1

162 |

: Definition 1
163 | with lazy continuation.

164 |

Term 2 with inline markup

165 |

: Definition 2

166 |
    { some code, part of Definition 2 }
167 | 
168 | Third paragraph of definition 2.
169 | 
170 |

Compact style:

171 |

Term 1
172 | ~ Definition 1

173 |

Term 2
174 | ~ Definition 2a
175 | ~ Definition 2b

176 |

Abbreviations

177 |

This is HTML abbreviation example.

178 |

It converts «HTML», but keep intact partial entries like «xxxHTMLyyy» and so on.

179 |

*[HTML]: Hyper Text Markup Language

180 |

Custom containers

181 |

::: warning
182 | here be dragons
183 | :::

184 |

Attributes { data-attr=true }

185 |

This is an attribute example.

186 | -------------------------------------------------------------------------------- /test/fixtures/outputs/default-disable_rules.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

© © ® ® ™ ™ ±

16 |

test… test… test… test?.. test!..

17 |

!!! ??? , – —

18 |

“Smartypants, double quotes” and ‘single quotes’

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

Strikethrough

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested…

28 |
29 |

…by using additional greater-than signs right next to each other…

30 |
31 |

…or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code “fences”

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
103 |

Right aligned columns

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
126 | 127 |

[link text](http://dev.nodeca.com)

128 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

129 |

Images

130 |

Minion
131 | Stormtroopocat

132 |

Alt text

133 |

Plugins

134 |

The killer feature of markdown-it is very effective support of
135 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).

136 |

[Emojies](https://github.com/markdown-it/markdown-it-emoji)

137 |
138 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

139 |

Shortcuts (emoticons): :-) :-( 8-) ;)

140 |
141 |

see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.

142 |

[Subscipt](https://github.com/markdown-it/markdown-it-sub) / [Superscirpt](https://github.com/markdown-it/markdown-it-sup)

143 |
    144 |
  • 19^th^
  • 145 |
  • H~2~O
  • 146 |
147 |

[<ins>](https://github.com/markdown-it/markdown-it-ins)

148 |

++Inserted text++

149 |

[<mark>](https://github.com/markdown-it/markdown-it-mark)

150 |

==Marked text==

151 |

[Footnotes](https://github.com/markdown-it/markdown-it-footnote)

152 |

Footnote 1 link[^first].

153 |

Footnote 2 link[^second].

154 |

Inline footnote^[Text of inline footnote] definition.

155 |

Duplicated footnote reference[^second].

156 |

[^first]: Footnote can have markup

157 |
and multiple paragraphs.
158 | 
159 |

[^second]: Footnote text.

160 |

[Definition lists](https://github.com/markdown-it/markdown-it-deflist)

161 |

Term 1

162 |

: Definition 1
163 | with lazy continuation.

164 |

Term 2 with inline markup

165 |

: Definition 2

166 |
    { some code, part of Definition 2 }
167 | 
168 | Third paragraph of definition 2.
169 | 
170 |

Compact style:

171 |

Term 1
172 | ~ Definition 1

173 |

Term 2
174 | ~ Definition 2a
175 | ~ Definition 2b

176 |

[Abbreviations](https://github.com/markdown-it/markdown-it-abbr)

177 |

This is HTML abbreviation example.

178 |

It converts “HTML”, but keep intact partial entries like “xxxHTMLyyy” and so on.

179 |

*[HTML]: Hyper Text Markup Language

180 |

[Custom containers](https://github.com/markdown-it/markdown-it-container)

181 |

::: warning
182 | here be dragons
183 | :::

184 |

[Attributes](https://github.com/arve0/markdown-it-attrs) { data-attr=true }

185 |

This is an attribute example.

186 | -------------------------------------------------------------------------------- /test/fixtures/outputs/default.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading :P

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

© © ® ® ™ ™ ±

16 |

test… test… test… test?.. test!..

17 |

!!! ??? , – —

18 |

“Smartypants, double quotes” and ‘single quotes’

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

Strikethrough

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested…

28 |
29 |

…by using additional greater-than signs right next to each other…

30 |
31 |

…or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code “fences”

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
103 |

Right aligned columns

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
126 | 127 |

link text

128 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

129 |

Images

130 |

Minion
131 | Stormtroopocat

132 |

Alt text

133 |

Plugins

134 |

The killer feature of markdown-it is very effective support of
135 | syntax plugins.

136 |

Emojies

137 |
138 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

139 |

Shortcuts (emoticons): :-) :-( 8-) ;)

140 |
141 |

see how to change output with twemoji.

142 |

Subscipt / Superscirpt

143 |
    144 |
  • 19^th^
  • 145 |
  • H~2~O
  • 146 |
147 |

<ins>

148 |

++Inserted text++

149 |

<mark>

150 |

==Marked text==

151 |

Footnotes

152 |

Footnote 1 link[^first].

153 |

Footnote 2 link[^second].

154 |

Inline footnote^[Text of inline footnote] definition.

155 |

Duplicated footnote reference[^second].

156 |

[^first]: Footnote can have markup

157 |
and multiple paragraphs.
158 | 
159 |

[^second]: Footnote text.

160 |

Definition lists

161 |

Term 1

162 |

: Definition 1
163 | with lazy continuation.

164 |

Term 2 with inline markup

165 |

: Definition 2

166 |
    { some code, part of Definition 2 }
167 | 
168 | Third paragraph of definition 2.
169 | 
170 |

Compact style:

171 |

Term 1
172 | ~ Definition 1

173 |

Term 2
174 | ~ Definition 2a
175 | ~ Definition 2b

176 |

Abbreviations

177 |

This is HTML abbreviation example.

178 |

It converts “HTML”, but keep intact partial entries like “xxxHTMLyyy” and so on.

179 |

*[HTML]: Hyper Text Markup Language

180 |

Custom containers

181 |

::: warning
182 | here be dragons
183 | :::

184 |

Attributes { data-attr=true }

185 |

This is an attribute example.

186 | -------------------------------------------------------------------------------- /test/fixtures/outputs/plugins.html: -------------------------------------------------------------------------------- 1 |

h1 Heading em português

2 |

h2 Heading 😛

3 |

h3 Heading

4 |

h4 Heading

5 |
h5 Heading
6 |
h6 Heading
7 |

Horizontal Rule

8 |
9 |

Horizontal Rule

10 |
11 |

Horizontal Rule

12 |
13 |

Typographic replacements

14 |

Enable typographer option to see result.

15 |

© © ® ® ™ ™ ±

16 |

test… test… test… test?.. test!..

17 |

!!! ??? , – —

18 |

“Smartypants, double quotes” and ‘single quotes’

19 |

Emphasis

20 |

This is bold text

21 |

This is bold text

22 |

This is italic text

23 |

This is italic text

24 |

Strikethrough

25 |

Blockquotes

26 |
27 |

Blockquotes can also be nested…

28 |
29 |

…by using additional greater-than signs right next to each other…

30 |
31 |

…or with spaces between arrows.

32 |
33 |
34 |
35 |

Lists

36 |

Unordered

37 |
    38 |
  • Create a list by starting a line with +, -, or *
  • 39 |
  • Sub-lists are made by indenting 2 spaces: 40 |
      41 |
    • Marker character change forces new list start: 42 |
        43 |
      • Ac tristique libero volutpat at
      • 44 |
      45 |
        46 |
      • Facilisis in pretium nisl aliquet
      • 47 |
      48 |
        49 |
      • Nulla volutpat aliquam velit
      • 50 |
      51 |
    • 52 |
    53 |
  • 54 |
  • Very easy!
  • 55 |
56 |

Ordered

57 |
    58 |
  1. Lorem ipsum dolor sit amet
  2. 59 |
  3. Consectetur adipiscing elit
  4. 60 |
  5. Integer molestie lorem at massa
  6. 61 |
62 |

Code

63 |

Inline code

64 |

Indented code

65 |
// Some comments
 66 | line 1 of code
 67 | line 2 of code
 68 | line 3 of code
 69 | 
70 |

Block code “fences”

71 |
Sample text here...
 72 | 
73 |

Syntax highlighting

74 |
var foo = function (bar) {
 75 |   return bar++;
 76 | };
 77 | 
 78 | console.log(foo(5));
 79 | 
80 |

Tables

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
103 |

Right aligned columns

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
OptionDescription
datapath to data files to supply the data that will be passed into templates.
engineengine to be used for processing templates. Handlebars is the default.
extextension to be used for dest files.
126 | 127 |

link text

128 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

129 |

Images

130 |

Minion
131 | Stormtroopocat

132 |

Alt text

133 |

Plugins

134 |

The killer feature of markdown-it is very effective support of
135 | syntax plugins.

136 |

Emojies

137 |
138 |

Classic markup: 😉 :crush: 😢 :tear: 😆 😋

139 |

Shortcuts (emoticons): 😃 😦 😎 😉

140 |
141 |

see how to change output with twemoji.

142 |

Subscipt / Superscirpt

143 |
    144 |
  • 19th
  • 145 |
  • H2O
  • 146 |
147 |

<ins>

148 |

Inserted text

149 |

<mark>

150 |

Marked text

151 |

Footnotes

152 |

Footnote 1 link[1].

153 |

Footnote 2 link[2].

154 |

Inline footnote[3] definition.

155 |

Duplicated footnote reference[2:1].

156 |

Definition lists

157 |
158 |
Term 1
159 |
160 |

Definition 1
161 | with lazy continuation.

162 |
163 |
Term 2 with inline markup
164 |
165 |

Definition 2

166 |
  { some code, part of Definition 2 }
167 | 
168 |

Third paragraph of definition 2.

169 |
170 |
171 |

Compact style:

172 |
173 |
Term 1
174 |
Definition 1
175 |
Term 2
176 |
Definition 2a
177 |
Definition 2b
178 |
179 |

Abbreviations

180 |

This is HTML abbreviation example.

181 |

It converts “HTML”, but keep intact partial entries like “xxxHTMLyyy” and so on.

182 |

Custom containers

183 |

::: warning
184 | here be dragons
185 | :::

186 |

Attributes

187 |

This is an attribute example.

188 |
189 |
190 |
    191 |
  1. Footnote can have markup

    192 |

    and multiple paragraphs. ↩︎

    193 |
  2. 194 |
  3. Footnote text. ↩︎ ↩︎

    195 |
  4. 196 |
  5. Text of inline footnote ↩︎

    197 |
  6. 198 |
199 |
200 | -------------------------------------------------------------------------------- /test/fixtures/outputs/zero-enable_rules.html: -------------------------------------------------------------------------------- 1 |

# h1 Heading em português 2 | ## h2 Heading :P 3 | ### h3 Heading 4 | #### h4 Heading 5 | ##### h5 Heading 6 | ###### h6 Heading

7 |

## Horizontal Rule

8 |

___

9 |

## Horizontal Rule

10 |

---

11 |

## Horizontal Rule

12 |

***

13 |

## Typographic replacements

14 |

Enable typographer option to see result.

15 |

(c) (C) (r) (R) (tm) (TM) +-

16 |

test.. test... test..... test?..... test!....

17 |

!!!!!! ???? ,, -- ---

18 |

"Smartypants, double quotes" and 'single quotes'

19 |

## Emphasis

20 |

**This is bold text**

21 |

__This is bold text__

22 |

*This is italic text*

23 |

_This is italic text_

24 |

~~Strikethrough~~

25 |

## Blockquotes

26 |

> Blockquotes can also be nested... 27 | >> ...by using additional greater-than signs right next to each other... 28 | > > > ...or with spaces between arrows.

29 |

## Lists

30 |

Unordered

31 |

+ Create a list by starting a line with `+`, `-`, or `*` 32 | + Sub-lists are made by indenting 2 spaces: 33 | - Marker character change forces new list start: 34 | * Ac tristique libero volutpat at 35 | + Facilisis in pretium nisl aliquet 36 | - Nulla volutpat aliquam velit 37 | + Very easy!

38 |

Ordered

39 |

1. Lorem ipsum dolor sit amet 40 | 2. Consectetur adipiscing elit 41 | 3. Integer molestie lorem at massa

42 |

## Code

43 |

Inline `code`

44 |

Indented code

45 |

// Some comments 46 | line 1 of code 47 | line 2 of code 48 | line 3 of code

49 |

Block code "fences"

50 |

``` 51 | Sample text here... 52 | ```

53 |

Syntax highlighting

54 |

``` js 55 | var foo = function (bar) { 56 | return bar++; 57 | };

58 |

console.log(foo(5)); 59 | ```

60 |

## Tables

61 |

| Option | Description | 62 | | ------ | ----------- | 63 | | data | path to data files to supply the data that will be passed into templates. | 64 | | engine | engine to be used for processing templates. Handlebars is the default. | 65 | | ext | extension to be used for dest files. |

66 |

Right aligned columns

67 |

| Option | Description | 68 | | ------:| -----------:| 69 | | data | path to data files to supply the data that will be passed into templates. | 70 | | engine | engine to be used for processing templates. Handlebars is the default. | 71 | | ext | extension to be used for dest files. |

72 |

## Links

73 |

link text

74 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

75 |

## Images

76 |

Minion 77 | Stormtroopocat

78 |

![Alt text][id]

79 |

[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"

80 |

## Plugins

81 |

The killer feature of `markdown-it` is very effective support of 82 | syntax plugins.

83 |

### Emojies

84 |

> Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 85 | > 86 | > Shortcuts (emoticons): :-) :-( 8-) ;)

87 |

see how to change output with twemoji.

88 |

### Subscipt / Superscirpt

89 |

- 19^th^ 90 | - H~2~O

91 |

### \<ins>

92 |

++Inserted text++

93 |

### \<mark>

94 |

==Marked text==

95 |

### Footnotes

96 |

Footnote 1 link[^first].

97 |

Footnote 2 link[^second].

98 |

Inline footnote^[Text of inline footnote] definition.

99 |

Duplicated footnote reference[^second].

100 |

[^first]: Footnote **can have markup**

101 |

and multiple paragraphs.

102 |

[^second]: Footnote text.

103 |

### Definition lists

104 |

Term 1

105 |

: Definition 1 106 | with lazy continuation.

107 |

Term 2 with *inline markup*

108 |

: Definition 2

109 |

{ some code, part of Definition 2 }

110 |

Third paragraph of definition 2.

111 |

_Compact style:_

112 |

Term 1 113 | ~ Definition 1

114 |

Term 2 115 | ~ Definition 2a 116 | ~ Definition 2b

117 |

### Abbreviations

118 |

This is HTML abbreviation example.

119 |

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

120 |

*[HTML]: Hyper Text Markup Language

121 |

### Custom containers

122 |

::: warning 123 | *here be dragons* 124 | :::

125 |

### Attributes { data-attr=true }

126 |

This is an attribute example.

127 | -------------------------------------------------------------------------------- /test/fixtures/outputs/zero.html: -------------------------------------------------------------------------------- 1 |

# h1 Heading em português 2 | ## h2 Heading :P 3 | ### h3 Heading 4 | #### h4 Heading 5 | ##### h5 Heading 6 | ###### h6 Heading

7 |

## Horizontal Rule

8 |

___

9 |

## Horizontal Rule

10 |

---

11 |

## Horizontal Rule

12 |

***

13 |

## Typographic replacements

14 |

Enable typographer option to see result.

15 |

(c) (C) (r) (R) (tm) (TM) +-

16 |

test.. test... test..... test?..... test!....

17 |

!!!!!! ???? ,, -- ---

18 |

"Smartypants, double quotes" and 'single quotes'

19 |

## Emphasis

20 |

**This is bold text**

21 |

__This is bold text__

22 |

*This is italic text*

23 |

_This is italic text_

24 |

~~Strikethrough~~

25 |

## Blockquotes

26 |

> Blockquotes can also be nested... 27 | >> ...by using additional greater-than signs right next to each other... 28 | > > > ...or with spaces between arrows.

29 |

## Lists

30 |

Unordered

31 |

+ Create a list by starting a line with `+`, `-`, or `*` 32 | + Sub-lists are made by indenting 2 spaces: 33 | - Marker character change forces new list start: 34 | * Ac tristique libero volutpat at 35 | + Facilisis in pretium nisl aliquet 36 | - Nulla volutpat aliquam velit 37 | + Very easy!

38 |

Ordered

39 |

1. Lorem ipsum dolor sit amet 40 | 2. Consectetur adipiscing elit 41 | 3. Integer molestie lorem at massa

42 |

## Code

43 |

Inline `code`

44 |

Indented code

45 |

// Some comments 46 | line 1 of code 47 | line 2 of code 48 | line 3 of code

49 |

Block code "fences"

50 |

``` 51 | Sample text here... 52 | ```

53 |

Syntax highlighting

54 |

``` js 55 | var foo = function (bar) { 56 | return bar++; 57 | };

58 |

console.log(foo(5)); 59 | ```

60 |

## Tables

61 |

| Option | Description | 62 | | ------ | ----------- | 63 | | data | path to data files to supply the data that will be passed into templates. | 64 | | engine | engine to be used for processing templates. Handlebars is the default. | 65 | | ext | extension to be used for dest files. |

66 |

Right aligned columns

67 |

| Option | Description | 68 | | ------:| -----------:| 69 | | data | path to data files to supply the data that will be passed into templates. | 70 | | engine | engine to be used for processing templates. Handlebars is the default. | 71 | | ext | extension to be used for dest files. |

72 |

## Links

73 |

[link text](http://dev.nodeca.com)

74 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

75 |

## Images

76 |

![Minion](https://octodex.github.com/images/minion.png) 77 | ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")

78 |

![Alt text][id]

79 |

[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"

80 |

## Plugins

81 |

The killer feature of `markdown-it` is very effective support of 82 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).

83 |

### [Emojies](https://github.com/markdown-it/markdown-it-emoji)

84 |

> Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 85 | > 86 | > Shortcuts (emoticons): :-) :-( 8-) ;)

87 |

see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.

88 |

### [Subscipt](https://github.com/markdown-it/markdown-it-sub) / [Superscirpt](https://github.com/markdown-it/markdown-it-sup)

89 |

- 19^th^ 90 | - H~2~O

91 |

### [\<ins>](https://github.com/markdown-it/markdown-it-ins)

92 |

++Inserted text++

93 |

### [\<mark>](https://github.com/markdown-it/markdown-it-mark)

94 |

==Marked text==

95 |

### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)

96 |

Footnote 1 link[^first].

97 |

Footnote 2 link[^second].

98 |

Inline footnote^[Text of inline footnote] definition.

99 |

Duplicated footnote reference[^second].

100 |

[^first]: Footnote **can have markup**

101 |

and multiple paragraphs.

102 |

[^second]: Footnote text.

103 |

### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)

104 |

Term 1

105 |

: Definition 1 106 | with lazy continuation.

107 |

Term 2 with *inline markup*

108 |

: Definition 2

109 |

{ some code, part of Definition 2 }

110 |

Third paragraph of definition 2.

111 |

_Compact style:_

112 |

Term 1 113 | ~ Definition 1

114 |

Term 2 115 | ~ Definition 2a 116 | ~ Definition 2b

117 |

### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)

118 |

This is HTML abbreviation example.

119 |

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

120 |

*[HTML]: Hyper Text Markup Language

121 |

### [Custom containers](https://github.com/markdown-it/markdown-it-container)

122 |

::: warning 123 | *here be dragons* 124 | :::

125 |

### [Attributes](https://github.com/arve0/markdown-it-attrs) { data-attr=true }

126 |

This is an attribute example.

127 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('chai').should(); 4 | const { readFileSync } = require('fs'); 5 | const { join } = require('path').posix; 6 | const { sep } = require('path'); 7 | const Renderer = require('../lib/renderer'); 8 | const source = readFileSync('./test/fixtures/markdownit.md', 'utf8'); 9 | const Hexo = require('hexo'); 10 | const { url_for } = require('hexo-util'); 11 | 12 | describe('Hexo Renderer Markdown-it', () => { 13 | const hexo = new Hexo(__dirname, { silent: true }); 14 | const defaultCfg = JSON.parse(JSON.stringify(Object.assign(hexo.config, { 15 | markdown: { 16 | preset: 'default', 17 | render: { 18 | html: true, 19 | xhtmlOut: false, 20 | breaks: true, 21 | linkify: true, 22 | typographer: true, 23 | quotes: '“”‘’' 24 | }, 25 | anchors: { 26 | level: 2, 27 | collisionSuffix: '', 28 | permalink: false, 29 | permalinkClass: 'header-anchor', 30 | permalinkSide: 'left', 31 | permalinkSymbol: '¶', 32 | case: 0, 33 | separator: '' 34 | } 35 | } 36 | }))); 37 | 38 | beforeEach(() => { 39 | hexo.config = JSON.parse(JSON.stringify(defaultCfg)); 40 | }); 41 | 42 | describe('preset', () => { 43 | it('default', () => { 44 | hexo.config.markdown.preset = 'default'; 45 | 46 | const parsed_gfm = readFileSync('./test/fixtures/outputs/default.html', 'utf8'); 47 | const renderer = new Renderer(hexo); 48 | const result = renderer.parser.render(source); 49 | 50 | result.should.equal(parsed_gfm); 51 | }); 52 | 53 | it('commonmark', () => { 54 | hexo.config.markdown.preset = 'commonmark'; 55 | 56 | const parsed_commonmark = readFileSync('./test/fixtures/outputs/commonmark.html', 'utf8'); 57 | const renderer = new Renderer(hexo); 58 | const result = renderer.parser.render(source); 59 | 60 | result.should.equal(parsed_commonmark); 61 | }); 62 | 63 | // Deprecated 64 | it('handle deprecated config', () => { 65 | hexo.config.markdown = 'commonmark'; 66 | 67 | const parsed_commonmark = readFileSync('./test/fixtures/outputs/commonmark-deprecated.html', 'utf8'); 68 | const renderer = new Renderer(hexo); 69 | const result = renderer.parser.render(source); 70 | 71 | result.should.equal(parsed_commonmark); 72 | }); 73 | 74 | it('zero', () => { 75 | hexo.config.markdown.preset = 'zero'; 76 | 77 | const parsed_zero = readFileSync('./test/fixtures/outputs/zero.html', 'utf8'); 78 | const renderer = new Renderer(hexo); 79 | const result = renderer.parser.render(source); 80 | result.should.equal(parsed_zero); 81 | }); 82 | }); 83 | 84 | describe('render options', () => { 85 | it('custom options', () => { 86 | hexo.config.markdown.render = { 87 | html: false, 88 | xhtmlOut: true, 89 | breaks: true, 90 | langPrefix: '', 91 | linkify: true, 92 | typographer: true, 93 | quotes: '«»“”' 94 | }; 95 | 96 | const parsed_custom = readFileSync('./test/fixtures/outputs/custom.html', 'utf8'); 97 | const source = readFileSync('./test/fixtures/markdownit.md', 'utf8'); 98 | const renderer = new Renderer(hexo); 99 | const result = renderer.parser.render(source); 100 | result.should.equal(parsed_custom); 101 | }); 102 | 103 | it('langPrefix', () => { 104 | hexo.config.markdown.render = { 105 | langPrefix: 'lang-' 106 | }; 107 | 108 | const text = '```js\nexample\n```'; 109 | const renderer = new Renderer(hexo); 110 | const result = renderer.parser.render(text); 111 | result.should.eql('
example\n
\n'); 112 | }); 113 | 114 | it('render inline', () => { 115 | const text = 'inline text'; 116 | const renderer = new Renderer(hexo); 117 | const resultBlock = renderer.render({ text }); 118 | const resultInline = renderer.render({ text }, { inline: true }); 119 | resultBlock.should.eql('

inline text

\n'); 120 | resultInline.should.eql('inline text'); 121 | }); 122 | }); 123 | 124 | describe('plugins', () => { 125 | it('default', () => { 126 | hexo.config.markdown.plugins = [ 127 | 'markdown-it-abbr', 128 | 'markdown-it-attrs', 129 | 'markdown-it-container', 130 | 'markdown-it-deflist', 131 | 'markdown-it-emoji', 132 | 'markdown-it-footnote', 133 | 'markdown-it-ins', 134 | 'markdown-it-mark', 135 | 'markdown-it-sub', 136 | 'markdown-it-sup' 137 | ]; 138 | 139 | const parsed_plugins = readFileSync('./test/fixtures/outputs/plugins.html', 'utf8'); 140 | const source = readFileSync('./test/fixtures/markdownit.md', 'utf8'); 141 | const renderer = new Renderer(hexo); 142 | const result = renderer.parser.render(source); 143 | result.should.equal(parsed_plugins); 144 | }); 145 | 146 | it('custom options', () => { 147 | hexo.config.markdown.plugins = [ 148 | { 149 | name: 'markdown-it-emoji', 150 | options: { 151 | defs: { 152 | smile: ':lorem:' 153 | } 154 | } 155 | } 156 | ]; 157 | 158 | const text = ':smile:'; 159 | const renderer = new Renderer(hexo); 160 | const result = renderer.parser.render(text); 161 | result.should.equal('

:lorem:

\n'); 162 | }); 163 | }); 164 | 165 | describe('anchors', () => { 166 | it('collisionSuffix & permalinkClass', () => { 167 | hexo.config.markdown.anchors = { 168 | level: 2, 169 | collisionSuffix: 'ver', 170 | permalink: true, 171 | permalinkClass: 'header-anchor', 172 | permalinkSymbol: '¶' 173 | }; 174 | const expected = readFileSync('./test/fixtures/outputs/anchors.html', 'utf8'); 175 | const renderer = new Renderer(hexo); 176 | const result = renderer.parser.render(source); 177 | 178 | result.should.eql(expected); 179 | }); 180 | 181 | it('permalink disabled', () => { 182 | hexo.config.markdown.anchors = { 183 | level: 1, 184 | collisionSuffix: '', 185 | permalink: false, 186 | permalinkClass: 'header-anchor', 187 | permalinkSymbol: '¶' 188 | }; 189 | 190 | const renderer = new Renderer(hexo); 191 | const result = renderer.parser.render('# This is an H1 title\n# This is an H1 title'); 192 | const expected = '

This is an H1 title

\n

This is an H1 title

\n'; 193 | 194 | result.should.eql(expected); 195 | }); 196 | 197 | it('slugize - case & separator', () => { 198 | hexo.config.markdown.anchors = { 199 | level: 2, 200 | case: 1, 201 | separator: '_' 202 | }; 203 | 204 | const renderer = new Renderer(hexo); 205 | const result = renderer.parser.render('## foo BAR'); 206 | 207 | result.should.equal('

foo BAR

\n'); 208 | }); 209 | 210 | it('multiple posts anchor id', () => { 211 | hexo.config.markdown.anchors = { 212 | level: 2, 213 | collisionSuffix: 'ver', 214 | permalink: true, 215 | permalinkClass: 'header-anchor', 216 | permalinkSymbol: '¶' 217 | }; 218 | const expected = readFileSync('./test/fixtures/outputs/anchors.html', 'utf8'); 219 | const renderer = new Renderer(hexo); 220 | const result = renderer.parser.render(source); 221 | const result2 = renderer.parser.render(source); 222 | 223 | result.should.eql(expected); 224 | result2.should.eql(expected); 225 | }); 226 | 227 | describe('permalinkSide', () => { 228 | const text = '## foo'; 229 | 230 | it('left', () => { 231 | hexo.config.markdown.anchors = { 232 | level: 2, 233 | permalink: true, 234 | permalinkClass: 'anchor', 235 | permalinkSide: 'left', 236 | permalinkSymbol: '#' 237 | }; 238 | const renderer = new Renderer(hexo); 239 | const result = renderer.parser.render(text); 240 | 241 | result.should.equal('

#foo

\n'); 242 | }); 243 | 244 | it('right', () => { 245 | hexo.config.markdown.anchors = { 246 | level: 2, 247 | permalink: true, 248 | permalinkClass: 'anchor', 249 | permalinkSide: 'right', 250 | permalinkSymbol: '#' 251 | }; 252 | const renderer = new Renderer(hexo); 253 | const result = renderer.parser.render(text); 254 | 255 | result.should.equal('

foo#

\n'); 256 | }); 257 | }); 258 | }); 259 | 260 | describe('rules', () => { 261 | it('enable_rules', () => { 262 | hexo.config.markdown.preset = 'zero'; 263 | hexo.config.markdown.enable_rules = ['link', 'image']; 264 | 265 | const parsed_zero = readFileSync('./test/fixtures/outputs/zero-enable_rules.html', 'utf8'); 266 | const renderer = new Renderer(hexo); 267 | const result = renderer.parser.render(source); 268 | 269 | result.should.equal(parsed_zero); 270 | }); 271 | 272 | it('disable_rules', () => { 273 | hexo.config.markdown.preset = 'default'; 274 | hexo.config.markdown.render.linkify = false; 275 | hexo.config.markdown.disable_rules = 'link'; 276 | 277 | const parsed = readFileSync('./test/fixtures/outputs/default-disable_rules.html', 'utf8'); 278 | const renderer = new Renderer(hexo); 279 | const result = renderer.parser.render(source); 280 | 281 | result.should.equal(parsed); 282 | }); 283 | }); 284 | 285 | describe('execFilter', () => { 286 | it('default', () => { 287 | 288 | const renderer = new Renderer(hexo); 289 | const result = renderer.render({ text: '[foo](javascript:bar)' }); 290 | 291 | result.should.equal('

[foo](javascript:bar)

\n'); 292 | }); 293 | 294 | it('enable unsafe link', () => { 295 | const filter = md => { 296 | md.validateLink = function() { return true; }; 297 | }; 298 | hexo.extend.filter.register('markdown-it:renderer', filter); 299 | 300 | const renderer = new Renderer(hexo); 301 | const result = renderer.render({ text: '[foo](javascript:bar)' }); 302 | 303 | hexo.extend.filter.unregister('markdown-it:renderer', filter); 304 | 305 | result.should.equal('

foo

\n'); 306 | }); 307 | 308 | it('should execute loaded scripts', async () => { 309 | const renderer = new Renderer(hexo); 310 | 311 | hexo.env.init = true; 312 | await hexo.init(); 313 | 314 | const result = renderer.render({ text: '[foo](javascript:bar)' }); 315 | 316 | result.should.equal('

foo

\n'); 317 | }); 318 | 319 | it('should be called in render', () => { 320 | const iterates = 3; 321 | const spy = { 322 | called: 0, 323 | call() { 324 | this.called++; 325 | } 326 | }; 327 | 328 | const filter = md => spy.call(md); 329 | hexo.extend.filter.register('markdown-it:renderer', filter); 330 | 331 | const renderer = new Renderer(hexo); 332 | for (let i = 0; i < iterates; i++) { 333 | renderer.render({ text: '' }); 334 | } 335 | 336 | hexo.extend.filter.unregister('markdown-it:renderer', filter); 337 | 338 | spy.called.should.equal(iterates); 339 | }); 340 | }); 341 | 342 | describe('nunjucks', () => { 343 | const hexo = new Hexo(__dirname, { silent: true }); 344 | const loremFn = () => { return 'ipsum'; }; 345 | const engine = 'md'; 346 | 347 | before(async () => { 348 | await hexo.init(); 349 | hexo.config.markdown = {}; 350 | 351 | const renderer = new Renderer(hexo); 352 | function render(data, options) { 353 | return renderer.parser.render(data.text); 354 | } 355 | 356 | hexo.extend.tag.register('lorem', loremFn); 357 | hexo.extend.renderer.register('md', 'html', render, true); 358 | }); 359 | 360 | it('default', async () => { 361 | const result = await hexo.post.render(null, { content: '**foo** {% lorem %}', engine }); 362 | result.content.should.eql('

foo ipsum

\n'); 363 | }); 364 | 365 | it('enable disableNunjucks', async () => { 366 | const renderer = hexo.render.renderer.get('md'); 367 | renderer.disableNunjucks = true; 368 | hexo.extend.renderer.register('md', 'html', renderer, true); 369 | const result = await hexo.post.render(null, { content: '**foo** {% lorem %}', engine }); 370 | result.content.should.eql('

foo {% lorem %}

\n'); 371 | }); 372 | }); 373 | 374 | describe('image options', () => { 375 | const body = '![](/bar/baz.jpg)'; 376 | 377 | it('add lazyload attribute', () => { 378 | hexo.config.markdown.images = { lazyload: true }; 379 | 380 | const renderer = new Renderer(hexo); 381 | const result = renderer.render({ text: body }); 382 | 383 | result.should.eql('

\n'); 384 | }); 385 | 386 | it('keep lazyload attribute', () => { 387 | hexo.config.markdown.images = { lazyload: true }; 388 | 389 | const renderer = new Renderer(hexo); 390 | const result = renderer.render({ text: body }); 391 | 392 | result.should.eql('

\n'); 393 | }); 394 | 395 | it('should prepend root', () => { 396 | hexo.config.markdown.images = { prepend_root: true }; 397 | 398 | const renderer = new Renderer(hexo); 399 | hexo.config.root = '/blog'; 400 | 401 | const result = renderer.render({ text: body }); 402 | 403 | result.should.eql('

\n'); 404 | }); 405 | 406 | it('alt text', () => { 407 | hexo.config.markdown.images = { test: true }; 408 | 409 | const renderer = new Renderer(hexo); 410 | const result = renderer.render({ text: '![alt text](/bar/baz.jpg)' }); 411 | 412 | result.should.eql('

alt text

\n'); 413 | }); 414 | 415 | describe('post_asset', () => { 416 | const Post = hexo.model('Post'); 417 | const PostAsset = hexo.model('PostAsset'); 418 | 419 | beforeEach(() => { 420 | hexo.config.post_asset_folder = true; 421 | hexo.config.markdown.images = { 422 | prepend_root: true, 423 | post_asset: true 424 | }; 425 | }); 426 | 427 | it('should prepend post path', async () => { 428 | const renderer = new Renderer(hexo); 429 | 430 | const asset = 'img/bar.svg'; 431 | const slug = asset.replace(/\//g, sep); 432 | const content = `![](${asset})`; 433 | const post = await Post.insert({ 434 | source: '_posts/foo.md', 435 | slug: 'foo' 436 | }); 437 | const postasset = await PostAsset.insert({ 438 | _id: `source/_posts/foo/${asset}`, 439 | slug, 440 | post: post._id 441 | }); 442 | 443 | const expected = url_for.call(hexo, join(post.path, asset)); 444 | const result = renderer.render({ text: content, path: post.full_source }); 445 | result.should.eql(`

\n`); 446 | 447 | // should not be Windows path 448 | expected.includes('\\').should.eql(false); 449 | 450 | await PostAsset.removeById(postasset._id); 451 | await Post.removeById(post._id); 452 | }); 453 | 454 | it('should prepend post path without slug', async () => { 455 | const renderer = new Renderer(hexo); 456 | 457 | const asset = 'img/bar.svg'; 458 | const slug = asset.replace(/\//g, sep); 459 | const content = `![](foo/${asset})`; 460 | const post = await Post.insert({ 461 | source: '_posts/foo.md', 462 | slug: 'foo' 463 | }); 464 | const postasset = await PostAsset.insert({ 465 | _id: `source/_posts/foo/${asset}`, 466 | slug, 467 | post: post._id 468 | }); 469 | 470 | const expected = url_for.call(hexo, join(post.path, asset)); 471 | const result = renderer.render({ text: content, path: post.full_source }); 472 | result.should.eql(`

\n`); 473 | 474 | // should not be Windows path 475 | expected.includes('\\').should.eql(false); 476 | 477 | await PostAsset.removeById(postasset._id); 478 | await Post.removeById(post._id); 479 | }); 480 | 481 | it('should not modify non-post asset', async () => { 482 | const renderer = new Renderer(hexo); 483 | 484 | const asset = 'bar.svg'; 485 | const siteasset = '/logo/brand.png'; 486 | const site = 'http://lorem.ipsum/dolor/huri.bun'; 487 | const content = `![](${asset})![](${siteasset})![](${site})`; 488 | const post = await Post.insert({ 489 | source: '_posts/foo.md', 490 | slug: 'foo' 491 | }); 492 | const postasset = await PostAsset.insert({ 493 | _id: `source/_posts/foo/${asset}`, 494 | slug: asset, 495 | post: post._id 496 | }); 497 | 498 | const result = renderer.render({ text: content, path: post.full_source }); 499 | result.should.eql([ 500 | `

`, 501 | ``, 502 | `

` 503 | ].join('') + '\n'); 504 | 505 | await PostAsset.removeById(postasset._id); 506 | await Post.removeById(post._id); 507 | }); 508 | 509 | it('post located in subfolder', async () => { 510 | const renderer = new Renderer(hexo); 511 | 512 | const asset = 'img/bar.svg'; 513 | const slug = asset.replace(/\//g, sep); 514 | const content = `![](${asset})`; 515 | const post = await Post.insert({ 516 | source: '_posts/lorem/foo.md', 517 | slug: 'foo' 518 | }); 519 | const postasset = await PostAsset.insert({ 520 | _id: `source/_posts/lorem/foo/${asset}`, 521 | slug, 522 | post: post._id 523 | }); 524 | 525 | const expected = url_for.call(hexo, join(post.path, asset)); 526 | const result = renderer.render({ text: content, path: post.full_source }); 527 | result.should.eql(`

\n`); 528 | 529 | await PostAsset.removeById(postasset._id); 530 | await Post.removeById(post._id); 531 | }); 532 | }); 533 | }); 534 | }); 535 | -------------------------------------------------------------------------------- /test/scripts/enable_unsafe_link.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // eslint-disable-next-line no-undef 4 | hexo.extend.filter.register('markdown-it:renderer', md => { 5 | md.validateLink = function() { return true; }; 6 | }); 7 | --------------------------------------------------------------------------------