2021-10-05 13:52:27 +00:00
|
|
|
import { Position } from "codemirror";
|
|
|
|
|
import {
|
|
|
|
|
EvaluationError,
|
|
|
|
|
extraLibraries,
|
|
|
|
|
PropertyEvaluationErrorType,
|
|
|
|
|
} from "utils/DynamicBindingUtils";
|
|
|
|
|
import { JSHINT as jshint } from "jshint";
|
2021-12-14 08:30:43 +00:00
|
|
|
import { isEmpty, keys, last } from "lodash";
|
2021-10-07 12:33:15 +00:00
|
|
|
import {
|
|
|
|
|
EvaluationScripts,
|
|
|
|
|
EvaluationScriptType,
|
|
|
|
|
ScriptTemplate,
|
|
|
|
|
} from "workers/evaluate";
|
2021-12-02 10:03:43 +00:00
|
|
|
import { ECMA_VERSION } from "workers/constants";
|
2022-01-28 08:18:19 +00:00
|
|
|
import { getLintSeverity } from "components/editorComponents/CodeEditor/lintHelpers";
|
2021-10-07 12:33:15 +00:00
|
|
|
|
|
|
|
|
export const getPositionInEvaluationScript = (
|
|
|
|
|
type: EvaluationScriptType,
|
|
|
|
|
): Position => {
|
|
|
|
|
const script = EvaluationScripts[type];
|
|
|
|
|
|
|
|
|
|
const index = script.indexOf(ScriptTemplate);
|
|
|
|
|
const substr = script.substr(0, index);
|
|
|
|
|
const lines = substr.split("\n");
|
|
|
|
|
const lastLine = last(lines) || "";
|
|
|
|
|
|
|
|
|
|
return { line: lines.length, ch: lastLine.length };
|
|
|
|
|
};
|
|
|
|
|
|
2021-12-14 08:30:43 +00:00
|
|
|
const EvaluationScriptPositions: Record<string, Position> = {};
|
2021-10-07 12:33:15 +00:00
|
|
|
|
|
|
|
|
function getEvaluationScriptPosition(scriptType: EvaluationScriptType) {
|
2021-12-14 08:30:43 +00:00
|
|
|
if (isEmpty(EvaluationScriptPositions)) {
|
2021-10-07 12:33:15 +00:00
|
|
|
// We are computing position of <<script>> in our templates.
|
|
|
|
|
// This will be used to get the exact location of error in linting
|
|
|
|
|
keys(EvaluationScripts).forEach((type) => {
|
2021-12-14 08:30:43 +00:00
|
|
|
EvaluationScriptPositions[type] = getPositionInEvaluationScript(
|
2021-10-07 12:33:15 +00:00
|
|
|
type as EvaluationScriptType,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 08:30:43 +00:00
|
|
|
return EvaluationScriptPositions[scriptType];
|
2021-10-07 12:33:15 +00:00
|
|
|
}
|
2021-10-05 13:52:27 +00:00
|
|
|
|
|
|
|
|
export const getLintingErrors = (
|
|
|
|
|
script: string,
|
|
|
|
|
data: Record<string, unknown>,
|
|
|
|
|
originalBinding: string,
|
|
|
|
|
scriptType: EvaluationScriptType,
|
|
|
|
|
): EvaluationError[] => {
|
2021-10-07 12:33:15 +00:00
|
|
|
const scriptPos = getEvaluationScriptPosition(scriptType);
|
2021-10-05 13:52:27 +00:00
|
|
|
const globalData: Record<string, boolean> = {};
|
|
|
|
|
for (const dataKey in data) {
|
|
|
|
|
globalData[dataKey] = true;
|
|
|
|
|
}
|
|
|
|
|
// Jshint shouldn't throw errors for additional libraries
|
|
|
|
|
extraLibraries.forEach((lib) => (globalData[lib.accessor] = true));
|
|
|
|
|
|
|
|
|
|
globalData.console = true;
|
|
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
|
indent: 2,
|
2021-12-02 10:03:43 +00:00
|
|
|
esversion: ECMA_VERSION,
|
2021-10-05 13:52:27 +00:00
|
|
|
eqeqeq: false, // Not necessary to use ===
|
|
|
|
|
curly: false, // Blocks can be added without {}, eg if (x) return true
|
|
|
|
|
freeze: true, // Overriding inbuilt classes like Array is not allowed
|
|
|
|
|
undef: true, // Undefined variables should be reported as error
|
|
|
|
|
forin: false, // Doesn't require filtering for..in loops with obj.hasOwnProperty()
|
|
|
|
|
noempty: false, // Empty blocks are allowed
|
|
|
|
|
strict: false, // We won't force strict mode
|
2022-01-28 08:18:19 +00:00
|
|
|
unused: "strict", // Unused variables are not allowed
|
2021-10-05 13:52:27 +00:00
|
|
|
asi: true, // Tolerate Automatic Semicolon Insertion (no semicolons)
|
|
|
|
|
boss: true, // Tolerate assignments where comparisons would be expected
|
|
|
|
|
evil: false, // Use of eval not allowed
|
|
|
|
|
funcscope: true, // Tolerate variable definition inside control statements
|
|
|
|
|
sub: true, // Don't force dot notation
|
|
|
|
|
// environments
|
|
|
|
|
browser: true,
|
|
|
|
|
worker: true,
|
|
|
|
|
mocha: false,
|
|
|
|
|
// global values
|
|
|
|
|
globals: globalData,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
jshint(script, options);
|
|
|
|
|
|
|
|
|
|
return jshint.errors.map((lintError) => {
|
|
|
|
|
const ch = lintError.character;
|
|
|
|
|
return {
|
|
|
|
|
errorType: PropertyEvaluationErrorType.LINT,
|
|
|
|
|
raw: script,
|
2022-01-28 08:18:19 +00:00
|
|
|
severity: getLintSeverity(lintError.code),
|
2021-10-05 13:52:27 +00:00
|
|
|
errorMessage: lintError.reason,
|
|
|
|
|
errorSegment: lintError.evidence,
|
|
|
|
|
originalBinding,
|
|
|
|
|
// By keeping track of these variables we can highlight the exact text that caused the error.
|
|
|
|
|
variables: [lintError.a, lintError.b, lintError.c, lintError.d],
|
|
|
|
|
code: lintError.code,
|
|
|
|
|
line: lintError.line - scriptPos.line,
|
|
|
|
|
ch: lintError.line === scriptPos.line ? ch - scriptPos.ch : ch,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
};
|