npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

markdown-magic

v3.3.0

Published

Automatically update markdown files with content from external sources

Downloads

35,805

Readme

Markdown Magic npm-version

✨ Add a little magic to your markdown ✨

About

Markdown magic uses comment blocks in markdown files to automatically sync or transform its contents.

  • Automatically keep markdown files up to date from local or remote code sources
  • Transform markdown content with custom transform functions
  • Render markdown with any template engine
  • Automatically generate a table of contents
  • ... etc

The comments markdown magic uses are hidden in markdown and when viewed as HTML.

This README.md is generated with markdown-magic view the raw file to see how.

Video demoExample Repo

Table of Contents

Install

To get started. Install the npm package.

npm install markdown-magic --save-dev

Usage

Use comment blocks in your markdown

Example:

<!-- doc-gen remote url=http://url-to-raw-md-file.md -->
This content will be dynamically replaced from the remote url
<!-- end-doc-gen -->

Then run markdown-magic via it's CLI or programmatically.

Running via CLI

Run markdown --help to see all available CLI options

markdown
# or
md-magic

CLI usage example with options

md-magic --path '**/*.md' --config ./config.file.js

In NPM scripts, npm run docs would run the markdown magic and parse all the .md files in the directory.

"scripts": {
  "docs": "md-magic --path '**/*.md'"
},

If you have a markdown.config.js file where markdown-magic is invoked, it will automatically use that as the configuration unless otherwise specified by --config flag.

Running programmatically

const { markdownMagic } = require('../lib')

/* By default all .md files in cwd will be processed */
markdownMagic().then((results) => {
  console.log('result keys', Object.keys(results))
})
import path from 'path'
import markdownMagic from 'markdown-magic'

// Process a Single File
const markdownPath = path.join(__dirname, 'README.md')
markdownMagic(markdownPath)

Syntax Examples

There are various syntax options. Choose your favorite.

Basic

openWord transformName [opts]

<!-- doc-gen transformName optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Curly braces

openWord {transformName} [opts]

<!-- doc-gen {transformName} optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Square brackets

openWord [transformName] [opts]

<!-- doc-gen [transformName] optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Parentheses

openWord (transformName) [opts]

<!-- doc-gen (transformName) optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Functions

openWord transformName([opts])

<!-- doc-gen transformName(
  foo='bar'
  baz=['qux', 'quux']
) -->
content to be replaced
<!-- end-doc-gen -->

API

Markdown Magic Instance

markdownMagic(globOrOpts, options)

| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | globOrOpts | FilePathsOrGlobs or MarkdownMagicOptions | Files to process or config. | | options (optional) | MarkdownMagicOptions | Markdown magic config. |

Returns

Promise<MarkdownMagicResult>

Example

markdownMagic(['**.**.md'], options).then((result) => {
  console.log(`Processing complete`, result)
})

MarkdownMagicOptions

Configuration for markdown magic

Below is the main config for markdown-magic

| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | files (optional) | FilePathsOrGlobs | Files to process. | | transforms (optional) | Array | Custom commands to transform block contents, see transforms & custom transforms sections below. Default: defaultTransforms | | output (optional) | OutputConfig | Output configuration. | | syntax (optional) | SyntaxType | Syntax to parse. Default: md | | open (optional) | string | Opening match word. Default: doc-gen | | close (optional) | string | Closing match word. If not defined will be same as opening word. Default: end-doc-gen | | cwd (optional) | string | Current working directory. Default process.cwd(). Default: process.cwd() | | outputFlatten (optional) | boolean | Flatten files that are output. | | useGitGlob (optional) | boolean | Use git glob for LARGE file directories. | | dryRun (optional) | boolean | See planned execution of matched blocks. Default: false | | debug (optional) | boolean | See debug details. Default: false | | silent (optional) | boolean | Silence all console output. Default: false | | failOnMissingTransforms (optional) | boolean | Fail if transform functions are missing. Default skip blocks. Default: false |

OutputConfig

Optional output configuration

| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | directory (optional) | string | Change output path of new content. Default behavior is replacing the original file. | | removeComments (optional) | boolean | Remove comments from output. Default is false. Default: false | | pathFormatter (optional) | function | Custom function for altering output paths. | | applyTransformsToSource (optional) | boolean | Apply transforms to source file. Default is true. This is for when outputDir is set. Default: false |

