const utils_1 = require("@typescript-eslint/utils"); const load_1 = require("../utils/load"); const utils_2 = require("../utils/utils"); const getFunction_1 = require("../utils/getFunction"); module.exports = { name: "load-object-before-read", meta: { type: "problem", messages: { loadBeforeRead: "An explicit load call on '{{name}}' for property '{{loadValue}}' needs to be made before the property can be read.", }, docs: { description: "Before you can read the properties of a proxy object, you must explicitly load the properties.", category: "Possible Errors", recommended: false, url: "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#load", }, schema: [], }, create: function (context) { function isInsideWriteStatement(node) { while (node.parent) { node = node.parent; if (node.type === utils_1.TSESTree.AST_NODE_TYPES.AssignmentExpression) return true; } return false; } function hasBeenLoaded(node, loadLocation, propertyName) { var _a; return (loadLocation.has(propertyName) && // If reference came after load, return node.range[1] > ((_a = loadLocation.get(propertyName)) !== null && _a !== void 0 ? _a : 0)); } function findLoadBeforeRead(scope) { scope.variables.forEach((variable) => { let loadLocation = new Map(); let getFound = false; variable.references.forEach((reference) => { const node = reference.identifier; const parent = node.parent; if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.TSESTree.AST_NODE_TYPES.VariableDeclarator) { getFound = false; // In case of reassignment if (parent.init && (0, getFunction_1.isGetFunction)(parent.init) && !(0, getFunction_1.isGetOrNullObjectFunction)(parent.init)) { getFound = true; return; } } if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.TSESTree.AST_NODE_TYPES.AssignmentExpression) { getFound = false; // In case of reassignment if ((0, getFunction_1.isGetFunction)(parent.right) && !(0, getFunction_1.isGetOrNullObjectFunction)(parent.right)) { getFound = true; return; } } if (!getFound) { // If reference was not related to a previous get return; } // Look for .load(...) call if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) { const methodCall = (0, utils_2.findCallExpression)(parent); if (methodCall && (0, load_1.isLoadCall)(methodCall)) { const argument = methodCall.arguments[0]; let propertyNames = argument ? (0, load_1.parsePropertiesArgument)(argument) : ["*"]; propertyNames.forEach((propertyName) => { loadLocation.set(propertyName, node.range[1]); }); return; } } // Look for context.load(, "...") call if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.TSESTree.AST_NODE_TYPES.CallExpression) { const args = parent === null || parent === void 0 ? void 0 : parent.arguments; if ((0, load_1.isLoadCall)(parent) && args[0] == node && args.length < 3) { const propertyNames = args[1] ? (0, load_1.parsePropertiesArgument)(args[1]) : ["*"]; propertyNames.forEach((propertyName) => { loadLocation.set(propertyName, node.range[1]); }); return; } } const propertyName = (0, utils_2.findPropertiesRead)(parent); if (!propertyName || hasBeenLoaded(node, loadLocation, propertyName) || hasBeenLoaded(node, loadLocation, "*") || isInsideWriteStatement(node)) { return; } context.report({ node: node, messageId: "loadBeforeRead", data: { name: node.name, loadValue: propertyName }, }); }); }); scope.childScopes.forEach(findLoadBeforeRead); } return { Program() { findLoadBeforeRead(context.getScope()); }, }; }, }; //# sourceMappingURL=load-object-before-read.js.map