├── .travis.yml ├── .editorconfig ├── example ├── package.json ├── example.js ├── package-lock.json └── example.md ├── package.json ├── license.md ├── .gitignore ├── index.js ├── readme.md └── test.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'lts/carbon' 4 | - '9.0' 5 | after_script: npm run report 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.yml] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-code-blocks-example", 3 | "dependencies": { 4 | "prettier": "^1.14.2", 5 | "remark-parse": "^5.0.0", 6 | "remark-stringify": "^5.0.0", 7 | "to-vfile": "^5.0.1", 8 | "unified": "^7.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | const stringify = require('remark-stringify') 2 | const parser = require('remark-parse') 3 | const toVfile = require('to-vfile') 4 | const unified = require('unified') 5 | 6 | const codeblocks = require('..') 7 | 8 | unified() 9 | .use(parser) 10 | .use(stringify) 11 | .use(codeblocks, { lang: 'js' }) 12 | .process(toVfile.readSync('./example.md')) 13 | .then(file => { 14 | const code = file.data.codeblocks.join('\n') 15 | console.log(code) 16 | }) 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-code-blocks", 3 | "version": "2.0.2", 4 | "description": "Extract code blocks from an MDAST tree", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rimraf coverage .nyc_output", 8 | "test": "tap --100 -Rspec test.js", 9 | "report": "nyc report --reporter=text-lcov | coveralls" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/mrzmmr/remark-code-blocks.git" 14 | }, 15 | "keywords": [ 16 | "remark", 17 | "util", 18 | "utility", 19 | "code", 20 | "blocks", 21 | "extract" 22 | ], 23 | "author": "Paul Zimmer", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/mrzmmr/remark-code-blocks/issues" 27 | }, 28 | "homepage": "https://github.com/mrzmmr/remark-code-blocks#readme", 29 | "devDependencies": { 30 | "coveralls": "^3.0.5", 31 | "remark-parse": "^7.0.0", 32 | "remark-stringify": "^7.0.1", 33 | "tap": "^14.10.7", 34 | "unified": "^8.3.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paul Zimmer 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const codeblocks = (tree, options) => { 2 | options = options || {} 3 | 4 | const lang = options.lang || 'all' 5 | const name = options.name || 'codeblocks' 6 | const formatter = options.formatter || (v => v) 7 | 8 | const { children } = tree 9 | let data = {} 10 | let i = -1 11 | let child 12 | 13 | if (lang === 'all') { 14 | data[name] = {} 15 | } else { 16 | data[name] = [] 17 | } 18 | 19 | while (++i < children.length) { 20 | child = children[i] 21 | 22 | if (child.type === 'code' && child.value) { 23 | if (lang === 'all') { 24 | child.lang = child.lang || '_' 25 | data[name][child.lang] = data[name][child.lang] || [] 26 | data[name][child.lang].push(formatter(child.value)) 27 | } else { 28 | if (child.lang === lang) { 29 | data[name].push(formatter(child.value)) 30 | } 31 | } 32 | } 33 | } 34 | 35 | return data 36 | } 37 | 38 | module.exports = options => (tree, file) => { 39 | const data = codeblocks(tree, options) 40 | file.data = Object.assign({}, file.data, data) 41 | } 42 | 43 | module.exports.codeblocks = (tree, options) => codeblocks(tree, options) 44 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # remark-code-blocks 2 | 3 | > [Remark](https://github.com/syntax-tree/remark) plugin to extract `code` nodes from markdown. 4 | 5 | [![Travis](https://img.shields.io/travis/mrzmmr/remark-code-blocks.svg)](https://travis-ci.org/mrzmmr/remark-code-blocks) 6 | [![Coverage 7 | Status](https://coveralls.io/repos/github/mrzmmr/remark-code-blocks/badge.svg?branch=master)](https://coveralls.io/github/mrzmmr/remark-code-blocks?branch=master) 8 | 9 | ## Install 10 | 11 | ``` 12 | npm i -S remark-code-blocks 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```js 18 | const toVfile = require('to-vfile') 19 | const unified = require('unified') 20 | const parser = require('remark-parser') 21 | const stringify = require('remark-stringify') 22 | const codeblocks = require('remark-code-blocks') 23 | 24 | unified() 25 | .use(parser) 26 | .use(stringify) 27 | .use(codeblocks, { /* options */ }) 28 | .process(toVfile('./example.md')) 29 | .then(file => { 30 | /* file.data.codeblocks = [ ... ] */ 31 | }) 32 | ``` 33 | 34 | or use the standalone function which takes a tree as its first argument. 35 | 36 | ```js 37 | const toVfile = require('to-vfile') 38 | const unified = require('unified') 39 | const parser = require('remark-parser') 40 | const { codeblocks } = require('remark-code-blocks') 41 | 42 | const tree = unified().use(parser).parse(toVfile('./example.md')) 43 | const code = codeblocks(tree, { /* options */ }) 44 | ``` 45 | 46 | ## API 47 | 48 | ### .use(codeblocks[, options]) 49 | Use as a plugin to extract code nodes. 50 | 51 | The results are stored in `file.data` in a `codeblocks` property by default. You can override the name of the property using `options.name`. 52 | 53 | ### .codeblocks(tree[, options]) 54 | 55 | Also exports a standalone function. 56 | 57 | ### Options 58 | 59 | #### lang 60 | 61 | Type: `string` 62 | Default: `all` 63 | 64 | Specify a language and only extract code nodes with that language. Otherwise `all` code nodes are extracted. 65 | 66 | #### name 67 | 68 | Type: `string` 69 | Default: `codeblocks` 70 | 71 | Specify the name of the property in `file.data` 72 | 73 | #### formatter 74 | 75 | Type: `function` 76 | Default: none 77 | 78 | Add a function to run over the nodes values before storing them in `file.data` 79 | 80 | ## License 81 | 82 | MIT © Paul Zimmer 83 | 84 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-code-blocks-example", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "is-buffer": { 7 | "version": "2.0.3", 8 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", 9 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" 10 | }, 11 | "prettier": { 12 | "version": "1.14.2", 13 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", 14 | "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==" 15 | }, 16 | "replace-ext": { 17 | "version": "1.0.0", 18 | "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", 19 | "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" 20 | }, 21 | "to-vfile": { 22 | "version": "5.0.1", 23 | "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-5.0.1.tgz", 24 | "integrity": "sha512-6Vk50kB5K71b2fNP4eLBCBlfS1Q1tCcFvJvWyDR1mJ1uyH0N4Ux9CakLseEFE9FFpokdMpbHHAXZhcCJITrY3A==", 25 | "requires": { 26 | "is-buffer": "^2.0.0", 27 | "vfile": "^3.0.0" 28 | } 29 | }, 30 | "unist-util-stringify-position": { 31 | "version": "1.1.2", 32 | "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", 33 | "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" 34 | }, 35 | "vfile": { 36 | "version": "3.0.0", 37 | "resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.0.tgz", 38 | "integrity": "sha512-X2DiPHL9Nxgfyu5DNVgtTkZtD4d4Zzf7rVBVI+uXP2pWWIQG8Ri+xAP9KdH/sB6SS0a1niWp5bRF88n4ciwhoA==", 39 | "requires": { 40 | "is-buffer": "^2.0.0", 41 | "replace-ext": "1.0.0", 42 | "unist-util-stringify-position": "^1.0.0", 43 | "vfile-message": "^1.0.0" 44 | } 45 | }, 46 | "vfile-message": { 47 | "version": "1.0.1", 48 | "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.1.tgz", 49 | "integrity": "sha512-vSGCkhNvJzO6VcWC6AlJW4NtYOVtS+RgCaqFIYUjoGIlHnFL+i0LbtYvonDWOMcB97uTPT4PRsyYY7REWC9vug==", 50 | "requires": { 51 | "unist-util-stringify-position": "^1.1.1" 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /example/example.md: -------------------------------------------------------------------------------- 1 | # remark-code-blocks 2 | 3 | This is a remark plugin to extract out the values from `code` nodes in a markdown input, and save it in VFiles data property. 4 | 5 | ## options 6 | 7 | ### options.lang 8 | 9 | :default = 'all' 10 | 11 | Specify to select only `code` nodes with a type of `lang`. When this option is set, than codeblocks will be an array. 12 | 13 | i.e: 14 | `VFile.data.codeblocks = []` 15 | 16 | ### options.name 17 | 18 | :default = 'codeblocks' 19 | 20 | Specify what the property in data should be called. 21 | 22 | i.e 23 | `VFile.data[ options.name ] = []` 24 | 25 | ### options.formatter 26 | 27 | :default = (r => r) 28 | 29 | Optionally pass a formatter function to run on the code stored. 30 | 31 | ```js 32 | module.exports = options => (tree, file) => { 33 | const settings = options || {} 34 | const lang = settings.lang || 'all' 35 | const name = settings.name || 'codeblocks' 36 | const formatter = settings.formatter || (r => r) 37 | 38 | ``` 39 | 40 | If codeblocks is not defined already in data, then it is created. If lang is specified than codeblocks will be an array, otherwise it will be an object where the key is the lang of the found code block and the value is an array of values. 41 | 42 | ```js 43 | if (!file.data[name]) { 44 | if (lang === 'all') { 45 | file.data[name] = {} 46 | } else { 47 | file.data[name] = [] 48 | } 49 | } 50 | 51 | ``` 52 | 53 | For each code node found, if lang is specified, then a new prop in codeblocks is set to the lang type. Then the value is run through the formatter function provided and pushed into properties array. Otherwise, codeblocks is set to an array, and the `code` node's value is run through the formatter function provided and pushed onto the array. 54 | 55 | ```js 56 | const { children } = tree 57 | let i = -1 58 | let child 59 | 60 | while (++i < children.length) { 61 | child = children[i] 62 | 63 | if (child.type === 'code' && child.value) { 64 | if (lang === 'all') { 65 | child.lang = child.lang || '_' 66 | if (!file.data[name][child.lang]) { 67 | file.data[name][child.lang] = [] 68 | } 69 | file.data[name][child.lang].push(formatter(child.value)) 70 | } else { 71 | if (child.lang === lang) { 72 | file.data[name].push(formatter(child.value)) 73 | } 74 | } 75 | } 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const unified = require('unified') 2 | const parser = require('remark-parse') 3 | const stringify = require('remark-stringify') 4 | const { test } = require('tap') 5 | 6 | const codeblocks = require('.') 7 | 8 | test('remark-code-blocks', t => { 9 | const mixLang = '# Test\n```\nconst a = 42\n```\n\n```go\nfmt.Println("Hi")\n```' 10 | const noLang = '# Test\n```\nconst a = 42\n```' 11 | const processor = unified() 12 | .use(parser) 13 | .use(stringify) 14 | 15 | t.test('standalone function', it => { 16 | const tree = unified() 17 | .use(parser) 18 | .parse(noLang) 19 | const code = codeblocks.codeblocks(tree) 20 | 21 | it.ok( 22 | code.codeblocks, 23 | 'It should return an object with a codeblocks property' 24 | ) 25 | it.ok( 26 | Array.isArray(code.codeblocks._), 27 | 'It should create a `_` property in `codeblocks`' 28 | ) 29 | it.end() 30 | }) 31 | 32 | t.test('with codeblocks already taken', it => { 33 | let file = processor().processSync(noLang) 34 | file.data.codeblocks = {_: []} 35 | let newFile = processor() 36 | .use(codeblocks) 37 | .processSync(file) 38 | it.ok( 39 | newFile.data.codeblocks._, 40 | 'it should not replace already stored.' 41 | ) 42 | it.end() 43 | }) 44 | 45 | t.test('it should work without options', it => { 46 | let p = processor().use(codeblocks) 47 | 48 | it.ok( 49 | p.processSync(noLang).data.codeblocks, 50 | 'it should create a `codeblocks` property in vfile.data' 51 | ) 52 | 53 | it.ok( 54 | Array.isArray(p.processSync(noLang).data.codeblocks._), 55 | 'it should create a `_` property in `codeblocks` for nodes with no lang' 56 | ) 57 | it.end() 58 | }) 59 | 60 | t.test('it should change prop name in VFile.data based options.name', it => { 61 | let p = processor().use(codeblocks, { name: 'code' }) 62 | it.ok( 63 | p.processSync(noLang).data.code, 64 | 'it should change name for property' 65 | ) 66 | it.end() 67 | }) 68 | 69 | t.test('it should only select code nodes with specified lang from options', it => { 70 | let p = processor().use(codeblocks, { name: 'code', lang: 'go' }) 71 | it.ok( 72 | Array.isArray(p.processSync(mixLang).data.code), 73 | 'When lang options is specified store in array' 74 | ) 75 | it.ok( 76 | p.processSync(mixLang).data.code[0] === 'fmt.Println("Hi")', 77 | 'it should only select one language' 78 | ) 79 | it.end() 80 | }) 81 | 82 | t.end() 83 | }) 84 | --------------------------------------------------------------------------------