You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
8.2 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

'use strict';
const object = {};
const hasOwnProperty = object.hasOwnProperty;
const forOwn = (object, callback) => {
for (const key in object) {
if (hasOwnProperty.call(object, key)) {
callback(key, object[key]);
}
}
};
const extend = (destination, source) => {
if (!source) {
return destination;
}
forOwn(source, (key, value) => {
destination[key] = value;
});
return destination;
};
const forEach = (array, callback) => {
const length = array.length;
let index = -1;
while (++index < length) {
callback(array[index]);
}
};
const toString = object.toString;
const isArray = Array.isArray;
const isBuffer = Buffer.isBuffer;
const isObject = (value) => {
// This is a very simple check, but its good enough for what we need.
return toString.call(value) == '[object Object]';
};
const isString = (value) => {
return typeof value == 'string' ||
toString.call(value) == '[object String]';
};
const isNumber = (value) => {
return typeof value == 'number' ||
toString.call(value) == '[object Number]';
};
const isFunction = (value) => {
return typeof value == 'function';
};
const isMap = (value) => {
return toString.call(value) == '[object Map]';
};
const isSet = (value) => {
return toString.call(value) == '[object Set]';
};
/*--------------------------------------------------------------------------*/
// https://mathiasbynens.be/notes/javascript-escapes#single
const singleEscapes = {
'"': '\\"',
'\'': '\\\'',
'\\': '\\\\',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t'
// `\v` is omitted intentionally, because in IE < 9, '\v' == 'v'.
// '\v': '\\x0B'
};
const regexSingleEscape = /["'\\\b\f\n\r\t]/;
const regexDigit = /[0-9]/;
const regexWhitelist = /[ !#-&\(-\[\]-_a-~]/;
const jsesc = (argument, options) => {
const increaseIndentation = () => {
oldIndent = indent;
++options.indentLevel;
indent = options.indent.repeat(options.indentLevel)
};
// Handle options
const defaults = {
'escapeEverything': false,
'minimal': false,
'isScriptContext': false,
'quotes': 'single',
'wrap': false,
'es6': false,
'json': false,
'compact': true,
'lowercaseHex': false,
'numbers': 'decimal',
'indent': '\t',
'indentLevel': 0,
'__inline1__': false,
'__inline2__': false
};
const json = options && options.json;
if (json) {
defaults.quotes = 'double';
defaults.wrap = true;
}
options = extend(defaults, options);
if (
options.quotes != 'single' &&
options.quotes != 'double' &&
options.quotes != 'backtick'
) {
options.quotes = 'single';
}
const quote = options.quotes == 'double' ?
'"' :
(options.quotes == 'backtick' ?
'`' :
'\''
);
const compact = options.compact;
const lowercaseHex = options.lowercaseHex;
let indent = options.indent.repeat(options.indentLevel);
let oldIndent = '';
const inline1 = options.__inline1__;
const inline2 = options.__inline2__;
const newLine = compact ? '' : '\n';
let result;
let isEmpty = true;
const useBinNumbers = options.numbers == 'binary';
const useOctNumbers = options.numbers == 'octal';
const useDecNumbers = options.numbers == 'decimal';
const useHexNumbers = options.numbers == 'hexadecimal';
if (json && argument && isFunction(argument.toJSON)) {
argument = argument.toJSON();
}
if (!isString(argument)) {
if (isMap(argument)) {
if (argument.size == 0) {
return 'new Map()';
}
if (!compact) {
options.__inline1__ = true;
options.__inline2__ = false;
}
return 'new Map(' + jsesc(Array.from(argument), options) + ')';
}
if (isSet(argument)) {
if (argument.size == 0) {
return 'new Set()';
}
return 'new Set(' + jsesc(Array.from(argument), options) + ')';
}
if (isBuffer(argument)) {
if (argument.length == 0) {
return 'Buffer.from([])';
}
return 'Buffer.from(' + jsesc(Array.from(argument), options) + ')';
}
if (isArray(argument)) {
result = [];
options.wrap = true;
if (inline1) {
options.__inline1__ = false;
options.__inline2__ = true;
}
if (!inline2) {
increaseIndentation();
}
forEach(argument, (value) => {
isEmpty = false;
if (inline2) {
options.__inline2__ = false;
}
result.push(
(compact || inline2 ? '' : indent) +
jsesc(value, options)
);
});
if (isEmpty) {
return '[]';
}
if (inline2) {
return '[' + result.join(', ') + ']';
}
return '[' + newLine + result.join(',' + newLine) + newLine +
(compact ? '' : oldIndent) + ']';
} else if (isNumber(argument)) {
if (json) {
// Some number values (e.g. `Infinity`) cannot be represented in JSON.
return JSON.stringify(argument);
}
if (useDecNumbers) {
return String(argument);
}
if (useHexNumbers) {
let hexadecimal = argument.toString(16);
if (!lowercaseHex) {
hexadecimal = hexadecimal.toUpperCase();
}
return '0x' + hexadecimal;
}
if (useBinNumbers) {
return '0b' + argument.toString(2);
}
if (useOctNumbers) {
return '0o' + argument.toString(8);
}
} else if (!isObject(argument)) {
if (json) {
// For some values (e.g. `undefined`, `function` objects),
// `JSON.stringify(value)` returns `undefined` (which isnt valid
// JSON) instead of `'null'`.
return JSON.stringify(argument) || 'null';
}
return String(argument);
} else { // its an object
result = [];
options.wrap = true;
increaseIndentation();
forOwn(argument, (key, value) => {
isEmpty = false;
result.push(
(compact ? '' : indent) +
jsesc(key, options) + ':' +
(compact ? '' : ' ') +
jsesc(value, options)
);
});
if (isEmpty) {
return '{}';
}
return '{' + newLine + result.join(',' + newLine) + newLine +
(compact ? '' : oldIndent) + '}';
}
}
const string = argument;
// Loop over each code unit in the string and escape it
let index = -1;
const length = string.length;
result = '';
while (++index < length) {
const character = string.charAt(index);
if (options.es6) {
const first = string.charCodeAt(index);
if ( // check if its the start of a surrogate pair
first >= 0xD800 && first <= 0xDBFF && // high surrogate
length > index + 1 // there is a next code unit
) {
const second = string.charCodeAt(index + 1);
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
const codePoint = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
let hexadecimal = codePoint.toString(16);
if (!lowercaseHex) {
hexadecimal = hexadecimal.toUpperCase();
}
result += '\\u{' + hexadecimal + '}';
++index;
continue;
}
}
}
if (!options.escapeEverything) {
if (regexWhitelist.test(character)) {
// Its a printable ASCII character that is not `"`, `'` or `\`,
// so dont escape it.
result += character;
continue;
}
if (character == '"') {
result += quote == character ? '\\"' : character;
continue;
}
if (character == '`') {
result += quote == character ? '\\`' : character;
continue;
}
if (character == '\'') {
result += quote == character ? '\\\'' : character;
continue;
}
}
if (
character == '\0' &&
!json &&
!regexDigit.test(string.charAt(index + 1))
) {
result += '\\0';
continue;
}
if (regexSingleEscape.test(character)) {
// no need for a `hasOwnProperty` check here
result += singleEscapes[character];
continue;
}
const charCode = character.charCodeAt(0);
if (options.minimal && charCode != 0x2028 && charCode != 0x2029) {
result += character;
continue;
}
let hexadecimal = charCode.toString(16);
if (!lowercaseHex) {
hexadecimal = hexadecimal.toUpperCase();
}
const longhand = hexadecimal.length > 2 || json;
const escaped = '\\' + (longhand ? 'u' : 'x') +
('0000' + hexadecimal).slice(longhand ? -4 : -2);
result += escaped;
continue;
}
if (options.wrap) {
result = quote + result + quote;
}
if (quote == '`') {
result = result.replace(/\$\{/g, '\\\$\{');
}
if (options.isScriptContext) {
// https://mathiasbynens.be/notes/etago
return result
.replace(/<\/(script|style)/gi, '<\\/$1')
.replace(/<!--/g, json ? '\\u003C!--' : '\\x3C!--');
}
return result;
};
jsesc.version = '2.5.2';
module.exports = jsesc;