mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
feat(CLI): add a simple cli tool
This commit is contained in:
parent
ba7eb7ebaf
commit
f6a33e402c
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,4 +3,4 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
npm-debug.log
|
||||
localtest.html
|
||||
/*.test.*
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
Gruntfile.js
|
||||
dist/**/*.js
|
||||
build/**/*.js
|
||||
src/options.js
|
||||
bin/*
|
||||
|
|
|
@ -20,6 +20,7 @@ module.exports = function (grunt) {
|
|||
},
|
||||
dist: {
|
||||
src: [
|
||||
'src/options.js',
|
||||
'src/showdown.js',
|
||||
'src/helpers.js',
|
||||
'src/converter.js',
|
||||
|
|
BIN
bin/showdown.js
Normal file
BIN
bin/showdown.js
Normal file
Binary file not shown.
BIN
dist/showdown.js
vendored
BIN
dist/showdown.js
vendored
Binary file not shown.
BIN
dist/showdown.js.map
vendored
BIN
dist/showdown.js.map
vendored
Binary file not shown.
BIN
dist/showdown.min.js
vendored
BIN
dist/showdown.min.js
vendored
Binary file not shown.
BIN
dist/showdown.min.js.map
vendored
BIN
dist/showdown.min.js.map
vendored
Binary file not shown.
|
@ -36,7 +36,7 @@
|
|||
"test": "grunt test"
|
||||
},
|
||||
"bin": {
|
||||
"showdown": "src/cli.js"
|
||||
"showdown": "bin/showdown.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^1.10.0",
|
||||
|
@ -53,5 +53,8 @@
|
|||
"quiet-grunt": "^0.2.3",
|
||||
"sinon": "^1.14.1",
|
||||
"source-map-support": "^0.2.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"yargs": "^3.15.0"
|
||||
}
|
||||
}
|
||||
|
|
23
src/cli.js
23
src/cli.js
|
@ -1,23 +0,0 @@
|
|||
var Showdown = require('./showdown');
|
||||
var cli = require('cli');
|
||||
var fs = require('fs');
|
||||
|
||||
var converter = new Showdown.converter();
|
||||
|
||||
/*
|
||||
If an argument is given, treat it as the file to be read.
|
||||
Otherwise, read from stdin.
|
||||
*/
|
||||
if(process.argv.length > 2) {
|
||||
fs.readFile(process.argv[2], function(err, data) {
|
||||
if(err) {
|
||||
console.error("Error: Invalid file");
|
||||
} else {
|
||||
console.log(converter.makeHtml(data.toString()));
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cli.withStdin(function(data) {
|
||||
this.output(converter.makeHtml(data.toString()));
|
||||
})
|
||||
}
|
30
src/cli/cli.js
Normal file
30
src/cli/cli.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
var version = require('../../package.json').version,
|
||||
yargs = require('yargs');
|
||||
|
||||
yargs
|
||||
.version(version, 'v')
|
||||
.alias('v', 'version')
|
||||
.option('h', {
|
||||
alias: 'help',
|
||||
description: 'Show help'
|
||||
})
|
||||
.usage('Usage: showdown <command> [options]')
|
||||
.demand(1, 'You must provide a valid command')
|
||||
.command('makehtml', 'Converts markdown into html')
|
||||
.example('showdown makehtml -i foo.md -o bar.html', 'Converts \'foo.md\' to \'bar.html\'')
|
||||
.wrap(yargs.terminalWidth());
|
||||
|
||||
var argv = yargs.argv,
|
||||
command = argv._[0];
|
||||
if (command === 'makehtml') {
|
||||
require('./makehtml.cmd.js').run();
|
||||
} else {
|
||||
yargs.showHelp();
|
||||
}
|
||||
|
||||
if (argv.help) {
|
||||
yargs.showHelp();
|
||||
}
|
||||
process.exit(0);
|
6
src/cli/errorexit.js
Normal file
6
src/cli/errorexit.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = exports = function errorExit(e) {
|
||||
'use strict';
|
||||
console.error('ERROR: ' + e.message);
|
||||
console.error('Run \'showdown <command> -h\' for help');
|
||||
process.exit(1);
|
||||
};
|
123
src/cli/makehtml.cmd.js
Normal file
123
src/cli/makehtml.cmd.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
var yargs = require('yargs'),
|
||||
fs = require('fs'),
|
||||
errorExit = require('./errorexit.js'),
|
||||
showdown = require('../../dist/showdown');
|
||||
|
||||
yargs.reset()
|
||||
.usage('Usage: showdown makehtml [options]')
|
||||
.example('showdown makehtml -i', 'Reads from stdin and outputs to stdout')
|
||||
.example('showdown makehtml -i foo.md -o bar.html', 'Reads \'foo.md\' and writes to \'bar.html\'')
|
||||
//.demand(['i'])
|
||||
.option('i', {
|
||||
alias : 'input',
|
||||
describe: 'Input source. Usually a md file. If omitted or empty, reads from stdin',
|
||||
type: 'string'
|
||||
})
|
||||
.option('o', {
|
||||
alias : 'output',
|
||||
describe: 'Output target. Usually a html file. If omitted or empty, writes to stdout',
|
||||
type: 'string',
|
||||
default: false
|
||||
})
|
||||
.option('u', {
|
||||
alias : 'encoding',
|
||||
describe: 'Input encoding',
|
||||
type: 'string'
|
||||
})
|
||||
.option('a', {
|
||||
alias : 'append',
|
||||
describe: 'Append data to output instead of overwriting',
|
||||
type: 'string'
|
||||
})
|
||||
.option('e', {
|
||||
alias : 'extensions',
|
||||
describe: 'Load the specified extensions. Should be valid paths to node compatible extensions',
|
||||
type: 'array'
|
||||
})
|
||||
.config('c')
|
||||
.alias('c', 'config')
|
||||
.help('h')
|
||||
.alias('h', 'help');
|
||||
|
||||
yargs.options(showdown.getDefaultOptions(false));
|
||||
argv = yargs.argv;
|
||||
|
||||
function run() {
|
||||
'use strict';
|
||||
var input = '',
|
||||
enc = 'utf8',
|
||||
output;
|
||||
|
||||
if (argv.encoding) {
|
||||
enc = argv.encoding;
|
||||
}
|
||||
|
||||
// to avoid passing extensions to converter
|
||||
delete argv.extensions;
|
||||
var converter = new showdown.Converter(argv);
|
||||
|
||||
// Load extensions
|
||||
if (argv.e) {
|
||||
for (var i = 0; i < argv.e.length; ++i) {
|
||||
loadExtension(argv.e[i], converter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!argv.i || argv.i === '') {
|
||||
// 'i' is undefined or empty, read from stdin
|
||||
try {
|
||||
var size = fs.fstatSync(process.stdin.fd).size;
|
||||
input = size > 0 ? fs.readSync(process.stdin.fd, size)[0] : '';
|
||||
} catch (e) {
|
||||
var err = new Error('Could not read from stdin, reason: ' + e.message);
|
||||
errorExit(err);
|
||||
}
|
||||
} else {
|
||||
// 'i' has a value, read from file
|
||||
try {
|
||||
input = fs.readFileSync(argv.i, enc);
|
||||
} catch (err) {
|
||||
errorExit(err);
|
||||
}
|
||||
}
|
||||
|
||||
// parse and convert file
|
||||
output = converter.makeHtml(input);
|
||||
|
||||
// Write output
|
||||
if (!argv.o || argv.o === '') {
|
||||
// o is undefined or empty, write to stdout
|
||||
process.stdout.write(output);
|
||||
// we won't print anything since it would conspurcate stdout and,
|
||||
// consequently, the outputted file
|
||||
} else {
|
||||
// o is has a value, presumably a file, write to it.
|
||||
|
||||
// If a flag is passed, it means we should append instead of overwriting.
|
||||
// Only works with files, obviously
|
||||
var write = (argv.a) ? fs.appendFileSync : fs.writeFileSync;
|
||||
|
||||
try {
|
||||
write(argv.o, output);
|
||||
} catch (err) {
|
||||
errorExit(err);
|
||||
}
|
||||
console.error('DONE!');
|
||||
}
|
||||
}
|
||||
|
||||
function loadExtension(path, converter) {
|
||||
'use strict';
|
||||
var ext;
|
||||
try {
|
||||
ext = require(path);
|
||||
converter.addExtension(ext, path);
|
||||
} catch (e) {
|
||||
console.error('Could not load extension ' + path + '. Reason:');
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = {
|
||||
run: run
|
||||
};
|
|
@ -86,13 +86,16 @@ showdown.Converter = function (converterOptions) {
|
|||
/**
|
||||
* Parse extension
|
||||
* @param {*} ext
|
||||
* @param {string} [name='']
|
||||
* @private
|
||||
*/
|
||||
function _parseExtension(ext) {
|
||||
function _parseExtension(ext, name) {
|
||||
|
||||
name = name || null;
|
||||
// If it's a string, the extension was previously loaded
|
||||
if (showdown.helper.isString(ext)) {
|
||||
ext = showdown.helper.stdExtName(ext);
|
||||
name = ext;
|
||||
|
||||
// LEGACY_SUPPORT CODE
|
||||
if (showdown.extensions[ext]) {
|
||||
|
@ -118,8 +121,9 @@ showdown.Converter = function (converterOptions) {
|
|||
ext = [ext];
|
||||
}
|
||||
|
||||
if (!showdown.validateExtension(ext)) {
|
||||
return;
|
||||
var validExt = validate(ext, name);
|
||||
if (!validExt.valid) {
|
||||
throw Error(validExt.error);
|
||||
}
|
||||
|
||||
for (var i = 0; i < ext.length; ++i) {
|
||||
|
@ -272,9 +276,11 @@ showdown.Converter = function (converterOptions) {
|
|||
/**
|
||||
* Add extension to THIS converter
|
||||
* @param {{}} extension
|
||||
* @param {string} [name=null]
|
||||
*/
|
||||
this.addExtension = function (extension) {
|
||||
_parseExtension(extension);
|
||||
this.addExtension = function (extension, name) {
|
||||
name = name || null;
|
||||
_parseExtension(extension, name);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
80
src/options.js
Normal file
80
src/options.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Created by Tivie on 13-07-2015.
|
||||
*/
|
||||
|
||||
function getDefaultOpts(simple) {
|
||||
'use strict';
|
||||
|
||||
var defaultOptions = {
|
||||
omitExtraWLInCodeBlocks: {
|
||||
default: false,
|
||||
describe: 'Omit the default extra whiteline added to code blocks',
|
||||
type: 'boolean'
|
||||
},
|
||||
noHeaderId: {
|
||||
default: false,
|
||||
describe: 'Turn on/off generated header id',
|
||||
type: 'boolean'
|
||||
},
|
||||
prefixHeaderId: {
|
||||
default: false,
|
||||
describe: 'Specify a prefix to generated header ids',
|
||||
type: 'string'
|
||||
},
|
||||
headerLevelStart: {
|
||||
default: false,
|
||||
describe: 'The header blocks level start',
|
||||
type: 'integer'
|
||||
},
|
||||
parseImgDimensions: {
|
||||
default: false,
|
||||
describe: 'Turn on/off image dimension parsing',
|
||||
type: 'boolean'
|
||||
},
|
||||
simplifiedAutoLink: {
|
||||
default: false,
|
||||
describe: 'Turn on/off GFM autolink style',
|
||||
type: 'boolean'
|
||||
},
|
||||
literalMidWordUnderscores: {
|
||||
default: false,
|
||||
describe: 'Parse midword underscores as literal underscores',
|
||||
type: 'boolean'
|
||||
},
|
||||
strikethrough: {
|
||||
default: false,
|
||||
describe: 'Turn on/off strikethrough support',
|
||||
type: 'boolean'
|
||||
},
|
||||
tables: {
|
||||
default: false,
|
||||
describe: 'Turn on/off tables support',
|
||||
type: 'boolean'
|
||||
},
|
||||
tablesHeaderId: {
|
||||
default: false,
|
||||
describe: 'Add an id to table headers',
|
||||
type: 'boolean'
|
||||
},
|
||||
ghCodeBlocks: {
|
||||
default: true,
|
||||
describe: 'Turn on/off GFM fenced code blocks support',
|
||||
type: 'boolean'
|
||||
},
|
||||
tasklists: {
|
||||
default: false,
|
||||
describe: 'Turn on/off GFM tasklist support',
|
||||
type: 'boolean'
|
||||
}
|
||||
};
|
||||
if (simple === false) {
|
||||
return JSON.parse(JSON.stringify(defaultOptions));
|
||||
}
|
||||
var ret = {};
|
||||
for (var opt in defaultOptions) {
|
||||
if (defaultOptions.hasOwnProperty(opt)) {
|
||||
ret[opt] = defaultOptions[opt].default;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -6,21 +6,7 @@
|
|||
var showdown = {},
|
||||
parsers = {},
|
||||
extensions = {},
|
||||
defaultOptions = {
|
||||
omitExtraWLInCodeBlocks: false,
|
||||
prefixHeaderId: false,
|
||||
noHeaderId: false,
|
||||
headerLevelStart: 1,
|
||||
parseImgDimensions: false,
|
||||
simplifiedAutoLink: false,
|
||||
literalMidWordUnderscores: false,
|
||||
strikethrough: false,
|
||||
tables: false,
|
||||
tablesHeaderId: false,
|
||||
ghCodeBlocks: true, // true due to historical reasons
|
||||
tasklists: false
|
||||
},
|
||||
globalOptions = JSON.parse(JSON.stringify(defaultOptions)),
|
||||
globalOptions = getDefaultOpts(true),
|
||||
flavor = {
|
||||
github: {
|
||||
omitExtraWLInCodeBlocks: true,
|
||||
|
@ -33,7 +19,7 @@ var showdown = {},
|
|||
ghCodeBlocks: true,
|
||||
tasklists: true
|
||||
},
|
||||
vanilla: JSON.parse(JSON.stringify(defaultOptions))
|
||||
vanilla: getDefaultOpts(true)
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -88,7 +74,7 @@ showdown.getOptions = function () {
|
|||
*/
|
||||
showdown.resetOptions = function () {
|
||||
'use strict';
|
||||
globalOptions = JSON.parse(JSON.stringify(defaultOptions));
|
||||
globalOptions = getDefaultOpts(true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -110,11 +96,12 @@ showdown.setFlavor = function (name) {
|
|||
/**
|
||||
* Get the default options
|
||||
* @static
|
||||
* @param {boolean} [simple=true]
|
||||
* @returns {{}}
|
||||
*/
|
||||
showdown.getDefaultOptions = function () {
|
||||
showdown.getDefaultOptions = function (simple) {
|
||||
'use strict';
|
||||
return defaultOptions;
|
||||
return getDefaultOpts(simple);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -233,7 +220,7 @@ function validate(extension, name) {
|
|||
}
|
||||
|
||||
for (var i = 0; i < extension.length; ++i) {
|
||||
var baseMsg = errMsg + 'sub-extension ' + i + ': ',
|
||||
var baseMsg = errMsg + ' sub-extension ' + i + ': ',
|
||||
ext = extension[i];
|
||||
if (typeof ext !== 'object') {
|
||||
ret.valid = false;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
showdown.subParser('headers', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
var prefixHeader = options.prefixHeaderId;
|
||||
var prefixHeader = options.prefixHeaderId,
|
||||
headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart);
|
||||
|
||||
// Set text-style headers:
|
||||
// Header 1
|
||||
|
@ -14,7 +15,7 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
|
||||
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
|
||||
hLevel = parseInt(options.headerLevelStart),
|
||||
hLevel = headerLevelStart,
|
||||
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
|
||||
return showdown.subParser('hashBlock')(hashBlock, options, globals);
|
||||
});
|
||||
|
@ -22,7 +23,7 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, function (matchFound, m1) {
|
||||
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
|
||||
hLevel = parseInt(options.headerLevelStart) + 1,
|
||||
hLevel = headerLevelStart + 1,
|
||||
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
|
||||
return showdown.subParser('hashBlock')(hashBlock, options, globals);
|
||||
});
|
||||
|
@ -49,7 +50,7 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
|
||||
var span = showdown.subParser('spanGamut')(m2, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
|
||||
hLevel = parseInt(options.headerLevelStart) - 1 + m1.length,
|
||||
hLevel = headerLevelStart - 1 + m1.length,
|
||||
header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
|
||||
|
||||
return showdown.subParser('hashBlock')(header, options, globals);
|
||||
|
|
3
test/cli/basic.html
Normal file
3
test/cli/basic.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<h1 id="sometitle">some title</h1>
|
||||
|
||||
<p>Test <strong>bold</strong> and <em>italic</em></p>
|
3
test/cli/basic.md
Normal file
3
test/cli/basic.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# some title
|
||||
|
||||
Test **bold** and _italic_
|
13
test/node/cli.js
Normal file
13
test/node/cli.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
var execSync = require('child_process').execSync;
|
||||
|
||||
describe('showdown cli', function () {
|
||||
'use strict';
|
||||
it('basic stdin stdout', function () {
|
||||
var otp = execSync('showdown makehtml', {
|
||||
encoding: 'utf8',
|
||||
input: '**foo**'
|
||||
});
|
||||
otp.trim().should.equal('<p><strong>foo</strong></p>');
|
||||
});
|
||||
});
|
||||
|
|
@ -17,6 +17,8 @@ describe('showdown.options', function () {
|
|||
|
||||
describe('getDefaultOptions()', function () {
|
||||
it('should get default options', function () {
|
||||
var opts = require('../optionswp').getDefaultOpts(true);
|
||||
/*
|
||||
var opts = {
|
||||
omitExtraWLInCodeBlocks: false,
|
||||
prefixHeaderId: false,
|
||||
|
@ -31,6 +33,7 @@ describe('showdown.options', function () {
|
|||
ghCodeBlocks: true,
|
||||
tasklists: false
|
||||
};
|
||||
*/
|
||||
expect(showdown.getDefaultOptions()).to.be.eql(opts);
|
||||
});
|
||||
});
|
||||
|
|
9
test/optionswp.js
Normal file
9
test/optionswp.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* jshint ignore:start */
|
||||
var fs = require('fs'),
|
||||
filedata;
|
||||
filedata = fs.readFileSync('src/options.js', 'utf8');
|
||||
eval(filedata);
|
||||
module.exports = {
|
||||
getDefaultOpts: getDefaultOpts
|
||||
};
|
||||
/* jshint ignore:end */
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Created by Estevao on 10-07-2015.
|
||||
*/
|
||||
/*
|
||||
var showdown = require('../dist/showdown.js'),
|
||||
bootstrap = require('./bootstrap.js'),
|
||||
assertion = bootstrap.assertion,
|
||||
testsuite = bootstrap.getTestSuite('test/features/');
|
||||
|
||||
describe('makeHtml() single test', function () {
|
||||
'use strict';
|
||||
});
|
||||
*/
|
Loading…
Reference in New Issue
Block a user