first commit
This commit is contained in:
7
node_modules/hexo-renderer-marked/LICENSE
generated
vendored
Normal file
7
node_modules/hexo-renderer-marked/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2014 Tommy Chen
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
285
node_modules/hexo-renderer-marked/README.md
generated
vendored
Normal file
285
node_modules/hexo-renderer-marked/README.md
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
# hexo-renderer-marked
|
||||
|
||||
[](https://github.com/hexojs/hexo-renderer-marked/actions/workflows/tester.yml)
|
||||
[](https://www.npmjs.com/package/hexo-renderer-marked)
|
||||
[](https://coveralls.io/r/hexojs/hexo-renderer-marked?branch=master)
|
||||
[](https://libraries.io/npm/hexo-renderer-marked)
|
||||
|
||||
Add support for [Markdown]. This plugin uses [marked] as its render engine.
|
||||
|
||||
## Important note on security
|
||||
|
||||
By default, this plugin contains a potential security issue: **It is possible to inject Markdown containing Unsafe HTML that will not be sanitized**
|
||||
|
||||
This issue might not affect you because you checked the content of the markdown before using this plugin, but it's still a risk
|
||||
|
||||
There are two solutions to avoid those issues:
|
||||
|
||||
1. First solution is to enable option `dompurify: true`, which will sanitize the rendered HTML. The side effect of this solution is that it will break any [tag plugin](https://hexo.io/docs/tag-plugins) (aka `{% codeblock %}`). This explains why the safer option has not been enabled by default
|
||||
2. Second solution is to migrate to [hexo-renderer-markdown-it](https://github.com/hexojs/hexo-renderer-markdown-it/) which is safe by default and does not suffer from the same limitations
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install hexo-renderer-marked --save
|
||||
```
|
||||
|
||||
- Hexo 4: >= 2.0
|
||||
- Hexo 3: >= 0.2
|
||||
- Hexo 2: 0.1.x
|
||||
|
||||
## Options
|
||||
|
||||
You can configure this plugin in `_config.yml`.
|
||||
|
||||
```yml
|
||||
marked:
|
||||
gfm: true
|
||||
pedantic: false
|
||||
breaks: true
|
||||
smartLists: true
|
||||
smartypants: true
|
||||
quotes: '“”‘’'
|
||||
modifyAnchors: 0
|
||||
anchorAlias: false
|
||||
autolink: true
|
||||
mangle: true
|
||||
sanitizeUrl: false
|
||||
dompurify: false
|
||||
headerIds: true
|
||||
lazyload: false
|
||||
figcaption: false
|
||||
prependRoot: true
|
||||
postAsset: false
|
||||
external_link:
|
||||
enable: false
|
||||
exclude: []
|
||||
nofollow: false
|
||||
disableNunjucks: false
|
||||
descriptionLists: true
|
||||
```
|
||||
|
||||
- **gfm** - Enables [GitHub flavored markdown](https://help.github.com/articles/github-flavored-markdown)
|
||||
- **pedantic** - Conform to obscure parts of `markdown.pl` as much as possible. Don't fix any of the original markdown bugs or poor behavior.
|
||||
- **breaks** - Enable GFM [line breaks](https://help.github.com/articles/github-flavored-markdown#newlines). This option requires the `gfm` option to be true.
|
||||
- **smartLists** - Use smarter list behavior than the original markdown.
|
||||
- **smartypants** - Use "smart" typographic punctuation for things like quotes and dashes.
|
||||
- **quotes** - Defines the double and single quotes used for substituting regular quotes if **smartypants** is enabled.
|
||||
* Example: '«»“”'
|
||||
* "double" will be turned into «double»
|
||||
* 'single' will be turned into “single”
|
||||
* Both double and single quotes substitution must be specified, otherwise it will be silently ignored.
|
||||
- **modifyAnchors** - Transform the anchorIds into lower case (`1`) or upper case (`2`).
|
||||
- **autolink** - Enable autolink for URLs. E.g. `https://hexo.io` will become `<a href="https://hexo.io">https://hexo.io</a>`.
|
||||
- **mangle** - Escape autolinked email address with HTML character references.
|
||||
* This is to obscure email address from _basic_ crawler used by spam bot, while still readable to web browsers.
|
||||
- **sanitizeUrl** - Remove URLs that start with `javascript:`, `vbscript:` and `data:`.
|
||||
- **dompurify** - Enable [DOMPurify](https://github.com/cure53/DOMPurify) to be run on the rendered Markdown. See below for configuration
|
||||
- **headerIds** - Insert header id, e.g. `<h1 id="value">text</h1>`. Useful for inserting anchor link to each paragraph with a heading.
|
||||
- **anchorAlias** - Enables custom header id
|
||||
* Example: `## [foo](#bar)`, id will be set as "bar".
|
||||
* Requires **headerIds** to be enabled.
|
||||
- **lazyload** - Lazy loading images via `loading="lazy"` attribute.
|
||||
- **figcaption** - Append `figcaption` element after each image.
|
||||
- **prependRoot** - Prepend root value to (internal) image path.
|
||||
* Example `_config.yml`:
|
||||
```yml
|
||||
root: /blog/
|
||||
```
|
||||
* `` becomes `<img src="/blog/path/to/image.jpg" alt="text">`
|
||||
- **postAsset** - Resolve post asset's image path to relative path and prepend root value when [`post_asset_folder`](https://hexo.io/docs/asset-folders) is enabled.
|
||||
* "image.jpg" is located at "/2020/01/02/foo/image.jpg", which is a post asset of "/2020/01/02/foo/".
|
||||
* `` becomes `<img src="/2020/01/02/foo/image.jpg">`
|
||||
* Requires **prependRoot** to be enabled.
|
||||
- **external_link**
|
||||
* **enable** - Open external links in a new tab.
|
||||
* **exclude** - Exclude hostname. Specify subdomain when applicable, including `www`.
|
||||
- Example: `[foo](https://example.com)` becomes `<a href="https://example.com" target="_blank" rel="noopener">foo</a>`
|
||||
* **nofollow** - Add `rel="noopener external nofollow noreferrer"` to all external links for security, privacy and SEO. [Read more](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types). _This can be enabled regardless of `external_link.enable`_
|
||||
- Example: `[foo](https://example.com)` becomes `<a href="https://example.com" rel="noopener external nofollow noreferrer">foo</a>`
|
||||
- **disableNunjucks**: If true, Nunjucks tags `{{ }}` or `{% %}` (usually used by [tag plugins](https://hexo.io/docs/tag-plugins)) will not be rendered.
|
||||
- **descriptionLists**: Enable support for [description lists syntax](https://kramdown.gettalong.org/syntax.html#definition-lists).
|
||||
* Currently description lists syntax is not in neither [CommonMark](https://commonmark.org/) or [GFM](https://github.github.com/gfm/#task-list-items-extension-), `hexo-renderer-marked` only provides the option for backward compatibility.
|
||||
* By disabling the `descriptionLists`, markdown rendering performance will be improved by **a lot**.
|
||||
|
||||
For more options, see [Marked](https://marked.js.org/using_advanced#options). Due to the customizations implemented by this plugin, some of the Marked's options may not work as expected. Feel free to raise an [issue](https://github.com/hexojs/hexo-renderer-marked/issues) to us for clarification.
|
||||
|
||||
## Extras
|
||||
|
||||
### Sanitize HTML with DOMPurify
|
||||
|
||||
[DOMPurify](https://github.com/cure53/DOMPurify) can be enabled to sanitize the rendered HTML.
|
||||
|
||||
To enable it, pass an object containing the DOMPurify options:
|
||||
|
||||
```yml
|
||||
dompurify: true
|
||||
```
|
||||
|
||||
Or you can enable specific DOMPurify options (but according to DOMPurify authors, the default options are safe):
|
||||
|
||||
```yml
|
||||
dompurify:
|
||||
FORBID_TAGS:
|
||||
- "style"
|
||||
```
|
||||
|
||||
See https://github.com/cure53/DOMPurify#can-i-configure-dompurify for a full reference of available options
|
||||
|
||||
### Definition/Description Lists
|
||||
|
||||
`hexo-renderer-marked` also implements description/definition lists using the same syntax as [PHP Markdown Extra][PHP Markdown Extra].
|
||||
|
||||
This Markdown:
|
||||
|
||||
```markdown
|
||||
Definition Term
|
||||
: This is the definition for the term
|
||||
```
|
||||
|
||||
will generate this HTML:
|
||||
|
||||
```html
|
||||
<dl>
|
||||
<dt>Definition Term</dt>
|
||||
<dd>This is the definition for the term</dd>
|
||||
</dl>
|
||||
```
|
||||
|
||||
Note: There is currently a limitation in this implementation. If multiple definitions are provided, the rendered HTML will be incorrect.
|
||||
|
||||
For example, this Markdown:
|
||||
|
||||
```markdown
|
||||
Definition Term
|
||||
: Definition 1
|
||||
: Definition 2
|
||||
```
|
||||
|
||||
will generate this HTML:
|
||||
|
||||
```html
|
||||
<dl>
|
||||
<dt>Definition Term<br>: Definition 1</dt>
|
||||
<dd>Definition 2</dd>
|
||||
</dl>
|
||||
```
|
||||
|
||||
If you've got ideas on how to support multiple definitions, please provide a pull request. We'd love to support it.
|
||||
|
||||
### Extensibility
|
||||
|
||||
This plugin overrides some default behaviours of how [marked] 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.
|
||||
|
||||
For example, to override how heading like `# heading text` is rendered:
|
||||
|
||||
```js
|
||||
hexo.extend.filter.register('marked:renderer', function(renderer) {
|
||||
const { config } = this; // Optional if you don't need user config from _config.yml
|
||||
renderer.heading = function({ tokens, depth: level }) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
// Default behaviour
|
||||
// return `<h${level}>${text}</h${level}>`;
|
||||
// outputs <h1>heading text</h1>
|
||||
|
||||
// If you want to insert custom class name
|
||||
return `<h${level} class="headerlink">${text}</h${level}>`;
|
||||
// outputs <h1 class="headerlink">heading text</h1>
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Save the file in "scripts/" folder and run Hexo as usual.
|
||||
|
||||
Refer to [renderer.js](https://github.com/hexojs/hexo-renderer-marked/blob/master/lib/renderer.js) on how this plugin overrides the default methods. For other methods not covered by this plugin, refer to marked's [documentation](https://marked.js.org/using_pro#renderer).
|
||||
|
||||
#### Tokenizer
|
||||
|
||||
It is also possible to customize the [tokenizer](https://marked.js.org/using_pro#tokenizer).
|
||||
|
||||
```js
|
||||
const { escapeHTML: escape } = require('hexo-util');
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Lexer.js#L8-L24
|
||||
// Replace dashes only
|
||||
const smartypants = (str) => {
|
||||
return str
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
};
|
||||
|
||||
hexo.extend.filter.register('marked:tokenizer', function(tokenizer) {
|
||||
const { smartypants: isSmarty } = this.config.marked;
|
||||
tokenizer.inlineText = function(src, inRawBlock) {
|
||||
const { rules } = this;
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Tokenizer.js#L643-L658
|
||||
const cap = rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (inRawBlock) {
|
||||
text = cap[0];
|
||||
} else {
|
||||
text = escape(isSmarty ? smartypants(cap[0]) : cap[0]);
|
||||
}
|
||||
return {
|
||||
// `type` value is a corresponding renderer method
|
||||
// https://marked.js.org/using_pro#inline-level-renderer-methods
|
||||
type: 'text',
|
||||
raw: cap[0],
|
||||
text
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Extensions
|
||||
|
||||
It is also possible to customize the [extensions](https://marked.js.org/using_pro#extensions).
|
||||
For example, use [KaTeX](https://katex.org/) to render block-level math:
|
||||
|
||||
```js
|
||||
const katex = require('katex');
|
||||
|
||||
hexo.extend.filter.register('marked:extensions', function(extensions) {
|
||||
// Info: `extensions` is an array.
|
||||
extensions.push({
|
||||
name: 'blockMath',
|
||||
level: 'block',
|
||||
tokenizer(src) {
|
||||
const cap = /^\s{0,3}\$\$((?:[^\n]|\n[^\n])+?)\n{0,1}\$\$/.exec(src);
|
||||
|
||||
if (cap !== null) {
|
||||
return {
|
||||
type: 'blockMath',
|
||||
raw: cap[0],
|
||||
math: cap[1]
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
renderer(token) {
|
||||
return `<p>${katex.renderToString(token.math, {displayMode: true})}</p>\n`;
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
You may also get access to `marked.use` function.
|
||||
For example to use the [marked-alert](https://github.com/bent10/marked-extensions/tree/main/packages/alert) extention wich also provides a `walkTokens` functions:
|
||||
|
||||
```js
|
||||
const markedAlert = require('marked-alert');
|
||||
|
||||
hexo.extend.filter.register('marked:use', function (markedUse) {
|
||||
markedUse(markedAlert());
|
||||
});
|
||||
```
|
||||
|
||||
[Markdown]: https://daringfireball.net/projects/markdown/
|
||||
[marked]: https://github.com/chjj/marked
|
||||
[PHP Markdown Extra]: https://michelf.ca/projects/php-markdown/extra/#def-list
|
||||
39
node_modules/hexo-renderer-marked/index.js
generated
vendored
Normal file
39
node_modules/hexo-renderer-marked/index.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/* global hexo */
|
||||
|
||||
'use strict';
|
||||
|
||||
const renderer = require('./lib/renderer');
|
||||
|
||||
hexo.config.marked = Object.assign({
|
||||
gfm: true,
|
||||
pedantic: false,
|
||||
breaks: true,
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
modifyAnchors: 0,
|
||||
autolink: true,
|
||||
mangle: true,
|
||||
sanitizeUrl: false,
|
||||
dompurify: false,
|
||||
headerIds: true,
|
||||
anchorAlias: false,
|
||||
lazyload: false,
|
||||
prependRoot: true,
|
||||
postAsset: false,
|
||||
external_link: {
|
||||
enable: false,
|
||||
exclude: [],
|
||||
nofollow: false
|
||||
},
|
||||
descriptionLists: true
|
||||
}, hexo.config.marked);
|
||||
|
||||
renderer.disableNunjucks = Boolean(hexo.config.marked.disableNunjucks);
|
||||
|
||||
hexo.extend.renderer.register('md', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('markdown', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('mkd', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('mkdn', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('mdwn', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('mdtxt', 'html', renderer, true);
|
||||
hexo.extend.renderer.register('mdtext', 'html', renderer, true);
|
||||
283
node_modules/hexo-renderer-marked/lib/renderer.js
generated
vendored
Normal file
283
node_modules/hexo-renderer-marked/lib/renderer.js
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
'use strict';
|
||||
|
||||
const { marked } = require('marked');
|
||||
|
||||
let JSDOM,
|
||||
createDOMPurify;
|
||||
|
||||
const { encodeURL, slugize, stripHTML, url_for, isExternalLink, escapeHTML: escape, unescapeHTML: unescape } = require('hexo-util');
|
||||
const { basename, dirname, extname, join } = require('path').posix;
|
||||
const rATag = /<a(?:\s+?|\s+?[^<>]+\s+?)?href=["'](?:#)([^<>"']+)["'][^<>]*>/i;
|
||||
const rDlSyntax = /(?:^|\s)(\S.+)<br>:\s+(\S.+)/;
|
||||
|
||||
const anchorId = (str, transformOption) => {
|
||||
return slugize(stripHTML(unescape(str)).trim(), { transform: transformOption });
|
||||
};
|
||||
|
||||
function mangleEmail(text) {
|
||||
let out = '';
|
||||
let i,
|
||||
ch;
|
||||
|
||||
const l = text.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
ch = text.charCodeAt(i);
|
||||
if (Math.random() > 0.5) {
|
||||
ch = 'x' + ch.toString(16);
|
||||
}
|
||||
out += '&#' + ch + ';';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
const renderer = {
|
||||
// Add id attribute to headings
|
||||
heading({ tokens, depth: level }) {
|
||||
let text = this.parser.parseInline(tokens);
|
||||
const { anchorAlias, headerIds, modifyAnchors, _headingId } = this.options;
|
||||
|
||||
if (!headerIds) {
|
||||
return `<h${level}>${text}</h${level}>`;
|
||||
}
|
||||
|
||||
const transformOption = modifyAnchors;
|
||||
let id = anchorId(text, transformOption);
|
||||
const headingId = _headingId;
|
||||
|
||||
const anchorAliasOpt = anchorAlias && text.startsWith('<a href="#');
|
||||
if (anchorAliasOpt) {
|
||||
const customAnchor = text.match(rATag)[1];
|
||||
id = anchorId(customAnchor, transformOption);
|
||||
}
|
||||
|
||||
// Add a number after id if repeated
|
||||
if (headingId[id]) {
|
||||
id += `-${headingId[id]++}`;
|
||||
} else {
|
||||
headingId[id] = 1;
|
||||
}
|
||||
|
||||
if (anchorAliasOpt) {
|
||||
text = text.replace(rATag, (str, alias) => {
|
||||
return str.replace(alias, id);
|
||||
});
|
||||
}
|
||||
|
||||
// add headerlink
|
||||
return `<h${level} id="${id}"><a href="#${id}" class="headerlink" title="${stripHTML(text)}"></a>${text}</h${level}>`;
|
||||
},
|
||||
|
||||
link({ tokens, href, title }) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
const { external_link, sanitizeUrl, hexo, mangle } = this.options;
|
||||
const { url: urlCfg } = hexo.config;
|
||||
|
||||
if (sanitizeUrl) {
|
||||
if (href.startsWith('javascript:') || href.startsWith('vbscript:') || href.startsWith('data:')) {
|
||||
href = '';
|
||||
}
|
||||
}
|
||||
if (mangle) {
|
||||
if (href.startsWith('mailto:')) {
|
||||
const email = href.substring(7);
|
||||
const mangledEmail = mangleEmail(email);
|
||||
|
||||
href = `mailto:${mangledEmail}`;
|
||||
}
|
||||
}
|
||||
|
||||
let out = '<a href="';
|
||||
|
||||
try {
|
||||
out += encodeURL(href);
|
||||
} catch (e) {
|
||||
out += href;
|
||||
}
|
||||
|
||||
out += '"';
|
||||
|
||||
if (title) {
|
||||
out += ` title="${escape(title)}"`;
|
||||
}
|
||||
if (external_link) {
|
||||
const target = ' target="_blank"';
|
||||
const noopener = ' rel="noopener"';
|
||||
const nofollowTag = ' rel="noopener external nofollow noreferrer"';
|
||||
if (isExternalLink(href, urlCfg, external_link.exclude)) {
|
||||
if (external_link.enable && external_link.nofollow) {
|
||||
out += target + nofollowTag;
|
||||
} else if (external_link.enable) {
|
||||
out += target + noopener;
|
||||
} else if (external_link.nofollow) {
|
||||
out += nofollowTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out += `>${text}</a>`;
|
||||
return out;
|
||||
},
|
||||
|
||||
// Support Basic Description Lists
|
||||
paragraph({ tokens }) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
const { descriptionLists = true } = this.options;
|
||||
|
||||
if (descriptionLists && text.includes('<br>:')) {
|
||||
if (rDlSyntax.test(text)) {
|
||||
return text.replace(rDlSyntax, '<dl><dt>$1</dt><dd>$2</dd></dl>');
|
||||
}
|
||||
}
|
||||
|
||||
return `<p>${text}</p>\n`;
|
||||
},
|
||||
|
||||
// Prepend root to image path
|
||||
image({ href, title, text }) {
|
||||
const { options } = this;
|
||||
const { hexo } = options;
|
||||
const { relative_link } = hexo.config;
|
||||
const { lazyload, figcaption, prependRoot, postPath } = options;
|
||||
|
||||
if (!/^(#|\/\/|http(s)?:)/.test(href) && !relative_link && prependRoot) {
|
||||
if (!href.startsWith('/') && !href.startsWith('\\') && postPath) {
|
||||
const PostAsset = hexo.model('PostAsset');
|
||||
// findById requires forward slash
|
||||
const asset = PostAsset.findById(join(postPath, href.replace(/\\/g, '/')));
|
||||
// asset.path is backward slash in Windows
|
||||
if (asset) href = asset.path.replace(/\\/g, '/');
|
||||
}
|
||||
href = url_for.call(hexo, href);
|
||||
}
|
||||
|
||||
let out = `<img src="${encodeURL(href)}"`;
|
||||
if (text) out += ` alt="${escape(text)}"`;
|
||||
if (title) out += ` title="${escape(title)}"`;
|
||||
if (lazyload) out += ' loading="lazy"';
|
||||
|
||||
out += '>';
|
||||
if (figcaption && text) {
|
||||
return `<figure>${out}<figcaption aria-hidden="true">${text}</figcaption></figure>`;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Lexer.js#L8-L24
|
||||
const smartypants = (str, quotes) => {
|
||||
const [openDbl, closeDbl, openSgl, closeSgl] = typeof quotes === 'string' && quotes.length === 4
|
||||
? quotes
|
||||
: ['\u201c', '\u201d', '\u2018', '\u2019'];
|
||||
|
||||
return str
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/([{"\s])'/g, '$1' + openSgl)
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, closeSgl)
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/([{\u2018\s])"/g, '$1' + openDbl)
|
||||
// closing doubles
|
||||
.replace(/"/g, closeDbl)
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026');
|
||||
};
|
||||
|
||||
const tokenizer = {
|
||||
// Support autolink option
|
||||
url(src) {
|
||||
const { autolink } = this.options;
|
||||
|
||||
if (!autolink) return;
|
||||
// return false to use original url tokenizer
|
||||
return false;
|
||||
},
|
||||
|
||||
// Override smartypants
|
||||
inlineText(src) {
|
||||
const { options, rules } = this;
|
||||
const { quotes, smartypants: isSmarty } = options;
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Tokenizer.js#L643-L658
|
||||
const cap = rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (this.lexer.state.inRawBlock || this.rules.inline.url.exec(src)) {
|
||||
text = cap[0];
|
||||
} else {
|
||||
text = escape(isSmarty ? smartypants(cap[0], quotes) : cap[0]);
|
||||
}
|
||||
return {
|
||||
type: 'text',
|
||||
raw: cap[0],
|
||||
text
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function(data, options) {
|
||||
const { post_asset_folder, marked: markedCfg, source_dir } = this.config;
|
||||
const { prependRoot, postAsset, dompurify } = markedCfg;
|
||||
const { path, text } = data;
|
||||
|
||||
marked.defaults.extensions = null;
|
||||
marked.defaults.tokenizer = null;
|
||||
marked.defaults.renderer = null;
|
||||
marked.defaults.hooks = null;
|
||||
marked.defaults.walkTokens = null;
|
||||
|
||||
// exec filter to extend marked
|
||||
this.execFilterSync('marked:use', marked.use, { context: this });
|
||||
|
||||
// exec filter to extend renderer
|
||||
this.execFilterSync('marked:renderer', renderer, { context: this });
|
||||
|
||||
// exec filter to extend tokenizer
|
||||
this.execFilterSync('marked:tokenizer', tokenizer, { context: this });
|
||||
|
||||
const extensions = [];
|
||||
this.execFilterSync('marked:extensions', extensions, { context: this });
|
||||
marked.use({ extensions });
|
||||
|
||||
let postPath = '';
|
||||
if (path && post_asset_folder && prependRoot && postAsset) {
|
||||
const Post = this.model('Post');
|
||||
// Windows compatibility, Post.findOne() requires forward slash
|
||||
const source = path.substring(this.source_dir.length).replace(/\\/g, '/');
|
||||
const post = Post.findOne({ source });
|
||||
if (post) {
|
||||
const { source: postSource } = post;
|
||||
postPath = join(source_dir, dirname(postSource), basename(postSource, extname(postSource)));
|
||||
}
|
||||
}
|
||||
|
||||
let sanitizer = function(html) { return html; };
|
||||
|
||||
if (dompurify) {
|
||||
if (createDOMPurify === undefined && JSDOM === undefined) {
|
||||
createDOMPurify = require('dompurify');
|
||||
JSDOM = require('jsdom').JSDOM;
|
||||
}
|
||||
const window = new JSDOM('').window;
|
||||
const DOMPurify = createDOMPurify(window);
|
||||
let param = {};
|
||||
if (dompurify !== true) {
|
||||
param = dompurify;
|
||||
}
|
||||
sanitizer = function(html) { return DOMPurify.sanitize(html, param); };
|
||||
}
|
||||
|
||||
marked.use({
|
||||
renderer,
|
||||
tokenizer
|
||||
});
|
||||
return sanitizer(marked.parse(text, Object.assign({
|
||||
// headerIds was removed in marked v8.0.0, but we still need it
|
||||
headerIds: true
|
||||
}, markedCfg, options, { postPath, hexo: this, _headingId: {} })));
|
||||
};
|
||||
50
node_modules/hexo-renderer-marked/package.json
generated
vendored
Normal file
50
node_modules/hexo-renderer-marked/package.json
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "hexo-renderer-marked",
|
||||
"version": "7.0.1",
|
||||
"description": "Markdown renderer plugin for Hexo",
|
||||
"main": "index",
|
||||
"scripts": {
|
||||
"eslint": "eslint .",
|
||||
"test": "mocha test/index.js",
|
||||
"test-cov": "c8 --reporter=lcovonly npm run test"
|
||||
},
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"index.js"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/hexojs/hexo-renderer-marked.git"
|
||||
},
|
||||
"keywords": [
|
||||
"hexo",
|
||||
"markdown",
|
||||
"marked",
|
||||
"renderer"
|
||||
],
|
||||
"author": "Tommy Chen <tommy351@gmail.com> (https://zespia.tw)",
|
||||
"maintainers": [
|
||||
"Abner Chou <hi@abnerchou.me> (https://abnerchou.me)"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dompurify": "^3.0.3",
|
||||
"hexo-util": "^3.1.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"marked": "^15.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"c8": "^10.1.2",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-hexo": "^5.0.0",
|
||||
"hexo": "^7.1.1",
|
||||
"mocha": "^11.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user