Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown configuration: marked extensions don't work #1874

Closed
1 task
dzylikecode opened this issue Sep 4, 2022 · 1 comment
Closed
1 task

Markdown configuration: marked extensions don't work #1874

dzylikecode opened this issue Sep 4, 2022 · 1 comment

Comments

@dzylikecode
Copy link

dzylikecode commented Sep 4, 2022

Bug Report

  • OS: windows
  • browser: Google Chrome 104.0.5112.102

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

  • configure the docsify

    window.$docsify = {
      markdown: function (marked, 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);
          }
        }
        marked.use({
          extensions: [descriptionList, description],
          walkTokens,
        });
    
        // EQUIVALENT TO:
    
        marked.use({ extensions: [descriptionList] });
        marked.use({ extensions: [description] });
        marked.use({ walkTokens });
        return marked;
      },
    };
  • write in README.md file

    A Description List:
    : Topic 1 : Description 1
    : **Topic 2** : _Description 2_
    
  • open chrome to debug

What is current behaviour

  • extension does not work

    The output of the browser is as follows:

    <p>
      A Description List: : Topic 1 : Description 1 : <strong>Topic 2</strong> :
      <em>Description 2</em>
    </p>

What is the expected behaviour

  • marked documentation says that the output will contain elements <dt> and <dd>

    <p>A Description List:</p>
    <dl>
      <dt>Topic 1</dt>
      <dd>Description 1</dd>
      <dt><strong>Topic 2 walked</strong></dt>
      <dd><em>Description 2</em></dd>
    </dl>

Other relevant information

  • Bug does still occur when all/other plugins are disabled?

  • Your OS:

  • Node.js version:

  • npm/yarn version:

  • Browser version:

  • Docsify version:

  • Docsify plugins:

Please create a reproducible sandbox

Edit 307qqv236

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

@dzylikecode
Copy link
Author

solved

  • need to import the original marked library

    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant