| 1 | 'use strict';
|
|---|
| 2 | const isRegexp = require('is-regexp');
|
|---|
| 3 | const isObj = require('is-obj');
|
|---|
| 4 | const getOwnEnumPropSymbols = require('get-own-enumerable-property-symbols').default;
|
|---|
| 5 |
|
|---|
| 6 | module.exports = (val, opts, pad) => {
|
|---|
| 7 | const seen = [];
|
|---|
| 8 |
|
|---|
| 9 | return (function stringify(val, opts, pad) {
|
|---|
| 10 | opts = opts || {};
|
|---|
| 11 | opts.indent = opts.indent || '\t';
|
|---|
| 12 | pad = pad || '';
|
|---|
| 13 |
|
|---|
| 14 | let tokens;
|
|---|
| 15 |
|
|---|
| 16 | if (opts.inlineCharacterLimit === undefined) {
|
|---|
| 17 | tokens = {
|
|---|
| 18 | newLine: '\n',
|
|---|
| 19 | newLineOrSpace: '\n',
|
|---|
| 20 | pad,
|
|---|
| 21 | indent: pad + opts.indent
|
|---|
| 22 | };
|
|---|
| 23 | } else {
|
|---|
| 24 | tokens = {
|
|---|
| 25 | newLine: '@@__STRINGIFY_OBJECT_NEW_LINE__@@',
|
|---|
| 26 | newLineOrSpace: '@@__STRINGIFY_OBJECT_NEW_LINE_OR_SPACE__@@',
|
|---|
| 27 | pad: '@@__STRINGIFY_OBJECT_PAD__@@',
|
|---|
| 28 | indent: '@@__STRINGIFY_OBJECT_INDENT__@@'
|
|---|
| 29 | };
|
|---|
| 30 | }
|
|---|
| 31 |
|
|---|
| 32 | const expandWhiteSpace = string => {
|
|---|
| 33 | if (opts.inlineCharacterLimit === undefined) {
|
|---|
| 34 | return string;
|
|---|
| 35 | }
|
|---|
| 36 |
|
|---|
| 37 | const oneLined = string
|
|---|
| 38 | .replace(new RegExp(tokens.newLine, 'g'), '')
|
|---|
| 39 | .replace(new RegExp(tokens.newLineOrSpace, 'g'), ' ')
|
|---|
| 40 | .replace(new RegExp(tokens.pad + '|' + tokens.indent, 'g'), '');
|
|---|
| 41 |
|
|---|
| 42 | if (oneLined.length <= opts.inlineCharacterLimit) {
|
|---|
| 43 | return oneLined;
|
|---|
| 44 | }
|
|---|
| 45 |
|
|---|
| 46 | return string
|
|---|
| 47 | .replace(new RegExp(tokens.newLine + '|' + tokens.newLineOrSpace, 'g'), '\n')
|
|---|
| 48 | .replace(new RegExp(tokens.pad, 'g'), pad)
|
|---|
| 49 | .replace(new RegExp(tokens.indent, 'g'), pad + opts.indent);
|
|---|
| 50 | };
|
|---|
| 51 |
|
|---|
| 52 | if (seen.indexOf(val) !== -1) {
|
|---|
| 53 | return '"[Circular]"';
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | if (val === null ||
|
|---|
| 57 | val === undefined ||
|
|---|
| 58 | typeof val === 'number' ||
|
|---|
| 59 | typeof val === 'boolean' ||
|
|---|
| 60 | typeof val === 'function' ||
|
|---|
| 61 | typeof val === 'symbol' ||
|
|---|
| 62 | isRegexp(val)) {
|
|---|
| 63 | return String(val);
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | if (val instanceof Date) {
|
|---|
| 67 | return `new Date('${val.toISOString()}')`;
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | if (Array.isArray(val)) {
|
|---|
| 71 | if (val.length === 0) {
|
|---|
| 72 | return '[]';
|
|---|
| 73 | }
|
|---|
| 74 |
|
|---|
| 75 | seen.push(val);
|
|---|
| 76 |
|
|---|
| 77 | const ret = '[' + tokens.newLine + val.map((el, i) => {
|
|---|
| 78 | const eol = val.length - 1 === i ? tokens.newLine : ',' + tokens.newLineOrSpace;
|
|---|
| 79 | let value = stringify(el, opts, pad + opts.indent);
|
|---|
| 80 | if (opts.transform) {
|
|---|
| 81 | value = opts.transform(val, i, value);
|
|---|
| 82 | }
|
|---|
| 83 | return tokens.indent + value + eol;
|
|---|
| 84 | }).join('') + tokens.pad + ']';
|
|---|
| 85 |
|
|---|
| 86 | seen.pop();
|
|---|
| 87 |
|
|---|
| 88 | return expandWhiteSpace(ret);
|
|---|
| 89 | }
|
|---|
| 90 |
|
|---|
| 91 | if (isObj(val)) {
|
|---|
| 92 | let objKeys = Object.keys(val).concat(getOwnEnumPropSymbols(val));
|
|---|
| 93 |
|
|---|
| 94 | if (opts.filter) {
|
|---|
| 95 | objKeys = objKeys.filter(el => opts.filter(val, el));
|
|---|
| 96 | }
|
|---|
| 97 |
|
|---|
| 98 | if (objKeys.length === 0) {
|
|---|
| 99 | return '{}';
|
|---|
| 100 | }
|
|---|
| 101 |
|
|---|
| 102 | seen.push(val);
|
|---|
| 103 |
|
|---|
| 104 | const ret = '{' + tokens.newLine + objKeys.map((el, i) => {
|
|---|
| 105 | const eol = objKeys.length - 1 === i ? tokens.newLine : ',' + tokens.newLineOrSpace;
|
|---|
| 106 | const isSymbol = typeof el === 'symbol';
|
|---|
| 107 | const isClassic = !isSymbol && /^[a-z$_][a-z$_0-9]*$/i.test(el);
|
|---|
| 108 | const key = isSymbol || isClassic ? el : stringify(el, opts);
|
|---|
| 109 | let value = stringify(val[el], opts, pad + opts.indent);
|
|---|
| 110 | if (opts.transform) {
|
|---|
| 111 | value = opts.transform(val, el, value);
|
|---|
| 112 | }
|
|---|
| 113 | return tokens.indent + String(key) + ': ' + value + eol;
|
|---|
| 114 | }).join('') + tokens.pad + '}';
|
|---|
| 115 |
|
|---|
| 116 | seen.pop();
|
|---|
| 117 |
|
|---|
| 118 | return expandWhiteSpace(ret);
|
|---|
| 119 | }
|
|---|
| 120 |
|
|---|
| 121 | val = String(val).replace(/[\r\n]/g, x => x === '\n' ? '\\n' : '\\r');
|
|---|
| 122 |
|
|---|
| 123 | if (opts.singleQuotes === false) {
|
|---|
| 124 | val = val.replace(/"/g, '\\"');
|
|---|
| 125 | return `"${val}"`;
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | val = val.replace(/\\?'/g, '\\\'');
|
|---|
| 129 | return `'${val}'`;
|
|---|
| 130 | })(val, opts, pad);
|
|---|
| 131 | };
|
|---|