Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
+44
View File
@@ -0,0 +1,44 @@
# eslint-plugin-office-addins
eslint plugin for office-addins
## Installation
This plugin is designed to work with the office-addin-lint package.
Install `office-addin-lint`
```
$ npm i office-addin-lint --save-dev
```
Next, install `eslint-plugin-office-addins`:
```
$ npm install eslint-plugin-office-addins --save-dev
```
## Usage
Add `office-addins` to the plugins section of your `eslint.config.js` configuration file. You can omit the `eslint-plugin-` prefix:
```json
{
"plugins": [
"office-addins"
]
}
```
Then configure the extended property by choosing one of the configurations for the plugin.
```json
{
"extended": [
"plugin:office-addins/recommended"
]
}
Other configurations available:
"plugin:office-addins/react",
"plugin:office-addins/reactnative"
```
+47
View File
@@ -0,0 +1,47 @@
import tsParser from "@typescript-eslint/parser";
import typescriptplugin from "@typescript-eslint/eslint-plugin";
import prettierplugin from "eslint-plugin-prettier";
import eslintjs from "@eslint/js";
import eslintts from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
export default [
eslintjs.configs.recommended,
...eslintts.configs.recommended,
eslintConfigPrettier,
{
files: ["**/*.{js,mjs,cjs,ts,cts,mts}"],
ignores: ["**/node_modules/**", "**/lib/**"],
plugins: {
"@typescript-eslint": typescriptplugin,
prettier: prettierplugin,
},
languageOptions: {
parser: tsParser,
ecmaVersion: 6,
sourceType: "module",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
"@typescript-eslint/no-unused-vars": "error",
"no-delete-var": "warn",
"no-eval": "error",
"no-inner-declarations": "warn",
"no-octal": "warn",
"no-unused-vars": "off",
"prettier/prettier": [
"error",
{
endOfLine: "auto",
},
],
},
},
];
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
};
+1
View File
@@ -0,0 +1 @@
export {};
+108
View File
@@ -0,0 +1,108 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const rules_1 = __importDefault(require("./rules"));
const parser_1 = __importDefault(require("@typescript-eslint/parser"));
const eslint_plugin_1 = __importDefault(require("@typescript-eslint/eslint-plugin"));
const eslint_plugin_prettier_1 = __importDefault(require("eslint-plugin-prettier"));
const eslint_plugin_react_1 = __importDefault(require("eslint-plugin-react"));
const js_1 = __importDefault(require("@eslint/js"));
const eslint_config_prettier_1 = __importDefault(require("eslint-config-prettier"));
// eslint-disable-next-line @typescript-eslint/no-require-imports
const reactnativeplugin = require("eslint-plugin-react-native");
const plugin = {
meta: {
name: "eslint-plugin-office-addins",
version: "5.0.0",
},
rules: rules_1.default,
configs: {},
};
const recommended = [
js_1.default.configs.recommended,
eslint_config_prettier_1.default,
{
files: ["**/*.{js,mjs,cjs,ts,cts,mts}"],
plugins: {
"@typescript-eslint": eslint_plugin_1.default,
"office-addins": plugin,
prettier: eslint_plugin_prettier_1.default,
},
languageOptions: {
parser: parser_1.default,
ecmaVersion: 6,
sourceType: "module",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
"@typescript-eslint/no-unused-vars": "error",
"no-delete-var": "warn",
"no-eval": "error",
"no-inner-declarations": "warn",
"no-octal": "warn",
"no-unused-vars": "off",
"office-addins/call-sync-after-load": "error",
"office-addins/call-sync-before-read": "error",
"office-addins/load-object-before-read": "error",
"office-addins/no-context-sync-in-loop": "warn",
"office-addins/no-empty-load": "warn",
"office-addins/no-navigational-load": "warn",
"office-addins/no-office-initialize": "warn",
"office-addins/test-for-null-using-isNullObject": "error",
"prettier/prettier": ["error", { endOfLine: "auto" }],
},
},
];
const react = [
eslint_plugin_react_1.default.configs.flat.recommended,
...recommended,
{
plugins: {
"office-addins": plugin,
react: eslint_plugin_react_1.default,
},
settings: {
react: {
version: "detect",
},
},
},
];
const reactnative = [
reactnativeplugin.configs.all,
...recommended,
{
plugins: {
"office-addins": plugin,
react: reactnativeplugin,
},
settings: {
react: {
version: "detect",
},
},
},
];
const test = [
...recommended,
{
plugins: {
"office-addins": plugin,
},
rules: {},
},
];
Object.assign(plugin.configs, {
recommended,
react,
reactnative,
test,
});
module.exports = plugin;
//# sourceMappingURL=main.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;AAAA,oDAA4B;AAC5B,uEAAiD;AACjD,qFAAgE;AAChE,oFAAoD;AACpD,8EAA8C;AAC9C,oDAAkC;AAClC,oFAA0D;AAE1D,iEAAiE;AACjE,MAAM,iBAAiB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;AAEhE,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,6BAA6B;QACnC,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAL,eAAK;IACL,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,YAAQ,CAAC,OAAO,CAAC,WAAW;IAC5B,gCAAoB;IACpB;QACE,KAAK,EAAE,CAAC,8BAA8B,CAAC;QACvC,OAAO,EAAE;YACP,oBAAoB,EAAE,uBAAgB;YACtC,eAAe,EAAE,MAAM;YACvB,QAAQ,EAAE,gCAAc;SACzB;QACD,eAAe,EAAE;YACf,MAAM,EAAE,gBAAQ;YAChB,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,QAAQ;YACpB,aAAa,EAAE;gBACb,YAAY,EAAE;oBACZ,GAAG,EAAE,IAAI;iBACV;aACF;SACF;QACD,KAAK,EAAE;YACL,mCAAmC,EAAE,OAAO;YAC5C,eAAe,EAAE,MAAM;YACvB,SAAS,EAAE,OAAO;YAClB,uBAAuB,EAAE,MAAM;YAC/B,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,KAAK;YACvB,oCAAoC,EAAE,OAAO;YAC7C,qCAAqC,EAAE,OAAO;YAC9C,uCAAuC,EAAE,OAAO;YAChD,uCAAuC,EAAE,MAAM;YAC/C,6BAA6B,EAAE,MAAM;YACrC,oCAAoC,EAAE,MAAM;YAC5C,oCAAoC,EAAE,MAAM;YAC5C,gDAAgD,EAAE,OAAO;YACzD,mBAAmB,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;SACtD;KACF;CACF,CAAC;AAEF,MAAM,KAAK,GAAG;IACZ,6BAAW,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW;IACpC,GAAG,WAAW;IACd;QACE,OAAO,EAAE;YACP,eAAe,EAAE,MAAM;YACvB,KAAK,EAAE,6BAAW;SACnB;QACD,QAAQ,EAAE;YACR,KAAK,EAAE;gBACL,OAAO,EAAE,QAAQ;aAClB;SACF;KACF;CACF,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,iBAAiB,CAAC,OAAO,CAAC,GAAG;IAC7B,GAAG,WAAW;IACd;QACE,OAAO,EAAE;YACP,eAAe,EAAE,MAAM;YACvB,KAAK,EAAE,iBAAiB;SACzB;QACD,QAAQ,EAAE;YACR,KAAK,EAAE;gBACL,OAAO,EAAE,QAAQ;aAClB;SACF;KACF;CACF,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,GAAG,WAAW;IACd;QACE,OAAO,EAAE;YACP,eAAe,EAAE,MAAM;SACxB;QACD,KAAK,EAAE,EAAE;KACV;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;IAC5B,WAAW;IACX,KAAK;IACL,WAAW;IACX,IAAI;CACL,CAAC,CAAC;AAEH,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"callSyncAfterLoad", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const load_1 = require("../utils/load");
const utils_2 = require("../utils/utils");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#load")({
name: "call-sync-after-load",
meta: {
type: "suggestion",
messages: {
callSyncAfterLoad: "Call context.sync() after calling load on '{{name}}' for the property '{{loadValue}}' and before reading the property.",
},
docs: {
description: "Always call context.sync() between loading one or more properties on objects and reading any of those properties.",
},
schema: [],
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
class VariablePropertySet extends Set {
add(variableProperty) {
return super.add(JSON.stringify(variableProperty));
}
has(variableProperty) {
return super.has(JSON.stringify(variableProperty));
}
}
let apiReferences = [];
function findLoadBeforeSync() {
const needSync = new VariablePropertySet();
apiReferences.forEach((apiReference) => {
const operation = apiReference.operation;
const reference = apiReference.reference;
const identifier = reference.identifier;
const variable = reference.resolved;
if (operation === "Load" && variable) {
const propertiesArgument = getPropertiesArgument(identifier);
const propertyNames = propertiesArgument
? (0, load_1.parsePropertiesArgument)(propertiesArgument)
: ["*"];
propertyNames.forEach((propertyName) => {
needSync.add({
variable: variable.name,
property: propertyName,
});
});
}
else if (operation === "Sync") {
needSync.clear();
}
else if (operation === "Read" && variable) {
const propertyName = (0, utils_2.findPropertiesRead)(reference.identifier.parent);
if (needSync.has({ variable: variable.name, property: propertyName }) ||
needSync.has({ variable: variable.name, property: "*" })) {
const node = reference.identifier;
context.report({
node: node,
messageId: "callSyncAfterLoad",
data: { name: node.name, loadValue: propertyName },
});
}
}
});
}
function getPropertiesArgument(identifier) {
var _a, _b;
let propertiesArgument;
if (((_a = identifier.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
// Look for <obj>.load(...) call
const methodCall = (0, utils_2.findCallExpression)(identifier.parent);
if (methodCall && (0, load_1.isLoadCall)(methodCall)) {
propertiesArgument = methodCall.arguments[0];
}
}
else if (((_b = identifier.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.TSESTree.AST_NODE_TYPES.CallExpression) {
// Look for context.load(<obj>, "...") call
const args = identifier.parent.arguments;
if ((0, load_1.isLoadCall)(identifier.parent) &&
args[0] == identifier &&
args.length < 3) {
propertiesArgument = args[1];
}
}
return propertiesArgument;
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
apiReferences = (0, utils_2.findOfficeApiReferences)(scope);
apiReferences.sort((left, right) => {
return (left.reference.identifier.range[1] -
right.reference.identifier.range[1]);
});
findLoadBeforeSync();
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=call-sync-after-load.js.map
@@ -0,0 +1 @@
{"version":3,"file":"call-sync-after-load.js","sourceRoot":"","sources":["../../src/rules/call-sync-after-load.ts"],"names":[],"mappings":";;AAAA,oDAAiE;AAEjE,wCAAoE;AACpE,0CAKwB;AAExB,kBAAe,mBAAW,CAAC,WAAW,CACpC,GAAG,EAAE,CACH,2FAA2F,CAC9F,CAAC;IACA,IAAI,EAAE,sBAAsB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE;YACR,iBAAiB,EACf,wHAAwH;SAC3H;QACD,IAAI,EAAE;YACJ,WAAW,EACT,mHAAmH;SACtH;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,EAAE,UAAU,OAAO;;QACvB,MAAM,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAMjE,MAAM,mBAAoB,SAAQ,GAAG;YACnC,GAAG,CAAC,gBAAkC;gBACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,GAAG,CAAC,gBAAkC;gBACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;YACrD,CAAC;SACF;QAED,IAAI,aAAa,GAAyB,EAAE,CAAC;QAE7C,SAAS,kBAAkB;YACzB,MAAM,QAAQ,GAAwB,IAAI,mBAAmB,EAAE,CAAC;YAEhE,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBACrC,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC;gBACzC,MAAM,SAAS,GAAc,YAAY,CAAC,SAAS,CAAC;gBACpD,MAAM,UAAU,GAAkB,SAAS,CAAC,UAAU,CAAC;gBACvD,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC;gBAEpC,IAAI,SAAS,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC;oBACrC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;oBAC7D,MAAM,aAAa,GAAa,kBAAkB;wBAChD,CAAC,CAAC,IAAA,8BAAuB,EAAC,kBAAkB,CAAC;wBAC7C,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;oBACV,aAAa,CAAC,OAAO,CAAC,CAAC,YAAoB,EAAE,EAAE;wBAC7C,QAAQ,CAAC,GAAG,CAAC;4BACX,QAAQ,EAAE,QAAQ,CAAC,IAAI;4BACvB,QAAQ,EAAE,YAAY;yBACvB,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;oBAChC,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,CAAC;qBAAM,IAAI,SAAS,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC;oBAC5C,MAAM,YAAY,GAAW,IAAA,0BAAkB,EAC7C,SAAS,CAAC,UAAU,CAAC,MAAM,CAC5B,CAAC;oBAEF,IACE,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;wBACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EACxD,CAAC;wBACD,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC;wBAClC,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,IAAI;4BACV,SAAS,EAAE,mBAAmB;4BAC9B,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE;yBACnD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,SAAS,qBAAqB,CAC5B,UAAwD;;YAExD,IAAI,kBAAkB,CAAC;YACvB,IACE,CAAA,MAAA,UAAU,CAAC,MAAM,0CAAE,IAAI,MAAK,gBAAQ,CAAC,cAAc,CAAC,gBAAgB,EACpE,CAAC;gBACD,gCAAgC;gBAChC,MAAM,UAAU,GAAG,IAAA,0BAAkB,EAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBAEzD,IAAI,UAAU,IAAI,IAAA,iBAAU,EAAC,UAAU,CAAC,EAAE,CAAC;oBACzC,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;iBAAM,IACL,CAAA,MAAA,UAAU,CAAC,MAAM,0CAAE,IAAI,MAAK,gBAAQ,CAAC,cAAc,CAAC,cAAc,EAClE,CAAC;gBACD,2CAA2C;gBAC3C,MAAM,IAAI,GACR,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC9B,IACE,IAAA,iBAAU,EAAC,UAAU,CAAC,MAAM,CAAC;oBAC7B,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU;oBACrB,IAAI,CAAC,MAAM,GAAG,CAAC,EACf,CAAC;oBACD,kBAAkB,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;YAED,OAAO,kBAAkB,CAAC;QAC5B,CAAC;QAED,OAAO;YACL,OAAO,CAAC,IAAI;gBACV,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ;oBAC/B,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAC3B,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACvB,aAAa,GAAG,IAAA,+BAAuB,EAAC,KAAK,CAAC,CAAC;gBAC/C,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;oBACjC,OAAO,CACL,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;wBAClC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CACpC,CAAC;gBACJ,CAAC,CAAC,CAAC;gBACH,kBAAkB,EAAE,CAAC;YACvB,CAAC;SACF,CAAC;IACJ,CAAC;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"callSync", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils/utils");
const utils_3 = require("../utils/utils");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#sync")({
name: "call-sync-before-read",
meta: {
type: "problem",
messages: {
callSync: "Call context.sync() before trying to read '{{name}}'.",
},
docs: {
description: "Always call load on the object's properties followed by a context.sync() before reading them.",
},
schema: [],
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
let apiReferences = [];
function checkPropertyIsRead(node) {
var _a;
const topExpression = (0, utils_2.findTopMemberExpression)(node);
switch ((_a = topExpression.parent) === null || _a === void 0 ? void 0 : _a.type) {
case utils_1.TSESTree.AST_NODE_TYPES.AssignmentExpression:
return topExpression.parent.right === topExpression;
default:
return true;
}
}
function findReadBeforeSync() {
const needSync = new Set();
apiReferences.forEach((apiReference) => {
var _a;
const operation = apiReference.operation;
const reference = apiReference.reference;
const variable = reference.resolved;
if (operation === "Get" && variable) {
needSync.add(variable);
}
if (operation === "Sync") {
needSync.clear();
}
if (operation === "Read" && variable && needSync.has(variable)) {
const node = reference.identifier;
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
checkPropertyIsRead(node.parent)) {
context.report({
node: node,
messageId: "callSync",
data: { name: node.name },
});
}
}
});
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
apiReferences = (0, utils_3.findOfficeApiReferences)(scope);
apiReferences.sort((left, right) => {
return (left.reference.identifier.range[1] -
right.reference.identifier.range[1]);
});
findReadBeforeSync();
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=call-sync-before-read.js.map
@@ -0,0 +1 @@
{"version":3,"file":"call-sync-before-read.js","sourceRoot":"","sources":["../../src/rules/call-sync-before-read.ts"],"names":[],"mappings":"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"}
+11
View File
@@ -0,0 +1,11 @@
declare const _default: {
"call-sync-before-read": import("@typescript-eslint/utils/ts-eslint").RuleModule<"callSync", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"load-object-before-read": import("@typescript-eslint/utils/ts-eslint").RuleModule<"loadBeforeRead", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"call-sync-after-load": import("@typescript-eslint/utils/ts-eslint").RuleModule<"callSyncAfterLoad", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"no-context-sync-in-loop": import("@typescript-eslint/utils/ts-eslint").RuleModule<"loopedSync", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"no-empty-load": import("@typescript-eslint/utils/ts-eslint").RuleModule<"emptyLoad", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"no-navigational-load": import("@typescript-eslint/utils/ts-eslint").RuleModule<"navigationalLoad", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"no-office-initialize": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOfficeInitialize", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
"test-for-null-using-isNullObject": import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNullObject", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
};
export default _default;
+24
View File
@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const call_sync_after_load_1 = __importDefault(require("./call-sync-after-load"));
const call_sync_before_read_1 = __importDefault(require("./call-sync-before-read"));
const load_object_before_read_1 = __importDefault(require("./load-object-before-read"));
const no_context_sync_in_loop_1 = __importDefault(require("./no-context-sync-in-loop"));
const no_empty_load_1 = __importDefault(require("./no-empty-load"));
const no_navigational_load_1 = __importDefault(require("./no-navigational-load"));
const no_office_initialize_1 = __importDefault(require("./no-office-initialize"));
const test_for_null_using_isNullObject_1 = __importDefault(require("./test-for-null-using-isNullObject"));
exports.default = {
"call-sync-before-read": call_sync_before_read_1.default,
"load-object-before-read": load_object_before_read_1.default,
"call-sync-after-load": call_sync_after_load_1.default,
"no-context-sync-in-loop": no_context_sync_in_loop_1.default,
"no-empty-load": no_empty_load_1.default,
"no-navigational-load": no_navigational_load_1.default,
"no-office-initialize": no_office_initialize_1.default,
"test-for-null-using-isNullObject": test_for_null_using_isNullObject_1.default,
};
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":";;;;;AAAA,kFAAuD;AACvD,oFAAyD;AACzD,wFAA6D;AAC7D,wFAA4D;AAC5D,oEAA0C;AAC1C,kFAAwD;AACxD,kFAAwD;AACxD,0GAA8E;AAE9E,kBAAe;IACb,uBAAuB,EAAE,+BAAkB;IAC3C,yBAAyB,EAAE,iCAAoB;IAC/C,sBAAsB,EAAE,8BAAiB;IACzC,yBAAyB,EAAE,iCAAmB;IAC9C,eAAe,EAAE,uBAAW;IAC5B,sBAAsB,EAAE,8BAAkB;IAC1C,sBAAsB,EAAE,8BAAkB;IAC1C,kCAAkC,EAAE,0CAA4B;CACjE,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"loadBeforeRead", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#load")({
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.",
},
schema: [],
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
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) => {
const 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 <obj>.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];
const propertyNames = argument
? (0, load_1.parsePropertiesArgument)(argument)
: ["*"];
propertyNames.forEach((propertyName) => {
loadLocation.set(propertyName, node.range[1]);
});
return;
}
}
// Look for context.load(<obj>, "...") 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(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findLoadBeforeRead(scope);
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=load-object-before-read.js.map
@@ -0,0 +1 @@
{"version":3,"file":"load-object-before-read.js","sourceRoot":"","sources":["../../src/rules/load-object-before-read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"loopedSync", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/concepts/correlated-objects-pattern")({
name: "no-context-sync-in-loop",
meta: {
type: "problem",
messages: {
loopedSync: "Calling context.sync() inside a loop can lead to poor performance",
},
docs: {
description: "Calling context.sync() inside of a loop dramatically increases the time the code runs, proportional to the number of iterations.",
},
schema: [],
},
create: function (context) {
return {
":matches(ForStatement, ForInStatement, WhileStatement, DoWhileStatement, ForOfStatement) CallExpression[callee.object.name='context'][callee.property.name='sync']"(node) {
context.report({
node: node.callee,
messageId: "loopedSync",
});
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=no-context-sync-in-loop.js.map
@@ -0,0 +1 @@
{"version":3,"file":"no-context-sync-in-loop.js","sourceRoot":"","sources":["../../src/rules/no-context-sync-in-loop.ts"],"names":[],"mappings":";;AAAA,oDAAiE;AAEjE,kBAAe,mBAAW,CAAC,WAAW,CACpC,GAAG,EAAE,CACH,mFAAmF,CACtF,CAAC;IACA,IAAI,EAAE,yBAAyB;IAC/B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,UAAU,EACR,mEAAmE;SACtE;QACD,IAAI,EAAE;YACJ,WAAW,EACT,kIAAkI;SACrI;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,EAAE,UAAU,OAAO;QACvB,OAAO;YACL,oKAAoK,CAClK,IAA6B;gBAE7B,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,IAAI,CAAC,MAAM;oBACjB,SAAS,EAAE,YAAY;iBACxB,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"emptyLoad", [], unknown, ESLintUtils.RuleListener>;
export default _default;
+76
View File
@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const getFunction_1 = require("../utils/getFunction");
const load_1 = require("../utils/load");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#calling-load-without-parameters-not-recommended")({
name: "no-empty-load",
meta: {
type: "problem",
messages: {
emptyLoad: "Calling load without any argument slows down your add-in.",
},
docs: {
description: "Calling load without any argument causes unneeded data to load and slows down your add-in.",
},
schema: [],
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
function isEmptyLoad(node) {
if ((0, load_1.isLoadFunction)(node)) {
const propertyNames = (0, load_1.parseLoadArguments)(node);
if (propertyNames.length === 0) {
return true;
}
let foundEmptyProperty = false;
propertyNames.forEach((property) => {
if (!property) {
foundEmptyProperty = true;
}
});
return foundEmptyProperty;
}
return false;
}
function findEmptyLoad(scope) {
scope.variables.forEach((variable) => {
let getFound = false;
variable.references.forEach((reference) => {
var _a;
const node = reference.identifier;
if (reference.isWrite()) {
getFound = false; // In case of reassignment
if (reference.writeExpr && (0, getFunction_1.isGetFunction)(reference.writeExpr)) {
getFound = true;
return;
}
}
if (!getFound) {
// If reference was not related to a previous get
return;
}
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
isEmptyLoad(node.parent)) {
context.report({
node: node.parent,
messageId: "emptyLoad",
});
}
});
});
scope.childScopes.forEach(findEmptyLoad);
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findEmptyLoad(scope);
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=no-empty-load.js.map
@@ -0,0 +1 @@
{"version":3,"file":"no-empty-load.js","sourceRoot":"","sources":["../../src/rules/no-empty-load.ts"],"names":[],"mappings":";;AAAA,oDAAiE;AAEjE,sDAAqD;AACrD,wCAAmE;AAEnE,kBAAe,mBAAW,CAAC,WAAW,CACpC,GAAG,EAAE,CACH,sIAAsI,CACzI,CAAC;IACA,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,SAAS,EAAE,2DAA2D;SACvE;QACD,IAAI,EAAE;YACJ,WAAW,EACT,4FAA4F;SAC/F;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,EAAE,UAAU,OAAO;;QACvB,MAAM,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QACjE,SAAS,WAAW,CAAC,IAA+B;YAClD,IAAI,IAAA,qBAAc,EAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,MAAM,aAAa,GAAa,IAAA,yBAAkB,EAAC,IAAI,CAAC,CAAC;gBACzD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC/B,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,IAAI,kBAAkB,GAAG,KAAK,CAAC;gBAC/B,aAAa,CAAC,OAAO,CAAC,CAAC,QAAgB,EAAE,EAAE;oBACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,kBAAkB,GAAG,IAAI,CAAC;oBAC5B,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,OAAO,kBAAkB,CAAC;YAC5B,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,SAAS,aAAa,CAAC,KAAY;YACjC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAkB,EAAE,EAAE;gBAC7C,IAAI,QAAQ,GAAY,KAAK,CAAC;gBAC9B,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAoB,EAAE,EAAE;;oBACnD,MAAM,IAAI,GAAkB,SAAS,CAAC,UAAU,CAAC;oBAEjD,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;wBACxB,QAAQ,GAAG,KAAK,CAAC,CAAC,0BAA0B;wBAC5C,IAAI,SAAS,CAAC,SAAS,IAAI,IAAA,2BAAa,EAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;4BAC9D,QAAQ,GAAG,IAAI,CAAC;4BAChB,OAAO;wBACT,CAAC;oBACH,CAAC;oBAED,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,iDAAiD;wBACjD,OAAO;oBACT,CAAC;oBAED,IACE,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,IAAI,MAAK,gBAAQ,CAAC,cAAc,CAAC,gBAAgB;wBAC9D,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EACxB,CAAC;wBACD,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,IAAI,CAAC,MAAM;4BACjB,SAAS,EAAE,WAAW;yBACvB,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO;YACL,OAAO,CAAC,IAAI;gBACV,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ;oBAC/B,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAC3B,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACvB,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;SACF,CAAC;IACJ,CAAC;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"navigationalLoad", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,105 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const getFunction_1 = require("../utils/getFunction");
const load_1 = require("../utils/load");
const propertiesType_1 = require("../utils/propertiesType");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#scalar-and-navigation-properties")({
name: "no-navigational-load",
meta: {
type: "problem",
messages: {
navigationalLoad: "Calling load on the navigation property '{{loadValue}}' slows down your add-in.",
},
docs: {
description: "Calling load on a navigation property causes unneeded data to load and slows down your add-in.",
},
schema: [],
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
function isLoadingValidPropeties(propertyName) {
const properties = propertyName.split("/");
const lastProperty = properties.pop();
if (!lastProperty)
return false;
for (const property of properties) {
const propertyType = (0, propertiesType_1.getPropertyType)(property);
if (propertyType !== propertiesType_1.PropertyType.navigational &&
propertyType !== propertiesType_1.PropertyType.ambiguous) {
return false;
}
}
if (lastProperty === "*") {
return true;
}
const propertyType = (0, propertiesType_1.getPropertyType)(lastProperty);
return (propertyType === propertiesType_1.PropertyType.scalar ||
propertyType === propertiesType_1.PropertyType.ambiguous);
}
function findNavigationalLoad(scope) {
scope.variables.forEach((variable) => {
let getFound = false;
variable.references.forEach((reference) => {
var _a, _b;
const node = reference.identifier;
if (reference.isWrite()) {
getFound = false; // In case of reassignment
if (reference.writeExpr && (0, getFunction_1.isGetFunction)(reference.writeExpr)) {
getFound = true;
return;
}
}
if (!getFound) {
// If reference was not related to a previous get
return;
}
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
(0, load_1.isLoadFunction)(node.parent)) {
// <obj>.load(...) call
const propertyNames = (0, load_1.parseLoadArguments)(node.parent);
propertyNames.forEach((propertyName) => {
if (propertyName && !isLoadingValidPropeties(propertyName)) {
context.report({
node: node.parent,
messageId: "navigationalLoad",
data: { name: node.name, loadValue: propertyName },
});
}
});
}
else if (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.TSESTree.AST_NODE_TYPES.CallExpression) {
//context.load(<obj>, "...") call
const callee = node.parent
.callee;
const args = node.parent.arguments;
if ((0, load_1.isLoadFunction)(callee) && args[0] == node && args.length < 3) {
const propertyNames = (0, load_1.parsePropertiesArgument)(args[1]);
propertyNames.forEach((propertyName) => {
if (propertyName && !isLoadingValidPropeties(propertyName)) {
context.report({
node: node.parent,
messageId: "navigationalLoad",
data: { name: node.name, loadValue: propertyName },
});
}
});
}
}
});
});
scope.childScopes.forEach(findNavigationalLoad);
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findNavigationalLoad(scope);
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=no-navigational-load.js.map
@@ -0,0 +1 @@
{"version":3,"file":"no-navigational-load.js","sourceRoot":"","sources":["../../src/rules/no-navigational-load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"noOfficeInitialize", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/initialize-add-in#initialize-with-officeonready")({
name: "no-office-initialize",
meta: {
type: "suggestion",
messages: {
noOfficeInitialize: "Office.onReady() is preferred over Office.initialize.",
},
docs: {
description: "Office.onReady() is more flexible than Office.initialize.",
},
schema: [],
},
create: function (context) {
return {
"AssignmentExpression[left.object.name='Office'][left.property.name='initialize']"(node) {
context.report({
node: node,
messageId: "noOfficeInitialize",
});
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=no-office-initialize.js.map
@@ -0,0 +1 @@
{"version":3,"file":"no-office-initialize.js","sourceRoot":"","sources":["../../src/rules/no-office-initialize.ts"],"names":[],"mappings":";;AAAA,oDAAiE;AAEjE,kBAAe,mBAAW,CAAC,WAAW,CACpC,GAAG,EAAE,CACH,uGAAuG,CAC1G,CAAC;IACA,IAAI,EAAE,sBAAsB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE;YACR,kBAAkB,EAChB,uDAAuD;SAC1D;QACD,IAAI,EAAE;YACJ,WAAW,EAAE,2DAA2D;SACzE;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,EAAE,UAAU,OAAO;QACvB,OAAO;YACL,kFAAkF,CAChF,IAAmC;gBAEnC,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,IAAI;oBACV,SAAS,EAAE,oBAAoB;iBAChC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
import { ESLintUtils } from "@typescript-eslint/utils";
declare const _default: ESLintUtils.RuleModule<"useIsNullObject", [], unknown, ESLintUtils.RuleListener>;
export default _default;
@@ -0,0 +1,119 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const getFunction_1 = require("../utils/getFunction");
exports.default = utils_1.ESLintUtils.RuleCreator(() => "https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#ornullobject-methods-and-properties")({
name: "test-for-null-using-isNullObject",
meta: {
type: "problem",
messages: {
useIsNullObject: "Test the isNullObject property of '{{name}}'.",
},
docs: {
description: "Do not test the truthiness of an object returned by an OrNullObject method or property. Test it's isNullObject property.",
},
schema: [],
fixable: "code",
},
create: function (context) {
var _a;
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
function isConditionalTestExpression(node) {
return (node.parent != undefined &&
(node.parent.type === utils_1.AST_NODE_TYPES.IfStatement ||
node.parent.type === utils_1.AST_NODE_TYPES.WhileStatement ||
node.parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement ||
node.parent.type === utils_1.AST_NODE_TYPES.ForStatement ||
node.parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression) &&
node === node.parent.test);
}
function isInUnaryNullTest(node) {
return (node.parent != undefined &&
node.parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
node.parent.operator === "!" &&
node.parent.argument === node);
}
function isInBinaryNullTest(node) {
return (node.parent != undefined &&
node.parent.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
((node.parent.left === node &&
node.parent.right.type === utils_1.AST_NODE_TYPES.Literal &&
node.parent.right.raw === "null") ||
(node.parent.right === node &&
node.parent.left.type === utils_1.AST_NODE_TYPES.Literal &&
node.parent.left.raw === "null")));
}
function isInNullTest(node) {
var _a;
return (isConditionalTestExpression(node) ||
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.LogicalExpression ||
isInUnaryNullTest(node) ||
isInBinaryNullTest(node));
}
function isNullObjectNode(node) {
if (node &&
((node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
node.init &&
(0, getFunction_1.isGetOrNullObjectFunction)(node.init) &&
node.id.type === utils_1.AST_NODE_TYPES.Identifier) ||
(node.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
(0, getFunction_1.isGetOrNullObjectFunction)(node.right) &&
node.left.type === utils_1.AST_NODE_TYPES.Identifier))) {
return true;
}
return false;
}
function findNullObjectNullTests(scope) {
const variables = scope.variables;
const childScopes = scope.childScopes;
for (let i = 0; i < variables.length; i++) {
const variable = variables[i];
const references = variable.references;
let nullObjectCall = false;
const nullTests = [];
for (let ref = 0; ref < references.length; ref++) {
const identifier = references[ref].identifier;
if (isNullObjectNode(identifier.parent)) {
nullObjectCall = true;
}
if (isInNullTest(identifier)) {
nullTests.push(identifier);
}
}
if (nullObjectCall === true && nullTests.length > 0) {
nullTests.forEach((identifier) => {
context.report({
node: identifier,
messageId: "useIsNullObject",
data: { name: identifier.name },
fix: function (fixer) {
let ruleFix;
if (isInBinaryNullTest(identifier) && identifier.parent) {
const newTest = identifier.name + ".isNullObject";
ruleFix = fixer.replaceText(identifier.parent, newTest);
}
else {
ruleFix = fixer.insertTextAfter(identifier, ".isNullObject");
}
return ruleFix;
},
});
});
}
}
for (let i = 0; i < childScopes.length; ++i) {
findNullObjectNullTests(childScopes[i]);
}
}
return {
"Program:exit"(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findNullObjectNullTests(scope);
},
};
},
defaultOptions: [],
});
//# sourceMappingURL=test-for-null-using-isNullObject.js.map
@@ -0,0 +1 @@
{"version":3,"file":"test-for-null-using-isNullObject.js","sourceRoot":"","sources":["../../src/rules/test-for-null-using-isNullObject.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
{
"getFunctions": [
"getAbsoluteResizedRange",
"getActiveCell",
"getActiveChart",
"getActiveNotebook",
"getActiveOutline",
"getActivePage",
"getActiveParagraph",
"getActiveSection",
"getActiveSlicer",
"getActiveWorksheet",
"getAncestor",
"getAsImage",
"getBase64Image",
"getBase64ImageSrc",
"getBorder",
"getBoundingRect",
"getById",
"getByName",
"getByNamespace",
"getByTag",
"getByTitle",
"getByTypes",
"getCell",
"getCellPadding",
"getCellProperties",
"getColumn",
"getColumnLabelRange",
"getColumnProperties",
"getColumnsAfter",
"getColumnsBefore",
"getCount",
"getDataBodyRange",
"getDataCommonPostprocess",
"getDataHierarchy",
"getDefault",
"getDescendants",
"getDimensionValues",
"getDirectPrecedents",
"getDocument",
"getEntireColumn",
"getEntireRow",
"getExtendedRange",
"getFilterAxisRange",
"getFirst",
"getFooter",
"getHeader",
"GetHeaderRowRange",
"getHtml",
"getHyperlinkRanges",
"getImage",
"getIntersection",
"getInvalidCells",
"getIsActiveCollabSession",
"getItem",
"getItemAt",
"getLast",
"getLastCell",
"getLastColumn",
"getLastRow",
"getLevelParagraphs",
"getLevelString",
"getLocation",
"getMergedAreas",
"getNext",
"getNextTextRange",
"getOffsetRange",
"getOffsetRangeAreas",
"getOoxml",
"getParagraphAfter",
"getParagraphBefore",
"getParagraphInfo",
"getParentComment",
"getPivotItems",
"getPivotTables",
"getPrevious",
"getPrintArea",
"getPrintTitleColumns",
"getPrintTitleRows",
"getRange",
"getRangeAreasBySheet",
"getRangeByIndexes",
"getRangeEdge",
"getRanges",
"getReferenceId",
"getResizedRange",
"getRestApiId",
"getRow",
"getRowLabelRange",
"getRowProperties",
"getRowsAbove",
"getRowsBelow",
"getSelectedRange",
"getSelectedRanges",
"getSelection",
"getSpecialCells",
"getSpillingToRange",
"getSpillParent",
"GetStencilInfo",
"getSubstring",
"getSurroundingRegion",
"getTables",
"getText",
"getTextRanges",
"getTotalRowRange",
"getUsedRange",
"getUsedRangeAreas",
"getVisibleView",
"getWindowSize",
"getXml"
],
"getOrNullObjectFunctions": [
"getActiveChartOrNullObject",
"getActiveNotebookOrNull",
"getActiveOutlineOrNull",
"getActivePageOrNull",
"getActiveParagraphOrNull",
"getActiveSectionOrNull",
"getActiveSlicerOrNullObject",
"getAncestorOrNullObject",
"getByIdOrNullObject",
"getCellOrNullObject",
"getFirstOrNullObject",
"getIntersectionOrNullObject",
"getIntersectionOrNullObject",
"getInvalidCellsOrNullObject",
"getItemOrNullObject",
"getLastOrNullObject",
"getLocationOrNullObject",
"getNextOrNullObject",
"getNextTextRangeOrNullObject",
"getParagraphAfterOrNullObject",
"getParagraphBeforeOrNullObject",
"getPreviousOrNullObject",
"getPrintAreaOrNullObject",
"getPrintTitleColumnsOrNullObject",
"getPrintTitleRowsOrNullObject",
"getRangeAreasOrNullObjectBySheet",
"getRangeOrNullObject",
"getSpecialCellsOrNullObject",
"getSpecialCellsOrNullObject",
"getSpillingToRangeOrNullObject",
"getSpillParentOrNullObject",
"getUsedRangeAreasOrNullObject",
"getUsedRangeOrNullObject"
]
}
+485
View File
@@ -0,0 +1,485 @@
{
"navigational": [
"_V1Api",
"application",
"areas",
"arrayValues",
"autoFilter",
"axes",
"beginConnectedShape",
"binOptions",
"bindings",
"border",
"borders",
"bottom",
"boxwhiskerOptions",
"categoryAxis",
"cellValue",
"cellValueOrNullObject",
"charts",
"colorScale",
"colorScaleOrNullObject",
"columnHierarchies",
"columns",
"conditionalFormats",
"cultureInfo",
"custom",
"customOrNullObject",
"customProperties",
"customXmlParts",
"dataBar",
"dataBarOrNullObject",
"dataConnections",
"dataHierarchies",
"dataLabel",
"dataLabels",
"dataValidation",
"datetimeFormat",
"defaultForAllPages",
"endConnectedShape",
"evenPages",
"field",
"fill",
"filter",
"filterHierarchies",
"firstPage",
"font",
"freezePanes",
"functions",
"geometricShape",
"group",
"headersFooters",
"hierarchies",
"horizontalPageBreaks",
"iconSet",
"iconSetOrNullObject",
"image",
"items",
"iterativeCalculation",
"label",
"layout",
"legend",
"legendEntries",
"line",
"lineFormat",
"majorGridlines",
"mapOptions",
"minorGridlines",
"namedSheetViews",
"names",
"negativeFormat",
"oddPages",
"pageLayout",
"parentGroup",
"pivotOptions",
"pivotStyle",
"pivotTableStyles",
"pivotTables",
"plotArea",
"points",
"positiveFormat",
"preset",
"presetOrNullObject",
"properties",
"protection",
"ranges",
"replies",
"right",
"rowHierarchies",
"rows",
"series",
"seriesAxis",
"settings",
"shape",
"shapes",
"slicerItems",
"slicerStyle",
"slicerStyles",
"slicers",
"sort",
"styles",
"tableStyle",
"tableStyles",
"tables",
"textComparison",
"textComparisonOrNullObject",
"textFrame",
"textRange",
"timelineStyles",
"topBottom",
"topBottomOrNullObject",
"trendlines",
"valueAxis",
"verticalPageBreaks",
"worksheet",
"worksheetOrNullObject",
"worksheets",
"xErrorBars",
"yErrorBars"
],
"scalar": [
"_Id",
"address",
"addressLocal",
"addresses",
"alignment",
"allowMultipleFiltersPerField",
"allowOverflow",
"allowUnderflow",
"altTextDescription",
"altTextTitle",
"areaCount",
"author",
"authorEmail",
"authorName",
"autoFormat",
"autoIndent",
"autoSave",
"autoSizeSetting",
"autoText",
"axisColor",
"axisFormat",
"axisGroup",
"backwardPeriod",
"barDirection",
"baseTimeUnit",
"beginArrowheadLength",
"beginArrowheadStyle",
"beginArrowheadWidth",
"beginConnectedSite",
"blackAndWhite",
"bold",
"borderColor",
"bottomMargin",
"bubbleScale",
"builtIn",
"calculationEngineVersion",
"calculationMode",
"calculationState",
"caption",
"category",
"categoryLabelLevel",
"categoryType",
"cellAddresses",
"cellCount",
"centerFooter",
"centerHeader",
"centerHorizontally",
"centerVertically",
"chartDataPointTrack",
"chartType",
"color",
"colorScheme",
"columnCount",
"columnHidden",
"columnIndex",
"columnWidth",
"comment",
"company",
"connectionSiteCount",
"connectorType",
"content",
"contentType",
"count",
"creationDate",
"criteria",
"customDisplayUnit",
"dashStyle",
"dateSeparator",
"decimalSeparator",
"displayBlanksAs",
"displayUnit",
"doughnutHoleSize",
"draftMode",
"emptyCellText",
"enableCalculation",
"enableDataValueEditing",
"enableEvents",
"enableFieldList",
"enableMultipleFilterItems",
"enabled",
"endArrowheadLength",
"endArrowheadStyle",
"endArrowheadWidth",
"endConnectedSite",
"endStyleCap",
"errorAlert",
"explosion",
"fillColor",
"fillEmptyCells",
"filtered",
"firstPageNumber",
"firstSliceAngle",
"footerMargin",
"foregroundColor",
"formula",
"formulaHidden",
"formulaLocal",
"formulaR1C1",
"formulas",
"formulasLocal",
"formulasR1C1",
"forwardPeriod",
"gapWidth",
"geometricShapeType",
"gradientFill",
"gradientMaximumColor",
"gradientMaximumType",
"gradientMaximumValue",
"gradientMidpointColor",
"gradientMidpointType",
"gradientMidpointValue",
"gradientMinimumColor",
"gradientMinimumType",
"gradientMinimumValue",
"gradientStyle",
"hasData",
"hasDataLabel",
"hasDataLabels",
"hasSpill",
"hasText",
"headerMargin",
"height",
"hidden",
"highlightFirstColumn",
"highlightLastColumn",
"horizontalAlignment",
"horizontalOverflow",
"hyperlink",
"id",
"ignoreBlanks",
"include",
"includeAlignment",
"includeBorder",
"includeFont",
"includeNumber",
"includePatterns",
"includeProtection",
"indentLevel",
"index",
"insideHeight",
"insideLeft",
"insideTop",
"insideWidth",
"intercept",
"invertColor",
"invertIfNegative",
"isBeginConnected",
"isBetweenCategories",
"isDataFiltered",
"isDirty",
"isEndConnected",
"isEntireColumn",
"isEntireRow",
"isExpanded",
"isFilterCleared",
"isSelected",
"italic",
"key",
"keywords",
"labelStrategy",
"lastAuthor",
"layoutType",
"leftFooter",
"leftHeader",
"leftMargin",
"legacyId",
"level",
"lineStyle",
"linkNumberFormat",
"linkedDataTypeState",
"lockAspectRatio",
"locked",
"logBase",
"longDatePattern",
"longTimePattern",
"lowerBoundRule",
"majorTickMark",
"majorTimeUnitScale",
"majorUnit",
"manager",
"markerBackgroundColor",
"markerForegroundColor",
"markerSize",
"markerStyle",
"matchCase",
"matchPositiveBorderColor",
"matchPositiveFillColor",
"maxChange",
"maxIteration",
"maximum",
"mentions",
"method",
"minimum",
"minorTickMark",
"minorTimeUnitScale",
"minorUnit",
"movingAveragePeriod",
"multiLevel",
"name",
"namespaceUri",
"numberDecimalSeparator",
"numberFormatCategories",
"numberFormatLocal",
"numberGroupSeparator",
"offset",
"options",
"orientation",
"overflowValue",
"overlap",
"overlay",
"paperSize",
"parentLabelStrategy",
"pattern",
"patternColor",
"patternTintAndShade",
"placement",
"plotBy",
"plotOrder",
"plotVisibleOnly",
"polynomialOrder",
"position",
"positionAt",
"preserveFormatting",
"previouslySaved",
"printComments",
"printErrors",
"printGridlines",
"printHeadings",
"printOrder",
"priority",
"projectionType",
"prompt",
"protected",
"quartileCalculation",
"readOnly",
"readingOrder",
"refreshOnOpen",
"removed",
"resolved",
"reverseIconOrder",
"reversePlotOrder",
"revisionNumber",
"richContent",
"rightFooter",
"rightHeader",
"rightMargin",
"rotation",
"roundedCorners",
"rowCount",
"rowHeight",
"rowHidden",
"rowIndex",
"savedAsArray",
"scaleType",
"scope",
"secondPlotSize",
"separator",
"seriesNameLevel",
"shortDatePattern",
"showAllFieldButtons",
"showAllItems",
"showAs",
"showAxisFieldButtons",
"showBandedColumns",
"showBandedRows",
"showBubbleSize",
"showCategoryName",
"showColumnGrandTotals",
"showConnectorLines",
"showDataBarOnly",
"showDataLabelsOverMaximum",
"showDisplayUnitLabel",
"showEquation",
"showFieldHeaders",
"showFilterButton",
"showGridlines",
"showHeaders",
"showHeadings",
"showIconOnly",
"showInnerPoints",
"showLeaderLines",
"showLegendFieldButtons",
"showLegendKey",
"showMeanLine",
"showMeanMarker",
"showOutlierPoints",
"showPercentage",
"showRSquared",
"showReportFilterFieldButtons",
"showRowGrandTotals",
"showSeriesName",
"showShadow",
"showTotals",
"showValue",
"showValueFieldButtons",
"shrinkToFit",
"sideIndex",
"size",
"smooth",
"sortBy",
"splitType",
"splitValue",
"standardHeight",
"standardWidth",
"state",
"stopIfTrue",
"strikethrough",
"style",
"subject",
"subscript",
"subtotalLocation",
"subtotals",
"summarizeBy",
"superscript",
"tabColor",
"text",
"textOrientation",
"thousandsSeparator",
"threeColorScale",
"tickLabelPosition",
"tickLabelSpacing",
"tickMarkSpacing",
"timeSeparator",
"tintAndShade",
"topMargin",
"transparency",
"type",
"types",
"underflowValue",
"underline",
"uniqueRemaining",
"upperBoundRule",
"useCustomSortLists",
"usePrecisionAsDisplayed",
"useSheetMargins",
"useSheetScale",
"useStandardHeight",
"useStandardWidth",
"useSystemSeparators",
"valid",
"value",
"valueTypes",
"values",
"varyByCategories",
"verticalAlignment",
"verticalOverflow",
"visibility",
"visible",
"weight",
"width",
"wrapText",
"zOrderPosition",
"zoom"
],
"ambiguous": [
"comments",
"fields",
"format",
"left",
"numberFormat",
"rule",
"title",
"top"
]
}
+3
View File
@@ -0,0 +1,3 @@
import { TSESTree } from "@typescript-eslint/utils";
export declare function isGetFunction(node: TSESTree.Node): boolean;
export declare function isGetOrNullObjectFunction(node: TSESTree.Node): boolean;
+45
View File
@@ -0,0 +1,45 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isGetFunction = isGetFunction;
exports.isGetOrNullObjectFunction = isGetOrNullObjectFunction;
const utils_1 = require("@typescript-eslint/utils");
const getJson = __importStar(require("./data/getFunctions.json"));
const getFunctions = new Set(getJson.getFunctions);
const getOrNullObjectFunctions = new Set(getJson.getOrNullObjectFunctions);
function isGetFunction(node) {
return (node.type == utils_1.TSESTree.AST_NODE_TYPES.CallExpression &&
node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
(getFunctions.has(node.callee.property.name) ||
getOrNullObjectFunctions.has(node.callee.property.name)));
}
function isGetOrNullObjectFunction(node) {
return (node.type == utils_1.TSESTree.AST_NODE_TYPES.CallExpression &&
node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
getOrNullObjectFunctions.has(node.callee.property.name));
}
//# sourceMappingURL=getFunction.js.map
@@ -0,0 +1 @@
{"version":3,"file":"getFunction.js","sourceRoot":"","sources":["../../src/utils/getFunction.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAQA,sCAQC;AAED,8DAOC;AAzBD,oDAAoD;AACpD,kEAAoD;AAEpD,MAAM,YAAY,GAAgB,IAAI,GAAG,CAAS,OAAO,CAAC,YAAY,CAAC,CAAC;AACxE,MAAM,wBAAwB,GAAgB,IAAI,GAAG,CACnD,OAAO,CAAC,wBAAwB,CACjC,CAAC;AAEF,SAAgB,aAAa,CAAC,IAAmB;IAC/C,OAAO,CACL,IAAI,CAAC,IAAI,IAAI,gBAAQ,CAAC,cAAc,CAAC,cAAc;QACnD,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,gBAAQ,CAAC,cAAc,CAAC,gBAAgB;QAC7D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,gBAAQ,CAAC,cAAc,CAAC,UAAU;QAChE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC1C,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC3D,CAAC;AACJ,CAAC;AAED,SAAgB,yBAAyB,CAAC,IAAmB;IAC3D,OAAO,CACL,IAAI,CAAC,IAAI,IAAI,gBAAQ,CAAC,cAAc,CAAC,cAAc;QACnD,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,gBAAQ,CAAC,cAAc,CAAC,gBAAgB;QAC7D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,gBAAQ,CAAC,cAAc,CAAC,UAAU;QAChE,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CACxD,CAAC;AACJ,CAAC"}
+7
View File
@@ -0,0 +1,7 @@
import { TSESTree } from "@typescript-eslint/utils";
export declare function isLoadFunction(node: TSESTree.MemberExpression): boolean;
export declare function isLoadCall(node: TSESTree.CallExpression): boolean;
export declare function isLoadReference(node: TSESTree.Identifier | TSESTree.JSXIdentifier): boolean;
export declare function isContextLoadArgumentReference(node: TSESTree.Identifier | TSESTree.JSXIdentifier): boolean;
export declare function parseLoadArguments(node: TSESTree.MemberExpression): string[];
export declare function parsePropertiesArgument(argument: TSESTree.CallExpressionArgument): string[];
+94
View File
@@ -0,0 +1,94 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isLoadFunction = isLoadFunction;
exports.isLoadCall = isLoadCall;
exports.isLoadReference = isLoadReference;
exports.isContextLoadArgumentReference = isContextLoadArgumentReference;
exports.parseLoadArguments = parseLoadArguments;
exports.parsePropertiesArgument = parsePropertiesArgument;
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("./utils");
function isLoadFunction(node) {
const methodCall = (0, utils_2.findCallExpression)(node);
return methodCall !== undefined && isLoadCall(methodCall);
}
function isLoadCall(node) {
return (node &&
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
node.callee.property.name === "load");
}
function isLoadReference(node) {
return (node.parent &&
node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
isLoadFunction(node.parent));
}
function isContextLoadArgumentReference(node) {
var _a;
return (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.CallExpression &&
node.parent.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
node.parent.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
node.parent.callee.object.name === "context" &&
node.parent.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
node.parent.callee.property.name === "load");
}
function parseObjectExpressionProperty(objectExpression) {
let composedProperties = [];
objectExpression.properties.forEach((property) => {
if (property.type === utils_1.AST_NODE_TYPES.Property &&
property.key.type === utils_1.AST_NODE_TYPES.Identifier) {
const propertyName = property.key.name;
if (property.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
const composedProperty = parseObjectExpressionProperty(property.value);
if (composedProperty.length !== 0) {
composedProperties = composedProperties.concat(propertyName + "/" + composedProperty);
}
}
else if (property.value.type === utils_1.AST_NODE_TYPES.Literal &&
property.value.value // Checking if the value assigined to the property is true
) {
composedProperties = composedProperties.concat(propertyName);
}
}
});
return composedProperties;
}
function parseLoadStringArgument(argument) {
const properties = [];
argument
.replace(/\s/g, "")
.split(",")
.forEach((property) => {
properties.push(property);
});
return properties;
}
function parseLoadArguments(node) {
const methodCall = (0, utils_2.findCallExpression)(node);
if (methodCall && isLoadCall(methodCall)) {
const argument = methodCall.arguments[0];
if (!argument) {
return [];
}
return parsePropertiesArgument(argument);
}
throw new Error("error in parseLoadArgument function.");
}
function parsePropertiesArgument(argument) {
let properties = [];
if (argument.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
argument.elements.forEach((element) => {
if (element != null && element.type === utils_1.TSESTree.AST_NODE_TYPES.Literal) {
properties = properties.concat(parseLoadStringArgument(element.value));
}
});
}
else if (argument.type === utils_1.TSESTree.AST_NODE_TYPES.Literal) {
properties = parseLoadStringArgument(argument.value);
}
else if (argument.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
properties = parseObjectExpressionProperty(argument);
}
return properties;
}
//# sourceMappingURL=load.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/utils/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
export declare enum PropertyType {
navigational = 0,
scalar = 1,
ambiguous = 2,// Can be scalar or navigational. Depends of the context.
notProperty = 3
}
export declare function getPropertyType(propertyName: string): PropertyType;
+53
View File
@@ -0,0 +1,53 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertyType = void 0;
exports.getPropertyType = getPropertyType;
const propertiesJson = __importStar(require("./data/properties.json"));
var PropertyType;
(function (PropertyType) {
PropertyType[PropertyType["navigational"] = 0] = "navigational";
PropertyType[PropertyType["scalar"] = 1] = "scalar";
PropertyType[PropertyType["ambiguous"] = 2] = "ambiguous";
PropertyType[PropertyType["notProperty"] = 3] = "notProperty";
})(PropertyType || (exports.PropertyType = PropertyType = {}));
const navigationProperties = new Set(propertiesJson.navigational);
const scalarProperties = new Set(propertiesJson.scalar);
const ambiguousProperties = new Set(propertiesJson.ambiguous);
function getPropertyType(propertyName) {
if (navigationProperties.has(propertyName)) {
return PropertyType.navigational;
}
else if (scalarProperties.has(propertyName)) {
return PropertyType.scalar;
}
else if (ambiguousProperties.has(propertyName)) {
return PropertyType.ambiguous;
}
else {
return PropertyType.notProperty;
}
}
//# sourceMappingURL=propertiesType.js.map
@@ -0,0 +1 @@
{"version":3,"file":"propertiesType.js","sourceRoot":"","sources":["../../src/utils/propertiesType.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,0CAUC;AA3BD,uEAAyD;AAEzD,IAAY,YAKX;AALD,WAAY,YAAY;IACtB,+DAAY,CAAA;IACZ,mDAAM,CAAA;IACN,yDAAS,CAAA;IACT,6DAAW,CAAA;AACb,CAAC,EALW,YAAY,4BAAZ,YAAY,QAKvB;AAED,MAAM,oBAAoB,GAAgB,IAAI,GAAG,CAC/C,cAAc,CAAC,YAAY,CAC5B,CAAC;AACF,MAAM,gBAAgB,GAAgB,IAAI,GAAG,CAAS,cAAc,CAAC,MAAM,CAAC,CAAC;AAC7E,MAAM,mBAAmB,GAAgB,IAAI,GAAG,CAC9C,cAAc,CAAC,SAAS,CACzB,CAAC;AAEF,SAAgB,eAAe,CAAC,YAAoB;IAClD,IAAI,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3C,OAAO,YAAY,CAAC,YAAY,CAAC;IACnC,CAAC;SAAM,IAAI,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9C,OAAO,YAAY,CAAC,MAAM,CAAC;IAC7B,CAAC;SAAM,IAAI,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QACjD,OAAO,YAAY,CAAC,SAAS,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,OAAO,YAAY,CAAC,WAAW,CAAC;IAClC,CAAC;AACH,CAAC"}
+17
View File
@@ -0,0 +1,17 @@
import { TSESTree } from "@typescript-eslint/utils";
import { Reference, Scope } from "@typescript-eslint/scope-manager";
export declare function findTopMemberExpression(node: TSESTree.MemberExpression): TSESTree.MemberExpression;
export declare function findCallExpression(node: TSESTree.MemberExpression): TSESTree.CallExpression | undefined;
export type OfficeApiReference = {
/**
* Get: An OfficeJs object, which is created when calling a `get` type function
* Load: A reference to `object.load()` type call
* Method: The reference is calling a method. Ex: `object.methodCall()`
* Read: The reference value is being read and it is not a method
* Sync: A call to `context.sync()`
*/
operation: "Get" | "Load" | "Method" | "Read" | "Sync";
reference: Reference;
};
export declare function findOfficeApiReferences(scope: Scope): OfficeApiReference[];
export declare function findPropertiesRead(node: TSESTree.Node | undefined): string;
+99
View File
@@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findTopMemberExpression = findTopMemberExpression;
exports.findCallExpression = findCallExpression;
exports.findOfficeApiReferences = findOfficeApiReferences;
exports.findPropertiesRead = findPropertiesRead;
const utils_1 = require("@typescript-eslint/utils");
const getFunction_1 = require("./getFunction");
const load_1 = require("./load");
function isContextSyncIdentifier(node) {
var _a, _b;
return (node.name === "context" &&
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MemberExpression &&
((_b = node.parent.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.CallExpression &&
node.parent.property.type === utils_1.AST_NODE_TYPES.Identifier &&
node.parent.property.name === "sync");
}
function findTopMemberExpression(node) {
while (node.parent && node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression) {
node = node.parent;
}
return node;
}
function findCallExpression(node) {
var _a;
while (node.parent && node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression) {
node = node.parent;
}
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.CallExpression) {
return node.parent;
}
return undefined;
}
let proxyVariables;
let apiReferences;
function findOfficeApiReferences(scope) {
proxyVariables = new Set();
apiReferences = [];
findOfficeApiReferencesInScope(scope);
return apiReferences;
}
function findOfficeApiReferencesInScope(scope) {
scope.references.forEach((reference) => {
const node = reference.identifier;
if (reference.isWrite() &&
reference.writeExpr &&
(0, getFunction_1.isGetFunction)(reference.writeExpr) &&
reference.resolved) {
proxyVariables.add(reference.resolved);
apiReferences.push({ operation: "Get", reference: reference });
}
else if (isContextSyncIdentifier(reference.identifier)) {
apiReferences.push({ operation: "Sync", reference: reference });
}
else if (reference.isRead() &&
reference.resolved &&
proxyVariables.has(reference.resolved)) {
if ((0, load_1.isLoadReference)(node)) {
// <obj>.load(...)
apiReferences.push({ operation: "Load", reference: reference });
}
else if ((0, load_1.isContextLoadArgumentReference)(node)) {
// context.load(<obj>, ...)
apiReferences.push({ operation: "Load", reference: reference });
}
else if (isMethodReference(node)) {
apiReferences.push({ operation: "Method", reference: reference });
}
else {
apiReferences.push({ operation: "Read", reference: reference });
}
}
});
scope.childScopes.forEach(findOfficeApiReferencesInScope);
}
function isMethod(node) {
var _a;
const topExpression = findTopMemberExpression(node);
return (((_a = topExpression.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.CallExpression &&
topExpression.parent.callee === topExpression);
}
function isMethodReference(node) {
return (node.parent &&
node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
isMethod(node.parent));
}
function findPropertiesRead(node) {
let propertyName = ""; // Will be a string combined with '/' for the case of navigation properties
while (node) {
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
!isMethod(node)) {
propertyName += node.property.name + "/";
}
node = node.parent;
}
return propertyName.slice(0, -1);
}
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"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"}
+53
View File
@@ -0,0 +1,53 @@
{
"name": "eslint-plugin-office-addins",
"version": "4.0.3",
"description": "eslint plugin for office-addins",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "OfficeDev",
"main": "lib/main.js",
"scripts": {
"build": "rimraf lib && tsc -p tsconfig.json",
"lint": "eslint src/**/*.{ts,tsx,js,jsx}",
"lint:fix": "eslint src/**/*.{ts,tsx,js,jsx} --fix",
"test": "jest"
},
"peerDependencies": {
"eslint": "^9.0.0"
},
"dependencies": {
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-native": "^5.0.0",
"office-addin-prettier-config": "^2.0.1",
"prettier": "^3.2.5",
"requireindex": "~1.2.0",
"typescript": "^5.4.3",
"typescript-eslint": "^8.0.0"
},
"devDependencies": {
"@types/eslint-config-prettier": "^6.11.3",
"@types/jest": "^29.5.12",
"@types/node": "^20.12.3",
"@typescript-eslint/rule-tester": "^8.0.0",
"jest": "^29.7.0",
"rimraf": "^6.0.1",
"ts-jest": "^29.1.2"
},
"engines": {
"node": ">=0.10.0"
},
"repository": {
"type": "git",
"url": "https://github.com/OfficeDev/Office-Addin-Scripts"
},
"bugs": {
"url": "https://github.com/OfficeDev/Office-Addin-Scripts/issues"
},
"license": "MIT",
"gitHead": "0a83e04842c872533d72b043ebc55e9a6013f34e"
}
+110
View File
@@ -0,0 +1,110 @@
import rules from "./rules";
import tsParser from "@typescript-eslint/parser";
import typescriptplugin from "@typescript-eslint/eslint-plugin";
import prettierplugin from "eslint-plugin-prettier";
import reactplugin from "eslint-plugin-react";
import eslintjs from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
// eslint-disable-next-line @typescript-eslint/no-require-imports
const reactnativeplugin = require("eslint-plugin-react-native");
const plugin = {
meta: {
name: "eslint-plugin-office-addins",
version: "5.0.0",
},
rules,
configs: {},
};
const recommended = [
eslintjs.configs.recommended,
eslintConfigPrettier,
{
files: ["**/*.{js,mjs,cjs,ts,cts,mts}"],
plugins: {
"@typescript-eslint": typescriptplugin,
"office-addins": plugin,
prettier: prettierplugin,
},
languageOptions: {
parser: tsParser,
ecmaVersion: 6,
sourceType: "module",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
"@typescript-eslint/no-unused-vars": "error",
"no-delete-var": "warn",
"no-eval": "error",
"no-inner-declarations": "warn",
"no-octal": "warn",
"no-unused-vars": "off",
"office-addins/call-sync-after-load": "error",
"office-addins/call-sync-before-read": "error",
"office-addins/load-object-before-read": "error",
"office-addins/no-context-sync-in-loop": "warn",
"office-addins/no-empty-load": "warn",
"office-addins/no-navigational-load": "warn",
"office-addins/no-office-initialize": "warn",
"office-addins/test-for-null-using-isNullObject": "error",
"prettier/prettier": ["error", { endOfLine: "auto" }],
},
},
];
const react = [
reactplugin.configs.flat.recommended,
...recommended,
{
plugins: {
"office-addins": plugin,
react: reactplugin,
},
settings: {
react: {
version: "detect",
},
},
},
];
const reactnative = [
reactnativeplugin.configs.all,
...recommended,
{
plugins: {
"office-addins": plugin,
react: reactnativeplugin,
},
settings: {
react: {
version: "detect",
},
},
},
];
const test = [
...recommended,
{
plugins: {
"office-addins": plugin,
},
rules: {},
},
];
Object.assign(plugin.configs, {
recommended,
react,
reactnative,
test,
});
module.exports = plugin;
@@ -0,0 +1,136 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { Reference } from "@typescript-eslint/scope-manager";
import { isLoadCall, parsePropertiesArgument } from "../utils/load";
import {
findPropertiesRead,
findOfficeApiReferences,
OfficeApiReference,
findCallExpression,
} from "../utils/utils";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#load",
)({
name: "call-sync-after-load",
meta: {
type: "suggestion",
messages: {
callSyncAfterLoad:
"Call context.sync() after calling load on '{{name}}' for the property '{{loadValue}}' and before reading the property.",
},
docs: {
description:
"Always call context.sync() between loading one or more properties on objects and reading any of those properties.",
},
schema: [],
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
type VariableProperty = {
variable: string;
property: string;
};
class VariablePropertySet extends Set {
add(variableProperty: VariableProperty) {
return super.add(JSON.stringify(variableProperty));
}
has(variableProperty: VariableProperty) {
return super.has(JSON.stringify(variableProperty));
}
}
let apiReferences: OfficeApiReference[] = [];
function findLoadBeforeSync(): void {
const needSync: VariablePropertySet = new VariablePropertySet();
apiReferences.forEach((apiReference) => {
const operation = apiReference.operation;
const reference: Reference = apiReference.reference;
const identifier: TSESTree.Node = reference.identifier;
const variable = reference.resolved;
if (operation === "Load" && variable) {
const propertiesArgument = getPropertiesArgument(identifier);
const propertyNames: string[] = propertiesArgument
? parsePropertiesArgument(propertiesArgument)
: ["*"];
propertyNames.forEach((propertyName: string) => {
needSync.add({
variable: variable.name,
property: propertyName,
});
});
} else if (operation === "Sync") {
needSync.clear();
} else if (operation === "Read" && variable) {
const propertyName: string = findPropertiesRead(
reference.identifier.parent,
);
if (
needSync.has({ variable: variable.name, property: propertyName }) ||
needSync.has({ variable: variable.name, property: "*" })
) {
const node = reference.identifier;
context.report({
node: node,
messageId: "callSyncAfterLoad",
data: { name: node.name, loadValue: propertyName },
});
}
}
});
}
function getPropertiesArgument(
identifier: TSESTree.Identifier | TSESTree.JSXIdentifier,
): TSESTree.CallExpressionArgument | undefined {
let propertiesArgument;
if (
identifier.parent?.type === TSESTree.AST_NODE_TYPES.MemberExpression
) {
// Look for <obj>.load(...) call
const methodCall = findCallExpression(identifier.parent);
if (methodCall && isLoadCall(methodCall)) {
propertiesArgument = methodCall.arguments[0];
}
} else if (
identifier.parent?.type === TSESTree.AST_NODE_TYPES.CallExpression
) {
// Look for context.load(<obj>, "...") call
const args: TSESTree.CallExpressionArgument[] =
identifier.parent.arguments;
if (
isLoadCall(identifier.parent) &&
args[0] == identifier &&
args.length < 3
) {
propertiesArgument = args[1];
}
}
return propertiesArgument;
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
apiReferences = findOfficeApiReferences(scope);
apiReferences.sort((left, right) => {
return (
left.reference.identifier.range[1] -
right.reference.identifier.range[1]
);
});
findLoadBeforeSync();
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,86 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { Variable } from "@typescript-eslint/scope-manager";
import { findTopMemberExpression } from "../utils/utils";
import { findOfficeApiReferences, OfficeApiReference } from "../utils/utils";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#sync",
)({
name: "call-sync-before-read",
meta: {
type: "problem",
messages: {
callSync: "Call context.sync() before trying to read '{{name}}'.",
},
docs: {
description:
"Always call load on the object's properties followed by a context.sync() before reading them.",
},
schema: [],
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
let apiReferences: OfficeApiReference[] = [];
function checkPropertyIsRead(node: TSESTree.MemberExpression): boolean {
const topExpression: TSESTree.MemberExpression =
findTopMemberExpression(node);
switch (topExpression.parent?.type) {
case TSESTree.AST_NODE_TYPES.AssignmentExpression:
return topExpression.parent.right === topExpression;
default:
return true;
}
}
function findReadBeforeSync(): void {
const needSync: Set<Variable> = new Set<Variable>();
apiReferences.forEach((apiReference) => {
const operation = apiReference.operation;
const reference = apiReference.reference;
const variable = reference.resolved;
if (operation === "Get" && variable) {
needSync.add(variable);
}
if (operation === "Sync") {
needSync.clear();
}
if (operation === "Read" && variable && needSync.has(variable)) {
const node: TSESTree.Node = reference.identifier;
if (
node.parent?.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
checkPropertyIsRead(node.parent)
) {
context.report({
node: node,
messageId: "callSync",
data: { name: node.name },
});
}
}
});
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
apiReferences = findOfficeApiReferences(scope);
apiReferences.sort((left, right) => {
return (
left.reference.identifier.range[1] -
right.reference.identifier.range[1]
);
});
findReadBeforeSync();
},
};
},
defaultOptions: [],
});
+19
View File
@@ -0,0 +1,19 @@
import callSyncAfterLoad from "./call-sync-after-load";
import callSyncBeforeRead from "./call-sync-before-read";
import loadObjectBeforeRead from "./load-object-before-read";
import noContextSyncInLoop from "./no-context-sync-in-loop";
import noEmptyLoad from "./no-empty-load";
import noNavigationalLoad from "./no-navigational-load";
import noOfficeInitialize from "./no-office-initialize";
import testForNullUsingIsNullObject from "./test-for-null-using-isNullObject";
export default {
"call-sync-before-read": callSyncBeforeRead,
"load-object-before-read": loadObjectBeforeRead,
"call-sync-after-load": callSyncAfterLoad,
"no-context-sync-in-loop": noContextSyncInLoop,
"no-empty-load": noEmptyLoad,
"no-navigational-load": noNavigationalLoad,
"no-office-initialize": noOfficeInitialize,
"test-for-null-using-isNullObject": testForNullUsingIsNullObject,
};
@@ -0,0 +1,146 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { Reference, Scope, Variable } from "@typescript-eslint/scope-manager";
import { isLoadCall, parsePropertiesArgument } from "../utils/load";
import { findCallExpression, findPropertiesRead } from "../utils/utils";
import { isGetFunction, isGetOrNullObjectFunction } from "../utils/getFunction";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#load",
)({
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.",
},
schema: [],
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
function isInsideWriteStatement(node: TSESTree.Node): boolean {
while (node.parent) {
node = node.parent;
if (node.type === TSESTree.AST_NODE_TYPES.AssignmentExpression)
return true;
}
return false;
}
function hasBeenLoaded(
node: TSESTree.Node,
loadLocation: Map<string, number>,
propertyName: string,
): boolean {
return (
loadLocation.has(propertyName) && // If reference came after load, return
node.range[1] > (loadLocation.get(propertyName) ?? 0)
);
}
function findLoadBeforeRead(scope: Scope) {
scope.variables.forEach((variable: Variable) => {
const loadLocation: Map<string, number> = new Map<string, number>();
let getFound: boolean = false;
variable.references.forEach((reference: Reference) => {
const node: TSESTree.Node = reference.identifier;
const parent = node.parent;
if (parent?.type === TSESTree.AST_NODE_TYPES.VariableDeclarator) {
getFound = false; // In case of reassignment
if (
parent.init &&
isGetFunction(parent.init) &&
!isGetOrNullObjectFunction(parent.init)
) {
getFound = true;
return;
}
}
if (parent?.type === TSESTree.AST_NODE_TYPES.AssignmentExpression) {
getFound = false; // In case of reassignment
if (
isGetFunction(parent.right) &&
!isGetOrNullObjectFunction(parent.right)
) {
getFound = true;
return;
}
}
if (!getFound) {
// If reference was not related to a previous get
return;
}
// Look for <obj>.load(...) call
if (parent?.type === TSESTree.AST_NODE_TYPES.MemberExpression) {
const methodCall = findCallExpression(parent);
if (methodCall && isLoadCall(methodCall)) {
const argument = methodCall.arguments[0];
const propertyNames: string[] = argument
? parsePropertiesArgument(argument)
: ["*"];
propertyNames.forEach((propertyName: string) => {
loadLocation.set(propertyName, node.range[1]);
});
return;
}
}
// Look for context.load(<obj>, "...") call
if (parent?.type === TSESTree.AST_NODE_TYPES.CallExpression) {
const args: TSESTree.CallExpressionArgument[] = parent?.arguments;
if (isLoadCall(parent) && args[0] == node && args.length < 3) {
const propertyNames: string[] = args[1]
? parsePropertiesArgument(args[1])
: ["*"];
propertyNames.forEach((propertyName: string) => {
loadLocation.set(propertyName, node.range[1]);
});
return;
}
}
const propertyName: string = 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(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findLoadBeforeRead(scope);
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,33 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/concepts/correlated-objects-pattern",
)({
name: "no-context-sync-in-loop",
meta: {
type: "problem",
messages: {
loopedSync:
"Calling context.sync() inside a loop can lead to poor performance",
},
docs: {
description:
"Calling context.sync() inside of a loop dramatically increases the time the code runs, proportional to the number of iterations.",
},
schema: [],
},
create: function (context) {
return {
":matches(ForStatement, ForInStatement, WhileStatement, DoWhileStatement, ForOfStatement) CallExpression[callee.object.name='context'][callee.property.name='sync']"(
node: TSESTree.CallExpression,
) {
context.report({
node: node.callee,
messageId: "loopedSync",
});
},
};
},
defaultOptions: [],
});
+85
View File
@@ -0,0 +1,85 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { Reference, Scope, Variable } from "@typescript-eslint/scope-manager";
import { isGetFunction } from "../utils/getFunction";
import { parseLoadArguments, isLoadFunction } from "../utils/load";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#calling-load-without-parameters-not-recommended",
)({
name: "no-empty-load",
meta: {
type: "problem",
messages: {
emptyLoad: "Calling load without any argument slows down your add-in.",
},
docs: {
description:
"Calling load without any argument causes unneeded data to load and slows down your add-in.",
},
schema: [],
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
function isEmptyLoad(node: TSESTree.MemberExpression): boolean {
if (isLoadFunction(node)) {
const propertyNames: string[] = parseLoadArguments(node);
if (propertyNames.length === 0) {
return true;
}
let foundEmptyProperty = false;
propertyNames.forEach((property: string) => {
if (!property) {
foundEmptyProperty = true;
}
});
return foundEmptyProperty;
}
return false;
}
function findEmptyLoad(scope: Scope) {
scope.variables.forEach((variable: Variable) => {
let getFound: boolean = false;
variable.references.forEach((reference: Reference) => {
const node: TSESTree.Node = reference.identifier;
if (reference.isWrite()) {
getFound = false; // In case of reassignment
if (reference.writeExpr && isGetFunction(reference.writeExpr)) {
getFound = true;
return;
}
}
if (!getFound) {
// If reference was not related to a previous get
return;
}
if (
node.parent?.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
isEmptyLoad(node.parent)
) {
context.report({
node: node.parent,
messageId: "emptyLoad",
});
}
});
});
scope.childScopes.forEach(findEmptyLoad);
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findEmptyLoad(scope);
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,125 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { Reference, Scope, Variable } from "@typescript-eslint/scope-manager";
import { isGetFunction } from "../utils/getFunction";
import {
parseLoadArguments,
isLoadFunction,
parsePropertiesArgument,
} from "../utils/load";
import { getPropertyType, PropertyType } from "../utils/propertiesType";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#scalar-and-navigation-properties",
)({
name: "no-navigational-load",
meta: {
type: "problem",
messages: {
navigationalLoad:
"Calling load on the navigation property '{{loadValue}}' slows down your add-in.",
},
docs: {
description:
"Calling load on a navigation property causes unneeded data to load and slows down your add-in.",
},
schema: [],
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
function isLoadingValidPropeties(propertyName: string): boolean {
const properties = propertyName.split("/");
const lastProperty = properties.pop();
if (!lastProperty) return false;
for (const property of properties) {
const propertyType = getPropertyType(property);
if (
propertyType !== PropertyType.navigational &&
propertyType !== PropertyType.ambiguous
) {
return false;
}
}
if (lastProperty === "*") {
return true;
}
const propertyType = getPropertyType(lastProperty);
return (
propertyType === PropertyType.scalar ||
propertyType === PropertyType.ambiguous
);
}
function findNavigationalLoad(scope: Scope) {
scope.variables.forEach((variable: Variable) => {
let getFound: boolean = false;
variable.references.forEach((reference: Reference) => {
const node: TSESTree.Node = reference.identifier;
if (reference.isWrite()) {
getFound = false; // In case of reassignment
if (reference.writeExpr && isGetFunction(reference.writeExpr)) {
getFound = true;
return;
}
}
if (!getFound) {
// If reference was not related to a previous get
return;
}
if (
node.parent?.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
isLoadFunction(node.parent)
) {
// <obj>.load(...) call
const propertyNames: string[] = parseLoadArguments(node.parent);
propertyNames.forEach((propertyName: string) => {
if (propertyName && !isLoadingValidPropeties(propertyName)) {
context.report({
node: node.parent,
messageId: "navigationalLoad",
data: { name: node.name, loadValue: propertyName },
});
}
});
} else if (
node.parent?.type === TSESTree.AST_NODE_TYPES.CallExpression
) {
//context.load(<obj>, "...") call
const callee: TSESTree.MemberExpression = node.parent
.callee as TSESTree.MemberExpression;
const args: TSESTree.CallExpressionArgument[] =
node.parent.arguments;
if (isLoadFunction(callee) && args[0] == node && args.length < 3) {
const propertyNames: string[] = parsePropertiesArgument(args[1]);
propertyNames.forEach((propertyName: string) => {
if (propertyName && !isLoadingValidPropeties(propertyName)) {
context.report({
node: node.parent,
messageId: "navigationalLoad",
data: { name: node.name, loadValue: propertyName },
});
}
});
}
}
});
});
scope.childScopes.forEach(findNavigationalLoad);
}
return {
Program(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findNavigationalLoad(scope);
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,32 @@
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/initialize-add-in#initialize-with-officeonready",
)({
name: "no-office-initialize",
meta: {
type: "suggestion",
messages: {
noOfficeInitialize:
"Office.onReady() is preferred over Office.initialize.",
},
docs: {
description: "Office.onReady() is more flexible than Office.initialize.",
},
schema: [],
},
create: function (context) {
return {
"AssignmentExpression[left.object.name='Office'][left.property.name='initialize']"(
node: TSESTree.AssignmentExpression,
) {
context.report({
node: node,
messageId: "noOfficeInitialize",
});
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,155 @@
import {
ESLintUtils,
TSESTree,
AST_NODE_TYPES,
} from "@typescript-eslint/utils";
import { Reference, Scope, Variable } from "@typescript-eslint/scope-manager";
import { RuleFix, RuleFixer } from "@typescript-eslint/utils/ts-eslint";
import { isGetOrNullObjectFunction } from "../utils/getFunction";
export default ESLintUtils.RuleCreator(
() =>
"https://docs.microsoft.com/office/dev/add-ins/develop/application-specific-api-model#ornullobject-methods-and-properties",
)({
name: "test-for-null-using-isNullObject",
meta: {
type: "problem",
messages: {
useIsNullObject: "Test the isNullObject property of '{{name}}'.",
},
docs: {
description:
"Do not test the truthiness of an object returned by an OrNullObject method or property. Test it's isNullObject property.",
},
schema: [],
fixable: <"code" | "whitespace">"code",
},
create: function (context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
function isConditionalTestExpression(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
): boolean {
return (
node.parent != undefined &&
(node.parent.type === AST_NODE_TYPES.IfStatement ||
node.parent.type === AST_NODE_TYPES.WhileStatement ||
node.parent.type === AST_NODE_TYPES.DoWhileStatement ||
node.parent.type === AST_NODE_TYPES.ForStatement ||
node.parent.type === AST_NODE_TYPES.ConditionalExpression) &&
node === node.parent.test
);
}
function isInUnaryNullTest(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
): boolean {
return (
node.parent != undefined &&
node.parent.type === AST_NODE_TYPES.UnaryExpression &&
node.parent.operator === "!" &&
node.parent.argument === node
);
}
function isInBinaryNullTest(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
): boolean {
return (
node.parent != undefined &&
node.parent.type === AST_NODE_TYPES.BinaryExpression &&
((node.parent.left === node &&
node.parent.right.type === AST_NODE_TYPES.Literal &&
node.parent.right.raw === "null") ||
(node.parent.right === node &&
node.parent.left.type === AST_NODE_TYPES.Literal &&
node.parent.left.raw === "null"))
);
}
function isInNullTest(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
): boolean {
return (
isConditionalTestExpression(node) ||
node.parent?.type === AST_NODE_TYPES.LogicalExpression ||
isInUnaryNullTest(node) ||
isInBinaryNullTest(node)
);
}
function isNullObjectNode(node: TSESTree.Node | undefined): boolean {
if (
node &&
((node.type === AST_NODE_TYPES.VariableDeclarator &&
node.init &&
isGetOrNullObjectFunction(node.init) &&
node.id.type === AST_NODE_TYPES.Identifier) ||
(node.type === AST_NODE_TYPES.AssignmentExpression &&
isGetOrNullObjectFunction(node.right) &&
node.left.type === AST_NODE_TYPES.Identifier))
) {
return true;
}
return false;
}
function findNullObjectNullTests(scope: Scope): void {
const variables = scope.variables;
const childScopes = scope.childScopes;
for (let i = 0; i < variables.length; i++) {
const variable: Variable = variables[i];
const references: Reference[] = variable.references;
let nullObjectCall: boolean = false;
const nullTests: (TSESTree.Identifier | TSESTree.JSXIdentifier)[] = [];
for (let ref = 0; ref < references.length; ref++) {
const identifier: TSESTree.Identifier | TSESTree.JSXIdentifier =
references[ref].identifier;
if (isNullObjectNode(identifier.parent)) {
nullObjectCall = true;
}
if (isInNullTest(identifier)) {
nullTests.push(identifier);
}
}
if (nullObjectCall === true && nullTests.length > 0) {
nullTests.forEach((identifier) => {
context.report({
node: identifier,
messageId: "useIsNullObject",
data: { name: identifier.name },
fix: function (fixer: RuleFixer) {
let ruleFix: RuleFix;
if (isInBinaryNullTest(identifier) && identifier.parent) {
const newTest = identifier.name + ".isNullObject";
ruleFix = fixer.replaceText(identifier.parent, newTest);
} else {
ruleFix = fixer.insertTextAfter(identifier, ".isNullObject");
}
return ruleFix;
},
});
});
}
}
for (let i = 0; i < childScopes.length; ++i) {
findNullObjectNullTests(childScopes[i]);
}
}
return {
"Program:exit"(node) {
const scope = sourceCode.getScope
? sourceCode.getScope(node)
: context.getScope();
findNullObjectNullTests(scope);
},
};
},
defaultOptions: [],
});
@@ -0,0 +1,148 @@
{
"getFunctions": [
"getAbsoluteResizedRange",
"getActiveCell",
"getActiveChart",
"getActiveNotebook",
"getActiveOutline",
"getActivePage",
"getActiveParagraph",
"getActiveSection",
"getActiveSlicer",
"getActiveWorksheet",
"getAncestor",
"getAsImage",
"getBase64Image",
"getBase64ImageSrc",
"getBorder",
"getBoundingRect",
"getById",
"getByName",
"getByNamespace",
"getByTag",
"getByTitle",
"getByTypes",
"getCell",
"getCellPadding",
"getCellProperties",
"getColumn",
"getColumnLabelRange",
"getColumnProperties",
"getColumnsAfter",
"getColumnsBefore",
"getCount",
"getDataBodyRange",
"getDataCommonPostprocess",
"getDataHierarchy",
"getDefault",
"getDescendants",
"getDimensionValues",
"getDirectPrecedents",
"getDocument",
"getEntireColumn",
"getEntireRow",
"getExtendedRange",
"getFilterAxisRange",
"getFirst",
"getFooter",
"getHeader",
"GetHeaderRowRange",
"getHtml",
"getHyperlinkRanges",
"getImage",
"getIntersection",
"getInvalidCells",
"getIsActiveCollabSession",
"getItem",
"getItemAt",
"getLast",
"getLastCell",
"getLastColumn",
"getLastRow",
"getLevelParagraphs",
"getLevelString",
"getLocation",
"getMergedAreas",
"getNext",
"getNextTextRange",
"getOffsetRange",
"getOffsetRangeAreas",
"getOoxml",
"getParagraphAfter",
"getParagraphBefore",
"getParagraphInfo",
"getParentComment",
"getPivotItems",
"getPivotTables",
"getPrevious",
"getPrintArea",
"getPrintTitleColumns",
"getPrintTitleRows",
"getRange",
"getRangeAreasBySheet",
"getRangeByIndexes",
"getRangeEdge",
"getRanges",
"getReferenceId",
"getResizedRange",
"getRestApiId",
"getRow",
"getRowLabelRange",
"getRowProperties",
"getRowsAbove",
"getRowsBelow",
"getSelectedRange",
"getSelectedRanges",
"getSelection",
"getSpecialCells",
"getSpillingToRange",
"getSpillParent",
"GetStencilInfo",
"getSubstring",
"getSurroundingRegion",
"getTables",
"getText",
"getTextRanges",
"getTotalRowRange",
"getUsedRange",
"getUsedRangeAreas",
"getVisibleView",
"getWindowSize",
"getXml"
],
"getOrNullObjectFunctions": [
"getActiveChartOrNullObject",
"getActiveNotebookOrNull",
"getActiveOutlineOrNull",
"getActivePageOrNull",
"getActiveParagraphOrNull",
"getActiveSectionOrNull",
"getActiveSlicerOrNullObject",
"getAncestorOrNullObject",
"getByIdOrNullObject",
"getCellOrNullObject",
"getFirstOrNullObject",
"getIntersectionOrNullObject",
"getIntersectionOrNullObject",
"getInvalidCellsOrNullObject",
"getItemOrNullObject",
"getLastOrNullObject",
"getLocationOrNullObject",
"getNextOrNullObject",
"getNextTextRangeOrNullObject",
"getParagraphAfterOrNullObject",
"getParagraphBeforeOrNullObject",
"getPreviousOrNullObject",
"getPrintAreaOrNullObject",
"getPrintTitleColumnsOrNullObject",
"getPrintTitleRowsOrNullObject",
"getRangeAreasOrNullObjectBySheet",
"getRangeOrNullObject",
"getSpecialCellsOrNullObject",
"getSpecialCellsOrNullObject",
"getSpillingToRangeOrNullObject",
"getSpillParentOrNullObject",
"getUsedRangeAreasOrNullObject",
"getUsedRangeOrNullObject"
]
}
+485
View File
@@ -0,0 +1,485 @@
{
"navigational": [
"_V1Api",
"application",
"areas",
"arrayValues",
"autoFilter",
"axes",
"beginConnectedShape",
"binOptions",
"bindings",
"border",
"borders",
"bottom",
"boxwhiskerOptions",
"categoryAxis",
"cellValue",
"cellValueOrNullObject",
"charts",
"colorScale",
"colorScaleOrNullObject",
"columnHierarchies",
"columns",
"conditionalFormats",
"cultureInfo",
"custom",
"customOrNullObject",
"customProperties",
"customXmlParts",
"dataBar",
"dataBarOrNullObject",
"dataConnections",
"dataHierarchies",
"dataLabel",
"dataLabels",
"dataValidation",
"datetimeFormat",
"defaultForAllPages",
"endConnectedShape",
"evenPages",
"field",
"fill",
"filter",
"filterHierarchies",
"firstPage",
"font",
"freezePanes",
"functions",
"geometricShape",
"group",
"headersFooters",
"hierarchies",
"horizontalPageBreaks",
"iconSet",
"iconSetOrNullObject",
"image",
"items",
"iterativeCalculation",
"label",
"layout",
"legend",
"legendEntries",
"line",
"lineFormat",
"majorGridlines",
"mapOptions",
"minorGridlines",
"namedSheetViews",
"names",
"negativeFormat",
"oddPages",
"pageLayout",
"parentGroup",
"pivotOptions",
"pivotStyle",
"pivotTableStyles",
"pivotTables",
"plotArea",
"points",
"positiveFormat",
"preset",
"presetOrNullObject",
"properties",
"protection",
"ranges",
"replies",
"right",
"rowHierarchies",
"rows",
"series",
"seriesAxis",
"settings",
"shape",
"shapes",
"slicerItems",
"slicerStyle",
"slicerStyles",
"slicers",
"sort",
"styles",
"tableStyle",
"tableStyles",
"tables",
"textComparison",
"textComparisonOrNullObject",
"textFrame",
"textRange",
"timelineStyles",
"topBottom",
"topBottomOrNullObject",
"trendlines",
"valueAxis",
"verticalPageBreaks",
"worksheet",
"worksheetOrNullObject",
"worksheets",
"xErrorBars",
"yErrorBars"
],
"scalar": [
"_Id",
"address",
"addressLocal",
"addresses",
"alignment",
"allowMultipleFiltersPerField",
"allowOverflow",
"allowUnderflow",
"altTextDescription",
"altTextTitle",
"areaCount",
"author",
"authorEmail",
"authorName",
"autoFormat",
"autoIndent",
"autoSave",
"autoSizeSetting",
"autoText",
"axisColor",
"axisFormat",
"axisGroup",
"backwardPeriod",
"barDirection",
"baseTimeUnit",
"beginArrowheadLength",
"beginArrowheadStyle",
"beginArrowheadWidth",
"beginConnectedSite",
"blackAndWhite",
"bold",
"borderColor",
"bottomMargin",
"bubbleScale",
"builtIn",
"calculationEngineVersion",
"calculationMode",
"calculationState",
"caption",
"category",
"categoryLabelLevel",
"categoryType",
"cellAddresses",
"cellCount",
"centerFooter",
"centerHeader",
"centerHorizontally",
"centerVertically",
"chartDataPointTrack",
"chartType",
"color",
"colorScheme",
"columnCount",
"columnHidden",
"columnIndex",
"columnWidth",
"comment",
"company",
"connectionSiteCount",
"connectorType",
"content",
"contentType",
"count",
"creationDate",
"criteria",
"customDisplayUnit",
"dashStyle",
"dateSeparator",
"decimalSeparator",
"displayBlanksAs",
"displayUnit",
"doughnutHoleSize",
"draftMode",
"emptyCellText",
"enableCalculation",
"enableDataValueEditing",
"enableEvents",
"enableFieldList",
"enableMultipleFilterItems",
"enabled",
"endArrowheadLength",
"endArrowheadStyle",
"endArrowheadWidth",
"endConnectedSite",
"endStyleCap",
"errorAlert",
"explosion",
"fillColor",
"fillEmptyCells",
"filtered",
"firstPageNumber",
"firstSliceAngle",
"footerMargin",
"foregroundColor",
"formula",
"formulaHidden",
"formulaLocal",
"formulaR1C1",
"formulas",
"formulasLocal",
"formulasR1C1",
"forwardPeriod",
"gapWidth",
"geometricShapeType",
"gradientFill",
"gradientMaximumColor",
"gradientMaximumType",
"gradientMaximumValue",
"gradientMidpointColor",
"gradientMidpointType",
"gradientMidpointValue",
"gradientMinimumColor",
"gradientMinimumType",
"gradientMinimumValue",
"gradientStyle",
"hasData",
"hasDataLabel",
"hasDataLabels",
"hasSpill",
"hasText",
"headerMargin",
"height",
"hidden",
"highlightFirstColumn",
"highlightLastColumn",
"horizontalAlignment",
"horizontalOverflow",
"hyperlink",
"id",
"ignoreBlanks",
"include",
"includeAlignment",
"includeBorder",
"includeFont",
"includeNumber",
"includePatterns",
"includeProtection",
"indentLevel",
"index",
"insideHeight",
"insideLeft",
"insideTop",
"insideWidth",
"intercept",
"invertColor",
"invertIfNegative",
"isBeginConnected",
"isBetweenCategories",
"isDataFiltered",
"isDirty",
"isEndConnected",
"isEntireColumn",
"isEntireRow",
"isExpanded",
"isFilterCleared",
"isSelected",
"italic",
"key",
"keywords",
"labelStrategy",
"lastAuthor",
"layoutType",
"leftFooter",
"leftHeader",
"leftMargin",
"legacyId",
"level",
"lineStyle",
"linkNumberFormat",
"linkedDataTypeState",
"lockAspectRatio",
"locked",
"logBase",
"longDatePattern",
"longTimePattern",
"lowerBoundRule",
"majorTickMark",
"majorTimeUnitScale",
"majorUnit",
"manager",
"markerBackgroundColor",
"markerForegroundColor",
"markerSize",
"markerStyle",
"matchCase",
"matchPositiveBorderColor",
"matchPositiveFillColor",
"maxChange",
"maxIteration",
"maximum",
"mentions",
"method",
"minimum",
"minorTickMark",
"minorTimeUnitScale",
"minorUnit",
"movingAveragePeriod",
"multiLevel",
"name",
"namespaceUri",
"numberDecimalSeparator",
"numberFormatCategories",
"numberFormatLocal",
"numberGroupSeparator",
"offset",
"options",
"orientation",
"overflowValue",
"overlap",
"overlay",
"paperSize",
"parentLabelStrategy",
"pattern",
"patternColor",
"patternTintAndShade",
"placement",
"plotBy",
"plotOrder",
"plotVisibleOnly",
"polynomialOrder",
"position",
"positionAt",
"preserveFormatting",
"previouslySaved",
"printComments",
"printErrors",
"printGridlines",
"printHeadings",
"printOrder",
"priority",
"projectionType",
"prompt",
"protected",
"quartileCalculation",
"readOnly",
"readingOrder",
"refreshOnOpen",
"removed",
"resolved",
"reverseIconOrder",
"reversePlotOrder",
"revisionNumber",
"richContent",
"rightFooter",
"rightHeader",
"rightMargin",
"rotation",
"roundedCorners",
"rowCount",
"rowHeight",
"rowHidden",
"rowIndex",
"savedAsArray",
"scaleType",
"scope",
"secondPlotSize",
"separator",
"seriesNameLevel",
"shortDatePattern",
"showAllFieldButtons",
"showAllItems",
"showAs",
"showAxisFieldButtons",
"showBandedColumns",
"showBandedRows",
"showBubbleSize",
"showCategoryName",
"showColumnGrandTotals",
"showConnectorLines",
"showDataBarOnly",
"showDataLabelsOverMaximum",
"showDisplayUnitLabel",
"showEquation",
"showFieldHeaders",
"showFilterButton",
"showGridlines",
"showHeaders",
"showHeadings",
"showIconOnly",
"showInnerPoints",
"showLeaderLines",
"showLegendFieldButtons",
"showLegendKey",
"showMeanLine",
"showMeanMarker",
"showOutlierPoints",
"showPercentage",
"showRSquared",
"showReportFilterFieldButtons",
"showRowGrandTotals",
"showSeriesName",
"showShadow",
"showTotals",
"showValue",
"showValueFieldButtons",
"shrinkToFit",
"sideIndex",
"size",
"smooth",
"sortBy",
"splitType",
"splitValue",
"standardHeight",
"standardWidth",
"state",
"stopIfTrue",
"strikethrough",
"style",
"subject",
"subscript",
"subtotalLocation",
"subtotals",
"summarizeBy",
"superscript",
"tabColor",
"text",
"textOrientation",
"thousandsSeparator",
"threeColorScale",
"tickLabelPosition",
"tickLabelSpacing",
"tickMarkSpacing",
"timeSeparator",
"tintAndShade",
"topMargin",
"transparency",
"type",
"types",
"underflowValue",
"underline",
"uniqueRemaining",
"upperBoundRule",
"useCustomSortLists",
"usePrecisionAsDisplayed",
"useSheetMargins",
"useSheetScale",
"useStandardHeight",
"useStandardWidth",
"useSystemSeparators",
"valid",
"value",
"valueTypes",
"values",
"varyByCategories",
"verticalAlignment",
"verticalOverflow",
"visibility",
"visible",
"weight",
"width",
"wrapText",
"zOrderPosition",
"zoom"
],
"ambiguous": [
"comments",
"fields",
"format",
"left",
"numberFormat",
"rule",
"title",
"top"
]
}
+26
View File
@@ -0,0 +1,26 @@
import { TSESTree } from "@typescript-eslint/utils";
import * as getJson from "./data/getFunctions.json";
const getFunctions: Set<string> = new Set<string>(getJson.getFunctions);
const getOrNullObjectFunctions: Set<string> = new Set<string>(
getJson.getOrNullObjectFunctions,
);
export function isGetFunction(node: TSESTree.Node): boolean {
return (
node.type == TSESTree.AST_NODE_TYPES.CallExpression &&
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
(getFunctions.has(node.callee.property.name) ||
getOrNullObjectFunctions.has(node.callee.property.name))
);
}
export function isGetOrNullObjectFunction(node: TSESTree.Node): boolean {
return (
node.type == TSESTree.AST_NODE_TYPES.CallExpression &&
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
getOrNullObjectFunctions.has(node.callee.property.name)
);
}
+117
View File
@@ -0,0 +1,117 @@
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
import { findCallExpression } from "./utils";
export function isLoadFunction(node: TSESTree.MemberExpression): boolean {
const methodCall = findCallExpression(node);
return methodCall !== undefined && isLoadCall(methodCall);
}
export function isLoadCall(node: TSESTree.CallExpression): boolean {
return (
node &&
node.callee.type === AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === AST_NODE_TYPES.Identifier &&
node.callee.property.name === "load"
);
}
export function isLoadReference(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
) {
return (
node.parent &&
node.parent.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
isLoadFunction(node.parent)
);
}
export function isContextLoadArgumentReference(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
) {
return (
node.parent?.type === AST_NODE_TYPES.CallExpression &&
node.parent.callee.type === AST_NODE_TYPES.MemberExpression &&
node.parent.callee.object.type === AST_NODE_TYPES.Identifier &&
node.parent.callee.object.name === "context" &&
node.parent.callee.property.type === AST_NODE_TYPES.Identifier &&
node.parent.callee.property.name === "load"
);
}
function parseObjectExpressionProperty(
objectExpression: TSESTree.ObjectExpression,
): string[] {
let composedProperties: string[] = [];
objectExpression.properties.forEach((property) => {
if (
property.type === AST_NODE_TYPES.Property &&
property.key.type === AST_NODE_TYPES.Identifier
) {
const propertyName: string = property.key.name;
if (property.value.type === AST_NODE_TYPES.ObjectExpression) {
const composedProperty = parseObjectExpressionProperty(property.value);
if (composedProperty.length !== 0) {
composedProperties = composedProperties.concat(
propertyName + "/" + composedProperty,
);
}
} else if (
property.value.type === AST_NODE_TYPES.Literal &&
property.value.value // Checking if the value assigined to the property is true
) {
composedProperties = composedProperties.concat(propertyName);
}
}
});
return composedProperties;
}
function parseLoadStringArgument(argument: string): string[] {
const properties: string[] = [];
argument
.replace(/\s/g, "")
.split(",")
.forEach((property: string) => {
properties.push(property);
});
return properties;
}
export function parseLoadArguments(node: TSESTree.MemberExpression): string[] {
const methodCall = findCallExpression(node);
if (methodCall && isLoadCall(methodCall)) {
const argument = methodCall.arguments[0];
if (!argument) {
return [];
}
return parsePropertiesArgument(argument);
}
throw new Error("error in parseLoadArgument function.");
}
export function parsePropertiesArgument(
argument: TSESTree.CallExpressionArgument,
): string[] {
let properties: string[] = [];
if (argument.type === AST_NODE_TYPES.ArrayExpression) {
argument.elements.forEach((element) => {
if (element != null && element.type === TSESTree.AST_NODE_TYPES.Literal) {
properties = properties.concat(
parseLoadStringArgument(element.value as string),
);
}
});
} else if (argument.type === TSESTree.AST_NODE_TYPES.Literal) {
properties = parseLoadStringArgument(argument.value as string);
} else if (argument.type === TSESTree.AST_NODE_TYPES.ObjectExpression) {
properties = parseObjectExpressionProperty(argument);
}
return properties;
}
+28
View File
@@ -0,0 +1,28 @@
import * as propertiesJson from "./data/properties.json";
export enum PropertyType {
navigational,
scalar,
ambiguous, // Can be scalar or navigational. Depends of the context.
notProperty,
}
const navigationProperties: Set<string> = new Set<string>(
propertiesJson.navigational,
);
const scalarProperties: Set<string> = new Set<string>(propertiesJson.scalar);
const ambiguousProperties: Set<string> = new Set<string>(
propertiesJson.ambiguous,
);
export function getPropertyType(propertyName: string): PropertyType {
if (navigationProperties.has(propertyName)) {
return PropertyType.navigational;
} else if (scalarProperties.has(propertyName)) {
return PropertyType.scalar;
} else if (ambiguousProperties.has(propertyName)) {
return PropertyType.ambiguous;
} else {
return PropertyType.notProperty;
}
}
+127
View File
@@ -0,0 +1,127 @@
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
import { Reference, Scope, Variable } from "@typescript-eslint/scope-manager";
import { isGetFunction } from "./getFunction";
import { isContextLoadArgumentReference, isLoadReference } from "./load";
function isContextSyncIdentifier(
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
): boolean {
return (
node.name === "context" &&
node.parent?.type === AST_NODE_TYPES.MemberExpression &&
node.parent.parent?.type === AST_NODE_TYPES.CallExpression &&
node.parent.property.type === AST_NODE_TYPES.Identifier &&
node.parent.property.name === "sync"
);
}
export function findTopMemberExpression(
node: TSESTree.MemberExpression,
): TSESTree.MemberExpression {
while (node.parent && node.parent.type === AST_NODE_TYPES.MemberExpression) {
node = node.parent;
}
return node;
}
export function findCallExpression(
node: TSESTree.MemberExpression,
): TSESTree.CallExpression | undefined {
while (node.parent && node.parent.type === AST_NODE_TYPES.MemberExpression) {
node = node.parent;
}
if (node.parent?.type === AST_NODE_TYPES.CallExpression) {
return node.parent;
}
return undefined;
}
export type OfficeApiReference = {
/**
* Get: An OfficeJs object, which is created when calling a `get` type function
* Load: A reference to `object.load()` type call
* Method: The reference is calling a method. Ex: `object.methodCall()`
* Read: The reference value is being read and it is not a method
* Sync: A call to `context.sync()`
*/
operation: "Get" | "Load" | "Method" | "Read" | "Sync";
reference: Reference;
};
let proxyVariables: Set<Variable>;
let apiReferences: OfficeApiReference[];
export function findOfficeApiReferences(scope: Scope): OfficeApiReference[] {
proxyVariables = new Set<Variable>();
apiReferences = [];
findOfficeApiReferencesInScope(scope);
return apiReferences;
}
function findOfficeApiReferencesInScope(scope: Scope): void {
scope.references.forEach((reference) => {
const node: TSESTree.Node = reference.identifier;
if (
reference.isWrite() &&
reference.writeExpr &&
isGetFunction(reference.writeExpr) &&
reference.resolved
) {
proxyVariables.add(reference.resolved);
apiReferences.push({ operation: "Get", reference: reference });
} else if (isContextSyncIdentifier(reference.identifier)) {
apiReferences.push({ operation: "Sync", reference: reference });
} else if (
reference.isRead() &&
reference.resolved &&
proxyVariables.has(reference.resolved)
) {
if (isLoadReference(node)) {
// <obj>.load(...)
apiReferences.push({ operation: "Load", reference: reference });
} else if (isContextLoadArgumentReference(node)) {
// context.load(<obj>, ...)
apiReferences.push({ operation: "Load", reference: reference });
} else if (isMethodReference(node)) {
apiReferences.push({ operation: "Method", reference: reference });
} else {
apiReferences.push({ operation: "Read", reference: reference });
}
}
});
scope.childScopes.forEach(findOfficeApiReferencesInScope);
}
function isMethod(node: TSESTree.MemberExpression): boolean {
const topExpression: TSESTree.MemberExpression =
findTopMemberExpression(node);
return (
topExpression.parent?.type === AST_NODE_TYPES.CallExpression &&
topExpression.parent.callee === topExpression
);
}
function isMethodReference(node: TSESTree.Identifier | TSESTree.JSXIdentifier) {
return (
node.parent &&
node.parent.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
isMethod(node.parent)
);
}
export function findPropertiesRead(node: TSESTree.Node | undefined): string {
let propertyName = ""; // Will be a string combined with '/' for the case of navigation properties
while (node) {
if (
node.type === AST_NODE_TYPES.MemberExpression &&
node.property.type === AST_NODE_TYPES.Identifier &&
!isMethod(node)
) {
propertyName += node.property.name + "/";
}
node = node.parent;
}
return propertyName.slice(0, -1);
}
@@ -0,0 +1,194 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/call-sync-after-load";
const ruleTester = new RuleTester();
ruleTester.run("call-sync-after-load", rule, {
valid: [
{
code: `
var property = worksheet.getItem("sheet");
property.load("values");
await context.sync();
console.log(property.values);`,
},
{
code: `
var fakeGet = worksheet.notAGetFunction("props");
await context.sync();
fakeGet.load("props");
console.log(fakeGet.props);`,
},
{
code: `
var fakeGet = worksheet.notAGetFunction("props");
await context.sync();
property.load("props");
console.log(property.props);`,
},
{
code: `
var table = worksheet.getTables();
return context.sync().then(function () {
table.delete();
});`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.getCell(0,0);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("font/fill/color, address");
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("*");
await context.sync();
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load();
await context.sync();
console.log(range.address);`,
},
{
code: `
var property = worksheet.getItem("sheet");
context.load(property, "values");
await context.sync();
console.log(property.values);`,
},
{
code: `
var property = worksheet.getItem("sheet");
context.load(property);
await context.sync();
console.log(property.values);`,
},
],
invalid: [
{
code: `
var property = worksheet.getItem("sheet");
await context.sync();
property.load("values");
console.log(property.values);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "values" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
property.load("values");
await context.sync();
console.log(property.values);
property.load("length");
console.log(property.length);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "length" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load(["font/fill/color", "address"]);
console.log(range.font.fill.color);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "range", loadValue: "font/fill/color" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("*");
console.log(range.address);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "range", loadValue: "address" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load();
console.log(range.address);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "range", loadValue: "address" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
await context.sync();
context.load(property, "values");
console.log(property.values);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "values" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
context.load(property, "values");
console.log(property.values);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "values" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
await context.sync();
context.load(property);
console.log(property.values);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "values" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
context.load(property);
console.log(property.values);`,
errors: [
{
messageId: "callSyncAfterLoad",
data: { name: "property", loadValue: "values" },
},
],
},
],
});
@@ -0,0 +1,185 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/call-sync-before-read";
const ruleTester = new RuleTester();
ruleTester.run("call-sync-before-read", rule, {
valid: [
{
code: `
Excel.run(function (context) {
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
await context.sync()
console.log('The selected range is: ' + selectedRange.address);
});`,
},
{
code: `
Excel.run(function (context) {
var selectedRange;
selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
await context.sync()
console.log('The selected range is: ' + selectedRange.address);
});`,
},
{
code: `
Excel.run(function (context) {
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
return context.sync()
.then(function () {
console.log('The selected range is: ' + selectedRange.address);
});
})`,
},
{
code: `
Excel.run(function (context) {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
await context.sync();
if (dataSheet.isNullObject) {
dataSheet.position = 1;
}
})`,
},
{
code: `
Excel.run(function (context) {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (true) {
await context.sync();
dataSheet.position = 1;
}
})`,
},
{
code: `
Excel.run(function (context) {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (true) {
dataSheet.position = 1; // Write is OK
}
await context.sync();
});`,
},
{
code: `
var range = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
range.format.fill.color = "yellow";`,
},
{
code: `
var range = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
range.format.font.load('color');`,
},
{
code: `
var table = worksheet.getTables();
return context.sync().then(function () {
table.delete();
});`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.getCell(0,0);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("font");
context.sync();
range.font.getColor();`,
},
{
code: `
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getActiveWorksheet();
let data: string[][] = [["one"], ["two"]];
const rangeAddress = data.length;
const range = sheet.getRange(rangeAddress);
range.values = data;
range.format.autofitColumns();
return context.sync();
});`,
},
],
invalid: [
{
code: `
Excel.run(function (context) {
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
console.log('The selected range is: ' + selectedRange.address);
});`,
errors: [{ messageId: "callSync", data: { name: "selectedRange" } }],
},
{
code: `
Excel.run(function (context) {
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
console.log('The selected range is: ' + selectedRange.address);
await context.sync();
});`,
errors: [{ messageId: "callSync", data: { name: "selectedRange" } }],
},
{
code: `
Excel.run(function (context) {
var selectedRange = context.workbook.getSelectedRange();
var selectedRange2 = context.workbook.getSelectedRange();
selectedRange.load('address');
selectedRange2.load('address');
console.log('The selected range is: ' + selectedRange.address);
console.log('This should be the same: ' + selectedRange2.address);
});`,
errors: [
{ messageId: "callSync", data: { name: "selectedRange" } },
{ messageId: "callSync", data: { name: "selectedRange2" } },
],
},
{
code: `
Excel.run(function (context) {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (dataSheet.isNullObject) {
dataSheet.position = 1;
}
});`,
errors: [{ messageId: "callSync", data: { name: "dataSheet" } }],
},
{
code: `
Excel.run(function (context) {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (true) {
var pos = dataSheet.position;
}
await context.sync();
});`,
errors: [{ messageId: "callSync", data: { name: "dataSheet" } }],
},
{
code: `
var range = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
range.format.fill.color == "yellow";`,
errors: [{ messageId: "callSync", data: { name: "range" } }],
},
{
code: `
var range = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
var value = range.format.fill.color;
value = range.format.fill.color;`,
errors: [
{ messageId: "callSync", data: { name: "range" } },
{ messageId: "callSync", data: { name: "range" } },
],
},
],
});
@@ -0,0 +1,333 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/load-object-before-read";
const ruleTester = new RuleTester();
ruleTester.run("load-object-before-read", rule, {
valid: [
{
code: `
var sheetName = 'Sheet1';
var rangeAddress = 'A1:B2';
var myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
context.sync()
.then(function () {
console.log (myRange.address); // ok
});`,
},
{
code: `
var property = worksheet.getItem("sheet");
property.load('G2');
var variableName = property.G2;`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('values');
if(selectedRange.values === [2]){}`,
},
{
code: `
var selectedRange;
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('values');
console.log(selectedRange.values);`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('values');
console.log(myRange.values);`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
myRange.load('values');
myRange.load('address');
console.log(myRange.address);
console.log(myRange.values);`,
},
{
code: `
var myRange = context.thisIsNotAGetFunction();
myRange.load('values')
var test = myRange.values;
var myRange = context.workbook.worksheets.getSelectedRange();`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
myRange = context.notAGetFunction;
myRange.load('values');
var test = myRange.values;`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
myRange = context.notAGetFunction;
var test = myRange.values;`,
},
{
code: `
var range = worksheet.getRange("A1");
range.format.fill.color = "red";
range.numberFormat = "0.00%";
range.values = [[1]];`,
},
{
code: `
var range = worksheet.getRange("A1");
range.load("format/fill/size");
console.log(range.format.fill.size);`,
},
{
code: `
var table = worksheet.getTables();
return context.sync().then(function () {
table.delete();
});`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.getCell(0,0);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load(["font/fill/color", "address"]);
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("*");
await context.sync();
console.log(range.address);
console.log(range.font);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load();
await context.sync();
console.log(range.address);
console.log(range.font);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load({ format: { fill: { color: true } }, address: true } );
await context.sync();
console.log(range.format.fill.color);
console.log(range.address);`,
},
{
code: `
const range = context.workbook.getSelectedRange();
const first = range.getCell(0, 0);
const spillParent = first.getSpillParentOrNullObject();
await context.sync();
const cell = spillParent.isNullObject ? first : spillParent;
console.log(cell);`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
context.load(myRange, 'values');
console.log(myRange.values);`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
context.load(myRange);
console.log(myRange.values);`,
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
context.load(myRange, ['values', 'address']);
console.log(myRange.values);`,
},
],
invalid: [
{
code: `
var sheetName = 'Sheet1';
var rangeAddress = 'A1:B2';
console.log(rangeAddress);
var myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
context.sync()
.then(function () {
console.log (myRange.values); // not ok as it was not loaded
});`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "values" },
},
],
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
if(selectedRange.values === ["sampleText"]){}`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "selectedRange", loadValue: "values" },
},
],
},
{
code: `
var myRange = context.workbook.worksheets.getItem("sheet").getRange("A1");
console.log (myRange.adress);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "adress" },
},
],
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
console.log(selectedRange.values);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "selectedRange", loadValue: "values" },
},
],
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
console.log(selectedRange.values);
selectedRange.load('values')`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "selectedRange", loadValue: "values" },
},
],
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
var test = selectedRange.values;`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "selectedRange", loadValue: "values" },
},
],
},
{
code: `
var myRange;
myRange = context.workbook.worksheets.getSelectedRange();
var test = myRange.values;`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "values" },
},
],
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
var test = myRange.values;`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "values" },
},
],
},
{
code: `
var range = worksheet.getRange("A1");
range.load("range/format/fill/size");
console.log(range.format.fill.color);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "range", loadValue: "format/fill/color" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
await context.sync();
console.log(range.font.fill.color);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "range", loadValue: "font/fill/color" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load({ format: { fill: { color: true } } } );
await context.sync();
console.log(range.format.fill.color);
console.log(range.address);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "range", loadValue: "address" },
},
],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load({ format: { fill: { color: false } } } );
await context.sync();
console.log(range.format.fill.color);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "range", loadValue: "format/fill/color" },
},
],
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
context.load("myRange", "values");
console.log(myRange.values);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "values" },
},
],
},
{
code: `
var myRange = context.workbook.worksheets.getSelectedRange();
context.load(myRange, "values", "address");
console.log(myRange.values);`,
errors: [
{
messageId: "loadBeforeRead",
data: { name: "myRange", loadValue: "values" },
},
],
},
],
});
@@ -0,0 +1,53 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/no-context-sync-in-loop";
const ruleTester = new RuleTester();
ruleTester.run("no-context-sync-in-loop", rule, {
valid: [
{
code: "context.sync()",
},
{
code: `Excel.run(async (context) => {
context.sync();
});`,
},
],
invalid: [
{
code: `Word.run(async (context) => {
for(i = 0; i < 5; i++) { context.sync(); }
});`,
errors: [{ messageId: "loopedSync" }],
},
{
code: `Excel.run(async (context) => {
var person = { fname:\"John\", lname:\"Doe\", age:25 };
var x;
for(x in person) { context.sync(); }
});`,
errors: [{ messageId: "loopedSync" }],
},
{
code: `PowerPoint.run(async (context) => {
var cars = ['BMW', 'Volvo', 'Mini'];
var x;
for(x of cars) { context.sync(); }
});`,
errors: [{ messageId: "loopedSync" }],
},
{
code: `Word.run(async (context) => {
while(true) { context.sync() }
});`,
errors: [{ messageId: "loopedSync" }],
},
{
code: `Excel.run(async (context) => {
do { context.sync() } while(true);
});`,
errors: [{ messageId: "loopedSync" }],
},
],
});
@@ -0,0 +1,80 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/no-empty-load";
const ruleTester = new RuleTester();
ruleTester.run("no-empty-load", rule, {
valid: [
{
code: `
var sheetName = 'Sheet1';
var rangeAddress = 'A1:B2';
var myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
context.sync()
.then(function () {
console.log (myRange.address); // ok
});`,
},
{
code: `
var property = worksheet.getItem("sheet");
property.load('G2');
var variableName = property.G2;`,
},
{
code: `
const notProxyObject = anotherObject.thisIsNotAGetFunction();
notProxyObject.load();`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange = "new variable";
selectedRange.load()`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load("*")`,
},
],
invalid: [
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load();`,
errors: [{ messageId: "emptyLoad" }],
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load("");`,
errors: [{ messageId: "emptyLoad" }],
},
{
code: `
var myRange;
myRange = context.workbook.worksheets.getSelectedRange();
myRange.load();
console.log(myRange.values);`,
errors: [{ messageId: "emptyLoad" }],
},
{
code: `
var myRange;
myRange = context.workbook.worksheets.getSelectedRange();
myRange.load(["address", "values", ""]);
console.log(myRange.values);`,
errors: [{ messageId: "emptyLoad" }],
},
{
code: `
var myRange;
myRange = context.workbook.worksheets.getSelectedRange();
myRange.load("address, values, ");
console.log(myRange.values);`,
errors: [{ messageId: "emptyLoad" }],
},
],
});
@@ -0,0 +1,186 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/no-navigational-load";
const ruleTester = new RuleTester();
ruleTester.run("no-navigational-load", rule, {
valid: [
{
code: `
var range = worksheet.getRange("A1");
range.load("borders/fill/size");
console.log(range.borders.fill.size);`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load("format/font/name");`,
},
{
code: `
var sheetName = 'Sheet1';
var rangeAddress = 'A1:B2';
var myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
context.sync()
.then(function () {
console.log (myRange.address); // ok
});`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('text'); // Scalar`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load('values');
if(selectedRange.values === [2]){}`,
},
{
code: `
var myRange = context.workbook.worksheets.notAGet();
myRange.load('notAProperty');
var test = myRange.notAProperty;`,
},
{
code: `
var range = context.workbook.getRange();
range.load({borders: { fill: { color: true } } });
if (range.borders.fill.color);`,
},
{
code: `
var range = context.workbook.getRange();
range.borders.fill.load("color");
console.log(range.borders.fill.color);`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load(''); // Empty`,
},
{
code: `
var selectedRange = context.workbook.getSelectedRange();
selectedRange.load(); // Empty`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("font/fill/color, address");
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load(["font/fill/color", "address"]);
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("*");
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
},
{
code: `
var range = worksheet.getSelectedRange();
range.load({ format: { fill: { color: true } } });
await context.sync();
console.log(range.font.fill.color);`,
},
],
invalid: [
{
code: `
var property = worksheet.getItem("sheet");
property.load('thisIsNotAProperty'); //Not a property`,
errors: [
{
messageId: "navigationalLoad",
data: { loadValue: "thisIsNotAProperty" },
},
],
},
{
code: `
var property = worksheet.getItem("sheet");
property.load('styles'); // Navigational`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "styles" } },
],
},
{
code: `
var property = worksheet.getItem("sheet");
context.load(property, 'styles'); // Navigational`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "styles" } },
],
},
{
code: `
var range = worksheet.getRange("A1");
range.load('fill'); // Navigational`,
errors: [{ messageId: "navigationalLoad", data: { loadValue: "fill" } }],
},
{
code: `
var range = worksheet.getRange("A1");
context.load(range, 'fill'); // Navigational`,
errors: [{ messageId: "navigationalLoad", data: { loadValue: "fill" } }],
},
{
code: `
var range = worksheet.getRange("A1");
range.load("borders/fill");
console.log(range.borders.fill); // Navigational`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "borders/fill" } },
],
},
{
code: `
var range = worksheet.getRange("A1");
context.load(range, "borders/fill");
console.log(range.borders.fill); // Navigational`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "borders/fill" } },
],
},
{
code: `
var range = worksheet.getRange("A1");
range.load({borders: { fill: true } });`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "borders/fill" } },
],
},
{
code: `
var range = context.workbook.getRange();
range.borders.load("fill");
console.log(range.borders.fill);`,
errors: [{ messageId: "navigationalLoad", data: { loadValue: "fill" } }],
},
{
code: `
var range = worksheet.getSelectedRange();
range.load("address, font/fill");
await context.sync();
console.log(range.font.fill.color);
console.log(range.address);`,
errors: [
{ messageId: "navigationalLoad", data: { loadValue: "font/fill" } },
],
},
],
});
@@ -0,0 +1,55 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/no-office-initialize";
const ruleTester = new RuleTester();
ruleTester.run("no-office-initialize", rule, {
valid: [
{
code: `Office.onReady();`,
},
{
code: `Office.onReady(function(info) {
console.log(info);
});`,
},
{
code: `Office.onReady()
.then(function() {
console.log("Testing Office.onReady followed by .then");
});`,
},
{
code: `(async () => {
await Office.onReady();
console.log("Testing Office.onReady followed by await");
})();`,
},
],
invalid: [
{
code: `Office.initialize = function () {};`,
errors: [{ messageId: "noOfficeInitialize" }],
},
{
code: `Office.initialize = function () {
console.log("Testing Office.initialize with normal function");
};`,
errors: [{ messageId: "noOfficeInitialize" }],
},
{
code: `Office.initialize = function (reason) {
$(document).ready(function () {
console.log(reason);
});
};`,
errors: [{ messageId: "noOfficeInitialize" }],
},
{
code: `Office.initialize = () => {
console.log("Testing Office.initialize with arrow function");
};`,
errors: [{ messageId: "noOfficeInitialize" }],
},
],
});
@@ -0,0 +1,214 @@
import { RuleTester, TestCaseError } from "@typescript-eslint/rule-tester";
import rule from "../../src/rules/test-for-null-using-isNullObject";
const ruleTester = new RuleTester();
const errors: TestCaseError<"useIsNullObject">[] = [
{ messageId: "useIsNullObject", data: { name: "dataSheet" } },
];
ruleTester.run("test-for-null-using-isNullObject", rule, {
valid: [
{
code: `
await Excel.run(async (context) => {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}`,
},
],
invalid: [
{
code: `
await Excel.run(async (context) => {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});
});`,
errors,
output: `
await Excel.run(async (context) => {
var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (!dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (!dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (true && dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (true && dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (null != dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
for (var i = 0; i < 5; i++) {
if (dataSheet) {
dataSheet = context.workbook.worksheets.add("Data");
}
}
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
for (var i = 0; i < 5; i++) {
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
}
dataSheet.position = 1;
});`,
},
{
code: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
do {
console.log("test case");
} while (dataSheet);
dataSheet.position = 1;
});`,
errors,
output: `
var dataSheet;
dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
return context.sync().then(function () {
do {
console.log("test case");
} while (dataSheet.isNullObject);
dataSheet.position = 1;
});`,
},
],
});
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "Node16",
"moduleResolution": "node16",
"outDir": "lib",
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["lib", "node_modules"],
}