json-fixer-browser
Version:
215 lines (189 loc) • 7.73 kB
JavaScript
const chalk = require('chalk');
const { psw, removeLinebreak, replaceChar, curlyBracesIncluded } = require('./utils');
const { quotify, numberify, baseNumify } = require('./transform');
const { parse } = require('./json.js');
const fixExtraChar = ({ fixedData, verbose, targetLine }) => {
/* eslint-disable security/detect-object-injection */
if (verbose) psw(chalk.magenta('Extra character'));
if (fixedData[targetLine] === '') --targetLine;
const brokenLine = removeLinebreak(fixedData[targetLine]);
let fixedLine = brokenLine.trimEnd();
fixedLine = fixedLine.substr(0, fixedLine.length - 1);
fixedData[targetLine] = fixedLine;
return fixedData;
};
const fixSingleQuotes = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Single quotes'));
const targetLine = start.line - 1;
const brokenLine = removeLinebreak(fixedData[targetLine]);
const fixedLine = brokenLine.replace(/(":\s*)'(.*?)'/g, '$1"$2"');
fixedData[targetLine] = fixedLine;
return fixedData;
};
const fixTrailingChar = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Trailing character'));
const targetLine = start.line - 1;
const brokenLine = removeLinebreak(fixedData[targetLine]);
const fixedLine = brokenLine.replace(/(":\s*)[.,](\d*)/g, '$10.$2');
const unquotedWord = /(":\s*)(\S*)/g.exec(fixedLine);
// if (unquotedWord === null) throw new Error('Unquoted word expected!');
const NN = Number.isNaN(Number(unquotedWord[2]));
if (NN && !/([xbo][0-9a-fA-F]+)/.test(unquotedWord[2])) {
return quotify({ fixedData, targetLine, fixedLine, verbose });
}
if (!NN && !/\0([xbo][0-9a-fA-F]+)/.test(unquotedWord[2])) {
return numberify({ fixedData, targetLine, fixedLine, unquotedWord, verbose });
}
let baseNumber = fixedLine.replace(/(":\s*)([xbo][0-9a-fA-F]*)/g, '$1"0$2"');
if (baseNumber !== fixedLine) {
baseNumber = baseNumify({ baseNumber, verbose });
}
fixedData[targetLine] = baseNumber;
return fixedData;
};
const fixMissingQuotes = ({ start, fixedData, verbose }) => {
/* eslint-disable security/detect-object-injection */
if (verbose) psw(chalk.magenta('Missing quotes'));
const targetLine = start.line - 1;
let brokenLine = removeLinebreak(fixedData[targetLine]);
const seCurlyBraces = curlyBracesIncluded(brokenLine);
if (seCurlyBraces) {
brokenLine = brokenLine.substring(1, brokenLine.length - 1);
}
const NO_RH_QUOTES = /(":\s*)([^,{}[\]]+)/;
const NO_LH_QUOTES = /(^[^"][\S\s]*)(:\s*["\w{[])/;
const RH = NO_RH_QUOTES.test(brokenLine);
let fixedLine = RH ? brokenLine.replace(NO_RH_QUOTES, '$1"$2"') : brokenLine;
const leftSpace = fixedLine.match(/^(\s+)/);
fixedLine = fixedLine.trimStart();
if (NO_LH_QUOTES.test(fixedLine)) {
const firstColon = fixedLine.indexOf(':');
const leftHand = fixedLine.substring(0, firstColon);
fixedLine = `"${leftHand}"${fixedLine.substring(firstColon)}`;
}
fixedData[targetLine] = `${leftSpace === null ? '' : leftSpace[0]}${fixedLine}`;
if (seCurlyBraces) {
fixedData[targetLine] = `{${fixedData[targetLine]}}`;
}
return fixedData;
};
const fixSquareBrackets = ({ start, fixedData, verbose, targetLine }) => {
/* eslint-disable security/detect-object-injection */
if (verbose) psw(chalk.magenta('Square brackets instead of curly ones'));
const lineToChange = fixedData[targetLine].includes('[')
? fixedData[targetLine]
: fixedData[++targetLine];
const brokenLine = removeLinebreak(lineToChange);
const fixedLine = replaceChar(brokenLine, start.column - 1, '{');
fixedData[targetLine] = fixedLine;
try {
parse(fixedData.join('\n'));
} catch (e) {
targetLine = e.location.start.line - 1;
const newLine = removeLinebreak(fixedData[targetLine]).replace(']', '}');
fixedData[targetLine] = newLine;
}
return fixedData;
};
const fixCurlyBrackets = ({ fixedData, verbose, targetLine }) => {
if (verbose) psw(chalk.magenta('Curly brackets instead of square ones'));
const brokenLine = removeLinebreak(
fixedData[targetLine].includes('{') ? fixedData[targetLine] : fixedData[++targetLine]
);
const fixedLine = replaceChar(brokenLine, brokenLine.indexOf('{'), '[');
fixedData[targetLine] = fixedLine;
try {
parse(fixedData.join('\n'));
} catch (e) {
targetLine = e.location.start.line - 1;
const newLine = removeLinebreak(fixedData[targetLine]).replace('}', ']');
fixedData[targetLine] = newLine;
}
return fixedData;
};
const fixMultilineComment = ({ fixedData, targetLine }) => {
let end = targetLine + 1;
while (end <= fixedData.length && !fixedData[end].includes('*/')) ++end;
for (let i = targetLine + 1; i <= end; ++i) fixedData[i] = '#RM';
fixedData[targetLine] = fixedData[targetLine].replace(/\s*\/\*+.*/g, '#RM');
return fixedData.filter((l) => l !== '#RM');
};
const fixComment = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Comment'));
const targetLine = start.line - 1;
const brokenLine = removeLinebreak(fixedData[targetLine]);
const fixedLine = brokenLine.replace(/(\s*)(\/\/.*|\/\*+.*?\*+\/)/g, '');
if (fixedLine.includes('/*')) {
return fixMultilineComment({ fixedData, targetLine });
}
fixedData[targetLine] = fixedLine;
return fixedData;
};
const fixOpConcat = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Operations/Concatenations'));
psw(
chalk.yellow(
'Please note: calculations made here may not be entirely correct on complex operations'
)
);
const targetLine = start.line - 1;
const brokenLine = removeLinebreak(fixedData[targetLine]);
const fixedLine = brokenLine
/* eslint-disable no-eval, security/detect-eval-with-expression */
.replace(
/(\d+)\s*([+\-*/%&|^><]|\*\*|>{2,3}|<<|[=!><]=|[=!]==)\s*(\d+)\s*([+\-*/%&|^><]|\*\*|>{2,3}|<<|[=!><]=|[=!]==)*\s*(\d+)*/g,
(eq) => eval(eq)
)
.replace(/[~!+-]\(?(\d+)\)?/g, (eq) => eval(eq))
.replace(/(":\s*)"(.*?)"\s*\+\s*"(.*?)"/g, '$1"$2$3"');
/* eslint-enable no-eval */
fixedData[targetLine] = fixedLine;
return fixedData;
};
const fixExtraCurlyBrackets = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Extra curly brackets'));
const targetLine = start.line - 1;
const fullData = fixedData.join('\n');
let fixedLine = removeLinebreak(fixedData[targetLine]);
const data = fullData.split('');
const openingCount = data.filter((c) => c === '{').length;
const closingCount = data.filter((c) => c === '}').length;
const bracketDiff = closingCount - openingCount;
for (let i = 0; i < bracketDiff; i++) {
const index = fixedLine.lastIndexOf('}');
fixedLine = fixedLine.slice(0, index) + fixedLine.slice(index + 1);
}
fixedData[targetLine] = fixedLine;
return fixedData;
};
const fixSpecialChar = ({ start, fixedData, verbose }) => {
if (verbose) psw(chalk.magenta('Special character'));
const targetLine = start.line - 1;
const brokenLine = fixedData[targetLine];
let fixedLine = brokenLine
.replace(/\f/g, '\\f')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
if (brokenLine.endsWith('"') && brokenLine[start.column] === undefined) {
if (verbose) psw(chalk.magenta('New line'));
const removedIndex = targetLine + 1;
const continuation = fixedData[removedIndex];
fixedLine = `${brokenLine}\\n${continuation}`;
fixedData.splice(removedIndex, 1);
}
fixedData[targetLine] = fixedLine;
return fixedData;
};
module.exports = {
fixExtraChar,
fixSingleQuotes,
fixTrailingChar,
fixMissingQuotes,
fixSquareBrackets,
fixCurlyBrackets,
fixComment,
fixOpConcat,
fixExtraCurlyBrackets,
fixSpecialChar
};