├── .github └── FUNDING.yml ├── .gitignore ├── CONTRIBUTING.md ├── DOCUMENTATION.md ├── LICENSE ├── README.md ├── example └── index.js ├── lib ├── index.d.ts └── index.js └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ionicabizau 2 | patreon: ionicabizau 3 | open_collective: ionicabizau 4 | custom: https://www.buymeacoffee.com/h96wwchmy -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | *.log 5 | node_modules 6 | *.env 7 | .DS_Store 8 | package-lock.json 9 | .bloggify/* 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 🌟 Contributing 2 | 3 | Want to contribute to this project? Great! Please read these quick steps to streamline the process and avoid unnecessary tasks. ✨ 4 | 5 | ## 💬 Discuss Changes 6 | Start by opening an issue in the repository using the [bug tracker][1]. Describe your proposed contribution or the bug you've found. If relevant, include platform info and screenshots. 🖼️ 7 | 8 | Wait for feedback before proceeding unless the fix is straightforward, like a typo. 📝 9 | 10 | ## 🔧 Fixing Issues 11 | 12 | Fork the project and create a branch for your fix, naming it `some-great-feature` or `some-issue-fix`. Commit changes while following the [code style][2]. If the project has tests, add one. ✅ 13 | 14 | If a `package.json` or `bower.json` exists, add yourself to the `contributors` array; create it if it doesn't. 🙌 15 | 16 | ```json 17 | { 18 | "contributors": [ 19 | "Your Name (http://your.website)" 20 | ] 21 | } 22 | ``` 23 | 24 | ## 📬 Creating a Pull Request 25 | Open a pull request and reference the initial issue (e.g., *fixes #*). Provide a clear title and consider adding visual aids for clarity. 📊 26 | 27 | ## ⏳ Wait for Feedback 28 | Your contributions will be reviewed. If feedback is given, update your branch as needed, and the pull request will auto-update. 🔄 29 | 30 | ## 🎉 Everyone Is Happy! 31 | Your contributions will be merged, and everyone will appreciate your effort! 😄❤️ 32 | 33 | Thanks! 🤩 34 | 35 | [1]: /issues 36 | [2]: https://github.com/IonicaBizau/code-style -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | 3 | You can see below the API reference of this module. 4 | 5 | ### `stringify(metadata, content, [options])` 6 | Stringify metadata and content. 7 | 8 | #### Params 9 | 10 | - **Object** `metadata`: The metadata object. 11 | - **String** `content`: The markdown content. 12 | - **Object** `[options]`: An object containing the following fields: 13 | - `start` (String): The start delimiter of the metadata (default: `---`). 14 | - `end` (String): The end delimiter of the metadata (default: `---`). 15 | - `yamlOptions` (Object): Custom js-yaml options. 16 | 17 | #### Return 18 | - **String** The markdown content prefixed by the stringified metadata. 19 | 20 | ### `parse(input, [options])` 21 | Parses the markdown input and the metadata. 22 | 23 | #### Params 24 | 25 | - **String** `input`: The markdown code. If it contains metadata, it will be parsed. 26 | - **Object** `[options]`: An object containing the following fields: 27 | - `start` (String): The metadata prefix (default: `---`). 28 | - `end` (RegExp): The metadata end. 29 | - `html` (Boolean): If `true`, the markdown code will be parsed into HTML (default: `true`). 30 | - `converterOptions` (Object): The converter options passed to [`showdown`](https://github.com/showdownjs/showdown). 31 | 32 | #### Return 33 | - **ParseResult** 34 | 35 | ### `writeFile(path, metadata, content, [options], [cb])` 36 | Writes the generated content into a file. 37 | 38 | #### Params 39 | 40 | - **String** `path`: The file path. 41 | - **Object** `metadata`: The metadata object. 42 | - **String** `content`: The markdown content. 43 | - **Object** `[options]`: The stringify options. 44 | - **Function** `[cb]`: The callback function. 45 | 46 | ### `parseFile(path, [options], [cb])` 47 | Parses a markdown file. 48 | 49 | #### Params 50 | 51 | - **String** `path`: The file path. 52 | - **Object** `[options]`: The parser options. 53 | - **Function** `[cb]`: The callback function. 54 | 55 | #### Return 56 | - **ParseResult** Only **if `cb` was omitted**. 57 | 58 | An object containing the following fields: 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-25 Ionică Bizău (https://ionicabizau.net) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | [![mdify](http://i.imgur.com/koH6iq4.png)](#) 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | # mdify 23 | 24 | [![Support me on Patreon][badge_patreon]][patreon] [![Buy me a book][badge_amazon]][amazon] [![PayPal][badge_paypal_donate]][paypal-donations] [![Ask me anything](https://img.shields.io/badge/ask%20me-anything-1abc9c.svg)](https://github.com/IonicaBizau/ama) [![Version](https://img.shields.io/npm/v/mdify.svg)](https://www.npmjs.com/package/mdify) [![Downloads](https://img.shields.io/npm/dt/mdify.svg)](https://www.npmjs.com/package/mdify) [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/@johnnyb?utm_source=github&utm_medium=button&utm_term=johnnyb&utm_campaign=github) 25 | 26 | Buy Me A Coffee 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | > Markdown helpers with metadata support. 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ## :cloud: Installation 53 | 54 | ```sh 55 | # Using npm 56 | npm install --save mdify 57 | 58 | # Using yarn 59 | yarn add mdify 60 | ``` 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ## :clipboard: Example 75 | 76 | 77 | 78 | ```js 79 | const mdify = require("mdify"); 80 | 81 | let md = mdify.stringify({ 82 | author: "Johnny B." 83 | , date: new Date() 84 | , tags: ["hello", "world"] 85 | }, `## Hello World! 86 | Hey **there**!`); 87 | 88 | console.log(md); 89 | // --- 90 | // author: Johnny B. 91 | // date: 2016-06-06T07:12:17.533Z 92 | // tags: 93 | // - hello 94 | // - world 95 | // --- 96 | // ## Hello World! 97 | // Hey **there**! 98 | 99 | 100 | console.log(mdify.parse(md)); 101 | // { markdown: '\n## Hello World!\nHey **there**!', 102 | // metadata: 103 | // { author: 'Johnny B.', 104 | // date: Mon Jun 06 2016 10:12:17 GMT+0300 (EEST), 105 | // tags: [ 'hello', 'world' ] }, 106 | // rawMeta: '---\nauthor: Johnny B.\ndate: 2016-06-06T07:12:17.533Z\ntags:\n - hello\n - world', 107 | // html: '

