diff --git a/README.md b/README.md index 2f667b4..936241c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ +* [markdown-include](#markdown-include) + * [Compile your markdown files](#compile-your-markdown-files) + * [Make a table of contents](#make-a-table-of-contents) +* [How To Install](#how-to-install) +* [How To Use](#how-to-use) + * [markdown.json](#markdown.json) +* [How It Works](#how-it-works) + + # markdown-include -markdown-include is built using Node.js and allows you to include markdown files into other markdown files using a C style include syntax: +markdown-include is built using Node.js and allows you to include markdown files into other markdown files using a C style include syntax. + +## Compile your markdown files + +markdown-include's main feature is that it allows you to include allows you to include markdown files into other markdown files, like so: ``` #include "markdown-file.md" @@ -26,6 +39,12 @@ Something in markdown file! Something in another markdown file! ``` +## Make a table of contents + +Aside from compiling your markdown files, markdown-include can also build your table of contents. This works by evaluating the heading tags inside of your files. Since markdown works on using `#` for headings, this makes it easy to assemble table of contents from them. The more `#` you have in front of your headings (up to 6) will decide how the table of contents is built. Use one `#` and it's a top level navigation item... Use two `#` and it would be underneath the previous navigation item. + +For each heading that you would like to be included in a table of contents just add ` !heading` to the end of it. + # How To Install @@ -50,15 +69,16 @@ node path/to/markdown-include.js path/to/markdown.json `markdown.json` can be populated with the following options: -| Option | Type | Description | -|:-------------:|:-------------:|:--------------------------------------------------------------------------:| -| `build` | String | File path of where everything should be compiled, like `README.md` | -| `files` | Array | Array of files to to compile | +| Option | Type | Description | +|:-----------------:|:-------------:|:--------------------------------------------------------------------------:| +| `build` | String | File path of where everything should be compiled, like `README.md` | +| `files` | Array | Array of files to to compile | +| `tableOfContents` | Boolean | `true` to build table of contents dynamically | # How It Works -markdown-include works by recursively going through files based on the tags that are found. For instance, considering the following in a `_README.md` file: +markdown-include works by recursively going through files based on the tags that are found. For instance, consider the following in a `_README.md` file: ``` #include "first-file.md" diff --git a/docs/how_it_works.md b/docs/how_it_works.md index 9104a37..40de750 100644 --- a/docs/how_it_works.md +++ b/docs/how_it_works.md @@ -1,6 +1,6 @@ -# How It Works +# How It Works !heading -markdown-include works by recursively going through files based on the tags that are found. For instance, considering the following in a `_README.md` file: +markdown-include works by recursively going through files based on the tags that are found. For instance, consider the following in a `_README.md` file: ``` #include "first-file.md" !ignore diff --git a/docs/how_to_install.md b/docs/how_to_install.md index df1563e..6ae9816 100644 --- a/docs/how_to_install.md +++ b/docs/how_to_install.md @@ -1,4 +1,4 @@ -# How To Install +# How To Install !heading markdown-include is available on npm for easy installation: diff --git a/docs/how_to_use.md b/docs/how_to_use.md index 78f71d5..bc46ee6 100644 --- a/docs/how_to_use.md +++ b/docs/how_to_use.md @@ -1,4 +1,4 @@ -# How To Use +# How To Use !heading markdown-include is very easy to use. Just include a `markdown.json` file in your project root with your options and run from the command line to compile your documents like so: @@ -6,12 +6,13 @@ markdown-include is very easy to use. Just include a `markdown.json` file in yo node path/to/markdown-include.js path/to/markdown.json ``` -## markdown.json +## markdown.json !heading `markdown.json` can be populated with the following options: -| Option | Type | Description | -|:-------------:|:-------------:|:--------------------------------------------------------------------------:| -| `build` | String | File path of where everything should be compiled, like `README.md` | -| `files` | Array | Array of files to to compile | +| Option | Type | Description | +|:-----------------:|:-------------:|:--------------------------------------------------------------------------:| +| `build` | String | File path of where everything should be compiled, like `README.md` | +| `files` | Array | Array of files to to compile | +| `tableOfContents` | Boolean | `true` to build table of contents dynamically | diff --git a/docs/markdown_include.md b/docs/markdown_include.md index b013661..267b782 100644 --- a/docs/markdown_include.md +++ b/docs/markdown_include.md @@ -1,6 +1,10 @@ -# markdown-include +# markdown-include !heading -markdown-include is built using Node.js and allows you to include markdown files into other markdown files using a C style include syntax: +markdown-include is built using Node.js and allows you to include markdown files into other markdown files using a C style include syntax. + +## Compile your markdown files !heading + +markdown-include's main feature is that it allows you to include allows you to include markdown files into other markdown files, like so: ``` #include "markdown-file.md" !ignore @@ -26,3 +30,9 @@ Something in markdown file! Something in another markdown file! ``` +## Make a table of contents !heading + +Aside from compiling your markdown files, markdown-include can also build your table of contents. This works by evaluating the heading tags inside of your files. Since markdown works on using `#` for headings, this makes it easy to assemble table of contents from them. The more `#` you have in front of your headings (up to 6) will decide how the table of contents is built. Use one `#` and it's a top level navigation item... Use two `#` and it would be underneath the previous navigation item. + +For each heading that you would like to be included in a table of contents just add ` !heading` to the end of it. + diff --git a/markdown-include.js b/markdown-include.js index d0c4786..c3b75b8 100644 --- a/markdown-include.js +++ b/markdown-include.js @@ -10,13 +10,61 @@ var fs = require('fs'); var includePattern = /^#include\s"(.+\/|\/|\w|-|\/)+.md"/gm; var ignorePattern = /^#include\s"(.+\/|\/|\w|-|\/)+.md" !ignore/gm; + var headingPattern = /#+\s.+ !heading/gm; var build = {}; + var tableOfContents = ''; /** - * Build files from markdown.json + * Build content item for navigation + * @param {Object} obj Object containing count and headingTag + * @return {String} String for markdown navigation item + */ + function buildContentItem(obj) { + var headingTag = obj.headingTag; + var count = obj.count; + var item = headingTag.substring(count + 1); + var index = headingTag.indexOf(item); + var headingTrimmed = headingTag.substring(index).trim().split(' ').join('-').toLowerCase(); + var navItem; + + /** + * Small utility function for building links for navigation items + * @param {String} heading Navigation item + * @return {String} Navigation item that's linked + */ + function buildNavItem(heading) { + return '[' + item + '](#' + heading + ')\n'; + } + + switch (obj.count) { + case 1: + navItem = '* ' + buildNavItem(headingTrimmed); + break; + case 2: + navItem = ' * ' + buildNavItem(headingTrimmed); + break; + case 3: + navItem = ' * ' + buildNavItem(headingTrimmed); + break; + case 4: + navItem = ' * ' + buildNavItem(headingTrimmed); + break; + case 5: + navItem = ' * ' + buildNavItem(headingTrimmed); + break; + case 6: + navItem = ' * ' + buildNavItem(headingTrimmed); + break; + } + + return navItem; + } + + /** + * Compile files from markdown.json * @param {String} path File path to markdown.json */ - function buildFiles(path) { + function compileFiles(path) { fs.readFile(path, function (err, data) { if (err) { throw err; @@ -28,13 +76,57 @@ for (i = 0; i < files.length; i += 1) { var file = files[i]; + processFile(file); - replaceIgnoreTags(file); - writeFile(options.build, build[file].parsedData); + build[file].parsedData = stripFileTags({ + data: build[file].parsedData, + pattern: ignorePattern, + string: ' !ignore' + }); + + if (options.tableOfContents) { + compileHeadingTags(file); + build[file].parsedData = tableOfContents + '\n\n' + build[file].parsedData; + } + + writeFile(options, build[file].parsedData); } }); } + + /** + * Compiling heading tags in a parsed file + * @param {String} file File path + */ + function compileHeadingTags(file) { + var headingTags = findHeadingTags(build[file].parsedData); + var replacedHeadingTag; + var parsedHeading; + var i; + + for (i = 0; i < headingTags.length; i += 1) { + replacedHeadingTag = headingTags[i].replace(' !heading', ''); + parsedHeading = parseHeadingTag(replacedHeadingTag); + tableOfContents += buildContentItem(parsedHeading); + } + + build[file].parsedData = stripFileTags({ + data: build[file].parsedData, + pattern: headingPattern, + string: ' !heading' + }); + } + + /** + * Finding heading tags that have !heading + * @param {String} parsedData Parsed data from includes + * @return {Array} Array of matching heading tags + */ + function findHeadingTags(parsedData) { + return parsedData.match(headingPattern) || []; + } + /** * Finds include tags in file content based on a regular expression * @param {String} rawData Raw data from file @@ -107,6 +199,30 @@ return tag.substring(firstQuote, lastQuote); } + /** + * [parseHeadingTag description] + * @param {[type]} headingTag [description] + * @return {[type]} [description] + */ + function parseHeadingTag(headingTag) { + var count = 0; + var i; + + for (i = 0; i < headingTag.length; i += 1) { + if (headingTag[i] === '#') { + count += 1; + } + else { + break; + } + } + + return { + count: count, + headingTag: headingTag + }; + } + /** * Processes array of include tags and passes file for recursion * @param {String} file File passed for additional processing to check for more includes @@ -161,34 +277,38 @@ } /** - * Replaces include tags with an !ignore - * @param {String} file File path + * Strips tags in a given file + * @param {Object} obj Object containing file path, pattern to match and string to replace + * @return {String} Replaced data from object keys */ - function replaceIgnoreTags(file) { - var obj = build[file]; - var parsedData = obj.parsedData; + function stripFileTags(obj) { var replacedData; - if (ignorePattern.test(parsedData)) { - var ignores = parsedData.match(ignorePattern); + if (obj.pattern.test(obj.data)) { + var patterns = obj.data.match(obj.pattern); var i; - for (i = 0; i < ignores.length; i += 1) { - var ignore = ignores[i]; - var index = parsedData.indexOf(ignore); - var ignoreLength = ' !ignore'.length; - var ignoreTagLength = ignores[i].length; - var replacedTag = ignore.substring(0, ignoreTagLength - ignoreLength); - - if (replacedData) { - replacedData = replacedData.replace(ignore, replacedTag); + for (i = 0; i < patterns.length; i += 1) { + var currentPattern = patterns[i]; + var index = obj.data.indexOf(currentPattern); + var stringLength = obj.string.length; + var currentPatternTagLength = patterns[i].length; + var replacedTag = currentPattern.substring(0, currentPatternTagLength - stringLength); + + if (obj.replace) { + console.log('do something else'); } else { - replacedData = obj.parsedData.replace(ignore, replacedTag); + if (replacedData) { + replacedData = replacedData.replace(currentPattern, replacedTag); + } + else { + replacedData = obj.data.replace(currentPattern, replacedTag); + } } } - obj.parsedData = replacedData; + return replacedData; } } @@ -197,15 +317,15 @@ * @param {String} path Path to build new file * @param {String} data Data to write into file */ - function writeFile(path, data) { - fs.writeFile(path, data, function (err) { + function writeFile(options, parsedData) { + fs.writeFile(options.build, parsedData, function (err) { if (err) { throw err; } - console.info(path + ' has been built successfully'); + console.info(options.build + ' has been built successfully'); }); } - buildFiles(process.argv[2]); + compileFiles(process.argv[2]); }()); \ No newline at end of file diff --git a/markdown.json b/markdown.json index af9fec3..f44dc94 100644 --- a/markdown.json +++ b/markdown.json @@ -1,4 +1,5 @@ { "build" : "README.md", - "files" : ["./docs/_README.md"] + "files" : ["./docs/_README.md"], + "tableOfContents": true } \ No newline at end of file diff --git a/package.json b/package.json index 2959404..5f4221b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "markdown-include", - "version": "0.1.0", + "version": "0.2.0", "description": "Include markdown files into other markdown files with C style syntax.", "main": "markdown-include.js", "repository": {