diff --git a/doctoc.js b/doctoc.js index 578dfa93..cb229047 100755 --- a/doctoc.js +++ b/doctoc.js @@ -16,7 +16,7 @@ function cleanPath(path) { return homeExpanded.replace(/\s/g, '\\ '); } -function transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly) { +function transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly, syntax) { if (processAll) { console.log('--all flag is enabled. Including headers before the TOC location.') } @@ -24,13 +24,13 @@ function transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPref if (updateOnly) { console.log('--update-only flag is enabled. Only updating files that already have a TOC.') } - + console.log('\n==================\n'); var transformed = files .map(function (x) { var content = fs.readFileSync(x.path, 'utf8') - , result = transform(content, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, updateOnly); + , result = transform(content, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, updateOnly, syntax); result.path = x.path; return result; }); @@ -48,7 +48,7 @@ function transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPref console.log('"%s" is up to date', x.path); }); - changed.forEach(function (x) { + changed.forEach(function (x) { if (stdOut) { console.log('==================\n\n"%s" should be updated', x.path) } else { @@ -62,7 +62,7 @@ function printUsageAndExit(isErr) { var outputFunc = isErr ? console.error : console.info; - outputFunc('Usage: doctoc [mode] [--entryprefix prefix] [--notitle | --title title] [--maxlevel level] [--all] [--update-only] (where path is some path to a directory (e.g., .) or a file (e.g., README.md))'); + outputFunc('Usage: doctoc [mode] [--entryprefix prefix] [--notitle | --title title] [--maxlevel level] [--all] [--update-only] [--syntax (' + supportedSyntaxes.join("|") + ')] (where path is some path to a directory (e.g., .) or a file (e.g., README.md))'); outputFunc('\nAvailable modes are:'); for (var key in modes) { outputFunc(' --%s\t%s', key, modes[key]); @@ -72,6 +72,7 @@ function printUsageAndExit(isErr) { process.exit(isErr ? 2 : 0); } +var supportedSyntaxes = ['md', 'mdx'] var modes = { bitbucket : 'bitbucket.org' , nodejs : 'nodejs.org' @@ -84,7 +85,7 @@ var mode = modes['github']; var argv = minimist(process.argv.slice(2) , { boolean: [ 'h', 'help', 'T', 'notitle', 's', 'stdout', 'all' , 'u', 'update-only'].concat(Object.keys(modes)) - , string: [ 'title', 't', 'maxlevel', 'm', 'entryprefix' ] + , string: [ 'title', 't', 'maxlevel', 'm', 'entryprefix', 'syntax' ] , unknown: function(a) { return (a[0] == '-' ? (console.error('Unknown option(s): ' + a), printUsageAndExit(true)) : true); } }); @@ -92,6 +93,11 @@ if (argv.h || argv.help) { printUsageAndExit(); } +if (argv['syntax'] !== undefined && !supportedSyntaxes.includes(argv['syntax'])) { + console.error('Unknown syntax:', argv['syntax']) + console.error('Supported options:', supportedSyntaxes.join(", ")) + process.exit(2) +} for (var key in modes) { if (argv[key]) { mode = modes[key]; @@ -104,7 +110,7 @@ var entryPrefix = argv.entryprefix || '-'; var processAll = argv.all; var stdOut = argv.s || argv.stdout var updateOnly = argv.u || argv['update-only'] - +var syntax = argv['syntax'] || 'md' var maxHeaderLevel = argv.m || argv.maxlevel; if (maxHeaderLevel && isNaN(maxHeaderLevel) || maxHeaderLevel < 0) { console.error('Max. heading level specified is not a positive number: ' + maxHeaderLevel), printUsageAndExit(true); } @@ -114,13 +120,13 @@ for (var i = 0; i < argv._.length; i++) { if (stat.isDirectory()) { console.log ('\nDocToccing "%s" and its sub directories for %s.', target, mode); - files = file.findMarkdownFiles(target); + files = file.findMarkdownFiles(target, syntax); } else { console.log ('\nDocToccing single file "%s" for %s.', target, mode); files = [{ path: target }]; } - transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly); + transformAndSave(files, mode, maxHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly, syntax); console.log('\nEverything is OK.'); } diff --git a/lib/file.js b/lib/file.js index 1479be99..0457cda5 100644 --- a/lib/file.js +++ b/lib/file.js @@ -2,41 +2,43 @@ var path = require('path') , fs = require('fs') , _ = require('underscore'); -var markdownExts = ['.md', '.markdown']; var ignoredDirs = ['.', '..', '.git', 'node_modules']; - -function separateFilesAndDirs(fileInfos) { +var extensions = { + mdx: ['.mdx'], + md: ['.md', '.markdown'], +} +function separateFilesAndDirs(fileInfos, syntax) { return { directories : _(fileInfos).filter(function (x) { return x.isDirectory() && !_(ignoredDirs).include(x.name); }), - markdownFiles : _(fileInfos).filter(function (x) { - return x.isFile() && _(markdownExts).include(path.extname(x.name)); + markdownFiles : _(fileInfos).filter(function (x) { + return x.isFile() && _(extensions[syntax]).include(path.extname(x.name)); }) }; } -function findRec(currentPath) { +function findRec(currentPath, syntax) { function getStat (entry) { var target = path.join(currentPath, entry), stat = fs.statSync(target); - return _(stat).extend({ + return _(stat).extend({ name: entry, path: target }); } - + function process (fileInfos) { - var res = separateFilesAndDirs(fileInfos); + var res = separateFilesAndDirs(fileInfos, syntax); var tgts = _(res.directories).pluck('path'); - if (res.markdownFiles.length > 0) + if (res.markdownFiles.length > 0) console.log('\nFound %s in "%s"', _(res.markdownFiles).pluck('name').join(', '), currentPath); - else + else console.log('\nFound nothing in "%s"', currentPath); - return { + return { markdownFiles : res.markdownFiles, subdirs : tgts }; @@ -44,16 +46,16 @@ function findRec(currentPath) { var stats = _(fs.readdirSync(currentPath)).map(getStat) , res = process(stats) - , markdownsInSubdirs = _(res.subdirs).map(findRec) + , markdownsInSubdirs = _(res.subdirs).map((subdir)=> findRec(subdir, syntax)) , allMarkdownsHereAndSub = res.markdownFiles.concat(markdownsInSubdirs); return _(allMarkdownsHereAndSub).flatten(); } // Finds all markdown files in given directory and its sub-directories -// @param {String } dir - the absolute directory to search in -exports.findMarkdownFiles = function(dir) { - return findRec(dir); +// @param {String } dir - the absolute directory to search in +exports.findMarkdownFiles = function(dir, syntax) { + return findRec(dir, syntax); }; /* Example: diff --git a/lib/transform.js b/lib/transform.js index afd713cf..9dabeb51 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -6,18 +6,33 @@ var _ = require('underscore') , getHtmlHeaders = require('./get-html-headers') , md = require('@textlint/markdown-to-ast'); -var start = '\n' + - '' - , end = '' - , skipTag = ''; +var commentTypes = { + md: { + start: "", + escapedStart: "' + , '' , '## Header' , 'some content' , '' @@ -240,7 +245,7 @@ test('transforming when old toc exists', function (t) { var res = transform(md) - t.ok(res.transformed, 'transforms it') + t.ok(res.transformed, 'transforms it') t.deepEqual( res.toc.split('\n') @@ -248,9 +253,9 @@ test('transforming when old toc exists', function (t) { '', '- [Header](#header)', '' ] - , 'replaces old toc' + , 'replaces old toc' ) - + t.deepEqual( res.wrappedToc.split('\n') , [ '', @@ -259,7 +264,7 @@ test('transforming when old toc exists', function (t) { '', '- [Header](#header)', '', - '' + '' ] , 'wraps old toc' ) @@ -281,12 +286,12 @@ test('transforming when old toc exists', function (t) { 'some content', '' ] , 'updates the content with the new toc and ignores header before existing toc' - ) + ) t.end() }) test('transforming when old toc exists and --all flag is set', function (t) { - var md = [ + var md = [ '# Header above' , '' , 'The above header should be ignored since it is above the existing toc' @@ -297,7 +302,7 @@ test('transforming when old toc exists and --all flag is set', function (t) { , '' , '- [OldHeader](#oldheader)' , '' - , '' + , '' , '## Header' , 'some content' , '' @@ -305,7 +310,7 @@ test('transforming when old toc exists and --all flag is set', function (t) { var res = transform(md, undefined, undefined, undefined, undefined, undefined, true) - t.ok(res.transformed, 'transforms it') + t.ok(res.transformed, 'transforms it') t.deepEqual( res.toc.split('\n') @@ -314,9 +319,9 @@ test('transforming when old toc exists and --all flag is set', function (t) { '- [Header above](#header-above)', ' - [Header](#header)', '' ] - , 'replaces old toc' + , 'replaces old toc' ) - + t.deepEqual( res.wrappedToc.split('\n') , [ '', @@ -326,7 +331,7 @@ test('transforming when old toc exists and --all flag is set', function (t) { '- [Header above](#header-above)', ' - [Header](#header)', '', - '' + '' ] , 'wraps old toc' ) @@ -349,7 +354,7 @@ test('transforming when old toc exists and --all flag is set', function (t) { 'some content', '' ] , 'updates the content with the new toc and ignores header before existing toc' - ) + ) t.end() }) @@ -470,3 +475,110 @@ check( , undefined , '1.' // pass '1.' as the prefix for toc entries ) + + +check( + [ '# My Module' + , 'Some text here' + , '## API' + , '### Method One' + , 'works like this' + , '### Method Two' + , '#### Main Usage' + , 'some main usage here' + ].join('\n') + , [ '**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n' + , '- [My Module](#my-module)\n' + , ' - [API](#api)\n' + , ' - [Method One](#method-one)\n' + , ' - [Method Two](#method-two)\n' + , ' - [Main Usage](#main-usage)\n\n\n' + ].join('') + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , 'md' +) + +check( + [ '# My Module' + , 'Some text here' + , '## API' + , '### Method One' + , 'works like this' + , '### Method Two' + , '#### Main Usage' + , 'some main usage here' + ].join('\n') + , [ '**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n' + , '- [My Module](#my-module)\n' + , ' - [API](#api)\n' + , ' - [Method One](#method-one)\n' + , ' - [Method Two](#method-two)\n' + , ' - [Main Usage](#main-usage)\n\n\n' + ].join('') + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , 'mdx' +) + +test('should use comments if syntax=md', function (t) { + var res = transform( + [ '# My Module' + , 'Some text here' + , '## API' + , '### Method One' + , 'works like this' + , '### Method Two' + , '#### Main Usage' + , 'some main usage here' + ].join('\n') + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , 'md' + ) + + var commentLines = getCommentLines(res); + t.deepEqual(commentLines.every((line) => line.startsWith('')), true) + t.end() +}) + +test('should use {/* */} comments if syntax=mdx', function (t) { + var res = transform( + [ '# My Module' + , 'Some text here' + , '## API' + , '### Method One' + , 'works like this' + , '### Method Two' + , '#### Main Usage' + , 'some main usage here' + ].join('\n') + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , undefined + , 'mdx' + ) + + var commentLines = getCommentLines(res); + t.deepEqual(commentLines.every((line) => line.startsWith('{/*') && line.endsWith('*/}')), true) + t.end() +}) \ No newline at end of file