UNPKG

json-string-formatter

Version:
185 lines (168 loc) 4.46 kB
const MODE = { START: 'open', BETWEEN: 'between', END: 'end', STRING_SINGLE: 'string_single', STRING_DOUBLE: 'string_double', ESCAPE_IN_SINGLE: 'escape_in_single', ESCAPE_IN_DOUBLE: 'escape_in_double', }; const HIERARCHY = { OBJECT: 'object', ARRAY: 'array', }; const HierarchyByToken = { '[': HIERARCHY.Array, ']': HIERARCHY.Array, '{': HIERARCHY.OBJECT, '}': HIERARCHY.OBJECT, } const ERROR_MESSAGE = { EMPTY_INPUT: 'Empty input! Expect a json string.', INVALID_INPUT: 'Invalid input json! Your input doesn\'t match json schema.', NOT_CLOSED_INPUT: 'Invalid input json! Your input is not closed.', UNEXPECTE: 'Unexpected error!', }; function createIndents(indent, n) { if (!indent) { return ''; } return Array(n+1).join(indent); } /** * Format the given json string with specified 'indent' and 'linebreak'. * * @param {string} jstring Input json in string format. * @param {string} indent Customized indent. '\t' by default. * @param {string} linebreak Customized linebreak. '\n' by default. * @returns {string} The transformed json string. */ function format(jstring, indent = '\t', linebreak = '\n') { if (!jstring) { throw new Error(ERROR_MESSAGE.EMPTY_INPUT); } let input = jstring.trim(); let output = ''; let hierarchyStack = []; let prevHierarchy; let mode = MODE.START; let new_line = linebreak || ''; let depth = 0; for (let i = 0; i < input.length; i++) { let ch = input[i]; switch (mode) { case MODE.START: switch (ch) { case '{': case '[': mode = MODE.BETWEEN; i--; break; case ' ': case '\t': case '\n': break; default: throw new Error(ERROR_MESSAGE.INVALID_INPUT); break; } break; case MODE.BETWEEN: switch (ch) { case '{': case '[': output += ch + new_line; depth++; hierarchyStack.push(HierarchyByToken[ch]); output += createIndents(indent, depth); break; case '}': case ']': output += new_line; depth--; output += createIndents(indent, depth) + ch; if (hierarchyStack.pop() !== HierarchyByToken[ch]) { throw new Error(ERROR_MESSAGE.NOT_CLOSED_INPUT); } if (depth === 0) { mode = MODE.END; } break; case ',': output += ch + new_line; output += createIndents(indent, depth); break; case ':': output += ch + ' '; break; case '\'': output += ch; mode = MODE.STRING_SINGLE; break; case '"': output += ch; mode = MODE.STRING_DOUBLE; break; case ' ': case '\n': case '\t': case '\r': break; default: output += ch; break; } break; case MODE.END: switch (ch) { case ' ': case '\t': case '\n': case '\r': break; default: throw new Error(ERROR_MESSAGE.NOT_CLOSED_INPUT); } break; case MODE.STRING_SINGLE: output += ch; switch (ch) { case '\'': mode = MODE.BETWEEN; break; case '\\': mode = MODE.ESCAPE_IN_SINGLE; break; } break; case MODE.STRING_DOUBLE: output += ch; switch (ch) { case '"': mode = MODE.BETWEEN; break; case '\\': mode = MODE.ESCAPE_IN_DOUBLE; break; } break; case MODE.ESCAPE_IN_SINGLE: output += ch; mode = MODE.STRING_SINGLE; break; case MODE.ESCAPE_IN_DOUBLE: output += ch; mode = MODE.STRING_DOUBLE; break; } } if (depth !== 0) { throw new Error(ERROR_MESSAGE.NOT_CLOSED_INPUT); } return output; } module.exports = { format, createIndents, ERROR_MESSAGE, }