Merge pull request #69 from fardog/master
Adds Command Line Interface for generating PNGs from mermaid description files
This commit is contained in:
commit
a83639addd
30
README.md
30
README.md
|
@ -52,6 +52,36 @@ graph LR
|
||||||
![Example 2](http://www.sveido.com/mermaid/img/ex2.png)
|
![Example 2](http://www.sveido.com/mermaid/img/ex2.png)
|
||||||
|
|
||||||
|
|
||||||
|
# mermaid CLI
|
||||||
|
|
||||||
|
Installing mermaid globally (`npm install -g mermaid`) will expose the `mermaid` command to your environment, allowing you to generate PNGs from any file containing mermaid markup via the command line.
|
||||||
|
|
||||||
|
**Note:** The `mermaid` command requires [PhantomJS](http://phantomjs.org/) (version `^1.9.0`) to be installed and available in your *$PATH*, or you can specify it's location with the `-e` option. For most environments, `npm install -g phantomjs` will satisfy this requirement.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mermaid --help
|
||||||
|
|
||||||
|
Usage: mermaid [options] <file>...
|
||||||
|
|
||||||
|
file The mermaid description file to be rendered
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-s --svg Output SVG instead of PNG (experimental)
|
||||||
|
-p --png If SVG was selected, and you also want PNG, set this flag
|
||||||
|
-o --outputDir Directory to save files, will be created automatically, defaults to `cwd`
|
||||||
|
-e --phantomPath Specify the path to the phantomjs executable
|
||||||
|
-h --help Show this message
|
||||||
|
-v --verbose Show logging
|
||||||
|
--version Print version and quit
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Known Issues
|
||||||
|
|
||||||
|
- SVG output currently does some replacement on text, as mermaid's SVG output is only appropriate for browsers. Text color and background color is not yet replicated; please use PNGs for most purposes until this is resolved.
|
||||||
|
- SVG output is decidedly non-standard. It works, but may cause issues in some viewers.
|
||||||
|
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries! Thanks also to the [js-sequence-diagram](http://bramp.github.io/js-sequence-diagrams) project for usage of the grammar for the sequence diagrams.
|
Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries! Thanks also to the [js-sequence-diagram](http://bramp.github.io/js-sequence-diagrams) project for usage of the grammar for the sequence diagrams.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var fs = require('fs')
|
||||||
|
, chalk = require('chalk')
|
||||||
|
, error = chalk.bold.red
|
||||||
|
, cli = require('../lib/cli.js')
|
||||||
|
, lib = require('../lib')
|
||||||
|
|
||||||
|
cli.parse(process.argv.slice(2), function(err, message, options) {
|
||||||
|
if (err) {
|
||||||
|
console.error(
|
||||||
|
error('\nYou had errors in your syntax. Use --help for further information.')
|
||||||
|
)
|
||||||
|
err.forEach(function (e) {
|
||||||
|
console.error(e.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if (message) {
|
||||||
|
console.log(message)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.process(options.files, options)
|
||||||
|
})
|
|
@ -45,6 +45,8 @@ gulp.task('jasmine',['jison','lint'], function () {
|
||||||
.pipe(jasmine({includeStackTrace:true}));
|
.pipe(jasmine({includeStackTrace:true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task('tape', shell.task(['./node_modules/.bin/tape ./test/cli_test-*.js']));
|
||||||
|
|
||||||
gulp.task('coverage', function (cb) {
|
gulp.task('coverage', function (cb) {
|
||||||
gulp.src(['src/**/*.js', '!src/**/*.spec.js'])
|
gulp.src(['src/**/*.js', '!src/**/*.spec.js'])
|
||||||
.pipe(istanbul()) // Covering files
|
.pipe(istanbul()) // Covering files
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
var fs = require('fs')
|
||||||
|
, exec = require('child_process').exec
|
||||||
|
, chalk = require('chalk')
|
||||||
|
, which = require('which')
|
||||||
|
, parseArgs = require('minimist')
|
||||||
|
, semver = require('semver')
|
||||||
|
|
||||||
|
var PHANTOM_VERSION = "^1.9.0"
|
||||||
|
|
||||||
|
var info = chalk.blue.bold
|
||||||
|
, note = chalk.green.bold
|
||||||
|
|
||||||
|
var cli = function(options) {
|
||||||
|
this.options = {
|
||||||
|
alias: {
|
||||||
|
help: 'h'
|
||||||
|
, png: 'p'
|
||||||
|
, outputDir: 'o'
|
||||||
|
, svg: 's'
|
||||||
|
, verbose: 'v'
|
||||||
|
, phantomPath: 'e'
|
||||||
|
}
|
||||||
|
, 'boolean': ['help', 'png', 'svg']
|
||||||
|
, 'string': ['outputDir']
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errors = []
|
||||||
|
this.message = null
|
||||||
|
|
||||||
|
this.helpMessage = [
|
||||||
|
, info('Usage: mermaid [options] <file>...')
|
||||||
|
, ""
|
||||||
|
, "file The mermaid description file to be rendered"
|
||||||
|
, ""
|
||||||
|
, "Options:"
|
||||||
|
, " -s --svg Output SVG instead of PNG (experimental)"
|
||||||
|
, " -p --png If SVG was selected, and you also want PNG, set this flag"
|
||||||
|
, " -o --outputDir Directory to save files, will be created automatically, defaults to `cwd`"
|
||||||
|
, " -e --phantomPath Specify the path to the phantomjs executable"
|
||||||
|
, " -h --help Show this message"
|
||||||
|
, " -v --verbose Show logging"
|
||||||
|
, " --version Print version and quit"
|
||||||
|
]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.prototype.parse = function(argv, next) {
|
||||||
|
var options = parseArgs(argv, this.options)
|
||||||
|
, phantom
|
||||||
|
|
||||||
|
if (options.version) {
|
||||||
|
var pkg = require('../package.json')
|
||||||
|
this.message = "" + pkg.version
|
||||||
|
}
|
||||||
|
else if (options.help) {
|
||||||
|
this.message = this.helpMessage.join('\n')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.files = options._
|
||||||
|
|
||||||
|
if (!options.files.length) {
|
||||||
|
this.errors.push(new Error("You must specify at least one source file."))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that parameter-expecting options have parameters
|
||||||
|
;['outputDir', 'phantomPath'].forEach(function(i) {
|
||||||
|
if(typeof options[i] !== 'undefined') {
|
||||||
|
if (typeof options[i] !== 'string' || options[i].length < 1) {
|
||||||
|
this.errors.push(new Error(i + " expects a value."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.svg && !options.png) {
|
||||||
|
options.png = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.png = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If phantom hasn't been specified, see if we can find it
|
||||||
|
if (!options.phantomPath) {
|
||||||
|
try {
|
||||||
|
var phantom = require('phantomjs')
|
||||||
|
options.phantomPath = phantom.path
|
||||||
|
} catch (e) {
|
||||||
|
try {
|
||||||
|
options.phantomPath = which.sync('phantomjs')
|
||||||
|
} catch (e) {
|
||||||
|
if (!options.phantomPath) {
|
||||||
|
var err = [
|
||||||
|
"Cannot find phantomjs in your PATH. If phantomjs is installed"
|
||||||
|
, "you may need to specify its path manually with the '-e' option."
|
||||||
|
, "If it is not installed, you should view the README for further"
|
||||||
|
, "details."
|
||||||
|
]
|
||||||
|
|
||||||
|
this.errors.push(new Error(err.join('\n')))
|
||||||
|
next(
|
||||||
|
this.errors.length > 0 ? this.errors : null
|
||||||
|
, this.message
|
||||||
|
, options)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have phantompath, see if its version satisfies our requirements
|
||||||
|
exec(options.phantomPath + ' --version', function(err, stdout, stderr) {
|
||||||
|
if (err) {
|
||||||
|
this.errors.push(
|
||||||
|
new Error("Could not find phantomjs at the specified path.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (!semver.satisfies(stdout, PHANTOM_VERSION)) {
|
||||||
|
this.message = note(
|
||||||
|
'mermaid requires phantomjs '
|
||||||
|
+ PHANTOM_VERSION
|
||||||
|
+ ' to be installed, found version '
|
||||||
|
+ stdout
|
||||||
|
)
|
||||||
|
}
|
||||||
|
next(this.errors.length > 0 ? this.errors : null, this.message, options)
|
||||||
|
}.bind(this))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
return new cli()
|
||||||
|
}()
|
|
@ -0,0 +1,40 @@
|
||||||
|
var os = require('os')
|
||||||
|
, fs = require('fs')
|
||||||
|
, path = require('path')
|
||||||
|
, spawn = require('child_process').spawn
|
||||||
|
|
||||||
|
var mkdirp = require('mkdirp')
|
||||||
|
|
||||||
|
var phantomscript = path.join(__dirname, 'phantomscript.js')
|
||||||
|
|
||||||
|
module.exports = { process: processMermaid }
|
||||||
|
|
||||||
|
function processMermaid(files, _options, _next) {
|
||||||
|
var options = _options || {}
|
||||||
|
, outputDir = options.outputDir || process.cwd()
|
||||||
|
, next = _next || function() {}
|
||||||
|
, phantomArgs = [
|
||||||
|
phantomscript
|
||||||
|
, outputDir
|
||||||
|
, options.png
|
||||||
|
, options.svg
|
||||||
|
, options.verbose
|
||||||
|
]
|
||||||
|
|
||||||
|
files.forEach(function(file) {
|
||||||
|
phantomArgs.push(file)
|
||||||
|
})
|
||||||
|
|
||||||
|
mkdirp(outputDir, function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
phantom = spawn(options.phantomPath, phantomArgs)
|
||||||
|
|
||||||
|
phantom.on('exit', next)
|
||||||
|
|
||||||
|
phantom.stderr.pipe(process.stderr)
|
||||||
|
phantom.stdout.pipe(process.stdout)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
/**
|
||||||
|
* Credits:
|
||||||
|
* - SVG Processing from the NYTimes svg-crowbar, under an MIT license
|
||||||
|
* https://github.com/NYTimes/svg-crowbar
|
||||||
|
* - Thanks to the grunticon project for some guidance
|
||||||
|
* https://github.com/filamentgroup/grunticon
|
||||||
|
*/
|
||||||
|
|
||||||
|
phantom.onError = function(msg, trace) {
|
||||||
|
var msgStack = ['PHANTOM ERROR: ' + msg]
|
||||||
|
if (trace && trace.length) {
|
||||||
|
msgStack.push('TRACE:')
|
||||||
|
trace.forEach(function(t) {
|
||||||
|
msgStack.push(
|
||||||
|
' -> '
|
||||||
|
+ (t.file || t.sourceURL)
|
||||||
|
+ ': '
|
||||||
|
+ t.line
|
||||||
|
+ (t.function ? ' (in function ' + t.function +')' : '')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
system.stderr.write(msgStack.join('\n'))
|
||||||
|
phantom.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var system = require('system')
|
||||||
|
, fs = require('fs')
|
||||||
|
, webpage = require('webpage')
|
||||||
|
|
||||||
|
var page = webpage.create()
|
||||||
|
, files = phantom.args.slice(4, phantom.args.length)
|
||||||
|
, options = {
|
||||||
|
outputDir: phantom.args[0]
|
||||||
|
, png: phantom.args[1] === 'true' ? true : false
|
||||||
|
, svg: phantom.args[2] === 'true' ? true : false
|
||||||
|
, verbose: phantom.args[3] === 'true' ? true : false
|
||||||
|
}
|
||||||
|
, log = logger(options.verbose)
|
||||||
|
|
||||||
|
page.content = [
|
||||||
|
'<html>'
|
||||||
|
, '<head>'
|
||||||
|
, '<style type="text/css">'
|
||||||
|
, '* { margin: 0; padding: 0; }'
|
||||||
|
, '</style>'
|
||||||
|
, '</head>'
|
||||||
|
, '<body>'
|
||||||
|
, '</body>'
|
||||||
|
, '</html>'
|
||||||
|
].join('\n')
|
||||||
|
|
||||||
|
page.injectJs('../dist/mermaid.full.js')
|
||||||
|
|
||||||
|
files.forEach(function(file) {
|
||||||
|
var contents = fs.read(file)
|
||||||
|
, filename = file.split(fs.separator).slice(-1)
|
||||||
|
, oParser = new DOMParser()
|
||||||
|
, oDOM
|
||||||
|
, svgContent
|
||||||
|
, allElements
|
||||||
|
|
||||||
|
// this JS is executed in this statement is sandboxed, even though it doesn't
|
||||||
|
// look like it. we need to serialize then unserialize the svgContent that's
|
||||||
|
// taken from the DOM
|
||||||
|
svgContent = page.evaluate(executeInPage, contents)
|
||||||
|
oDOM = oParser.parseFromString(svgContent, "text/xml")
|
||||||
|
|
||||||
|
resolveSVGElement(oDOM.firstChild)
|
||||||
|
|
||||||
|
// traverse the SVG, and replace all foreignObject elements
|
||||||
|
// can be removed when https://github.com/knsv/mermaid/issues/58 is resolved
|
||||||
|
allElements = traverse(oDOM)
|
||||||
|
for (var i = 0, len = allElements.length; i < len; i++) {
|
||||||
|
resolveForeignObjects(allElements[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.png) {
|
||||||
|
page.viewportSize = {
|
||||||
|
width: ~~oDOM.documentElement.attributes.getNamedItem('width').value
|
||||||
|
, height: ~~oDOM.documentElement.attributes.getNamedItem('height').value
|
||||||
|
}
|
||||||
|
|
||||||
|
page.render(options.outputDir + fs.separator + filename + '.png')
|
||||||
|
log('saved png: ' + filename + '.png')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.svg) {
|
||||||
|
var serialize = new XMLSerializer()
|
||||||
|
fs.write(
|
||||||
|
options.outputDir + fs.separator + filename + '.svg'
|
||||||
|
, serialize.serializeToString(oDOM)
|
||||||
|
, 'w'
|
||||||
|
)
|
||||||
|
log('saved svg: ' + filename + '.svg')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
phantom.exit()
|
||||||
|
|
||||||
|
function logger(_verbose) {
|
||||||
|
var verbose = _verbose
|
||||||
|
|
||||||
|
return function(_message, _level) {
|
||||||
|
var level = level
|
||||||
|
, message = _message
|
||||||
|
, log
|
||||||
|
|
||||||
|
log = level === 'error' ? system.stderr : system.stdout
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
log.write(message + '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverse(obj){
|
||||||
|
var tree = []
|
||||||
|
|
||||||
|
tree.push(obj)
|
||||||
|
visit(obj)
|
||||||
|
|
||||||
|
function visit(node) {
|
||||||
|
if (node && node.hasChildNodes()) {
|
||||||
|
var child = node.firstChild
|
||||||
|
while (child) {
|
||||||
|
if (child.nodeType === 1 && child.nodeName != 'SCRIPT'){
|
||||||
|
tree.push(child)
|
||||||
|
visit(child)
|
||||||
|
}
|
||||||
|
child = child.nextSibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSVGElement(element) {
|
||||||
|
var prefix = {
|
||||||
|
xmlns: "http://www.w3.org/2000/xmlns/"
|
||||||
|
, xlink: "http://www.w3.org/1999/xlink"
|
||||||
|
, svg: "http://www.w3.org/2000/svg"
|
||||||
|
}
|
||||||
|
, doctype = '<!DOCTYPE svg:svg PUBLIC'
|
||||||
|
+ ' "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"'
|
||||||
|
+ ' "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">'
|
||||||
|
|
||||||
|
element.setAttribute("version", "1.1")
|
||||||
|
// removing attributes so they aren't doubled up
|
||||||
|
element.removeAttribute("xmlns")
|
||||||
|
element.removeAttribute("xlink")
|
||||||
|
// These are needed for the svg
|
||||||
|
if (!element.hasAttributeNS(prefix.xmlns, "xmlns")) {
|
||||||
|
element.setAttributeNS(prefix.xmlns, "xmlns", prefix.svg)
|
||||||
|
}
|
||||||
|
if (!element.hasAttributeNS(prefix.xmlns, "xmlns:xlink")) {
|
||||||
|
element.setAttributeNS(prefix.xmlns, "xmlns:xlink", prefix.xlink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveForeignObjects(element) {
|
||||||
|
var children
|
||||||
|
, textElement
|
||||||
|
, textSpan
|
||||||
|
|
||||||
|
if (element.tagName === 'foreignObject') {
|
||||||
|
textElement = document.createElement('text')
|
||||||
|
textSpan = document.createElement('tspan')
|
||||||
|
textSpan.setAttribute(
|
||||||
|
'style'
|
||||||
|
, 'font-size: 11.5pt; font-family: "sans-serif";'
|
||||||
|
)
|
||||||
|
textSpan.setAttribute('x', 0)
|
||||||
|
textSpan.setAttribute('y', 14.5)
|
||||||
|
textSpan.textContent = element.textContent
|
||||||
|
|
||||||
|
textElement.appendChild(textSpan)
|
||||||
|
element.parentElement.appendChild(textElement)
|
||||||
|
element.parentElement.removeChild(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sandboxed function that's executed in-page by phantom
|
||||||
|
function executeInPage(contents) {
|
||||||
|
var xmlSerializer = new XMLSerializer()
|
||||||
|
, toRemove
|
||||||
|
, el
|
||||||
|
, elContent
|
||||||
|
, svg
|
||||||
|
, svgValue
|
||||||
|
|
||||||
|
toRemove = document.getElementsByClassName('mermaid')
|
||||||
|
if (toRemove && toRemove.length) {
|
||||||
|
for (var i = 0, len = toRemove.length; i < len; i++) {
|
||||||
|
toRemove[i].parentNode.removeChild(toRemove[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
el = document.createElement("div")
|
||||||
|
el.className = 'mermaid'
|
||||||
|
elContent = document.createTextNode(contents)
|
||||||
|
el.appendChild(elContent)
|
||||||
|
|
||||||
|
document.body.appendChild(el)
|
||||||
|
|
||||||
|
mermaid.init()
|
||||||
|
|
||||||
|
svg = document.querySelector('svg')
|
||||||
|
svgValue = xmlSerializer.serializeToString(svg)
|
||||||
|
|
||||||
|
return svgValue
|
||||||
|
}
|
16
package.json
16
package.json
|
@ -3,6 +3,9 @@
|
||||||
"version": "0.2.16",
|
"version": "0.2.16",
|
||||||
"description": "Markdownish syntax for generating flowcharts",
|
"description": "Markdownish syntax for generating flowcharts",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
|
"bin": {
|
||||||
|
"mermaid": "./bin/mermaid.js"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "gulp coverage"
|
"test": "gulp coverage"
|
||||||
},
|
},
|
||||||
|
@ -13,11 +16,18 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^0.5.1",
|
||||||
|
"dagre-d3": "~0.3.2",
|
||||||
"he": "^0.5.0",
|
"he": "^0.5.0",
|
||||||
"dagre-d3": "~0.3.2"
|
"minimist": "^1.1.0",
|
||||||
|
"mkdirp": "^0.5.0",
|
||||||
|
"semver": "^4.1.1",
|
||||||
|
"which": "^1.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"async": "^0.9.0",
|
||||||
"browserify": "~6.2.0",
|
"browserify": "~6.2.0",
|
||||||
|
"clone": "^0.2.0",
|
||||||
"codeclimate-test-reporter": "0.0.4",
|
"codeclimate-test-reporter": "0.0.4",
|
||||||
"d3": "~3.4.13",
|
"d3": "~3.4.13",
|
||||||
"dagre-d3": "~0.3.2",
|
"dagre-d3": "~0.3.2",
|
||||||
|
@ -54,6 +64,8 @@
|
||||||
"mock-browser": "^0.90.27",
|
"mock-browser": "^0.90.27",
|
||||||
"path": "^0.4.9",
|
"path": "^0.4.9",
|
||||||
"phantomjs": "^1.9.12",
|
"phantomjs": "^1.9.12",
|
||||||
"rewire": "^2.1.3"
|
"rewire": "^2.1.3",
|
||||||
|
"rimraf": "^2.2.8",
|
||||||
|
"tape": "^3.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
var fs = require('fs')
|
||||||
|
, path = require('path')
|
||||||
|
|
||||||
|
var test = require('tape')
|
||||||
|
, async = require('async')
|
||||||
|
, clone = require('clone')
|
||||||
|
, rimraf = require('rimraf')
|
||||||
|
|
||||||
|
var mermaid = require('../lib')
|
||||||
|
|
||||||
|
var singleFile = {
|
||||||
|
files: ['test/fixtures/test.mermaid']
|
||||||
|
, outputDir: 'test/tmp/'
|
||||||
|
, phantomPath: './node_modules/.bin/phantomjs'
|
||||||
|
}
|
||||||
|
, multiFile = {
|
||||||
|
files: ['test/fixtures/test.mermaid', 'test/fixtures/test2.mermaid']
|
||||||
|
, outputDir: 'test/tmp/'
|
||||||
|
, phantomPath: './node_modules/.bin/phantomjs'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test('output of single png', function(t) {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
var expected = ['test.mermaid.png']
|
||||||
|
|
||||||
|
opt = clone(singleFile)
|
||||||
|
opt.png = true
|
||||||
|
|
||||||
|
mermaid.process(opt.files, opt, function(code) {
|
||||||
|
t.equal(code, 0, 'has clean exit code')
|
||||||
|
|
||||||
|
verifyFiles(expected, opt.outputDir, t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('output of multiple png', function(t) {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
var expected = ['test.mermaid.png', 'test2.mermaid.png']
|
||||||
|
|
||||||
|
opt = clone(multiFile)
|
||||||
|
opt.png = true
|
||||||
|
|
||||||
|
mermaid.process(opt.files, opt, function(code) {
|
||||||
|
t.equal(code, 0, 'has clean exit code')
|
||||||
|
|
||||||
|
verifyFiles(expected, opt.outputDir, t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('output of single svg', function(t) {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
var expected = ['test.mermaid.svg']
|
||||||
|
|
||||||
|
opt = clone(singleFile)
|
||||||
|
opt.svg = true
|
||||||
|
|
||||||
|
mermaid.process(opt.files, opt, function(code) {
|
||||||
|
t.equal(code, 0, 'has clean exit code')
|
||||||
|
|
||||||
|
verifyFiles(expected, opt.outputDir, t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('output of multiple svg', function(t) {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
var expected = ['test.mermaid.svg', 'test2.mermaid.svg']
|
||||||
|
|
||||||
|
opt = clone(multiFile)
|
||||||
|
opt.svg = true
|
||||||
|
|
||||||
|
mermaid.process(opt.files, opt, function(code) {
|
||||||
|
t.equal(code, 0, 'has clean exit code')
|
||||||
|
|
||||||
|
verifyFiles(expected, opt.outputDir, t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function verifyFiles(expected, dir, t) {
|
||||||
|
async.each(
|
||||||
|
expected
|
||||||
|
, function(file, cb) {
|
||||||
|
filename = path.join(dir, path.basename(file))
|
||||||
|
fs.stat(filename, function(err, stat) {
|
||||||
|
cb(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
, function(err) {
|
||||||
|
t.notOk(err, 'all files passed')
|
||||||
|
|
||||||
|
rimraf(dir, function(rmerr) {
|
||||||
|
t.notOk(rmerr, 'cleaned up')
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
var test = require('tape')
|
||||||
|
, cliPath = '../lib/cli'
|
||||||
|
|
||||||
|
test('parses multiple files', function(t) {
|
||||||
|
t.plan(2)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['example/file1.mermaid', 'file2.mermaid', 'file3.mermaid']
|
||||||
|
, expect = ['example/file1.mermaid', 'file2.mermaid', 'file3.mermaid']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opt) {
|
||||||
|
t.equal(opt.files.length, 3, 'should have 3 parameters')
|
||||||
|
t.deepEqual(opt.files, expect, 'should match expected values')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('defaults to png', function(t) {
|
||||||
|
t.plan(2)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['example/file1.mermaid']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opt) {
|
||||||
|
t.ok(opt.png, 'png is set by default')
|
||||||
|
t.notOk(opt.svg, 'svg is not set by default')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('setting svg unsets png', function(t) {
|
||||||
|
t.plan(2)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['example/file1.mermaid', '-s']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opt) {
|
||||||
|
|
||||||
|
t.ok(opt.svg, 'svg is set when requested')
|
||||||
|
t.notOk(opt.png, 'png is unset when svg is set')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('setting png and svg is allowed', function(t) {
|
||||||
|
t.plan(2)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['example/file1.mermaid', '-s', '-p']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opt) {
|
||||||
|
t.ok(opt.png, 'png is set when requested')
|
||||||
|
t.ok(opt.svg, 'svg is set when requested')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('setting an output directory succeeds', function(t) {
|
||||||
|
t.plan(1)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['-o', 'example/']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opt) {
|
||||||
|
t.equal(opt.outputDir, 'example/', 'output directory is set')
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('setting an output directory incorrectly causes an error', function(t) {
|
||||||
|
t.plan(1)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['-o']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err) {
|
||||||
|
t.ok(err, 'an error is raised')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('a callback function is called after parsing', function(t) {
|
||||||
|
t.plan(2)
|
||||||
|
|
||||||
|
var cli = require(cliPath)
|
||||||
|
, argv = ['example/test.mermaid']
|
||||||
|
, expects = ['example/test.mermaid']
|
||||||
|
|
||||||
|
cli.parse(argv, function(err, msg, opts) {
|
||||||
|
t.ok(true, 'callback was called')
|
||||||
|
t.deepEqual(argv, opts.files, 'options are as expected')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,8 @@
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
Note right of Bob: Bob thinks
|
||||||
|
Bob-->Alice: I am good thanks!
|
||||||
|
Bob-->John the Long: How about you John?
|
||||||
|
Bob-->Alice: Checking with John...
|
||||||
|
Alice->John the Long: Yes... John, how are you?
|
||||||
|
John the Long-->Alice: Better than you!
|
|
@ -0,0 +1,5 @@
|
||||||
|
graph TD;
|
||||||
|
A-->B;
|
||||||
|
A-->C;
|
||||||
|
B-->D;
|
||||||
|
C-->D;
|
|
@ -0,0 +1,7 @@
|
||||||
|
graph LR;
|
||||||
|
A[Hard edge]-->|Link text|B(Round edge);
|
||||||
|
B-->C{Decision};
|
||||||
|
C-->|One|D[Result one];
|
||||||
|
C-->|Two|E[Result two];
|
||||||
|
classDef pink fill:#f9f,stroke:#333,stroke-width:4px;
|
||||||
|
class C pink;
|
Loading…
Reference in New Issue