├── .gitignore ├── package.json ├── src ├── utils.js └── index.js ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lita", 3 | "version": "1.0.4", 4 | "description": "Literary programming in modern Javascript", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/are1000/lita.git" 12 | }, 13 | "keywords": [ 14 | "literary", 15 | "programming", 16 | "javascript", 17 | "ecmascript", 18 | "markdown" 19 | ], 20 | "author": "Artur Wojciechowski (http://iama.re)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/are1000/lita/issues" 24 | }, 25 | "homepage": "https://github.com/are1000/lita#readme", 26 | "dependencies": { 27 | "remark-parse": "^5.0.0", 28 | "remark-stringify": "^5.0.0", 29 | "unified": "^6.1.6" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | 3 | module.exports.recursiveSearch = function recursiveSearch (obj, matcher, path = '') { 4 | let result = [] 5 | 6 | if (Array.isArray(obj)) { 7 | result = result.concat(...value.map((o, i) => recursiveSearch(o, matcher, `${path}.${i}`))) 8 | } else if (typeof obj === 'object' && obj !== null) { 9 | if (matcher(obj)) { 10 | result.push({ path: path, value: obj }) 11 | } 12 | 13 | for (let [key, value] of Object.entries(obj)) { 14 | if (Array.isArray(value)) { 15 | result = result.concat(...value.map((o, i) => recursiveSearch(o, matcher, `${path}.${key}.${i}`))) 16 | } else if (typeof value === 'object' && value !== null) { 17 | let p = `${path}.${key}` 18 | 19 | if (matcher(value)) { 20 | result.push({ path: p, value: value }) 21 | } 22 | 23 | result = result.concat(recursiveSearch(value, matcher, p)) 24 | } 25 | } 26 | } 27 | 28 | return result.map(e => ({ path: e.path.replace(/^\./g, ''), value: e.value })) 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Artur Wojciechowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const unified = require('unified') 2 | const parse = require('remark-parse') 3 | const stringify = require('remark-stringify') 4 | 5 | const { recursiveSearch } = require('./utils.js') 6 | 7 | const parser = unified() 8 | .use(parse, { commonmark: true }) 9 | .use(stringify, { commonmark: true }) 10 | 11 | const R_DEFINE = /^\/\/= ?([^\n]*) ?\n?/g 12 | const R_IMPORT = /\/\/: ?([^\n]*) ?\n?/g 13 | 14 | module.exports = function process (input) { 15 | let predicate = o => o && o.type === 'code' 16 | let ast = parser.parse(input) 17 | 18 | let code = recursiveSearch(ast, predicate) 19 | 20 | let blocks = {} 21 | 22 | let oblocks = code.filter(({ path, value: entry }) => { 23 | let text = entry.value 24 | 25 | let defineRe = R_DEFINE.exec(text) 26 | 27 | if (defineRe !== null) { 28 | blocks[defineRe[1]] = text.replace(R_DEFINE, '') 29 | return false 30 | } 31 | 32 | return true 33 | }) 34 | 35 | let output = oblocks.map(({ path, value: entry }) => { 36 | return entry.value.replace(R_IMPORT, (match, p1) => { 37 | return '\n' + blocks[p1] + '\n' 38 | }) 39 | }) 40 | 41 | return output.join('\n') 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lita 2 | ==== 3 | 4 | Lita is a small library, that uses *unified* and *remark* to create an easy to use interface to literary programming in Javascript. 5 | It is meant to be used by other tools, but if it suits your needs - feel free to abuse it! 6 | 7 | ## Instalation 8 | 9 | $ npm install lita 10 | 11 | ## Usage 12 | 13 | const lita = require('lita') 14 | 15 | ### `lita(input: string [, options: object ]) -> string` 16 | Lita accepts a Markdown formated string and returns all of the code blocks concatenated together. 17 | 18 | ## Macros 19 | 20 | To extend the functionality a bit, I have added two simple macros into the markdown syntax. Both of them can be used only inside a code block. 21 | 22 | ### `define` - `//= ` 23 | At the beginning of a code block, binds a code block to a `name`, so it can be used later. Code is not included in the final output if it isn't `import`ed anywhere. 24 | 25 | ### `import` - `//: ` 26 | Anywhere in the code block, imports a code block defined earlier. It can be called multiple times for the same named code block. 27 | 28 | ## Example 29 | 30 | const lita = require('lita') // first, import `lita` into your project 31 | const fs = require('fs') 32 | const util = require('util') 33 | 34 | const readFile = util.promisify(fs.readFile) 35 | const writeFile = util.promisify(fs.writeFile) 36 | 37 | async function main () { 38 | // somehow obtain a string that contains markdown with code blocks 39 | let text = await readFile('./index.js.md', 'utf8') 40 | 41 | // lita is a function that accepts a markdown string and returns a javascript file string 42 | let result = lita(text) 43 | 44 | // do something with resultant code 45 | await writeFile('./index.js', result) 46 | } 47 | 48 | main().then(() => { 49 | 50 | }).catch(err => { // always remember to handle your errors! 51 | console.error(err) 52 | }) 53 | 54 | --------------------------------------------------------------------------------