diff --git a/docs/.eleventy.js b/docs/.eleventy.js index 8ad469cc736..32666ec624d 100644 --- a/docs/.eleventy.js +++ b/docs/.eleventy.js @@ -10,7 +10,7 @@ const Image = require("@11ty/eleventy-img"); const path = require("path"); const { slug } = require("github-slugger"); const yaml = require("js-yaml"); - +const { highlighter, preWrapperPlugin, lineNumberPlugin } = require("./src/_plugins/md-syntax-highlighter"); const { DateTime } = require("luxon"); @@ -145,7 +145,8 @@ module.exports = function(eleventyConfig) { eleventyConfig.addPlugin(eleventyNavigationPlugin); eleventyConfig.addPlugin(syntaxHighlight, { - alwaysWrapLineHighlights: true + alwaysWrapLineHighlights: true, + templateFormats: ["liquid", "njk"] }); eleventyConfig.addPlugin(pluginRss); eleventyConfig.addPlugin(pluginTOC, { @@ -186,30 +187,32 @@ module.exports = function(eleventyConfig) { } const markdownIt = require("markdown-it"); + const md = markdownIt({ html: true, linkify: true, typographer: true, highlight: (str, lang) => highlighter(md, str, lang) }) + .use(markdownItAnchor, { + slugify + }) + .use(markdownItContainer, "correct", {}) + .use(markdownItContainer, "incorrect", {}) + .use(markdownItContainer, "warning", { + render(tokens, idx) { + return generateAlertMarkup("warning", tokens, idx); + } + }) + .use(markdownItContainer, "tip", { + render(tokens, idx) { + return generateAlertMarkup("tip", tokens, idx); + } + }) + .use(markdownItContainer, "important", { + render(tokens, idx) { + return generateAlertMarkup("important", tokens, idx); + } + }) + .use(preWrapperPlugin) + .use(lineNumberPlugin) + .disable("code"); - eleventyConfig.setLibrary("md", - markdownIt({ html: true, linkify: true, typographer: true }) - .use(markdownItAnchor, { - slugify - }) - .use(markdownItContainer, "correct", {}) - .use(markdownItContainer, "incorrect", {}) - .use(markdownItContainer, "warning", { - render(tokens, idx) { - return generateAlertMarkup("warning", tokens, idx); - } - }) - .use(markdownItContainer, "tip", { - render(tokens, idx) { - return generateAlertMarkup("tip", tokens, idx); - } - }) - .use(markdownItContainer, "important", { - render(tokens, idx) { - return generateAlertMarkup("important", tokens, idx); - } - }) - .disable("code")); + eleventyConfig.setLibrary("md", md); //------------------------------------------------------------------------------ // Shortcodes diff --git a/docs/package.json b/docs/package.json index 91056615833..c9935e99321 100644 --- a/docs/package.json +++ b/docs/package.json @@ -42,6 +42,7 @@ "netlify-cli": "^10.3.1", "npm-run-all": "^4.1.5", "postcss-html": "^1.5.0", + "prismjs": "^1.29.0", "rimraf": "^3.0.2", "sass": "^1.52.1", "stylelint": "^14.13.0", diff --git a/docs/src/_plugins/md-syntax-highlighter.js b/docs/src/_plugins/md-syntax-highlighter.js new file mode 100644 index 00000000000..c886043821b --- /dev/null +++ b/docs/src/_plugins/md-syntax-highlighter.js @@ -0,0 +1,107 @@ +/** + * MIT License + +Copyright (c) 2019-present, Yuxi (Evan) You + +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. + */ + +/** @typedef {import("markdown-it")} MarkdownIt */ + +const Prism = require("prismjs"); +const loadLanguages = require("prismjs/components/"); + +/** + * + * @param {MarkdownIt} md markdown-it + * @param {string} str code + * @param {string} lang code language + * @returns + */ +const highlighter = function (md, str, lang) { + let result = ""; + if (lang) { + try { + loadLanguages([lang]); + result = Prism.highlight(str, Prism.languages[lang], lang); + } catch (err) { + console.log(err); + } + } else { + result = md.utils.escapeHtml(str); + } + + return `
${result}
`; +}; + +/** + * + * modified from https://github.com/vuejs/vitepress/blob/main/src/node/markdown/plugins/preWrapper.ts + * @param {MarkdownIt} md + * @license MIT License. See file header. + */ +const preWrapperPlugin = (md) => { + const fence = md.renderer.rules.fence; + md.renderer.rules.fence = (...args) => { + const [tokens, idx] = args; + const lang = tokens[idx].info.trim(); + const rawCode = fence(...args); + return `
${rawCode}
`; + }; +}; + +/** + * + * modified from https://github.com/vuejs/vitepress/blob/main/src/node/markdown/plugins/lineNumbers.ts + * @param {MarkdownIt} md + * @license MIT License. See file header. + */ +const lineNumberPlugin = (md) => { + const fence = md.renderer.rules.fence; + md.renderer.rules.fence = (...args) => { + const [tokens, idx] = args; + const lang = tokens[idx].info.trim(); + const rawCode = fence(...args); + const code = rawCode.slice( + rawCode.indexOf(""), + rawCode.indexOf("") + ); + const lines = code.split("\n"); + const lineNumbersCode = [...Array(lines.length - 1)] + .map( + (line, index) => + `${index + 1}
` + ) + .join(""); + + const lineNumbersWrapperCode = ``; + + const finalCode = rawCode + .replace(/<\/div>$/, `${lineNumbersWrapperCode}`) + .replace(/"(language-\S*?)"/, '"$1 line-numbers-mode"') + .replace(//, ``) + .replace(/
/, `
`);
+
+        return finalCode;
+    };
+};
+
+module.exports.highlighter = highlighter;
+module.exports.preWrapperPlugin = preWrapperPlugin;
+module.exports.lineNumberPlugin = lineNumberPlugin;
diff --git a/docs/src/assets/scss/syntax-highlighter.scss b/docs/src/assets/scss/syntax-highlighter.scss
index cb26ac75af4..18269425cb1 100644
--- a/docs/src/assets/scss/syntax-highlighter.scss
+++ b/docs/src/assets/scss/syntax-highlighter.scss
@@ -32,7 +32,7 @@ pre[class*="language-"] {
 }
 
 /* Code blocks */
-pre[class*="language-"] {
+div[class*="language-"] {
     padding: 1.5rem;
     margin: 1.5rem 0;
     overflow: auto;
@@ -52,8 +52,8 @@ pre[class*="language-"] {
 
 /* Inline code */
 :not(pre) > code[class*="language-"] {
-    padding: .1em;
-    border-radius: .3em;
+    padding: 0.1em;
+    border-radius: 0.3em;
     white-space: normal;
 }
 
@@ -69,7 +69,7 @@ pre[class*="language-"] {
 }
 
 .token.namespace {
-    opacity: .7;
+    opacity: 0.7;
 }
 
 .token.selector,
@@ -100,25 +100,32 @@ pre[class*="language-"] {
     cursor: help;
 }
 
-pre {
-    counter-reset: lineNumber;
-}
-
-code .highlight-line {
-    font-variant-ligatures: none;
+.line-numbers-wrapper {
+    position: absolute;
+    top: 0;
+    text-align: right;
+    padding-top: 1.5rem;
+    font-size: 1em;
+    font-family: var(--mono-font), Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
+    line-height: 1.5;
+    color: var(--icon-color);
 }
 
-code .highlight-line::before {
-    -webkit-user-select: none;
+.line-number {
+    user-select: none;
     color: var(--icon-color);
-    content: counter(lineNumber);
-    counter-increment: lineNumber;
     display: inline-block;
     font-variant-numeric: tabular-nums;
-    margin-right: 1.2em;
-    padding-right: 1.2em;
-    margin-inline-end: 1.2em;
-    padding-inline-end: 1.2em;
     text-align: right;
-    width: 2.4em;
+    width: 1.2em;
+}
+
+div[class*="language-"].line-numbers-mode {
+    position: relative;
+
+    pre[class*="language-"] {
+        padding-left: calc(2.4em + 1.2rem);
+        margin-top: 0;
+        margin-bottom: 0;
+    }
 }