2016-10-10 03:43:44 +02:00
|
|
|
'use strict';
|
2017-08-14 05:01:11 +02:00
|
|
|
const isRegexp = require('is-regexp');
|
|
|
|
const isObj = require('is-obj');
|
|
|
|
const getOwnEnumPropSymbols = require('get-own-enumerable-property-symbols');
|
2016-10-10 03:43:44 +02:00
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
module.exports = (val, opts, pad) => {
|
|
|
|
const seen = [];
|
2016-10-10 03:43:44 +02:00
|
|
|
|
|
|
|
return (function stringify(val, opts, pad) {
|
|
|
|
opts = opts || {};
|
|
|
|
opts.indent = opts.indent || '\t';
|
|
|
|
pad = pad || '';
|
2017-08-14 05:01:11 +02:00
|
|
|
|
|
|
|
let tokens;
|
|
|
|
|
|
|
|
if (opts.inlineCharacterLimit === undefined) {
|
2016-10-10 03:43:44 +02:00
|
|
|
tokens = {
|
|
|
|
newLine: '\n',
|
|
|
|
newLineOrSpace: '\n',
|
2017-08-14 05:01:11 +02:00
|
|
|
pad,
|
2016-10-10 03:43:44 +02:00
|
|
|
indent: pad + opts.indent
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
tokens = {
|
|
|
|
newLine: '@@__STRINGIFY_OBJECT_NEW_LINE__@@',
|
|
|
|
newLineOrSpace: '@@__STRINGIFY_OBJECT_NEW_LINE_OR_SPACE__@@',
|
|
|
|
pad: '@@__STRINGIFY_OBJECT_PAD__@@',
|
|
|
|
indent: '@@__STRINGIFY_OBJECT_INDENT__@@'
|
2017-08-14 05:01:11 +02:00
|
|
|
};
|
2016-10-10 03:43:44 +02:00
|
|
|
}
|
2017-08-14 05:01:11 +02:00
|
|
|
|
|
|
|
const expandWhiteSpace = string => {
|
|
|
|
if (opts.inlineCharacterLimit === undefined) {
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const oneLined = string
|
|
|
|
.replace(new RegExp(tokens.newLine, 'g'), '')
|
|
|
|
.replace(new RegExp(tokens.newLineOrSpace, 'g'), ' ')
|
|
|
|
.replace(new RegExp(tokens.pad + '|' + tokens.indent, 'g'), '');
|
|
|
|
|
|
|
|
if (oneLined.length <= opts.inlineCharacterLimit) {
|
2016-10-10 03:43:44 +02:00
|
|
|
return oneLined;
|
|
|
|
}
|
2017-08-14 05:01:11 +02:00
|
|
|
|
|
|
|
return string
|
|
|
|
.replace(new RegExp(tokens.newLine + '|' + tokens.newLineOrSpace, 'g'), '\n')
|
|
|
|
.replace(new RegExp(tokens.pad, 'g'), pad)
|
|
|
|
.replace(new RegExp(tokens.indent, 'g'), pad + opts.indent);
|
2016-10-10 03:43:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (seen.indexOf(val) !== -1) {
|
|
|
|
return '"[Circular]"';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val === null ||
|
|
|
|
val === undefined ||
|
|
|
|
typeof val === 'number' ||
|
|
|
|
typeof val === 'boolean' ||
|
|
|
|
typeof val === 'function' ||
|
2017-08-14 05:01:11 +02:00
|
|
|
typeof val === 'symbol' ||
|
2016-10-10 03:43:44 +02:00
|
|
|
isRegexp(val)) {
|
|
|
|
return String(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val instanceof Date) {
|
2017-08-14 05:01:11 +02:00
|
|
|
return `new Date('${val.toISOString()}')`;
|
2016-10-10 03:43:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
if (val.length === 0) {
|
|
|
|
return '[]';
|
|
|
|
}
|
|
|
|
|
|
|
|
seen.push(val);
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
const ret = '[' + tokens.newLine + val.map((el, i) => {
|
|
|
|
const eol = val.length - 1 === i ? tokens.newLine : ',' + tokens.newLineOrSpace;
|
|
|
|
let value = stringify(el, opts, pad + opts.indent);
|
|
|
|
if (opts.transform) {
|
|
|
|
value = opts.transform(val, i, value);
|
|
|
|
}
|
|
|
|
return tokens.indent + value + eol;
|
2016-10-10 03:43:44 +02:00
|
|
|
}).join('') + tokens.pad + ']';
|
|
|
|
|
|
|
|
seen.pop(val);
|
|
|
|
|
|
|
|
return expandWhiteSpace(ret);
|
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
if (isObj(val)) {
|
|
|
|
const objKeys = Object.keys(val).concat(getOwnEnumPropSymbols(val));
|
2016-10-10 03:43:44 +02:00
|
|
|
|
|
|
|
if (objKeys.length === 0) {
|
|
|
|
return '{}';
|
|
|
|
}
|
|
|
|
|
|
|
|
seen.push(val);
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
const ret = '{' + tokens.newLine + objKeys.map((el, i) => {
|
2016-10-10 03:43:44 +02:00
|
|
|
if (opts.filter && !opts.filter(val, el)) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
const eol = objKeys.length - 1 === i ? tokens.newLine : ',' + tokens.newLineOrSpace;
|
|
|
|
const isSymbol = typeof el === 'symbol';
|
|
|
|
const isClassic = !isSymbol && /^[a-z$_][a-z$_0-9]*$/i.test(el);
|
|
|
|
const key = isSymbol || isClassic ? el : stringify(el, opts);
|
|
|
|
let value = stringify(val[el], opts, pad + opts.indent);
|
|
|
|
if (opts.transform) {
|
|
|
|
value = opts.transform(val, el, value);
|
|
|
|
}
|
|
|
|
return tokens.indent + String(key) + ': ' + value + eol;
|
2016-10-10 03:43:44 +02:00
|
|
|
}).join('') + tokens.pad + '}';
|
|
|
|
|
|
|
|
seen.pop(val);
|
|
|
|
|
|
|
|
return expandWhiteSpace(ret);
|
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
val = String(val).replace(/[\r\n]/g, x => x === '\n' ? '\\n' : '\\r');
|
2016-10-10 03:43:44 +02:00
|
|
|
|
|
|
|
if (opts.singleQuotes === false) {
|
2017-08-14 05:01:11 +02:00
|
|
|
val = val.replace(/"/g, '\\"');
|
|
|
|
return `"${val}"`;
|
2016-10-10 03:43:44 +02:00
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
val = val.replace(/\\?'/g, '\\\'');
|
|
|
|
return `'${val}'`;
|
2016-10-10 03:43:44 +02:00
|
|
|
})(val, opts, pad);
|
|
|
|
};
|