docsifyjs / docsify

🃏 A magical documentation site generator.
https://docsify.js.org
MIT License
27.48k stars 5.67k forks source link

Markdown configuration: marked extensions don't work #1874

Closed dzylikecode closed 2 years ago

dzylikecode commented 2 years ago

Bug Report

In order to customize the parsing rules, I follow the advice of the document about Markdown configuration and read the marked documentation. However, when I wanna reproduce the example to add a custom syntax to generate <dl> description lists, it doesn't work.

Steps to reproduce

What is current behaviour

What is the expected behaviour

Other relevant information

Please create a reproducible sandbox

Edit 307qqv236

Mention the docsify version in which this bug was not present (if any)

dzylikecode commented 2 years ago

solved

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="./lib/vue.css" />
  </head>
  <body>
    <div id="app"></div>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <script>
      let newMarked = marked;
      window.$docsify = {
        markdown: function (markedOld, oldRenderer) {
          let renderer = {
            // block
            code: function (code, infostring, escaped) {
              return oldRenderer.code.apply(this, arguments);
            },
            blockquote: function (quote) {
              return oldRenderer.blockquote.apply(this, arguments);
            },
            html: function (html) {
              return oldRenderer.html.apply(this, arguments);
            },
            heading: function (text, level, raw, slugger) {
              return oldRenderer.heading.apply(this, arguments);
            },
            hr: function () {
              return oldRenderer.hr.apply(this, arguments);
            },
            list: function (body, ordered, start) {
              return oldRenderer.list.apply(this, arguments);
            },
            listitem: function (text, task, checked) {
              return oldRenderer.listitem.apply(this, arguments);
            },
            checkbox: function (checked) {
              return oldRenderer.checkbox.apply(this, arguments);
            },
            paragraph: function (text) {
              return oldRenderer.paragraph.apply(this, arguments);
            },
            table: function (header, body) {
              return oldRenderer.table.apply(this, arguments);
            },
            tablerow: function (content) {
              return oldRenderer.tablerow.apply(this, arguments);
            },
            tablecell: function (content, flags) {
              return oldRenderer.tablecell.apply(this, arguments);
            },
            // inline
            strong: function (text) {
              return oldRenderer.strong.apply(this, arguments);
            },
            em: function (text) {
              return oldRenderer.em.apply(this, arguments);
            },
            codespan: function (code) {
              return oldRenderer.codespan.apply(this, arguments);
            },
            br: function () {
              return oldRenderer.br.apply(this, arguments);
            },
            del: function (text) {
              return oldRenderer.del.apply(this, arguments);
            },
            link: function (href, title, text) {
              return oldRenderer.link.apply(this, arguments);
            },
            image: function (href, title, text) {
              return oldRenderer.image.apply(this, arguments);
            },
            text: function (text) {
              return oldRenderer.text.apply(this, arguments);
            },
          };
          const descriptionList = {
            name: "descriptionList",
            level: "block", // Is this a block-level or inline-level tokenizer?
            start(src) {
              return src.match(/:[^:\n]/)?.index;
            }, // Hint to Marked.js to stop and check for a match
            tokenizer(src, tokens) {
              const rule = /^(?::[^:\n]+:[^:\n]*(?:\n|$))+/; // Regex for the complete token, anchor to string start
              const match = rule.exec(src);
              if (match) {
                const token = {
                  // Token to generate
                  type: "descriptionList", // Should match "name" above
                  raw: match[0], // Text to consume from the source
                  text: match[0].trim(), // Additional custom properties
                  tokens: [], // Array where child inline tokens will be generated
                };
                this.lexer.inline(token.text, token.tokens); // Queue this data to be processed for inline tokens
                return token;
              }
            },
            renderer(token) {
              return `<dl>${this.parser.parseInline(token.tokens)}\n</dl>`; // parseInline to turn child tokens into HTML
            },
          };

          const description = {
            name: "description",
            level: "inline", // Is this a block-level or inline-level tokenizer?
            start(src) {
              return src.match(/:/)?.index;
            }, // Hint to Marked.js to stop and check for a match
            tokenizer(src, tokens) {
              const rule = /^:([^:\n]+):([^:\n]*)(?:\n|$)/; // Regex for the complete token, anchor to string start
              const match = rule.exec(src);
              if (match) {
                return {
                  // Token to generate
                  type: "description", // Should match "name" above
                  raw: match[0], // Text to consume from the source
                  dt: this.lexer.inlineTokens(match[1].trim()), // Additional custom properties, including
                  dd: this.lexer.inlineTokens(match[2].trim()), //   any further-nested inline tokens
                };
              }
            },
            renderer(token) {
              return `\n<dt>${this.parser.parseInline(
                token.dt
              )}</dt><dd>${this.parser.parseInline(token.dd)}</dd>`;
            },
            childTokens: ["dt", "dd"], // Any child tokens to be visited by walkTokens
          };

          function walkTokens(token) {
            // Post-processing on the completed token tree
            if (token.type === "strong") {
              token.text += " walked";
              token.tokens = this.Lexer.lexInline(token.text);
            }
          }
          newMarked.use({
            extensions: [descriptionList, description],
            walkTokens,
            renderer,
          });
          //   let res = newMarked.parse(
          //     "A Description List:\n" +
          //       ":   Topic 1   :  Description 1\n" +
          //       ": **Topic 2** : *Description 2*"
          //   );
          return newMarked;
        },
      };
    </script>
    <script src="./lib/docsify.js"></script>
  </body>
</html>