import type { DataTree, DataTreeEntity, } from "entities/DataTree/dataTreeFactory"; import type { Position } from "codemirror"; import type { LintError } from "utils/DynamicBindingUtils"; import { isDynamicValue, isPathADynamicBinding, PropertyEvaluationErrorType, } from "utils/DynamicBindingUtils"; import { MAIN_THREAD_ACTION } from "@appsmith/workers/Evaluation/evalWorkerActions"; import type { LintError as JSHintError } from "jshint"; import { JSHINT as jshint } from "jshint"; import { get, isEmpty, isNumber, keys, last } from "lodash"; import type { MemberExpressionData } from "@shared/ast"; import { extractInvalidTopLevelMemberExpressionsFromCode, isLiteralNode, } from "@shared/ast"; import { getDynamicBindings } from "utils/DynamicBindingUtils"; import type { createEvaluationContext } from "workers/Evaluation/evaluate"; import { EvaluationScripts, EvaluationScriptType, getScriptToEval, getScriptType, ScriptTemplate, } from "workers/Evaluation/evaluate"; import { getEntityNameAndPropertyPath, isAction, isATriggerPath, isJSAction, isWidget, } from "@appsmith/workers/Evaluation/evaluationUtils"; import { Severity } from "entities/AppsmithConsole"; import { JSLibraries } from "workers/common/JSLibrary"; import { WorkerMessenger } from "workers/Evaluation/fns/utils/Messenger"; import { asyncActionInSyncFieldLintMessage, CustomLintErrorCode, CUSTOM_LINT_ERRORS, IDENTIFIER_NOT_DEFINED_LINT_ERROR_CODE, IGNORED_LINT_ERRORS, INVALID_JSOBJECT_START_STATEMENT, JS_OBJECT_START_STATEMENT, lintOptions, SUPPORTED_WEB_APIS, WARNING_LINT_ERRORS, } from "./constants"; import { APPSMITH_GLOBAL_FUNCTIONS } from "components/editorComponents/ActionCreator/constants"; interface lintBindingPathProps { dynamicBinding: string; entity: DataTreeEntity; fullPropertyPath: string; globalData: ReturnType; } export function lintBindingPath({ dynamicBinding, entity, fullPropertyPath, globalData, }: lintBindingPathProps) { let lintErrors: LintError[] = []; if (isJSAction(entity)) { if (!entity.body) return lintErrors; if (!entity.body.startsWith(JS_OBJECT_START_STATEMENT)) { return lintErrors.concat([ { errorType: PropertyEvaluationErrorType.LINT, errorSegment: "", originalBinding: entity.body, line: 0, ch: 0, code: entity.body, variables: [], raw: entity.body, errorMessage: { name: "LintingError", message: INVALID_JSOBJECT_START_STATEMENT, }, severity: Severity.ERROR, }, ]); } } const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath); // Get the {{binding}} bound values const { jsSnippets, stringSegments } = getDynamicBindings( dynamicBinding, entity, ); if (stringSegments) { jsSnippets.forEach((jsSnippet, index) => { if (jsSnippet) { const jsSnippetToLint = getJSToLint(entity, jsSnippet, propertyPath); // {{user's code}} const originalBinding = getJSToLint( entity, stringSegments[index], propertyPath, ); const scriptType = getScriptType(false, false); const scriptToLint = getScriptToEval(jsSnippetToLint, scriptType); const lintErrorsFromSnippet = getLintingErrors({ script: scriptToLint, data: globalData, originalBinding, scriptType, entity, fullPropertyPath, }); lintErrors = lintErrors.concat(lintErrorsFromSnippet); } }); } return lintErrors; } interface lintTriggerPathProps { userScript: string; entity: DataTreeEntity; globalData: ReturnType; fullPropertyPath: string; } export function lintTriggerPath({ entity, fullPropertyPath, globalData, userScript, }: lintTriggerPathProps) { const { jsSnippets } = getDynamicBindings(userScript, entity); const script = getScriptToEval(jsSnippets[0], EvaluationScriptType.TRIGGERS); return getLintingErrors({ script, data: globalData, originalBinding: jsSnippets[0], scriptType: EvaluationScriptType.TRIGGERS, entity, fullPropertyPath, }); } export function pathRequiresLinting( dataTree: DataTree, entity: DataTreeEntity, fullPropertyPath: string, ): boolean { const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath); const unEvalPropertyValue = get( dataTree, fullPropertyPath, ) as unknown as string; if (isATriggerPath(entity, propertyPath)) { return isDynamicValue(unEvalPropertyValue); } const isADynamicBindingPath = (isAction(entity) || isWidget(entity) || isJSAction(entity)) && isPathADynamicBinding(entity, propertyPath); const requiresLinting = (isADynamicBindingPath && isDynamicValue(unEvalPropertyValue)) || isJSAction(entity); return requiresLinting; } // Removes "export default" statement from js Object export function getJSToLint( entity: DataTreeEntity, snippet: string, propertyPath: string, ): string { return entity && isJSAction(entity) && propertyPath === "body" ? snippet.replace(/export default/g, "") : snippet; } export function getPositionInEvaluationScript( type: EvaluationScriptType, ): Position { const script = EvaluationScripts[type]; const index = script.indexOf(ScriptTemplate); const substr = script.slice(0, index !== -1 ? index : 0); const lines = substr.split("\n"); const lastLine = last(lines) || ""; return { line: lines.length, ch: lastLine.length }; } const EvaluationScriptPositions: Record = {}; function getEvaluationScriptPosition(scriptType: EvaluationScriptType) { if (isEmpty(EvaluationScriptPositions)) { // We are computing position of <