2017-09-25 14:43:51 +00:00
|
|
|
const STATE_IN_NUN_VAR = 'nunvar';
|
|
|
|
const STATE_IN_NUN_TAG = 'nuntag';
|
|
|
|
const STATE_IN_NUN_COM = 'nuncom';
|
|
|
|
const STATE_IN_STRING = 'string';
|
|
|
|
const STATE_NONE = 'none';
|
|
|
|
|
|
|
|
const NUNJUCKS_OPEN_STATES = {
|
|
|
|
'{{': STATE_IN_NUN_VAR,
|
|
|
|
'{%': STATE_IN_NUN_TAG,
|
|
|
|
'{#': STATE_IN_NUN_COM
|
|
|
|
};
|
|
|
|
|
|
|
|
const NUNJUCKS_CLOSE_STATES = {
|
|
|
|
'}}': STATE_IN_NUN_VAR,
|
|
|
|
'%}': STATE_IN_NUN_TAG,
|
|
|
|
'#}': STATE_IN_NUN_COM
|
|
|
|
};
|
2017-08-03 21:45:32 +00:00
|
|
|
|
2017-02-07 17:13:59 +00:00
|
|
|
/**
|
|
|
|
* Format a JSON string without parsing it as JavaScript.
|
|
|
|
*
|
|
|
|
* Code taken from jsonlint (http://zaa.ch/jsonlint/)
|
|
|
|
*
|
|
|
|
* @param json
|
|
|
|
* @param indentChars
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2017-09-25 14:43:51 +00:00
|
|
|
export function prettifyJson (json, indentChars = '\t') {
|
2017-10-30 12:55:04 +00:00
|
|
|
if (!json) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2017-02-10 21:17:55 +00:00
|
|
|
// Convert the unicode. To correctly mimic JSON.stringify(JSON.parse(json), null, indentChars)
|
|
|
|
// we need to convert all escaped unicode characters to proper unicode characters.
|
|
|
|
try {
|
|
|
|
json = _convertUnicode(json);
|
|
|
|
} catch (err) {
|
|
|
|
// Just in case (should never happen)
|
|
|
|
console.warn('Prettify failed to handle unicode', err);
|
|
|
|
}
|
|
|
|
|
2017-02-07 17:13:59 +00:00
|
|
|
let i = 0;
|
|
|
|
let il = json.length;
|
2017-06-07 21:51:41 +00:00
|
|
|
let tab = indentChars;
|
2017-02-07 17:13:59 +00:00
|
|
|
let newJson = '';
|
|
|
|
let indentLevel = 0;
|
|
|
|
let currentChar = null;
|
|
|
|
let nextChar = null;
|
2017-09-25 14:43:51 +00:00
|
|
|
let nextTwo = null;
|
|
|
|
let state = STATE_NONE;
|
2017-02-07 17:13:59 +00:00
|
|
|
|
2017-02-10 21:17:55 +00:00
|
|
|
for (; i < il; i += 1) {
|
2017-02-07 17:13:59 +00:00
|
|
|
currentChar = json.charAt(i);
|
2017-09-25 14:43:51 +00:00
|
|
|
nextChar = json.charAt(i + 1) || '';
|
|
|
|
nextTwo = currentChar + nextChar;
|
|
|
|
|
|
|
|
if (state === STATE_IN_STRING) {
|
|
|
|
if (currentChar === '"') {
|
|
|
|
state = STATE_NONE;
|
|
|
|
newJson += currentChar;
|
|
|
|
continue;
|
|
|
|
} else if (currentChar === '\\') {
|
|
|
|
newJson += currentChar + nextChar;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
newJson += currentChar;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2017-02-07 17:13:59 +00:00
|
|
|
|
2017-09-25 14:43:51 +00:00
|
|
|
// Close Nunjucks states
|
|
|
|
if (Object.values(NUNJUCKS_CLOSE_STATES).includes(state)) {
|
|
|
|
const closeState = NUNJUCKS_CLOSE_STATES[nextTwo];
|
|
|
|
if (closeState) {
|
|
|
|
state = STATE_NONE;
|
|
|
|
if (closeState === STATE_IN_NUN_COM) {
|
|
|
|
// Put comments on their own lines
|
|
|
|
newJson += nextTwo + '\n' + _repeatString(tab, indentLevel);
|
|
|
|
} else {
|
|
|
|
newJson += nextTwo;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
newJson += currentChar;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~ //
|
|
|
|
// Handle "nothing" State //
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~ //
|
|
|
|
|
|
|
|
// Open Nunjucks states
|
|
|
|
const nextState = NUNJUCKS_OPEN_STATES[nextTwo];
|
|
|
|
if (nextState) {
|
|
|
|
state = nextState;
|
|
|
|
newJson += nextTwo;
|
|
|
|
i++;
|
2017-06-07 21:51:41 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-02-07 17:13:59 +00:00
|
|
|
switch (currentChar) {
|
2017-09-25 14:43:51 +00:00
|
|
|
case ',':
|
|
|
|
newJson += currentChar + '\n' + _repeatString(tab, indentLevel);
|
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case '{':
|
2017-09-25 14:43:51 +00:00
|
|
|
if (nextChar === '}') {
|
|
|
|
newJson += currentChar + nextChar;
|
|
|
|
i++;
|
2017-02-07 17:13:59 +00:00
|
|
|
} else {
|
2017-09-25 14:43:51 +00:00
|
|
|
indentLevel++;
|
|
|
|
newJson += currentChar + '\n' + _repeatString(tab, indentLevel);
|
2017-02-07 17:13:59 +00:00
|
|
|
}
|
2017-09-25 14:43:51 +00:00
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case '[':
|
2017-09-25 14:43:51 +00:00
|
|
|
if (nextChar === ']') {
|
|
|
|
newJson += currentChar + nextChar;
|
|
|
|
i++;
|
2017-02-07 17:13:59 +00:00
|
|
|
} else {
|
2017-09-25 14:43:51 +00:00
|
|
|
indentLevel++;
|
|
|
|
newJson += currentChar + '\n' + _repeatString(tab, indentLevel);
|
2017-02-07 17:13:59 +00:00
|
|
|
}
|
2017-09-25 14:43:51 +00:00
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case '}':
|
2017-09-25 14:43:51 +00:00
|
|
|
indentLevel--;
|
|
|
|
newJson += '\n' + _repeatString(tab, indentLevel) + currentChar;
|
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case ']':
|
2017-09-25 14:43:51 +00:00
|
|
|
indentLevel--;
|
|
|
|
newJson += '\n' + _repeatString(tab, indentLevel) + currentChar;
|
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case ':':
|
2017-09-25 14:43:51 +00:00
|
|
|
newJson += ': ';
|
|
|
|
continue;
|
|
|
|
case '"':
|
|
|
|
state = STATE_IN_STRING;
|
|
|
|
newJson += currentChar;
|
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
case ' ':
|
|
|
|
case '\n':
|
|
|
|
case '\t':
|
2017-05-03 17:48:23 +00:00
|
|
|
case '\r':
|
2017-09-25 14:43:51 +00:00
|
|
|
// Don't add whitespace
|
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
default:
|
|
|
|
newJson += currentChar;
|
2017-09-25 14:43:51 +00:00
|
|
|
continue;
|
2017-02-07 17:13:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-07 21:51:41 +00:00
|
|
|
// Remove lines that only contain whitespace
|
|
|
|
return newJson.replace(/^\s*\n/gm, '');
|
2017-02-07 17:13:59 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 21:17:55 +00:00
|
|
|
function _repeatString (s, count) {
|
2017-02-07 17:13:59 +00:00
|
|
|
return new Array(count + 1).join(s);
|
|
|
|
}
|
2017-02-10 21:17:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert escaped unicode characters to real characters. Any JSON parser will do this by
|
|
|
|
* default. This is really fast too. Around 25ms for ~2MB of data with LOTS of unicode.
|
|
|
|
*
|
|
|
|
* @param originalStr
|
|
|
|
* @returns {string}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
function _convertUnicode (originalStr) {
|
|
|
|
let m;
|
|
|
|
let c;
|
|
|
|
let lastI = 0;
|
|
|
|
|
|
|
|
// Matches \u####
|
|
|
|
const unicodeRegex = /\\u([0-9a-fA-F]{4})/g;
|
|
|
|
|
|
|
|
let convertedStr = '';
|
2017-03-03 20:09:08 +00:00
|
|
|
while ((m = unicodeRegex.exec(originalStr))) {
|
2017-02-10 21:17:55 +00:00
|
|
|
try {
|
|
|
|
c = String.fromCharCode(parseInt(m[1], 16));
|
2017-04-07 18:10:15 +00:00
|
|
|
if (c === '"') {
|
|
|
|
// Escape it if it's double quotes
|
|
|
|
c = `\\${c}`;
|
|
|
|
}
|
2017-02-10 21:17:55 +00:00
|
|
|
convertedStr += originalStr.slice(lastI, m.index) + c;
|
2017-04-07 18:10:15 +00:00
|
|
|
lastI = m.index + m[0].length;
|
2017-02-10 21:17:55 +00:00
|
|
|
} catch (err) {
|
|
|
|
// Some reason we couldn't convert a char. Should never actually happen
|
|
|
|
console.warn('Failed to convert unicode char', m[1], err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, add the rest of the string to the end.
|
|
|
|
convertedStr += originalStr.slice(lastI, originalStr.length);
|
|
|
|
|
|
|
|
return convertedStr;
|
|
|
|
}
|