base.css
* {
padding: 0;
margin: 0;
}
/* 自定义滚动条 */
::-webkit-scrollbar {
width: 12px;
height: 12px;
background-color: transparent;
}
::-webkit-scrollbar-corner {
background-color: black;
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.4);
border-radius: 32px;
}
body {
overflow: hidden;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
background-color: rgb(2, 33, 17);
color: #dddddd;
}
.container {
padding: 0;
/* margin: 10px 0 10px 0; */
display: flex;
flex-direction: row;
}
.left {
overflow: auto;
margin: 0;
/* padding: 0 20px 0 20px; */
/* width: 100px; */
background-color: rgba(34, 34, 34, 0.8);
}
.main {
overflow: auto;
box-sizing: border-box;
padding: 25px 25px 25px 25px;
width: 842px; /* A4 */
height: auto;
background-color: rgba(51, 51, 51, 1.0);
}
.right {
overflow: auto;
margin: 0;
padding: 0 20px 0 2em;
width: 300px;
background-color: rgba(34, 34, 34, 0.8);
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 链接 */
.container a {
color: dodgerblue;
}
.container a:after {
color: skyblue;
}
.container a:hover {
color: springgreen;
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 段落缩进 */
.main p:not(blockquote p),
ul,
ol,
pre,
table,
blockquote {
margin-left: 2em;
}
/* 段落间隔 */
.main p:not(blockquote p),
ul,
ol,
li,
pre,
table,
blockquote {
margin-bottom: 1em;
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 标题 */
.main h1 {
color: #ef7823;
margin-top: 1em;
margin-bottom: 1em;
border-bottom: 1px solid gray;
}
.main h1:before {
content: "";
}
.main h1 a {
font-size: 0;
}
/* ---------------- */
.main h2 {
color: #ff69b4;
margin-top: 1em;
margin-bottom: 1em;
border-bottom: 1px solid gray;
}
.main h2:before {
content: "";
}
.main h2 a {
font-size: 0;
}
/* ---------------- */
.main h3 {
color: #deda00;
margin-top: 1em;
margin-bottom: 1em;
border-bottom: 1px dashed gray;
}
.main h3:before {
content: "⭐";
}
.main h3 a {
font-size: 0;
}
/* ---------------- */
.main h4 {
color: #5cd009;
margin-top: 1em;
margin-bottom: 1em;
border-bottom: 1px dashed gray;
}
.main h4:before {
content: "";
}
.main h4 a {
font-size: 0;
}
/* ---------------- */
.main h5 {
color: #ffffff;
margin-top: 1em;
margin-bottom: 1em;
border-bottom: 1px dashed gray;
}
.main h5:before {
content: "✿";
}
.main h5 a {
font-size: 0;
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 代码: txt字体修正 */
pre code.language-txt,
code.language-batch {
font-family: Consolas, "黑体";
}
/* 代码: 行内代码块背景 */
.main code:not(pre code) {
/* font-family: "monospace", "Consolas", "黑体"; */
padding: 0.2em 0.5em 0.2em 0.5em;
border-radius: 6px;
background-color: #222;
}
/* 代码: 块代码块背景 */
.main pre {
padding: 1em;
border-radius: 3px;
background-color: #222;
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 引用 */
.main blockquote {
border-left: 5px solid;
padding: 0.5em;
border-radius: 3px;
background-color: rgba(100, 100, 100, 0.6);
}
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ */
/* 表格 */
.main table {
border-collapse: collapse;
border: 1px solid lightgray;
}
.main table th,
.main table td {
border: 1px solid gray;
padding: 0.5em;
}
demo.md
# 标题1
## 标题2
### 标题3
#### 标题4
##### 标题5
>引用
*斜体*
**粗体**
~~删除线~~
`代码`
*alpha*
0123456789
abcdefghijklmnopqrstuvwxyz
29^th^
H~2~0
==标记==
$E=mc^2$
$$E=mc^2$$
- 无序列表
- 无序列表
1. 有序列表1
2. 有序列表2
[连接](源地址)
![图片](https://pic.rmb.bdstatic.com/bjh/down/bf9dd40c28bf11e87e086d8ceab61dda.jpeg)
| 列1 | 列2 | 列3 |
| :--: | ---: | :--- |
| 居中 | 居右 | 居左 |
` ` `javascript
// A code block
var foo = 'bar';
` ` `
` ` `mermaid
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
` ` `
一个具有注脚的文本。[^1]
[^1]: 注脚的解释
index.js
const fs = require("fs");
const helper = require("./mdit");
const content = fs.readFileSync("Rust笔记.md").toString("utf-8");
const [html, toc] = helper.convertMarkdown2Html(content);
const fd = fs.openSync("./demo.html", "w");
fs.writeSync(fd, html);
fs.closeSync(fd);
mdit.js
/*
npm init -y
yarn add mermaid
yarn add katex
yarn add highlight.js
yarn add markdown-it
yarn add markdown-it-mark
yarn add markdown-it-sup
yarn add markdown-it-sub
yarn add markdown-it-footnote
yarn add markdown-it-katex
yarn add markdown-it-toc-and-anchor
*/
// HighlightJs
const hljs = require("highlight.js");
// Markdown
const mdItConfig = {
// 是否 在源码中启用 HTML 标签
html: false,
// 是否 使用 '/' 来闭合单标签(比如 <br />)
xhtmlOut: false,
// 是否 '\n' -> <br>
breaks: false,
// 代码块的 Class前缀
langPrefix: "language-",
// 将类似 URL 的文本自动转换为链接。
linkify: false,
// typographer: false, // 启用一些语言中立的替换 + 引号美化
// quotes: '“”‘’', // 双 + 单引号替换对,当 typographer 启用时。
// 高亮函数,返回转义的HTML 或 ''
highlight: function (code, lang, args) {
if (lang) {
if ("mermaid" === lang) {
return `<div class='mermaid'>${code}</div>`;
} else if (hljs.getLanguage(lang)) {
try {
const highlightCode = hljs.highlight(code, { language: lang, ignoreIllegals: true }).value;
return highlightCode;
} catch (error) {
console.log(error);
}
}
}
return "";
},
};
let mdToc = {};
const mdIt = require("markdown-it")(mdItConfig)
.use(require("markdown-it-mark")) // 标记
.use(require("markdown-it-sup")) // 上标
.use(require("markdown-it-sub")) // 下标
.use(require("markdown-it-footnote")) // 脚注
// 公式
.use(require("markdown-it-katex"), { throwOnError: false, errorColor: "#CC0000" })
// 目录
.use(require("markdown-it-toc-and-anchor").default, {
tocCallback: function (tocMarkdown, tocArray, tocHtml) {
mdToc = { tocMarkdown, tocArray, tocHtml };
},
});
const mdReader = (mdContent) => {
const html = mdIt.render(mdContent);
const template = `<html>
<head>
<link href="node_modules/katex/dist/katex.min.css" rel="stylesheet" />
<link href="node_modules/highlight.js/styles/github-dark.css" rel="stylesheet" />
<script src="node_modules/mermaid/dist/mermaid.min.js"></script>
<link href="base.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<div class="left"></div>
<div class="main">${html}</div>
<div class="right">${mdToc.tocHtml}</div>
</div>
<script>
mermaid.initialize({ startOnLoad: true, theme: "dark"});
</script>
</body>
</html>`;
return [template, mdToc];
};
module.exports = {
convertMarkdown2Html: mdReader,
};
/**
* @file 自定义规则
* @example
* mdIt.use(require("./YourRule"));
*/
const MarkdownIt = require("./types/markdown-it");
const StateCore = require("./types/markdown-it/rules_core/state_core");
const StateBlock = require("./types/markdown-it/rules_block/state_block");
const StateInline = require("./types/markdown-it/rules_inline/state_inline");
// const CoreRuleNames = ["normalize", "block", "inline", "linkify", "replacements", "smartquotes", "text_join"];
// const BlockRuleNames = ["table", "code", "fence", "blockquote", "hr", "list", "reference", "html_block", "heading", "lheading", "paragraph"];
// const InlineRuleNames = ["text", "linkify", "newline", "escape", "backticks", "strikethrough", "emphasis", "link", "image", "autolink", "html_inline", "entity"];
// const InlineRule2Names = ["balance_pairs", "strikethrough", "emphasis", "fragments_join"];
/**
* 自定义规则 匹配 {{}}
* @param {MarkdownIt} md
* @param {Object} options
*/
function YourRule(md, options) {
md.inline.ruler.after("emphasis", "test_inline", function (state, silent) {
const findL = "{{";
const findR = "}}";
const posStart = state.pos; // 起点
const posAvailable = () => state.pos < state.posMax;
const posRecovery = () => state.pos = posStart;
const srcPos = (length) => state.src.slice(state.pos, state.pos + (length || state.posMax));
const srcStartPos = () => state.src.slice(posStart, state.pos);
const srcFound = () => srcStartPos().slice(findL.length, -findR.length);
if (findL === srcPos(findL.length)) {
let found = false;
state.pos += findL.length;
while (posAvailable()) {
if (findR === srcPos(findR.length)) {
found = true;
state.pos += findR.length;
break;
}
state.pos += 1;
}
if (found) {
token = state.push("code_open", "code", 1);
token.markup = findL;
token = state.push("text", "", 0);
token.content = srcFound();
token = state.push("code_close", "code", -1);
token.markup = findR;
state.pos += 1;
return true;
}
posRecovery();
return false;
}
return false;
}); // push
}
module.exports = YourRule;
/*
【追溯 render 过程】
// 1 外部调用渲染
this.render(src, env);
// 1.1 解析出Token序列
let tokens = this.parse(src, env)
// 1.2 Token到Html
this.renderer.render(tokens, this.options, env)
// 1.1
this.parse(src, env)
// 1.1.1 创建 {StateCore} 对象
let state = new this.core.State(src, this, env);
// 1.1.2 处理
this.core.process(state);
// 1.1.3 返回
return state.tokens;
// 1.1.2
this.core.process(state);
// 1.1.2.1 返回全部 {CoreRuleNames} 对应的方法
let rules = this.ruler.getRules('');
for (let i = 0; i < rules.length; i++) {
// 1.1.2.2 执行对应方法
rules[i](state);
}
// 1.1.2.2 以 {core.block} {core.inline} 为例
function block(state) {
// 1.1.2.2.1 解析块
state.md.block.parse(state.src, state.md, state.env, state.tokens);
};
function inline(state) {
for (let i = 0; i < tokens.length; i++) {
let tok = tokens[i];
if ('inline' === tok.type) {
// 1.1.2.2.2 解析行
state.md.inline.parse(tok.content, state.md, state.env, tok.children);
}
}
};
// 1.1.2.2.1 解析块
function (src, md, env, outTokens) {
// 1.1.2.2.1.1 创建 {StateBlock} 对象
let state = new this.State(src, md, env, outTokens);
// 1.1.2.2.1.2 处理
this.tokenize(state, state.line, state.lineMax);
};
// 1.1.2.2.2 解析行
function (str, md, env, outTokens) {
// 1.1.2.2.2.1 创建 {StateInline} 对象
let state = new this.State(str, md, env, outTokens);
// 1.1.2.2.2.2 按 {InlineRuleNames} 解析
this.tokenize(state);
// 1.1.2.2.2.3 按 {InlineRule2Names} 解析
const rules = this.ruler2.getRules('');
for (let i = 0; i < rules.length; i++) {
rules[i](state);
}
};
// 1.1.2.2.2.2 按 {InlineRuleNames} 解析
function tokenize(state) {
const rules = this.ruler.getRules('');
let ok = false;
// ...
// 尝试所有可行规则,若成功:
// - 更新 `state.pos` 和 `state.tokens`
// - 返回 `true`
while (state.pos < state.posMax) {
if (state.level < state.md.options.maxNesting) {
for (let i = 0; i < rules.length; i++) {
ok = rules[i](state, false);
if (ok) break;
}
}
if (ok) {
if (state.pos >= state.posMax) break;
continue;
}
// 匹配失败的就是 普通文本
state.pending += state.src[state.pos++];
}
if (state.pending) {
// 收集的普通文本转变为 token text
state.pushPending();
}
};
*/