Nutritious Polygonal Meatball
    Wondering what’s next for npm?Check out our public roadmap! »

    @hyperfiddle/remark-generic-extensions

    1.3.0 • Public • Published

    remark-generic-extensions

    npm version npm Build Status Coverage Status Greenkeeper badge dependencies Status devDependencies Status

    styled with prettier

    Allows the use of commonmark generic directive extension in markdown, generating components through remark-html or remark-react.

    This module also works in browser environments.

    📖 Release Notes

    Demo

    Here's a demo using remark-react and react-toolbox. It is made using create-react-app, the source is here and can also be run locally.

    Edit medfreeman/remark-generic-extensions: react-toolbox-icon

    Disclaimer

    Remember that the following syntax is experimental in regards to the commonmark spec, and will perhaps never be supported officialy.

    Generic Directives is still in active discussion in http://talk.commonmark.org/t/generic-directives-plugins-syntax . But for brainstorming purposes, here is some possible extensions to support, or at least adhere to if not included.
    
    It might look like !extensionName[](){} for inline
    
    and for block (Current talk page at http://talk.commonmark.org/t/block-directives/802 )
    
    extentionNames: argumentField 
    :::
    ...BlockContent...
    :::
    { #id .class key1=value key2=value }
    

    There is a known bug in remark-react < 4.0.1, that wrongly coerces non-string values to strings.

    Make sure to use at least v4.0.1.

    Syntax

    Inline extensions

    !Extension[Content](Argument){Properties}

    ℹ️ The extension syntax is validated through regexes, that you can inspect here if needed

    • Extension defines the element you want to use

      It matches the \w character class.

      Matches any alphanumeric character from the basic Latin alphabet, including the underscore. Equivalent to [A-Za-z0-9_].
      
    • Content defines the element content

      It matches everything but the ] character.

      It can be mapped to any hast element property or value, see placeholders.

    • Argument defines the element argument

      It matches everything but the ) character.

      It can be mapped to any hast element property or value, see placeholders.

    • Properties defines the element properties

      They can have leading and / or trailing spaces before / after the opening / closing braces.

      The different properties are separated by spaces, and so each of them match any character but spaces, except for quoted properties.

    Block extensions

    Extension: Argument
    :::
    [Content]
    :::
    {Properties}
    

    ℹ️ The extension syntax is validated through regexes, that you can inspect here if needed

    • Extension defines the element you want to use

      It matches the \w character class.

      Matches any alphanumeric character from the basic Latin alphabet, including the underscore. Equivalent to [A-Za-z0-9_].
      
    • Argument defines the element argument

      It matches everything but a line break.

      It can be mapped to any hast element property or value, see placeholders.

    • Content defines the element content

      It matches everything, and stops at the next ::: occurence.

      Leading and trailing line breaks will be stripped.

      It can be mapped to any hast element property or value, see placeholders.

    • Properties defines the element properties

      They can have leading and / or trailing spaces before / after the opening / closing braces.

      The different properties are separated by spaces, and so each of them match any character but spaces, except for quoted properties.

    Available properties

    • id: #my-id

      It will be applied to the top-level hast element.

    • class: .my-class .my-other-class

      It will be applied to the top-level hast element.

    • key / value property: my-prop=my-value

      It can be mapped to any hast element property or value, see placeholders.

    • key / value quoted property: my-other-prop="my value with spaces"

      It can be mapped to any hast element property or value, see placeholders.

    • lone property: my-alone-prop

      It can be mapped to any hast element property or value, see placeholders.

    Installation

    npm install remark-generic-extensions

    OR

    yarn add remark-generic-extensions

    Usage (es6)

    with remark-react

    See demo

    with remark-html

    Say we have the following file, example.md:

    Alpha
     
    !alert[My message!](my subtext is rad){ #my-alert .custom-alert }
     
    youtube: C8NAYW-Z54o
    :::
    My featured video!
    :::
    { #my-video .custom-video-style spanClassName=custom-span-class }
     
    ## Bravo
     
    ## Delta

    And our script, example.js, looks as follows:

    import vfile from "to-vfile"
    import remark from "remark"
    import genericExtensions from "remark-generic-extensions"
    import html from "remark-html"
     
    remark()
    .use(genericExtensions,
      {
        elements: {
          alert: {
            html: {
              tagName: "span",
              children: [
                {
                  type: "element",
                  tagName: "i",
                  properties: {
                    className: "fa fa-exclamation",
                    ariaHidden: true
                  }
                },
                {
                  type: "element",
                  tagName: "span",
                  children: [
                    {
                      type: "text",
                      value: "::content::"
                    }
                  ]
                },
                {
                  type: "element",
                  tagName: "span",
                  properties: {
                    className: "subtext"
                  },
                  children: [
                    {
                      type: "text",
                      value: "::argument::"
                    }
                  ]
                }
              ]
            }
          }
        },
        youtube: {
          html: {
            tagName: "div",
            children: [
              {
                type: "element",
                tagName: "iframe",
                properties: {
                  width: 420,
                  height: 315,
                  src: "https://www.youtube.com/embed/::argument::"
                }
              },
              {
                tagName: "span",
                properties: {
                  className: "::prop::spanClassName::"
                },
                children: [
                  {
                    type: "text",
                    value: "::content::"
                  }
                ]
              }
            ]
          }
        }
      }
    )
    .use(html)
    .process(vfile.readSync('example.md'), (err, file) => {
      if (err) throw err
      console.log(String(file))
    })

    Now, running node example yields (indented):

    <h1>Alpha</h1>
     
    <p>
      <span id="my-alert" class="custom-alert">
        <i class="fa fa-exclamation" aria-hidden="true"></i>
        <span>My message!</span>
        <span class="subtext">my subtext is rad</span>
      </span>
    </p>
     
    <div class="custom-video-style" id="my-video">
      <iframe width="420" height="315" src="https://www.youtube.com/embed/C8NAYW-Z54o"></iframe>
      <span class="custom-span-class">My featured video!</span>
    </div>
     
    <h2>Bravo</h2>
     
    <h2>Delta</h2>

    API

    remark().use(genericExtensions[, options])

    Convert generic extensions in a markdown document to an hast syntax tree, suitable for rendering to html / react.

    • Looks for all inline extensions (if providing elements configuration the extension names are case sensitive)
    • Tells remark how to interpret them according to the options

    options (object)

    All options are validated through joi. You can find the schema here. In case of error, it will be logged to the console and this module bypassed by remark.

    Properties
    • elements (Elements, optional, default {}) - A list of inline elements to support along with their configuration

    • placeholderAffix (string, optional, default "::") - String that encloses content, argument and prop tokens in properties of the elements option, for dynamic replacement with properties specified in the markdown element.

      See HastProperties for more information.

    • debug (boolean, default: false) - Whether to show debug messages through vfile-reporter

      See Logging for more information.


    Elements (object)

    This object defines the extensions configuration.

    The keys are strings corresponding to the inline extension names (i.e. Icon).

    You can define one or more extensions if needed.

    Properties
    • extensionName (string): (object)
      • propsDefaultValues (object, optional)

        • propertyName (string): (any) - default value of the property

          It will be applied to items that specify the corresponding property without a defined value, i.e.

          example with `highlight` property on `icon` extension (es6)

          import remark from "remark"
          import genericExtensions "remark-generic-extensions"
          import html from "remark-html"
           
          remark()
          .use(genericExtensions,
            {
              elements: {
                icon: {
                  propsDefaultValues: {
                    highlight: true,
                  }
                }
              }
            }
          )
          .use(html)
          .process("!icon{highlight}", (err, file) => {
            if (err) throw err
            console.log(String(file))
          })

          Running this example would yield <p><icon highlight="true"></icon></p>

      • html (object, optional) - html element representation used for rendering the extension

        • tagName (string, optional, defaults to the extension name) - html5 element tag name
        • children (Hast, optional) - the element children
        • properties (HastProperties, optional) - the element properties

    Hast (array[object])

    This structure is a hast tree, with a few restrictions.

    It is recursive, the children property also being of Hast type.

    Properties
    • type (enum[string], optional, default "element") - hast node type
      • element
      • comment
      • text
    • tagName (string, required when type === "element") - html5 element tag name
    • value (string, required when type === ("comment" || "text")) - comment or text content, supports placeholders
    • children (Hast, optional) - the element children
    • properties (HastProperties) - the element properties

    HastProperties (object)

    This object pairs are mapped to hast properties, and thus follow the same rules.

    Properties
    • propertyName (string, except "class" && "for"): (enum)
      • (string) - When a property value is a string, it supports placeholders that will be replaced by their corresponding value.

      • (any) - A property value can also be of any other type

    ℹ️ Any property present in a markdown extension and not referenced by a placeholder will be applied to the top-level element.

    ℹ️ If the content property is not referenced on a block element, it will be applied to a child text node of the top-level element.

    Placeholders

    The value property and all properties members in Hast children support placeholders.

    The available placeholders are:

    • "::content::" -> will be replaced by the content part of the extension in markdown (the part between [])
    • "::argument::" -> will be replaced by the argument part of the extension in markdown (the part between ())
    • "::prop::property::" -> will be replaced by the corresponding property value present in the extension in markdown (in the part between {})

    ℹ️ the available properties are id, className or any other property defined in your markdown.

    Specifying the placeholderAffix option allows changing the placeholders. For example, using "||" would make the available placeholders become "||content||", "||argument||" and "||prop||property||".

    Logging

    All logging, except the options validation that is directly logged to the console, takes place through vfile-reporter.

    It allows having nice error messages with positional information, and is also the standard way of logging with unifiedjs.

    By default, this module logs warnings, but enabling the debug option will also log debug messages, useful for troubleshooting.

    logging example (es6)

    import remark from "remark"
    import genericExtensions "remark-generic-extensions"
    import html from "remark-html"
    import report from "vfile-reporter"
     
    remark()
    .use(genericExtensions, {})
    .use(html)
    .process(input, function(err, file) {
      console.log(report(err || file))
    })

    debugging example (es6)

    import remark from "remark"
    import genericExtensions "remark-generic-extensions"
    import html from "remark-html"
    import report from "vfile-reporter"
     
    remark()
    .use(genericExtensions, { debug: true })
    .use(html)
    .process(input, function(err, file) {
      console.log(report(err || file))
    })


    CONTRIBUTING

    • ⇄ Pull requests and ★ Stars are always welcome.
    • For bugs and feature requests, please create an issue.
    • Pull requests must be accompanied by passing automated tests ($ yarn test).

    LICENSE

    Apache License 2.0

    Copyright (c) Mehdi Lahlou mlahlou@protonmail.ch

    Install

    npm i @hyperfiddle/remark-generic-extensions

    DownloadsWeekly Downloads

    3

    Version

    1.3.0

    License

    Apache-2.0

    Unpacked Size

    683 kB

    Total Files

    20

    Last publish

    Collaborators

    • avatar