UNPKG

4.61 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5const highlight_js_1 = __importDefault(require("highlight.js"));
6const strip_indent_1 = __importDefault(require("strip-indent"));
7// eslint-disable-next-line @typescript-eslint/no-var-requires
8const alias = require('../highlight_alias.json');
9function highlightUtil(str, options = {}) {
10 if (typeof str !== 'string')
11 throw new TypeError('str must be a string!');
12 str = (0, strip_indent_1.default)(str);
13 const useHljs = Object.prototype.hasOwnProperty.call(options, 'hljs') ? options.hljs : false;
14 const { gutter = true, firstLine = 1, caption, mark = [], languageAttr = false, tab } = options;
15 let { wrap = true } = options;
16 highlight_js_1.default.configure({ classPrefix: useHljs ? 'hljs-' : '' });
17 const data = highlight(str, options);
18 const lang = options.lang || data.language || '';
19 const classNames = (useHljs ? 'hljs' : 'highlight') + (lang ? ` ${lang}` : '');
20 if (gutter && !wrap)
21 wrap = true; // arbitrate conflict ("gutter:true" takes priority over "wrap:false")
22 const before = useHljs ? `<pre><code class="${classNames}"${languageAttr && lang ? ` data-language="${lang}"` : ''}>` : '<pre>';
23 const after = useHljs ? '</code></pre>' : '</pre>';
24 const lines = data.value.split('\n');
25 let numbers = '';
26 let content = '';
27 for (let i = 0, len = lines.length; i < len; i++) {
28 let line = lines[i];
29 if (tab)
30 line = replaceTabs(line, tab);
31 numbers += `<span class="line">${Number(firstLine) + i}</span><br>`;
32 content += formatLine(line, Number(firstLine) + i, mark, options, wrap);
33 }
34 let codeCaption = '';
35 if (caption) {
36 codeCaption = wrap ? `<figcaption>${caption}</figcaption>` : `<div class="caption">${caption}</div>`;
37 }
38 if (!wrap) {
39 // if original content has one trailing newline, replace it only once, else remove all trailing newlines
40 content = /\r?\n$/.test(data.value) ? content.replace(/\n$/, '') : content.trimEnd();
41 return `<pre>${codeCaption}<code class="${classNames}"${languageAttr && lang ? ` data-language="${lang}"` : ''}>${content}</code></pre>`;
42 }
43 let result = `<figure class="highlight${data.language ? ` ${data.language}` : ''}"${languageAttr && lang ? ` data-language="${lang}"` : ''}>`;
44 result += codeCaption;
45 result += '<table><tr>';
46 if (gutter) {
47 result += `<td class="gutter"><pre>${numbers}</pre></td>`;
48 }
49 result += `<td class="code">${before}${content}${after}</td>`;
50 result += '</tr></table></figure>';
51 return result;
52}
53function formatLine(line, lineno, marked, options, wrap) {
54 const useHljs = (options.hljs || false) || !wrap;
55 const br = wrap ? '<br>' : '\n';
56 let res = useHljs ? '' : '<span class="line';
57 if (marked.includes(lineno)) {
58 // Handle marked lines.
59 res += useHljs ? `<mark>${line}</mark>` : ` marked">${line}</span>`;
60 }
61 else {
62 res += useHljs ? line : `">${line}</span>`;
63 }
64 res += br;
65 return res;
66}
67function replaceTabs(str, tab) {
68 return str.replace(/\t+/, match => tab.repeat(match.length));
69}
70function highlight(str, options) {
71 let { lang } = options;
72 const { autoDetect = false } = options;
73 if (lang) {
74 lang = lang.toLowerCase();
75 }
76 else if (autoDetect) {
77 const result = highlight_js_1.default.highlightAuto(str);
78 return closeTags(result);
79 }
80 if (!lang || !alias.aliases[lang]) {
81 lang = 'plaintext';
82 }
83 const res = highlight_js_1.default.highlight(str, {
84 language: lang,
85 ignoreIllegals: true
86 });
87 return closeTags(res);
88}
89// https://github.com/hexojs/hexo-util/issues/10
90function closeTags(res) {
91 const tokenStack = [];
92 res.value = res.value.split('\n').map(line => {
93 const prepend = tokenStack.map(token => `<span class="${token}">`).join('');
94 const matches = line.matchAll(/(<span class="(.*?)">|<\/span>)/g);
95 for (const match of matches) {
96 if (match[0] === '</span>')
97 tokenStack.shift();
98 else
99 tokenStack.unshift(match[2]);
100 }
101 const append = '</span>'.repeat(tokenStack.length);
102 return prepend + line + append;
103 }).join('\n');
104 return res;
105}
106module.exports = highlightUtil;
107//# sourceMappingURL=highlight.js.map
\No newline at end of file