MarkdownMagicResult

Result of markdown processing

| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | errors | Array | Any errors encountered. | | filesChanged | Array<string> | Modified files. | | results | Array | md data. |

Transforms

Markdown Magic comes with a couple of built-in transforms for you to use or you can extend it with your own transforms. See 'Custom Transforms' below.

> TOC

Generate table of contents from markdown file

Options:

  • firsth1 - boolean - (optional): Show first h1 of doc in table of contents. Default false
  • collapse - boolean - (optional): Collapse the table of contents in a detail accordian. Default false
  • collapseText - string - (optional): Text the toc accordian summary
  • excludeText - string - (optional): Text to exclude in the table of contents. Default Table of Contents
  • maxDepth - number - (optional): Max depth of headings. Default 4

Example:

<!-- doc-gen TOC -->
toc will be generated here
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | content | string | The current content of the comment block. | | options | object | The options passed in from the comment declaration. |

> CODE

Get code from file or URL and put in markdown

Options:

  • src: The relative path to the code to pull in, or the URL where the raw code lives
  • syntax (optional): Syntax will be inferred by fileType if not specified
  • header (optional): Will add header comment to code snippet. Useful for pointing to relative source directory or adding live doc links
  • lines (optional): a range with lines of code which will then be replaced with code from the file. The line range should be defined as: "lines=startLine-EndLine" (for example: "lines=22-44"). Please see the example below

Example:

<!-- doc-gen CODE src="./relative/path/to/code.js" -->
This content will be dynamically replaced with code from the file
<!-- end-doc-gen -->
 <!-- doc-gen CODE src="./relative/path/to/code.js" lines=22-44 -->
 This content will be dynamically replaced with code from the file lines 22 through 44
 <!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | content | string | The current content of the comment block. | | options | object | The options passed in from the comment declaration. |

> FILE

Get local file contents.

Options:

  • src: The relative path to the file to pull in

Example:

<!-- doc-gen FILE src=./path/to/file -->
This content will be dynamically replaced from the local file
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | content | string | The current content of the comment block. | | options | object | The options passed in from the comment declaration. |

> REMOTE

Get any remote Data and put in markdown

Options:

  • url: The URL of the remote content to pull in

Example:

<!-- doc-gen REMOTE url=http://url-to-raw-md-file.md -->
This content will be dynamically replaced from the remote url
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


| Name | Type | Description | |:---------------------------|:---------------:|:-----------| | content | string | The current content of the comment block. | | options | object | The options passed in from the comment declaration. |

Inline transforms

Any transform, including custom transforms can be used inline as well to insert content into paragraphs and other places.

The face symbol 👉 ⊂◉‿◉つ is auto generated inline.

Example:

<!-- doc-gen (FILE:src=./path/to/file) -->xyz<!-- end-doc-gen -->

Legacy v1 & v2 plugins

These plugins work with older versions of markdown-magic. Adapting them to the newer plugin syntax should be pretty straight forward.

Adding Custom Transforms

Markdown Magic is extendable via plugins.

Plugins allow developers to add new transforms to the config.transforms object. This allows for things like using different rendering engines, custom formatting, or any other logic you might want.

Plugins run in order of registration.

The below code is used to generate this markdown file via the plugin system.

const path = require('path')
const { readFileSync } = require('fs')
const { parseComments } = require('doxxx')
const { markdownMagic } = require('../lib')
const { deepLog } = require('../lib/utils/logs')

