UNPKG

code-stringify

Version:

code-stringify is node.js module that converts JavaScript variables into source codes. Unlike JSON.stringify, code-stringify converts things into strings of code, not JSON.

236 lines (185 loc) 4.86 kB
const {isArray, isRegExp, isFunction} = require('core-util-is') const { isNumberString, QUOTE, EMPTY, SPACE, checkStringifier, createOptions } = require('./options') const applyReplacer = require('./replacer') const PRE_CURLY_BRACKET = '{' const SUF_CURLY_BRACKET = '}' const PRE_BRACKET = '[' const SUF_BRACKET = ']' const STRINGIFY_SYMBOL = Symbol.for('code.stringify.custom') class Code { constructor (code) { this._code = code } [STRINGIFY_SYMBOL] () { return this._code } } const CODE_STRINGIFY_CUSTOM = { test (value) { return value && isFunction(value[STRINGIFY_SYMBOL]) }, stringify (value) { return value[STRINGIFY_SYMBOL]() } } const REGEX_IS_VALID_KEY_STRING = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/ class Stringifier { constructor (options) { this._options = createOptions(options) this.stringify = this.stringify.bind(this) this._stringifiers = [] } register (stringifier) { this._stringifiers.push(checkStringifier(stringifier)) return this } stringify (value, indent = EMPTY) { const { replacer } = this._options if (replacer) { value = applyReplacer(value, replacer) } return this._stringify(value, indent) } _check (value, indent) { if (this._stringifiers.length === 0) { return } const found = this._stringifiers.find(({test}) => test(value)) if (!found) { return } const {stringify: s} = found return { code: s.call(this, value, indent, this._options) } } _stringify (value, indent) { const checked = this._check(value) if (checked) { return checked.code } if (value === undefined) { return 'undefined' } if (value === null) { return 'null' } const type = typeof value if (type === 'number') { return String(value) } if (type === 'string') { return this.string(value) } if (type === 'boolean') { return value ? 'true' : 'false' } if (type === 'function' || isRegExp(value)) { return value.toString() } if (isArray(value)) { return this.array(value, indent) } return this.object(value, indent) } _escapeString (string) { const {quote} = this._options return string // #2: deals with backslash .replace(/\\/g, '\\\\') .replace(new RegExp(quote, 'g'), `\\${quote}`) } string (string) { const {quote} = this._options return quote + this._escapeString(string) + quote } _join (slices, indent, pre, suf) { const { space } = this._options // indent space // |--------|-----| // [ <--- \n + indent + space // value, <--- itemJoiner: , + \n + indent + space // value // ] // indent space keyValueJoiner // |--------|-----| |--| // { // key: value, // key: value // } const sufJoiner = space ? `\n${indent}` // [value : EMPTY const preJoiner = sufJoiner + space const code = slices.join(`,${preJoiner}`) return code ? pre + preJoiner + code + sufJoiner + suf : pre + suf } array (array, indent) { const {space} = this._options let i = 0 let v const {length} = array const slices = [] // Never use any iterators of Array, such as .reduce(), .forEach(), etc, // 'coz those method will never iterate unset items of an array for (; i < length; i ++) { v = array[i] slices.push(this._stringify(v, indent + space)) } return this._join(slices, indent, PRE_BRACKET, SUF_BRACKET) } key (key) { const {useNumberKey} = this._options // valid: {0: 1} if (useNumberKey && isNumberString(key)) { return key } // invalid: { 0a: 1 } if (REGEX_IS_VALID_KEY_STRING.test(key)) { return key } return this.string(key) } object (object, indent) { const {space} = this._options const keyValueJoiner = space ? SPACE : EMPTY const slices = [] Object.keys(object).forEach(key => { const value = object[key] slices.push( `${this.key(key)}:${keyValueJoiner}${ this._stringify(value, space + indent) }` ) }) return this._join(slices, indent, PRE_CURLY_BRACKET, SUF_CURLY_BRACKET) } } const stringify = (value, replacer, space, indent) => new Stringifier({ replacer, space, quote: stringify.QUOTE }) .register(CODE_STRINGIFY_CUSTOM) .stringify(value, indent) module.exports = stringify stringify.Code = Code stringify.STRINGIFY_SYMBOL = STRINGIFY_SYMBOL stringify.CODE_STRINGIFY_CUSTOM = CODE_STRINGIFY_CUSTOM stringify.QUOTE = QUOTE stringify.Stringifier = Stringifier