Hello World!

\n\n

Hey there!

' } 108 | ``` 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ## :question: Get Help 122 | 123 | There are few ways to get help: 124 | 125 | 126 | 127 | 1. Please [post questions on Stack Overflow](https://stackoverflow.com/questions/ask). You can open issues with questions, as long you add a link to your Stack Overflow question. 128 | 2. For bug reports and feature requests, open issues. :bug: 129 | 3. For direct and quick help, you can [use Codementor](https://www.codementor.io/johnnyb). :rocket: 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | ## :memo: Documentation 138 | 139 | 140 | ### `stringify(metadata, content, [options])` 141 | Stringify metadata and content. 142 | 143 | #### Params 144 | 145 | - **Object** `metadata`: The metadata object. 146 | - **String** `content`: The markdown content. 147 | - **Object** `[options]`: An object containing the following fields: 148 | - `start` (String): The start delimiter of the metadata (default: `---`). 149 | - `end` (String): The end delimiter of the metadata (default: `---`). 150 | - `yamlOptions` (Object): Custom js-yaml options. 151 | 152 | #### Return 153 | - **String** The markdown content prefixed by the stringified metadata. 154 | 155 | ### `parse(input, [options])` 156 | Parses the markdown input and the metadata. 157 | 158 | #### Params 159 | 160 | - **String** `input`: The markdown code. If it contains metadata, it will be parsed. 161 | - **Object** `[options]`: An object containing the following fields: 162 | - `start` (String): The metadata prefix (default: `---`). 163 | - `end` (RegExp): The metadata end. 164 | - `html` (Boolean): If `true`, the markdown code will be parsed into HTML (default: `true`). 165 | - `converterOptions` (Object): The converter options passed to [`showdown`](https://github.com/showdownjs/showdown). 166 | 167 | #### Return 168 | - **ParseResult** 169 | 170 | ### `writeFile(path, metadata, content, [options], [cb])` 171 | Writes the generated content into a file. 172 | 173 | #### Params 174 | 175 | - **String** `path`: The file path. 176 | - **Object** `metadata`: The metadata object. 177 | - **String** `content`: The markdown content. 178 | - **Object** `[options]`: The stringify options. 179 | - **Function** `[cb]`: The callback function. 180 | 181 | ### `parseFile(path, [options], [cb])` 182 | Parses a markdown file. 183 | 184 | #### Params 185 | 186 | - **String** `path`: The file path. 187 | - **Object** `[options]`: The parser options. 188 | - **Function** `[cb]`: The callback function. 189 | 190 | #### Return 191 | - **ParseResult** Only **if `cb` was omitted**. 192 | 193 | An object containing the following fields: 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | ## :yum: How to contribute 209 | Have an idea? Found a bug? See [how to contribute][contributing]. 210 | 211 | 212 | ## :sparkling_heart: Support my projects 213 | I open-source almost everything I can, and I try to reply to everyone needing help using these projects. Obviously, 214 | this takes time. You can integrate and use these projects in your applications *for free*! You can even change the source code and redistribute (even resell it). 215 | 216 | However, if you get some profit from this or just want to encourage me to continue creating stuff, there are few ways you can do it: 217 | 218 | 219 | - Starring and sharing the projects you like :rocket: 220 | - [![Buy me a book][badge_amazon]][amazon]—I love books! I will remember you after years if you buy me one. :grin: :book: 221 | - [![PayPal][badge_paypal]][paypal-donations]—You can make one-time donations via PayPal. I'll probably buy a ~~coffee~~ tea. :tea: 222 | - [![Support me on Patreon][badge_patreon]][patreon]—Set up a recurring monthly donation and you will get interesting news about what I'm doing (things that I don't share with everyone). 223 | - **Bitcoin**—You can send me bitcoins at this address (or scanning the code below): `1P9BRsmazNQcuyTxEqveUsnf5CERdq35V6` 224 | 225 | ![](https://i.imgur.com/z6OQI95.png) 226 | 227 | 228 | Thanks! :heart: 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | ## :dizzy: Where is this library used? 246 | If you are using this library in one of your projects, add it in this list. :sparkles: 247 | 248 | - `bloggify-markdown-adapter` 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | ## :scroll: License 261 | 262 | [MIT][license] © [Ionică Bizău][website] 263 | 264 | 265 | 266 | 267 | 268 | 269 | [license]: /LICENSE 270 | [website]: https://ionicabizau.net 271 | [contributing]: /CONTRIBUTING.md 272 | [docs]: /DOCUMENTATION.md 273 | [badge_patreon]: https://ionicabizau.github.io/badges/patreon.svg 274 | [badge_amazon]: https://ionicabizau.github.io/badges/amazon.svg 275 | [badge_paypal]: https://ionicabizau.github.io/badges/paypal.svg 276 | [badge_paypal_donate]: https://ionicabizau.github.io/badges/paypal_donate.svg 277 | [patreon]: https://www.patreon.com/ionicabizau 278 | [amazon]: http://amzn.eu/hRo9sIZ 279 | [paypal-donations]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RVXDDLKKLQRJW 280 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const mdify = require("../lib"); 4 | 5 | let md = mdify.stringify({ 6 | author: "Johnny B." 7 | , date: new Date() 8 | , tags: ["hello", "world"] 9 | }, `## Hello World! 10 | Hey **there**!`); 11 | 12 | console.log(md); 13 | // --- 14 | // author: Johnny B. 15 | // date: 2016-06-06T07:12:17.533Z 16 | // tags: 17 | // - hello 18 | // - world 19 | // --- 20 | // ## Hello World! 21 | // Hey **there**! 22 | 23 | 24 | console.log(mdify.parse(md)); 25 | // { markdown: '\n## Hello World!\nHey **there**!', 26 | // metadata: 27 | // { author: 'Johnny B.', 28 | // date: Mon Jun 06 2016 10:12:17 GMT+0300 (EEST), 29 | // tags: [ 'hello', 'world' ] }, 30 | // rawMeta: '---\nauthor: Johnny B.\ndate: 2016-06-06T07:12:17.533Z\ntags:\n - hello\n - world', 31 | // html: '

Hello World!

\n\n

Hey there!

' } 32 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import { DumpOptions } from 'js-yaml'; 2 | import { ConverterOptions } from 'showdown'; 3 | 4 | export = mdify; 5 | 6 | declare class mdify { 7 | 8 | static parse(input: string, opts?: mdify.parseOptions): mdify.parseResult; 9 | 10 | static parseFile(path: string, opts?: mdify.parseOptions): mdify.parseResult; 11 | 12 | static parseFile(path: string, opts?: mdify.parseOptions, cb?: (error: Error, result: mdify.parseResult) => void): void; 13 | 14 | static stringify(metadata: any, content: string, options?: mdify.stringifyOptions): string; 15 | 16 | static writeFile(path: string, metadata: any, content: string, options?: mdify.stringifyOptions, cb?: any): void; 17 | 18 | } 19 | 20 | 21 | declare namespace mdify { 22 | type stringifyOptions = { 23 | start?: string, 24 | end?: string, 25 | yamlOptions?: DumpOptions, 26 | }; 27 | 28 | type parseOptions = { 29 | start?: string, 30 | end?: RegExp, 31 | html?: boolean, 32 | converterOptions?: ConverterOptions, 33 | }; 34 | 35 | type parseResult = { 36 | markdown: string, 37 | metadata: any, 38 | rawMeta: string, 39 | html: string, 40 | }; 41 | } -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const yaml = require("js-yaml") 4 | , showdown = require("showdown") 5 | , ul = require("ul") 6 | , readFile = require("read-utf8") 7 | , fs = require("fs") 8 | ; 9 | 10 | module.exports = class Mdify { 11 | 12 | /** 13 | * stringify 14 | * Stringify metadata and content. 15 | * 16 | * @name stringify 17 | * @function 18 | * @param {Object} metadata The metadata object. 19 | * @param {String} content The markdown content. 20 | * @param {Object} [options] An object containing the following fields: 21 | * 22 | * - `start` (String): The start delimiter of the metadata (default: `---`). 23 | * - `end` (String): The end delimiter of the metadata (default: `---`). 24 | * - `yamlOptions` (Object): Custom js-yaml options. 25 | * 26 | * @returns {String} The markdown content prefixed by the stringified metadata. 27 | */ 28 | static stringify (metadata, content, options) { 29 | 30 | options = ul.merge({ 31 | start: "---" 32 | , end: "---" 33 | , yamlOptions: {} 34 | }, options); 35 | 36 | if (typeof metadata === "string") { 37 | content = metadata; 38 | metadata = null; 39 | } 40 | 41 | if (metadata) { 42 | metadata = yaml.dump(metadata, options.yamlOptions); 43 | } 44 | 45 | return options.start + "\n" 46 | + metadata 47 | + options.end + "\n" 48 | + content 49 | ; 50 | } 51 | 52 | /** 53 | * parse 54 | * Parses the markdown input and the metadata. 55 | * 56 | * @name parse 57 | * @function 58 | * @param {String} input The markdown code. If it contains metadata, it will be parsed. 59 | * @param {Object} [options] An object containing the following fields: 60 | * 61 | * - `start` (String): The metadata prefix (default: `---`). 62 | * - `end` (RegExp): The metadata end. 63 | * - `html` (Boolean): If `true`, the markdown code will be parsed into HTML (default: `true`). 64 | * - `converterOptions` (Object): The converter options passed to [`showdown`](https://github.com/showdownjs/showdown). 65 | * 66 | * @returns {ParseResult} 67 | */ 68 | static parse (input, options) { 69 | 70 | options = ul.merge(options, { 71 | start: "---" 72 | , end: /\n(\-{3})/g 73 | , html: true 74 | , converterOptions: {} 75 | }); 76 | 77 | 78 | let result = { 79 | markdown: input 80 | , metadata: null 81 | , rawMeta: null 82 | }; 83 | 84 | if (input.startsWith(options.start)) { 85 | let res = options.end.exec(input); 86 | if (res) { 87 | result.rawMeta = input.slice(0, res.index) || ""; 88 | result.markdown = input.slice(options.end.lastIndex); 89 | result.metadata = yaml.load(result.rawMeta); 90 | } 91 | } 92 | 93 | if (options.html) { 94 | options.converter = new showdown.Converter(options.converterOptions); 95 | result.html = options.converter.makeHtml(result.markdown); 96 | } 97 | 98 | return result; 99 | } 100 | 101 | /** 102 | * writeFile 103 | * Writes the generated content into a file. 104 | * 105 | * @name writeFile 106 | * @function 107 | * @param {String} path The file path. 108 | * @param {Object} metadata The metadata object. 109 | * @param {String} content The markdown content. 110 | * @param {Object} [options] The stringify options. 111 | * @param {Function} [cb] The callback function. 112 | */ 113 | static writeFile (path, metadata, content, options, cb) { 114 | 115 | if (typeof options === "function") { 116 | cb = options; 117 | options = {}; 118 | } 119 | 120 | content = Mdify.stringify(metadata, content, options); 121 | if (cb) { 122 | fs.writeFile(path, content, cb); 123 | } else { 124 | fs.writeFileSync(path, content); 125 | } 126 | } 127 | 128 | /** 129 | * parseFile 130 | * Parses a markdown file. 131 | * 132 | * @name parseFile 133 | * @function 134 | * @param {String} path The file path. 135 | * @param {Object} [options] The parser options. 136 | * @param {Function} [cb] The callback function. 137 | * @returns {ParseResult} Only **if `cb` was omitted**. 138 | */ 139 | static parseFile (path, options, cb) { 140 | if (typeof options === "function") { 141 | cb = options; 142 | options = {}; 143 | } 144 | if (cb) { 145 | readFile(path, (err, data) => { 146 | if (err) { return cb(err); } 147 | cb(null, Mdify.parse(data, options)); 148 | }); 149 | } else { 150 | return Mdify.parse(readFile(path), options); 151 | } 152 | } 153 | }; 154 | 155 | /** 156 | * An object containing the following fields: 157 | * @typedef {Object} ParseResult 158 | * @property {String} markdown The Markdown content. 159 | * @property {Object} metadata The parsed metadata. 160 | * @property {String} rawMeta The raw metadata content. 161 | * @property {String} html The generated HTML from Markdown. 162 | */ 163 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdify", 3 | "description": "Markdown helpers with metadata support.", 4 | "keywords": [ 5 | "mdify", 6 | "markdown", 7 | "helpers", 8 | "with", 9 | "metadata", 10 | "support" 11 | ], 12 | "license": "MIT", 13 | "version": "2.0.1", 14 | "main": "lib/index.js", 15 | "types": "lib/index.d.ts", 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "Ionică Bizău (https://ionicabizau.net)", 20 | "contributors": [ 21 | "gazmull (https://thegzm.space)", 22 | "Fabian Iwand (https://twitter.com/mootari)", 23 | "Simon Laux (https://simonlaux.de)" 24 | ], 25 | "files": [ 26 | "bin/", 27 | "app/", 28 | "lib/", 29 | "dist/", 30 | "src/", 31 | "scripts/", 32 | "resources/", 33 | "menu/", 34 | "cli.js", 35 | "index.js", 36 | "index.d.ts", 37 | "package-lock.json", 38 | "bloggify.js", 39 | "bloggify.json", 40 | "bloggify/" 41 | ], 42 | "repository": { 43 | "type": "git", 44 | "url": "git+ssh://git@github.com/IonicaBizau/mdify.git" 45 | }, 46 | "bugs": { 47 | "url": "https://github.com/IonicaBizau/mdify/issues" 48 | }, 49 | "homepage": "https://github.com/IonicaBizau/mdify#readme", 50 | "dependencies": { 51 | "@types/js-yaml": "^4.0.5", 52 | "@types/showdown": "^1.9.4", 53 | "js-yaml": "^4.1.0", 54 | "read-utf8": "^1.2.10", 55 | "showdown": "^2.0.3", 56 | "ul": "^5.2.15" 57 | }, 58 | "blah": { 59 | "h_img": "http://i.imgur.com/koH6iq4.png" 60 | } 61 | } --------------------------------------------------------------------------------