✨ Add a little magic to your markdown ✨
Markdown magic uses comment blocks in markdown files to automatically sync or transform its contents.
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.
To get started. Install the npm package.
npm install markdown-magic --save-dev
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.
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.
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)
There are various syntax options. Choose your favorite.
openWord transformName [opts]
<!-- doc-gen transformName optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->
openWord {transformName} [opts]
<!-- doc-gen {transformName} optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->
openWord [transformName] [opts]
<!-- doc-gen [transformName] optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->
openWord (transformName) [opts]
<!-- doc-gen (transformName) optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->
openWord transformName([opts])
<!-- doc-gen transformName(
foo='bar'
baz=['qux', 'quux']
) -->
content to be replaced
<!-- end-doc-gen -->
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. |
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.
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 summaryexcludeText
- string - (optional): Text to exclude in the table of contents. Default Table of Contents
maxDepth
- number - (optional): Max depth of headings. Default 4Example:
<!-- 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. |
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 livessyntax
(optional): Syntax will be inferred by fileType if not specifiedheader
(optional): Will add header comment to code snippet. Useful for pointing to relative source directory or adding live doc linkslines
(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 belowExample:
<!-- doc-gen CODE src="https://github.com/DavidWells/markdown-magic/raw/master/relative/path/to/code.js" -->
This content will be dynamically replaced with code from the file
<!-- end-doc-gen -->
<!-- doc-gen CODE src="https://github.com/DavidWells/markdown-magic/raw/master/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. |
Get local file contents.
Options:
src
: The relative path to the file to pull inExample:
<!-- 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. |
Get any remote Data and put in markdown
Options:
url
: The URL of the remote content to pull inExample:
<!-- 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. |
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 -->
These plugins work with older versions of markdown-magic. Adapting them to the newer plugin syntax should be pretty straight forward.
peerDependencies
includedpackage.json
scripts with descriptionsprettier
package.json
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')
})
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
json
file and converts it into a github flavored markdown tableView 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
This was inspired by Kent C Dodds and jfmengels's all contributors cli project.