const config = {
  matchWord: 'MD-MAGIC-EXAMPLE', // default matchWord is AUTO-GENERATED-CONTENT
  transforms: {
    /* Match <!-- AUTO-GENERATED-CONTENT:START (customTransform:optionOne=hi&optionOne=DUDE) --> */
    customTransform({ content, options }) {
      console.log('original content in comment block', content)
      console.log('options defined on transform', options)
      // options = { optionOne: hi, optionOne: DUDE}
      return `This will replace all the contents of inside the comment ${options.optionOne}`
    },
    /* Match <!-- AUTO-GENERATED-CONTENT:START JSDocs path="../file.js" --> */
    JSDocs(markdownMagicPluginAPI) {
      const { options } = markdownMagicPluginAPI
      const fileContents = readFileSync(options.path, 'utf8')
      const docBlocs = parseComments(fileContents, { skipSingleStar: true })
        .filter((item) => {
          return !item.isIgnored
        })
        /* Remove empty comments with no tags */
        .filter((item) => {
          return item.tags.length
        })
        /* Remove inline type defs */
        .filter((item) => {
          return item.description.text !== ''
        })
        /* Sort types to end */
        .sort((a, b) => {
          if (a.type && !b.type) return 1
          if (!a.type && b.type) return -1
          return 0
        })

      docBlocs.forEach((data) => {
        // console.log('data', data)
        delete data.code
      })
      // console.log('docBlocs', docBlocs)

      if (docBlocs.length === 0) {
        throw new Error('No docBlocs found')
      }

      // console.log(docBlocs.length)
      let updatedContent = ''
      docBlocs.forEach((data) => {
        if (data.type) {
          updatedContent += `#### \`${data.type}\`\n\n`
        }

        updatedContent += `${data.description.text}\n`

        if (data.tags.length) {
         let table =  '| Name | Type | Description |\n'
          table += '|:---------------------------|:---------------:|:-----------|\n'
          data.tags.filter((tag) => {
            if (tag.tagType === 'param') return true
            if (tag.tagType === 'property') return true
            return false
          }).forEach((tag) => {
            const optionalText = tag.isOptional ? ' (optional) ' : ' '
            const defaultValueText = (typeof tag.defaultValue !== 'undefined') ? ` Default: \`${tag.defaultValue}\` ` : ' '
            table += `| \`${tag.name}\`${optionalText}`
            table += `| \`${tag.type.replace('|', 'or')}\` `
            table += `| ${tag.description.replace(/\.\s?$/, '')}.${defaultValueText}|\n`
          })
          updatedContent+= `\n${table}\n`

          const returnValues = data.tags.filter((tag) => tag.tagType === 'returns')
          if (returnValues.length) {
            returnValues.forEach((returnValue) => {
              updatedContent += `**Returns**\n\n`
              updatedContent += `\`${returnValue.type}\`\n\n`
            })
          }

          const examples = data.tags.filter((tag) => tag.tagType === 'example')
          if (examples.length) {
            examples.forEach((example) => {
              updatedContent += `**Example**\n\n`
              updatedContent += `\`\`\`js\n${example.tagValue}\n\`\`\`\n\n`
            })
          }
        }
      })
      return updatedContent.replace(/^\s+|\s+$/g, '')
    },
    INLINE_EXAMPLE: () => {
      return '**⊂◉‿◉つ**'
    },
    lolz() {
      return `This section was generated by the cli config markdown.config.js file`
    },
    /* Match <!-- AUTO-GENERATED-CONTENT:START (pluginExample) --> */
    pluginExample: require('./plugin-example')({ addNewLine: true }),
    /* Include plugins from NPM */
    // count: require('markdown-magic-wordcount'),
    // github: require('markdown-magic-github-contributors')
  }
}

const markdownPath = path.join(__dirname, '..', 'README.md')
markdownMagic(markdownPath, config, () => {
  console.log('Docs ready')
})

Plugin Example

Plugins must return a transform function with the following signature.

return function myCustomTransform (content, options)
/* Custom Transform Plugin example */
module.exports = function customPlugin(pluginOptions) {
  // set plugin defaults
  const defaultOptions = {
    addNewLine: false
  }
  const userOptions = pluginOptions || {}
  const pluginConfig = Object.assign(defaultOptions, userOptions)
  // return the transform function
  return function myCustomTransform ({ content, options }) {
    const newLine = (pluginConfig.addNewLine) ? '\n' : ''
    const updatedContent = content + newLine
    return updatedContent
  }
}

View the raw file file and run npm run docs to see this plugin run

This content is altered by the pluginExample plugin registered in examples/generate-readme.js

Other usage examples

Custom Transform Demo

View the raw source of this README.md file to see the comment block and see how the customTransform function in examples/generate-readme.js works

This will replace all the contents of inside the comment DUDE

Usage examples

Misc Markdown helpers

  • https://github.com/azu/markdown-function

Prior Art

This was inspired by Kent C Dodds and jfmengels's all contributors cli project.

License

MIT © DavidWells