UNPKG

hpal

Version:
242 lines (176 loc) 6.9 kB
'use strict'; const Os = require('os'); const Marked = require('marked'); const TerminalRenderer = require('marked-terminal'); const Path = require('path'); const internals = {}; exports.example = (example, space) => { const EOL = Os.EOL; const INDENT = (typeof space === 'undefined') ? ' ' : space; const IDENTIFIER_EXP = /^[a-z_$][a-z\d_$]*$/i; const makeComment = (c) => { return (c ? ` // ${c}` : ''); }; const stringify = (obj, isLiteral, outerComment, depth) => { if (typeof obj !== 'object' || obj === null || isLiteral) { return ((typeof obj === 'string' && !isLiteral) ? `'${obj}'` : `${obj}`) + makeComment((depth === 0) && outerComment); } else if (Array.isArray(obj)) { if (obj.length === 0) { return '[]' + makeComment((depth === 0) && outerComment); } const beginArray = '[' + makeComment(outerComment); const endArray = EOL + INDENT.repeat(depth) + ']'; return obj.reduce((out, value, i) => { const isLast = obj.length === (i + 1); const displayValue = stringify(value, false, null, depth + 1); const isMultiLine = (displayValue.indexOf(EOL) !== -1); const innerComment = (typeof value === 'object' && value !== null && !isMultiLine) ? value.$comment : null; return out + EOL + INDENT.repeat(depth + 1) + displayValue + (isLast ? '' : ',') + makeComment(innerComment); }, beginArray) + endArray; } else if (Object.keys(obj).length === 0) { return '{}' + makeComment((depth === 0) && outerComment); } else if (obj.hasOwnProperty('$literal')) { return stringify(obj.$literal, true, obj.$comment, depth); } else if (obj.hasOwnProperty('$value')) { return stringify(obj.$value, false, obj.$comment, depth); } const beginObject = '{' + makeComment(outerComment); const endObject = EOL + INDENT.repeat(depth) + '}'; return Object.keys(obj).reduce((out, key, i, keys) => { const value = obj[key]; const isLast = keys.length === (i + 1); const displayKey = key.match(IDENTIFIER_EXP) ? key : `'${key}'`; const displayValue = stringify(value, false, null, depth + 1); const isMultiLine = (displayValue.indexOf(EOL) !== -1); const innerComment = (typeof value === 'object' && value !== null && !isMultiLine) ? value.$comment : null; return out + EOL + INDENT.repeat(depth + 1) + `${displayKey}: ${displayValue}` + (isLast ? '' : ',') + makeComment(innerComment); }, beginObject) + endObject; }; return stringify(example, false, null, 0); }; exports.requires = (example) => { const requires = (example && example.$requires) || []; const basename = (p) => Path.basename(p, Path.extname(p)); return requires .map((dep) => `const ${internals.pascalize(basename(dep))} = require('${dep}');`) .join(Os.EOL); }; exports.markdownSection = (content, matchers) => { const tokens = Marked.lexer(content); for (let i = 0; i < matchers.length; ++i) { const matcher = matchers[i]; const section = internals.getMarkdownSection(tokens, matcher); if (section.length) { return Marked.parser(section, { renderer: new TerminalRenderer() }); } } return null; }; exports.markdownListItem = (content, headingMatchers, listItemMatcher) => { const tokens = Marked.lexer(content); for (let i = 0; i < headingMatchers.length; ++i) { const headingMatcher = headingMatchers[i]; const section = internals.getMarkdownSection(tokens, headingMatcher); if (section.length) { const listItem = internals.getMarkdownListItem(section, listItemMatcher); if (listItem.length) { const processedListItem = [].concat( section[0], { type: 'list_start' }, listItem, { type: 'list_end' } ); processedListItem.links = listItem.links; return Marked.parser(processedListItem, { renderer: new TerminalRenderer() }); } } } return null; }; exports.colors = (enabled) => { const codes = { bold: 1, red: 31, green: 32, yellow: 33, grey: 92 }; const colors = {}; const names = Object.keys(codes); for (let i = 0; i < names.length; ++i) { const name = names[i]; colors[name] = internals.color(name, codes[name], enabled); } return colors; }; internals.getMarkdownSection = (tokens, matcher) => { const keep = Object.assign([], { links: {} }); let depth = null; for (let i = 0; i < tokens.length; ++i) { const token = tokens[i]; if (depth !== null) { if (token.type !== 'heading' || token.depth > depth) { keep.push(internals.removeHeadingAnchorTags(token)); } else { break; } } else if (token.type === 'heading' && matcher(token.text)) { keep.push(internals.removeHeadingAnchorTags(token)); depth = token.depth; } } return keep; }; exports.headingAnchorTagRegex = /[\s]*<a name="(.*)" \/>[\s]*/; internals.removeHeadingAnchorTags = (token) => { if (token.type !== 'heading') { return token; } return Object.assign({}, token, { text: token.text.replace(exports.headingAnchorTagRegex, '') }); }; internals.getMarkdownListItem = (tokens, matcher) => { const keep = Object.assign([], { links: {} }); let depth = null; for (let i = 0; i < tokens.length; ++i) { const token = tokens[i]; const nextToken = tokens[i + 1]; if (depth !== null) { keep.push(token); if (token.type === 'list_item_start') { depth++; } else if (token.type === 'list_item_end') { depth--; } if (depth === 0) { break; } } else if ((token.type === 'list_item_start') && (nextToken.type === 'text' && matcher(nextToken.text))) { keep.push(token); depth = 1; } } return keep; }; internals.pascalize = (str) => str.replace(/(?:^|-)(\w)/g, (match, char) => char.toUpperCase()); internals.color = function (name, code, enabled) { if (!enabled) { return (text) => text; } return (text) => `\u001b[${code}m${text}\u001b[0m`; };