eslint-plugin-ts-immutable
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -1,28 +0,54 @@ | ||
# Change Log | ||
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
The format is based on [Keep a Changelog](http://keepachangelog.com/) | ||
and this project adheres to [Semantic Versioning](http://semver.org/). | ||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) | ||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
## [Unreleased] | ||
## [Unreleased](https://github.com/jonaskello/eslint-plugin-ts-immutable/compare/v0.3.0...HEAD) | ||
## [v0.2.1] - 2019-07-12 | ||
## [v0.3.0](https://github.com/jonaskello/eslint-plugin-ts-immutable/compare/v0.2.1...v0.3.0) | ||
Remove fixer for `no-let`. | ||
TypeScript is no longer imported unless it is available. | ||
### Merged | ||
## [v0.2.0] - 2019-07-12 | ||
- ReadonlySet and ReadonlyMap [`#30`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/30) | ||
- prefer-readonly-types [`#29`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/29) | ||
- no-return-void [`#28`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/28) | ||
- functional-parameters [`#27`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/27) | ||
- feat(readonly-keyword) Add support for parameter properties [`#26`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/26) | ||
- no-conditional-statement [`#23`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/23) | ||
- chore: Remove no-delete rule. [`#21`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/21) | ||
- new rule: immutable-data [`#22`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/22) | ||
This is the first release where things should work. All the rules have been ported over from "ts-immutable" and tests are in place. | ||
Four configs are available: `recommended`, `functional-lite`, `functional` and `all`. | ||
### Fixed | ||
## [v0.1.0] - 2019-07-01 | ||
- feat(immutable-data): Prevent object mutation methods. [`#16`](https://github.com/jonaskello/eslint-plugin-ts-immutable/issues/16) | ||
### Added | ||
## [v0.2.1](https://github.com/jonaskello/eslint-plugin-ts-immutable/compare/v0.2.0...v0.2.1) - 2019-07-12 | ||
### Merged | ||
- Remove no-let fixer [`#14`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/14) | ||
### Fixed | ||
- TypeScript is no longer imported unless it is available. | ||
## [v0.2.0](https://github.com/jonaskello/eslint-plugin-ts-immutable/compare/v0.1.0...v0.2.0) - 2019-07-12 | ||
### Merged | ||
- docs: Update supported rules. [`#8`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/8) | ||
- Make the code more functional [`#9`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/9) | ||
- Added ignoreAccessorPattern, ignorePattern now uses Regex matching [`#7`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/7) | ||
- Ignore option fix [`#6`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/6) | ||
- Consistent file casing [`#5`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/5) | ||
- Port docs for each rule [`#3`](https://github.com/jonaskello/eslint-plugin-ts-immutable/pull/3) | ||
### Fixed | ||
- feat(ignore-option): Add ignoreAccessorPattern option to deal with accessors and switch ignorePatter [`#4`](https://github.com/jonaskello/eslint-plugin-ts-immutable/issues/4) | ||
## [v0.1.0](https://github.com/jonaskello/eslint-plugin-ts-immutable/releases/tag/v0.1.0) - 2019-07-01 | ||
This is the first release and everything is experimental at this point. | ||
[unreleased]: https://github.com/jonaskello/eslint-plugin-ts-immutable/compare/v0.2.0...master | ||
[v0.2.0]: https://github.com/jonaskello/eslint-plugin-ts-immutable/releases/tag/v0.2.0 | ||
[v0.1.0]: https://github.com/jonaskello/eslint-plugin-ts-immutable/releases/tag/v0.1.0 |
1648
lib/index.js
@@ -8,15 +8,15 @@ 'use strict'; | ||
var escapeRegExp = _interopDefault(require('escape-string-regexp')); | ||
var typescriptEstree = require('@typescript-eslint/typescript-estree'); | ||
var experimentalUtils = require('@typescript-eslint/experimental-utils'); | ||
var astNodeTypes = require('@typescript-eslint/typescript-estree/dist/ts-estree/ast-node-types'); | ||
var tslib_1 = require('tslib'); | ||
var config = { | ||
rules: { | ||
"ts-immutable/no-array-mutation": "error", | ||
"ts-immutable/immutable-data": "error", | ||
"ts-immutable/no-class": "error", | ||
"ts-immutable/no-delete": "error", | ||
"ts-immutable/no-conditional-statement": "error", | ||
"ts-immutable/no-expression-statement": "error", | ||
"ts-immutable/no-if-statement": "error", | ||
"ts-immutable/no-let": "error", | ||
"ts-immutable/no-loop-statement": "error", | ||
"ts-immutable/no-object-mutation": "error", | ||
"ts-immutable/no-reject": "error", | ||
@@ -33,4 +33,3 @@ "ts-immutable/no-this": "error", | ||
"ts-immutable/no-mixed-interface": "error", | ||
"ts-immutable/readonly-array": "error", | ||
"ts-immutable/readonly-keyword": "error" | ||
"ts-immutable/prefer-readonly-types": "error" | ||
} | ||
@@ -51,3 +50,10 @@ } | ||
rules: { | ||
"@typescript-eslint/explicit-function-return-type": "warn" | ||
"@typescript-eslint/explicit-function-return-type": [ | ||
"error", | ||
{ | ||
allowExpressions: true, | ||
allowTypedFunctionExpressions: true, | ||
allowHigherOrderFunctions: true | ||
} | ||
] | ||
} | ||
@@ -63,4 +69,3 @@ } | ||
"ts-immutable/no-let": "error", | ||
"ts-immutable/no-object-mutation": "error", | ||
"ts-immutable/no-delete": "error" | ||
"ts-immutable/immutable-data": "error" | ||
}, | ||
@@ -71,6 +76,4 @@ overrides: [ | ||
rules: { | ||
"ts-immutable/no-array-mutation": "error", | ||
"ts-immutable/no-method-signature": "warn", | ||
"ts-immutable/readonly-array": "error", | ||
"ts-immutable/readonly-keyword": "error" | ||
"ts-immutable/prefer-readonly-types": "error" | ||
} | ||
@@ -89,3 +92,9 @@ } | ||
"ts-immutable/no-loop-statement": "error", | ||
"ts-immutable/no-throw": "error" | ||
"ts-immutable/no-conditional-statement": [ | ||
"error", | ||
{ allowReturningBranches: true } | ||
], | ||
"ts-immutable/no-throw": "error", | ||
"ts-immutable/no-return-void": "error", | ||
"ts-immutable/functional-parameters": "error" | ||
}, | ||
@@ -107,4 +116,4 @@ overrides: [ | ||
rules: { | ||
"ts-immutable/no-conditional-statement": "error", | ||
"ts-immutable/no-expression-statement": "error", | ||
"ts-immutable/no-if-statement": "error", | ||
"ts-immutable/no-try": "error" | ||
@@ -115,4 +124,14 @@ } | ||
// Conditionally loaded TypeScript but only if it is avaliable. | ||
var ts = (function () { | ||
try { | ||
return require("typescript"); | ||
} | ||
catch (error) { | ||
return undefined; | ||
} | ||
})(); | ||
/** | ||
* This file has functions that typeguard the given node/type. | ||
* @file Functions that typeguard the given node/type. | ||
*/ | ||
@@ -122,57 +141,98 @@ /* | ||
*/ | ||
function isArrayExpression(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.ArrayExpression; | ||
} | ||
function isAssignmentExpression(node) { | ||
return node.type === "AssignmentExpression"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.AssignmentExpression; | ||
} | ||
function isAssignmentPattern(node) { | ||
return node.type === "AssignmentPattern"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.AssignmentPattern; | ||
} | ||
function isArrayExpression(node) { | ||
return node.type === "ArrayExpression"; | ||
function isBlockStatement(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.BlockStatement; | ||
} | ||
function isCallExpression(node) { | ||
return node.type === "CallExpression"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.CallExpression; | ||
} | ||
/** | ||
* Is the given node a class node? | ||
* | ||
* It doesn't matter what type of class. | ||
*/ | ||
function isClassLike(node) { | ||
return node.type === "ClassDeclaration" || node.type === "ClassExpression"; | ||
return (node.type === typescriptEstree.AST_NODE_TYPES.ClassDeclaration || | ||
node.type === typescriptEstree.AST_NODE_TYPES.ClassExpression); | ||
} | ||
/** | ||
* Is the given node a function node? | ||
* | ||
* It doesn't matter what type of function. | ||
*/ | ||
function isFunctionLike(node) { | ||
return (node.type === "FunctionDeclaration" || | ||
node.type === "FunctionExpression" || | ||
node.type === "ArrowFunctionExpression"); | ||
return (node.type === typescriptEstree.AST_NODE_TYPES.FunctionDeclaration || | ||
node.type === typescriptEstree.AST_NODE_TYPES.FunctionExpression || | ||
node.type === typescriptEstree.AST_NODE_TYPES.ArrowFunctionExpression); | ||
} | ||
function isIdentifier(node) { | ||
return node.type === "Identifier"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.Identifier; | ||
} | ||
function isIfStatement(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.IfStatement; | ||
} | ||
function isMemberExpression(node) { | ||
return node.type === "MemberExpression"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.MemberExpression; | ||
} | ||
function isMethodDefinition(node) { | ||
return node.type === "MethodDefinition"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.MethodDefinition; | ||
} | ||
function isNewExpression(node) { | ||
return node.type === "NewExpression"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.NewExpression; | ||
} | ||
function isProperty(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.Property; | ||
} | ||
function isRestElement(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.RestElement; | ||
} | ||
function isReturnStatement(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.ReturnStatement; | ||
} | ||
function isTSArrayType(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSArrayType; | ||
} | ||
function isTSIndexSignature(node) { | ||
return node.type === "TSIndexSignature"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSIndexSignature; | ||
} | ||
function isTSInterfaceBody(node) { | ||
return node.type === "TSInterfaceBody"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSInterfaceBody; | ||
} | ||
function isTSArrayType(node) { | ||
return node.type === "TSArrayType"; | ||
function isTSNullKeyword(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSNullKeyword; | ||
} | ||
function isTSParameterProperty(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSParameterProperty; | ||
} | ||
function isTSPropertySignature(node) { | ||
return node.type === "TSPropertySignature"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSPropertySignature; | ||
} | ||
function isTSTupleType(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSTupleType; | ||
} | ||
function isTSTypeAliasDeclaration(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSTypeAliasDeclaration; | ||
} | ||
function isTSTypeOperator(node) { | ||
return node.type === "TSTypeOperator"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSTypeOperator; | ||
} | ||
function isTypeAliasDeclaration(node) { | ||
return node.type === "TSTypeAliasDeclaration"; | ||
function isTSUndefinedKeyword(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSUndefinedKeyword; | ||
} | ||
function isTSVoidKeyword(node) { | ||
return node.type === typescriptEstree.AST_NODE_TYPES.TSVoidKeyword; | ||
} | ||
function isVariableDeclaration(node) { | ||
return node.type === "VariableDeclaration"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.VariableDeclaration; | ||
} | ||
function isVariableDeclarator(node) { | ||
return node.type === "VariableDeclarator"; | ||
return node.type === typescriptEstree.AST_NODE_TYPES.VariableDeclarator; | ||
} | ||
@@ -183,27 +243,46 @@ /* | ||
function isUnionType(type) { | ||
// TODO: Find a nicer why to conditionally require typescript. | ||
/* eslint-disable-next-line ts-immutable/no-try */ | ||
try { | ||
// Cannot use top-level typescript import - that is for types only, not values. | ||
/* eslint-disable-next-line @typescript-eslint/no-require-imports */ | ||
return type.flags === require("typescript").TypeFlags.Union; | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
return ts !== undefined && type.flags === ts.TypeFlags.Union; | ||
} | ||
function isArrayType(type) { | ||
return ((type.symbol && type.symbol.name === "Array") || | ||
(isUnionType(type) && type.types.some(isArrayType))); | ||
function isArrayType(type, assumeType, node) { | ||
if (assumeType === void 0) { assumeType = false; } | ||
if (node === void 0) { node = null; } | ||
return assumeType === true | ||
? node !== null | ||
: type !== null && | ||
((type.symbol && type.symbol.name === "Array") || | ||
(isUnionType(type) && | ||
type.types.some(function (t) { return isArrayType(t, false, null); }))); | ||
} | ||
function isArrayConstructorType(type) { | ||
return ((type.symbol && type.symbol.name === "ArrayConstructor") || | ||
(isUnionType(type) && type.types.some(isArrayConstructorType))); | ||
function isArrayConstructorType(type, assumeType, node) { | ||
if (assumeType === void 0) { assumeType = false; } | ||
if (node === void 0) { node = null; } | ||
return assumeType === true | ||
? node !== null && isIdentifier(node) && node.name === "Array" | ||
: type !== null && | ||
((type.symbol && type.symbol.name === "ArrayConstructor") || | ||
(isUnionType(type) && | ||
type.types.some(function (t) { return isArrayConstructorType(t, false, null); }))); | ||
} | ||
function isObjectConstructorType(type) { | ||
return ((type.symbol && type.symbol.name === "ObjectConstructor") || | ||
(isUnionType(type) && type.types.some(isObjectConstructorType))); | ||
function isObjectConstructorType(type, assumeType, node) { | ||
if (assumeType === void 0) { assumeType = false; } | ||
if (node === void 0) { node = null; } | ||
return assumeType === true | ||
? node !== null && isIdentifier(node) && node.name === "Object" | ||
: type !== null && | ||
((type.symbol && type.symbol.name === "ObjectConstructor") || | ||
(isUnionType(type) && | ||
type.types.some(function (t) { return isObjectConstructorType(t, false, null); }))); | ||
} | ||
/** | ||
* Return the parent that meets the given check criteria. | ||
*/ | ||
function getParentOfType(checker, node) { | ||
return checker(node) | ||
? node | ||
: node.parent == undefined | ||
? null | ||
: getParentOfType(checker, node.parent); | ||
} | ||
/** | ||
* Test if the given node is in a function. | ||
@@ -246,11 +325,17 @@ */ | ||
/** | ||
* Return the parent that meets the given check criteria. | ||
* Is the given identifier a property of an object? | ||
*/ | ||
function getParentOfType(checker, node) { | ||
return checker(node) | ||
? node | ||
: node.parent == undefined | ||
? null | ||
: getParentOfType(checker, node.parent); | ||
function isPropertyAccess(node) { | ||
return (node.parent !== undefined && | ||
isMemberExpression(node.parent) && | ||
node.parent.property === node); | ||
} | ||
/** | ||
* Is the given identifier a property name? | ||
*/ | ||
function isPropertyName(node) { | ||
return (node.parent !== undefined && | ||
isProperty(node.parent) && | ||
node.parent.key === node); | ||
} | ||
@@ -318,42 +403,17 @@ // Polyfill. | ||
}; | ||
var ignoreNewArrayOptionSchema = { | ||
type: "object", | ||
properties: { | ||
ignoreNewArray: { | ||
type: "boolean" | ||
} | ||
}, | ||
additionalProperties: false | ||
}; | ||
/** | ||
* Should the given node be ignored? | ||
* Recursive callback of `getNodeText`. | ||
* | ||
* This function not be called from anywhere else. | ||
*/ | ||
function shouldIgnore(node, context, ignoreOptions) { | ||
return ( | ||
// Ignore if in a function and ignoreLocal is set. | ||
(Boolean(ignoreOptions.ignoreLocal) && inFunction(node)) || | ||
// Ignore if in a class and ignoreClass is set. | ||
(Boolean(ignoreOptions.ignoreClass) && inClass(node)) || | ||
// Ignore if in an interface and ignoreInterface is set. | ||
(Boolean(ignoreOptions.ignoreInterface) && inInterface(node)) || | ||
(function (texts) { | ||
return texts.length > 0 | ||
? // Ignore if ignorePattern is set and a pattern matches. | ||
(ignoreOptions.ignorePattern !== undefined && | ||
texts.every(function (text) { | ||
return isIgnoredPattern(text, ignoreOptions.ignorePattern); | ||
})) || | ||
// Ignore if ignoreAccessorPattern is set and an accessor pattern matches. | ||
(ignoreOptions.ignoreAccessorPattern !== undefined && | ||
texts.every(function (text) { | ||
return isIgnoredAccessorPattern(text, ignoreOptions.ignoreAccessorPattern); | ||
})) | ||
: false; | ||
})(getNodeTexts(node, context))); | ||
function _getNodeText(node, context) { | ||
return isIdentifier(node) | ||
? node.name | ||
: isMemberExpression(node) | ||
? _getNodeText(node.object, context) + "." + _getNodeText(node.property, context) | ||
: context.getSourceCode().getText(node); | ||
} | ||
function getNodeTexts(node, context) { | ||
return (isVariableDeclaration(node) | ||
? node.declarations.flatMap(function (declarator) { return getNodeText(declarator, context); }) | ||
: [getNodeText(node, context)]).filter(function (name) { return name !== undefined; }); | ||
} | ||
/** | ||
* Get the text of the given node. | ||
*/ | ||
function getNodeText(node, context) { | ||
@@ -366,3 +426,3 @@ return isAssignmentExpression(node) | ||
? _getNodeText(node.object, context) | ||
: isVariableDeclarator(node) || isTypeAliasDeclaration(node) | ||
: isVariableDeclarator(node) || isTSTypeAliasDeclaration(node) | ||
? _getNodeText(node.id, context) | ||
@@ -373,10 +433,16 @@ : isTSPropertySignature(node) | ||
} | ||
function _getNodeText(node, context) { | ||
return isIdentifier(node) | ||
? node.name | ||
: isMemberExpression(node) | ||
? _getNodeText(node.object, context) + "." + _getNodeText(node.property, context) | ||
: context.getSourceCode().getText(node); | ||
/** | ||
* Get all the important bits of texts from the given node. | ||
*/ | ||
function getNodeTexts(node, context) { | ||
return (isVariableDeclaration(node) | ||
? node.declarations.flatMap(function (declarator) { return getNodeText(declarator, context); }) | ||
: [getNodeText(node, context)]).filter(function (name) { return name !== undefined; }); | ||
} | ||
function isIgnoredPattern(text, ignorePattern) { | ||
/** | ||
* Should the given text be ignore? | ||
* | ||
* Test using the given pattern(s). | ||
*/ | ||
function shouldIgnoreViaPattern(text, ignorePattern) { | ||
var patterns = Array.isArray(ignorePattern) | ||
@@ -388,12 +454,10 @@ ? ignorePattern | ||
} | ||
function isIgnoredAccessorPattern(text, ignorePattern) { | ||
var patterns = Array.isArray(ignorePattern) | ||
? ignorePattern | ||
: [ignorePattern]; | ||
// One or more patterns match? | ||
return patterns.some(function (pattern) { | ||
return findMatch(pattern.split("."), text.split(".")); | ||
}); | ||
} | ||
function findMatch(_a, textParts, allowExtra) { | ||
/** | ||
* Recursive callback of `shouldIgnoreViaAccessorPattern`. | ||
* | ||
* This function not be called from anywhere else. | ||
* | ||
* Does the given text match the given pattern. | ||
*/ | ||
function accessorPatternMatch(_a, textParts, allowExtra) { | ||
var pattern = _a[0], remainingPatternParts = _a.slice(1); | ||
@@ -406,7 +470,7 @@ if (allowExtra === void 0) { allowExtra = false; } | ||
? textParts.length === 0 | ||
? findMatch(remainingPatternParts, [], allowExtra) | ||
? accessorPatternMatch(remainingPatternParts, [], allowExtra) | ||
: Array.from({ length: textParts.length }) | ||
.map(function (_element, index) { return index; }) | ||
.some(function (offset) { | ||
return findMatch(remainingPatternParts, textParts.slice(offset), true); | ||
return accessorPatternMatch(remainingPatternParts, textParts.slice(offset), true); | ||
}) | ||
@@ -416,8 +480,49 @@ : // Match anything? | ||
? textParts.length > 0 && | ||
findMatch(remainingPatternParts, textParts.slice(1), allowExtra) | ||
accessorPatternMatch(remainingPatternParts, textParts.slice(1), allowExtra) | ||
: // Text matches pattern? | ||
new RegExp("^" + escapeRegExp(pattern).replace(/\\\*/g, ".*") + "$").test(textParts[0]) && findMatch(remainingPatternParts, textParts.slice(1), allowExtra); | ||
new RegExp("^" + escapeRegExp(pattern).replace(/\\\*/g, ".*") + "$").test(textParts[0]) && | ||
accessorPatternMatch(remainingPatternParts, textParts.slice(1), allowExtra); | ||
} | ||
/** | ||
* Should the given text be ignore? | ||
* | ||
* Test using the given accessor pattern(s). | ||
*/ | ||
function shouldIgnoreViaAccessorPattern(text, ignorePattern) { | ||
var patterns = Array.isArray(ignorePattern) | ||
? ignorePattern | ||
: [ignorePattern]; | ||
// One or more patterns match? | ||
return patterns.some(function (pattern) { | ||
return accessorPatternMatch(pattern.split("."), text.split(".")); | ||
}); | ||
} | ||
/** | ||
* Should the given node be ignored? | ||
*/ | ||
function shouldIgnore(node, context, options) { | ||
return ( | ||
// Ignore if in a function and ignoreLocal is set. | ||
(Boolean(options.ignoreLocal) && inFunction(node)) || | ||
// Ignore if in a class and ignoreClass is set. | ||
(Boolean(options.ignoreClass) && inClass(node)) || | ||
// Ignore if in an interface and ignoreInterface is set. | ||
(Boolean(options.ignoreInterface) && inInterface(node)) || | ||
(function (texts) { | ||
return texts.length > 0 | ||
? // Ignore if ignorePattern is set and a pattern matches. | ||
(options.ignorePattern !== undefined && | ||
texts.every(function (text) { | ||
return shouldIgnoreViaPattern(text, options.ignorePattern); | ||
})) || | ||
// Ignore if ignoreAccessorPattern is set and an accessor pattern matches. | ||
(options.ignoreAccessorPattern !== undefined && | ||
texts.every(function (text) { | ||
return shouldIgnoreViaAccessorPattern(text, options.ignoreAccessorPattern); | ||
})) | ||
: false; | ||
})(getNodeTexts(node, context))); | ||
} | ||
const version = "0.2.0"; | ||
const version = "0.2.1"; | ||
@@ -427,7 +532,19 @@ /** | ||
*/ | ||
function createRule(data) { | ||
function createRule(name, meta, defaultOptions, create) { | ||
return experimentalUtils.ESLintUtils.RuleCreator(function (name) { | ||
return "https://github.com/jonaskello/eslint-plugin-ts-immutable/blob/v" + version + "/docs/rules/" + name + ".md"; | ||
})(data); | ||
})({ | ||
name: name, | ||
meta: meta, | ||
defaultOptions: [defaultOptions], | ||
create: function (c, _a) { | ||
var o = _a[0]; | ||
return create(c, o); | ||
} | ||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ | ||
}); | ||
} | ||
// This function can't be functional as it needs to interact with 3rd-party | ||
// libraries that aren't functional. | ||
/* eslint-disable ts-immutable/no-return-void, ts-immutable/no-conditional-statement, ts-immutable/no-expression-statement */ | ||
/** | ||
@@ -437,10 +554,6 @@ * Create a function that processes common options and then runs the given | ||
*/ | ||
function checkNode(check, context, ignoreOptions, otherOptions) { | ||
if (otherOptions === void 0) { otherOptions = []; } | ||
function checkNode(check, context, options) { | ||
return function (node) { | ||
// This function can't be functional as it needs to interact with 3rd-party | ||
// libraries that aren't functional. | ||
/* eslint-disable ts-immutable/no-if-statement, ts-immutable/no-expression-statement */ | ||
if (!ignoreOptions || !shouldIgnore(node, context, ignoreOptions)) { | ||
var result_1 = check(node, context, [ignoreOptions].concat(otherOptions).filter(function (option) { return option !== undefined; })); | ||
if (!options || !shouldIgnore(node, context, options)) { | ||
var result_1 = check(node, context, options); | ||
result_1.descriptors.forEach(function (descriptor) { | ||
@@ -450,5 +563,5 @@ return result_1.context.report(descriptor); | ||
} | ||
/* eslint-enable ts-immutable/no-if-statement, ts-immutable/no-expression-statement */ | ||
}; | ||
} | ||
/* eslint-enable ts-immutable/no-return-void, ts-immutable/no-conditional-statement, ts-immutable/no-expression-statement */ | ||
/** | ||
@@ -458,68 +571,212 @@ * Get the type of the the given node. | ||
function getTypeOfNode(node, context) { | ||
var parserServices = getParserServices(context); | ||
return parserServices.program | ||
.getTypeChecker() | ||
.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(node)); | ||
var parserServices = context.parserServices; | ||
return parserServices === undefined || | ||
parserServices.program === undefined || | ||
parserServices.esTreeNodeToTSNodeMap === undefined | ||
? null | ||
: parserServices.program | ||
.getTypeChecker() | ||
.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(node)); | ||
} | ||
// The name of this rule. | ||
var name = "functional-parameters"; | ||
// The schema for the rule options. | ||
var schema = [ | ||
deepmerge.all([ | ||
ignorePatternOptionSchema, | ||
{ | ||
type: "object", | ||
properties: { | ||
allowRestParameter: { | ||
type: "boolean" | ||
}, | ||
allowArgumentsKeyword: { | ||
type: "boolean" | ||
}, | ||
enforceParameterCount: { | ||
oneOf: [ | ||
{ | ||
type: "boolean", | ||
enum: [false] | ||
}, | ||
{ | ||
type: "string", | ||
enum: ["atLeastOne", "exactlyOne"] | ||
} | ||
] | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
]) | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions = { | ||
allowRestParameter: false, | ||
allowArgumentsKeyword: false, | ||
enforceParameterCount: "atLeastOne" | ||
}; | ||
// The possible error messages. | ||
var errorMessages = { | ||
restParam: "Unexpected rest parameter. Use a regular parameter of type array instead.", | ||
arguments: "Unexpected use of `arguments`. Use regular function arguments instead.", | ||
paramCountAtLeastOne: "Functions must have at least one parameter.", | ||
paramCountExactlyOne: "Functions must have exactly one parameter." | ||
}; | ||
// The meta data for this rule. | ||
var meta = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Enforce functional parameters.", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
messages: errorMessages, | ||
schema: schema | ||
}; | ||
/** | ||
* Ensure the type info is avaliable. | ||
* Get the rest parameter violations. | ||
*/ | ||
function parserServicesAvaliable(context) { | ||
return (context.parserServices !== undefined && | ||
context.parserServices.program !== undefined && | ||
context.parserServices.esTreeNodeToTSNodeMap !== undefined); | ||
function getRestParamViolations(allowRestParameter, node) { | ||
return !allowRestParameter && | ||
node.params.length > 0 && | ||
isRestElement(node.params[node.params.length - 1]) | ||
? [ | ||
{ | ||
node: node.params[node.params.length - 1], | ||
messageId: "restParam" | ||
} | ||
] | ||
: []; | ||
} | ||
/** | ||
* Ensure the type info is avaliable. | ||
* Get the parameter count violations. | ||
*/ | ||
function getParserServices(context) { | ||
/* eslint-disable-next-line ts-immutable/no-if-statement */ | ||
if (parserServicesAvaliable(context)) { | ||
return context.parserServices; | ||
} | ||
/** | ||
* The user needs to have configured "project" in their parserOptions | ||
* for @typescript-eslint/parser | ||
*/ | ||
/* eslint-disable-next-line ts-immutable/no-throw */ | ||
throw new Error('You have used a rule which is only avaliable for TypeScript files and requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.'); | ||
function getParamCountViolations(enforceParameterCount, node) { | ||
return enforceParameterCount === "atLeastOne" && node.params.length < 1 | ||
? [ | ||
{ | ||
node: node, | ||
messageId: "paramCountAtLeastOne" | ||
} | ||
] | ||
: enforceParameterCount === "exactlyOne" && node.params.length !== 1 | ||
? [ | ||
{ | ||
node: node, | ||
messageId: "paramCountExactlyOne" | ||
} | ||
] | ||
: []; | ||
} | ||
/** | ||
* Check if the given function node has a reset parameter this rule. | ||
*/ | ||
function checkFunction(node, context, options) { | ||
return { | ||
context: context, | ||
descriptors: getRestParamViolations(options.allowRestParameter, node).concat(getParamCountViolations(options.enforceParameterCount, node)) | ||
}; | ||
} | ||
/** | ||
* Check if the given identifier is for the "arguments" keyword. | ||
*/ | ||
function checkIdentifier(node, context, options) { | ||
return { | ||
context: context, | ||
descriptors: !options.allowArgumentsKeyword && | ||
node.name === "arguments" && | ||
!isPropertyName(node) && | ||
!isPropertyAccess(node) | ||
? [ | ||
{ | ||
node: node, | ||
messageId: "arguments" | ||
} | ||
] | ||
: [] | ||
}; | ||
} | ||
// Create the rule. | ||
var rule = createRule(name, meta, defaultOptions, function (context, options) { | ||
var _checkFunction = checkNode(checkFunction, context, options); | ||
var _checkIdentifier = checkNode(checkIdentifier, context, options); | ||
return { | ||
FunctionDeclaration: _checkFunction, | ||
FunctionExpression: _checkFunction, | ||
ArrowFunctionExpression: _checkFunction, | ||
Identifier: _checkIdentifier | ||
}; | ||
}); | ||
/** | ||
* Returns a function that checks if the given value is the same as the expected | ||
* value. | ||
*/ | ||
function isExpected(expected) { | ||
return function (actual) { return actual === expected; }; | ||
} | ||
// The name of this rule. | ||
var name = "no-array-mutation"; | ||
var name$1 = "immutable-data"; | ||
// The schema for the rule options. | ||
var schema = [ | ||
var schema$1 = [ | ||
deepmerge.all([ | ||
ignorePatternOptionSchema, | ||
ignoreAccessorPatternOptionSchema, | ||
ignoreNewArrayOptionSchema | ||
{ | ||
type: "object", | ||
properties: { | ||
assumeTypes: { | ||
oneOf: [ | ||
{ | ||
type: "boolean" | ||
}, | ||
{ | ||
type: "object", | ||
properties: { | ||
forArrays: { | ||
type: "boolean" | ||
}, | ||
forObjects: { | ||
type: "boolean" | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
] | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
]) | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions = [ | ||
{ | ||
ignoreNewArray: true | ||
} | ||
]; | ||
var defaultOptions$1 = { | ||
assumeTypes: true | ||
}; | ||
// The possible error messages. | ||
var errorMessages = { | ||
generic: "Mutating an array is not allowed." | ||
var errorMessages$1 = { | ||
generic: "Modifying an existing object/array is not allowed.", | ||
object: "Modifying properties of existing object not allowed.", | ||
array: "Modifying an array is not allowed." | ||
}; | ||
// The meta data for this rule. | ||
var meta = { | ||
var meta$1 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Disallow mutating arrays.", | ||
description: "Enforce treating data as immutable.", | ||
category: "Best Practices", | ||
recommended: "error" | ||
}, | ||
messages: errorMessages, | ||
schema: schema | ||
messages: errorMessages$1, | ||
schema: schema$1 | ||
}; | ||
/** | ||
* Methods that mutate an array. | ||
* Array methods that mutate an array. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Methods#Mutator_methods | ||
*/ | ||
var mutatorMethods = [ | ||
var arrayMutatorMethods = [ | ||
"copyWithin", | ||
@@ -536,3 +793,3 @@ "fill", | ||
/** | ||
* Methods that return a new array without mutating the original. | ||
* Array methods that return a new object (or array) without mutating the original. | ||
* | ||
@@ -542,3 +799,3 @@ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Methods#Accessor_methods | ||
*/ | ||
var newArrayReturningMethods = [ | ||
var arrayNewObjectReturningMethods = [ | ||
"concat", | ||
@@ -552,10 +809,21 @@ "slice", | ||
/** | ||
* Functions that create a new array. | ||
* Array constructor functions that create a new array. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Methods | ||
*/ | ||
var constructorFunctions = ["from", "of"]; | ||
var arrayConstructorFunctions = ["from", "of"]; | ||
/** | ||
* Check if the given node violates this rule. | ||
* Object constructor functions that mutate an object. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Methods_of_the_Object_constructor | ||
*/ | ||
var objectConstructorMutatorFunctions = [ | ||
"assign", | ||
"defineProperties", | ||
"defineProperty", | ||
"setPrototypeOf" | ||
]; | ||
/** | ||
* Check if the given assignment expression violates this rule. | ||
*/ | ||
function checkAssignmentExpression(node, context) { | ||
@@ -565,3 +833,4 @@ return { | ||
descriptors: isMemberExpression(node.left) && | ||
isArrayType(getTypeOfNode(node.left.object, context)) | ||
// Ignore if in a constructor - allow for field initialization. | ||
!inConstructor(node) | ||
? [{ node: node, messageId: "generic" }] | ||
@@ -577,5 +846,3 @@ : [] | ||
context: context, | ||
descriptors: node.operator === "delete" && | ||
isMemberExpression(node.argument) && | ||
isArrayType(getTypeOfNode(node.argument.object, context)) | ||
descriptors: node.operator === "delete" && isMemberExpression(node.argument) | ||
? [{ node: node, messageId: "generic" }] | ||
@@ -591,4 +858,3 @@ : [] | ||
context: context, | ||
descriptors: isMemberExpression(node.argument) && | ||
isArrayType(getTypeOfNode(node.argument.object, context)) | ||
descriptors: isMemberExpression(node.argument) | ||
? [{ node: node, messageId: "generic" }] | ||
@@ -599,29 +865,2 @@ : [] | ||
/** | ||
* Check if the given node violates this rule. | ||
*/ | ||
function checkCallExpression(node, context, _a) { | ||
var options = _a[0]; | ||
return { | ||
context: context, | ||
descriptors: isMemberExpression(node.callee) && | ||
isIdentifier(node.callee.property) && | ||
mutatorMethods.some(function (m) { | ||
return m === | ||
node.callee | ||
.property.name; | ||
}) && | ||
(!options.ignoreNewArray || | ||
!isInChainCallAndFollowsNew(node.callee, context)) && | ||
isArrayType(getTypeOfNode(node.callee.object, context)) | ||
? [{ node: node, messageId: "generic" }] | ||
: [] | ||
}; | ||
} | ||
/** | ||
* Returns a function that checks if the given value is the same as the expected value. | ||
*/ | ||
function isExpected(expected) { | ||
return function (actual) { return actual === expected; }; | ||
} | ||
/** | ||
* Check if the given the given MemberExpression is part of a chain and | ||
@@ -633,3 +872,3 @@ * immediately follows a method/function call that returns a new array. | ||
*/ | ||
function isInChainCallAndFollowsNew(node, context) { | ||
function isInChainCallAndFollowsNew(node, context, assumeArrayTypes) { | ||
return ( | ||
@@ -640,42 +879,77 @@ // Check for: [0, 1, 2] | ||
((isNewExpression(node.object) && | ||
isArrayConstructorType(getTypeOfNode(node.object.callee, context))) || | ||
isArrayConstructorType(getTypeOfNode(node.object.callee, context), assumeArrayTypes, node.object.callee)) || | ||
(isCallExpression(node.object) && | ||
isMemberExpression(node.object.callee) && | ||
isIdentifier(node.object.callee.property) && | ||
// Check for: Object.from(iterable) | ||
((constructorFunctions.some(isExpected(node.object.callee.property.name)) && | ||
isArrayConstructorType(getTypeOfNode(node.object.callee.object, context))) || | ||
// Check for: Array.from(iterable) | ||
((arrayConstructorFunctions.some(isExpected(node.object.callee.property.name)) && | ||
isArrayConstructorType(getTypeOfNode(node.object.callee.object, context), assumeArrayTypes, node.object.callee.object)) || | ||
// Check for: array.slice(0) | ||
newArrayReturningMethods.some(isExpected(node.object.callee.property.name)))))); | ||
arrayNewObjectReturningMethods.some(isExpected(node.object.callee.property.name)))))); | ||
} | ||
/** | ||
* Check if the given node violates this rule. | ||
*/ | ||
function checkCallExpression(node, context, options) { | ||
var assumeTypesForArrays = options.assumeTypes === true || | ||
(options.assumeTypes !== false && Boolean(options.assumeTypes.forArrays)); | ||
var assumeTypesForObjects = options.assumeTypes === true || | ||
(options.assumeTypes !== false && Boolean(options.assumeTypes.forObjects)); | ||
return { | ||
context: context, | ||
descriptors: | ||
// Potential object mutation? | ||
isMemberExpression(node.callee) && isIdentifier(node.callee.property) | ||
? // Potential array mutation? | ||
arrayMutatorMethods.some(function (m) { | ||
return m === | ||
node.callee | ||
.property.name; | ||
}) && | ||
!isInChainCallAndFollowsNew(node.callee, context, assumeTypesForArrays) && | ||
isArrayType(getTypeOfNode(node.callee.object, context), assumeTypesForArrays, node.callee.object) | ||
? [{ node: node, messageId: "array" }] | ||
: // Potential non-array object mutation (ex. Object.assign on identifier)? | ||
objectConstructorMutatorFunctions.some(function (m) { | ||
return m === | ||
node.callee | ||
.property.name; | ||
}) && | ||
node.arguments.length >= 2 && | ||
(isIdentifier(node.arguments[0]) || | ||
isMemberExpression(node.arguments[0])) && | ||
isObjectConstructorType(getTypeOfNode(node.callee.object, context), assumeTypesForObjects, node.callee.object) | ||
? [{ node: node, messageId: "object" }] | ||
: [] | ||
: [] | ||
}; | ||
} | ||
// Create the rule. | ||
var rule = createRule({ | ||
name: name, | ||
meta: meta, | ||
defaultOptions: defaultOptions, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkAssignmentExpression = checkNode(checkAssignmentExpression, context, ignoreOptions, otherOptions); | ||
var _checkUnaryExpression = checkNode(checkUnaryExpression, context, ignoreOptions, otherOptions); | ||
var _checkUpdateExpression = checkNode(checkUpdateExpression, context, ignoreOptions, otherOptions); | ||
var _checkCallExpression = checkNode(checkCallExpression, context, ignoreOptions, otherOptions); | ||
return { | ||
AssignmentExpression: _checkAssignmentExpression, | ||
UnaryExpression: _checkUnaryExpression, | ||
UpdateExpression: _checkUpdateExpression, | ||
CallExpression: _checkCallExpression | ||
}; | ||
} | ||
var rule$1 = createRule(name$1, meta$1, defaultOptions$1, function (context, options) { | ||
var _checkAssignmentExpression = checkNode(checkAssignmentExpression, context, options); | ||
var _checkUnaryExpression = checkNode(checkUnaryExpression, context, options); | ||
var _checkUpdateExpression = checkNode(checkUpdateExpression, context, options); | ||
// This functionality is only avaliable if the parser services are | ||
// avaliable. | ||
var _checkCallExpression = checkNode(checkCallExpression, context, options); | ||
return { | ||
AssignmentExpression: _checkAssignmentExpression, | ||
UnaryExpression: _checkUnaryExpression, | ||
UpdateExpression: _checkUpdateExpression, | ||
CallExpression: _checkCallExpression | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$1 = "no-class"; | ||
var name$2 = "no-class"; | ||
// The schema for the rule options. | ||
var schema$2 = []; | ||
// The default options for the rule. | ||
var defaultOptions$1 = []; | ||
var defaultOptions$2 = {}; | ||
// The possible error messages. | ||
var errorMessages$1 = { | ||
var errorMessages$2 = { | ||
generic: "Unexpected class, use functions not classes." | ||
}; | ||
// The meta data for this rule. | ||
var meta$1 = { | ||
var meta$2 = { | ||
type: "suggestion", | ||
@@ -687,4 +961,4 @@ docs: { | ||
}, | ||
messages: errorMessages$1, | ||
schema: [] | ||
messages: errorMessages$2, | ||
schema: schema$2 | ||
}; | ||
@@ -699,155 +973,260 @@ /** | ||
// Create the rule. | ||
var rule$1 = createRule({ | ||
name: name$1, | ||
meta: meta$1, | ||
defaultOptions: defaultOptions$1, | ||
create: function (context) { | ||
var _checkClass = checkNode(checkClass, context); | ||
return { | ||
ClassDeclaration: _checkClass, | ||
ClassExpression: _checkClass | ||
}; | ||
} | ||
var rule$2 = createRule(name$2, meta$2, defaultOptions$2, function (context, options) { | ||
var _checkClass = checkNode(checkClass, context, options); | ||
return { | ||
ClassDeclaration: _checkClass, | ||
ClassExpression: _checkClass | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$2 = "no-delete"; | ||
var name$3 = "no-return-void"; | ||
// The schema for the rule options. | ||
var schema$3 = [ | ||
{ | ||
type: "object", | ||
properties: { | ||
allowNull: { | ||
type: "boolean" | ||
}, | ||
allowUndefined: { | ||
type: "boolean" | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions$2 = []; | ||
var defaultOptions$3 = { | ||
allowNull: true, | ||
allowUndefined: true | ||
}; | ||
// The possible error messages. | ||
var errorMessages$2 = { | ||
generic: "Unexpected delete, objects should be considered immutable." | ||
var errorMessages$3 = { | ||
generic: "Function must return a value." | ||
}; | ||
// The meta data for this rule. | ||
var meta$2 = { | ||
var meta$3 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Disallow delete expressions.", | ||
description: "Disallow functions that don't return anything.", | ||
category: "Best Practices", | ||
recommended: "error" | ||
recommended: false | ||
}, | ||
messages: errorMessages$2, | ||
schema: [] | ||
messages: errorMessages$3, | ||
schema: schema$3 | ||
}; | ||
/** | ||
* Check if the given UnaryExpression violates this rule. | ||
* Check if the given function node violates this rule. | ||
*/ | ||
function checkUnaryExpression$1(node, context) { | ||
function checkFunction$1(node, context, options) { | ||
return { | ||
context: context, | ||
descriptors: node.operator === "delete" ? [{ node: node, messageId: "generic" }] : [] | ||
descriptors: node.returnType !== undefined && | ||
(isTSVoidKeyword(node.returnType.typeAnnotation) || | ||
(!options.allowNull && | ||
isTSNullKeyword(node.returnType.typeAnnotation)) || | ||
(!options.allowUndefined && | ||
isTSUndefinedKeyword(node.returnType.typeAnnotation))) | ||
? [{ node: node.returnType, messageId: "generic" }] | ||
: [] | ||
}; | ||
} | ||
// Create the rule. | ||
var rule$2 = createRule({ | ||
name: name$2, | ||
meta: meta$2, | ||
defaultOptions: defaultOptions$2, | ||
create: function (context) { | ||
var _checkUnaryExpression = checkNode(checkUnaryExpression$1, context); | ||
return { | ||
UnaryExpression: _checkUnaryExpression | ||
}; | ||
} | ||
var rule$3 = createRule(name$3, meta$3, defaultOptions$3, function (context, options) { | ||
var _checkFunction = checkNode(checkFunction$1, context, options); | ||
return { | ||
FunctionDeclaration: _checkFunction, | ||
FunctionExpression: _checkFunction, | ||
ArrowFunctionExpression: _checkFunction, | ||
TSFunctionType: _checkFunction | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$3 = "no-expression-statement"; | ||
var name$4 = "no-conditional-statement"; | ||
// The schema for the rule options. | ||
var schema$1 = [ignorePatternOptionSchema]; | ||
var schema$4 = [ | ||
{ | ||
type: "object", | ||
properties: { | ||
allowReturningBranches: { | ||
oneOf: [ | ||
{ | ||
type: "boolean" | ||
}, | ||
{ | ||
type: "string", | ||
enum: ["ifExhaustive"] | ||
} | ||
] | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions$3 = [{}]; | ||
var defaultOptions$4 = { allowReturningBranches: false }; | ||
// The possible error messages. | ||
var errorMessages$3 = { | ||
generic: "Using expressions to cause side-effects not allowed." | ||
var errorMessages$4 = { | ||
incompleteBranch: "Incomplete branch, every branch in a conditional statement must contain a return statement.", | ||
incompleteIf: "Incomplete if, it must have an else statement and every branch must contain a return statement.", | ||
incompleteSwitch: "Incomplete switch, it must have an default case and every case must contain a return statement.", | ||
unexpectedIf: "Unexpected if, use a conditional expression (ternary operator) instead.", | ||
unexpectedSwitch: "Unexpected switch, use a conditional expression (ternary operator) instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$3 = { | ||
var meta$4 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Disallow expression statements.", | ||
description: "Disallow conditional statements.", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
messages: errorMessages$3, | ||
schema: schema$1 | ||
messages: errorMessages$4, | ||
schema: schema$4 | ||
}; | ||
/** | ||
* Check if the given ExpressionStatement violates this rule. | ||
* Get all of the violations in the given if statement assuming if statements | ||
* are allowed. | ||
*/ | ||
function checkExpressionStatement(node, context) { | ||
// All expression statements violate this rule. | ||
return { context: context, descriptors: [{ node: node, messageId: "generic" }] }; | ||
function getIfBranchViolations(node) { | ||
var nodes = [node.consequent, node.alternate].reduce(function (carry, branch) { | ||
return branch === null || | ||
isReturnStatement(branch) || | ||
(isBlockStatement(branch) && | ||
branch.body.some(function (statement) { | ||
return isReturnStatement(statement) || | ||
// Another instance of this rule will check nested if statements. | ||
isIfStatement(statement); | ||
})) || | ||
isIfStatement(branch) | ||
? carry | ||
: carry.concat([branch]); | ||
}, []); | ||
return nodes.flatMap(function (node) { return [{ node: node, messageId: "incompleteBranch" }]; }); | ||
} | ||
/** | ||
* Get all of the violations in the given switch statement assuming switch | ||
* statements are allowed. | ||
*/ | ||
function getSwitchViolations(node) { | ||
var nodes = node.cases.reduce(function (carry, branch) { | ||
return branch.consequent.length === 0 || | ||
branch.consequent.some(isReturnStatement) || | ||
(branch.consequent.every(isBlockStatement) && | ||
branch.consequent[branch.consequent.length - 1].body.some(isReturnStatement)) | ||
? carry | ||
: carry.concat([branch]); | ||
}, []); | ||
return nodes.flatMap(function (node) { return [{ node: node, messageId: "incompleteBranch" }]; }); | ||
} | ||
/** | ||
* Does the given if statement violate this rule if it must be exhaustive. | ||
*/ | ||
function isExhaustiveIfViolation(node) { | ||
return node.alternate === null; | ||
} | ||
/** | ||
* Does the given switch statement violate this rule if it must be exhaustive. | ||
*/ | ||
function isExhaustiveSwitchViolation(node) { | ||
return ( | ||
// No cases defined. | ||
node.cases.length === 0 || | ||
// No default case defined. | ||
node.cases.every(function (c) { return c.test !== null; })); | ||
} | ||
/** | ||
* Check if the given IfStatement violates this rule. | ||
*/ | ||
function checkIfStatement(node, context, options) { | ||
return { | ||
context: context, | ||
descriptors: options.allowReturningBranches | ||
? options.allowReturningBranches === "ifExhaustive" | ||
? isExhaustiveIfViolation(node) | ||
? [{ node: node, messageId: "incompleteIf" }] | ||
: getIfBranchViolations(node) | ||
: getIfBranchViolations(node) | ||
: [{ node: node, messageId: "unexpectedIf" }] | ||
}; | ||
} | ||
/** | ||
* Check if the given SwitchStatement violates this rule. | ||
*/ | ||
function checkSwitchStatement(node, context, options) { | ||
return { | ||
context: context, | ||
descriptors: options.allowReturningBranches | ||
? options.allowReturningBranches === "ifExhaustive" | ||
? isExhaustiveSwitchViolation(node) | ||
? [{ node: node, messageId: "incompleteSwitch" }] | ||
: getSwitchViolations(node) | ||
: getSwitchViolations(node) | ||
: [{ node: node, messageId: "unexpectedSwitch" }] | ||
}; | ||
} | ||
// Create the rule. | ||
var rule$3 = createRule({ | ||
name: name$3, | ||
meta: meta$3, | ||
defaultOptions: defaultOptions$3, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkExpressionStatement = checkNode(checkExpressionStatement, context, ignoreOptions, otherOptions); | ||
return { | ||
ExpressionStatement: _checkExpressionStatement | ||
}; | ||
} | ||
var rule$4 = createRule(name$4, meta$4, defaultOptions$4, function (context, options) { | ||
var _checkIfStatement = checkNode(checkIfStatement, context, options); | ||
var _checkSwitchStatement = checkNode(checkSwitchStatement, context, options); | ||
return { | ||
IfStatement: _checkIfStatement, | ||
SwitchStatement: _checkSwitchStatement | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$4 = "no-if-statement"; | ||
var name$5 = "no-expression-statement"; | ||
// The schema for the rule options. | ||
var schema$5 = [ignorePatternOptionSchema]; | ||
// The default options for the rule. | ||
var defaultOptions$4 = []; | ||
var defaultOptions$5 = {}; | ||
// The possible error messages. | ||
var errorMessages$4 = { | ||
generic: "Unexpected if, use a conditional expression (ternary operator) instead." | ||
var errorMessages$5 = { | ||
generic: "Using expressions to cause side-effects not allowed." | ||
}; | ||
// The meta data for this rule. | ||
var meta$4 = { | ||
var meta$5 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Disallow if statements.", | ||
description: "Disallow expression statements.", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
messages: errorMessages$4, | ||
schema: [] | ||
messages: errorMessages$5, | ||
schema: schema$5 | ||
}; | ||
/** | ||
* Check if the given IfStatement violates this rule. | ||
* Check if the given ExpressionStatement violates this rule. | ||
*/ | ||
function checkIfStatement(node, context) { | ||
// All if statements violate this rule. | ||
function checkExpressionStatement(node, context) { | ||
// All expression statements violate this rule. | ||
return { context: context, descriptors: [{ node: node, messageId: "generic" }] }; | ||
} | ||
// Create the rule. | ||
var rule$4 = createRule({ | ||
name: name$4, | ||
meta: meta$4, | ||
defaultOptions: defaultOptions$4, | ||
create: function (context) { | ||
var _checkIfStatement = checkNode(checkIfStatement, context); | ||
return { | ||
IfStatement: _checkIfStatement | ||
}; | ||
} | ||
var rule$5 = createRule(name$5, meta$5, defaultOptions$5, function (context, options) { | ||
var _checkExpressionStatement = checkNode(checkExpressionStatement, context, options); | ||
return { | ||
ExpressionStatement: _checkExpressionStatement | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$5 = "no-let"; | ||
var name$6 = "no-let"; | ||
// The schema for the rule options. | ||
var schema$2 = [ | ||
var schema$6 = [ | ||
deepmerge.all([ignoreLocalOptionSchema, ignorePatternOptionSchema]) | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions$5 = [ | ||
{ | ||
ignoreLocal: false | ||
} | ||
]; | ||
var defaultOptions$6 = { | ||
ignoreLocal: false | ||
}; | ||
// The possible error messages. | ||
var errorMessages$5 = { | ||
var errorMessages$6 = { | ||
generic: "Unexpected let, use const instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$5 = { | ||
var meta$6 = { | ||
type: "suggestion", | ||
@@ -859,5 +1238,5 @@ docs: { | ||
}, | ||
messages: errorMessages$5, | ||
messages: errorMessages$6, | ||
fixable: "code", | ||
schema: schema$2 | ||
schema: schema$6 | ||
}; | ||
@@ -874,25 +1253,21 @@ /** | ||
// Create the rule. | ||
var rule$5 = createRule({ | ||
name: name$5, | ||
meta: meta$5, | ||
defaultOptions: defaultOptions$5, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkVariableDeclaration = checkNode(checkVariableDeclaration, context, ignoreOptions, otherOptions); | ||
return { | ||
VariableDeclaration: _checkVariableDeclaration | ||
}; | ||
} | ||
var rule$6 = createRule(name$6, meta$6, defaultOptions$6, function (context, options) { | ||
var _checkVariableDeclaration = checkNode(checkVariableDeclaration, context, options); | ||
return { | ||
VariableDeclaration: _checkVariableDeclaration | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$6 = "no-loop-statement"; | ||
var name$7 = "no-loop-statement"; | ||
// The schema for the rule options. | ||
var schema$7 = []; | ||
// The default options for the rule. | ||
var defaultOptions$6 = []; | ||
var defaultOptions$7 = {}; | ||
// The possible error messages. | ||
var errorMessages$6 = { | ||
var errorMessages$7 = { | ||
generic: "Unexpected loop, use map or reduce instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$6 = { | ||
var meta$7 = { | ||
type: "suggestion", | ||
@@ -904,4 +1279,4 @@ docs: { | ||
}, | ||
messages: errorMessages$6, | ||
schema: [] | ||
messages: errorMessages$7, | ||
schema: schema$7 | ||
}; | ||
@@ -916,134 +1291,124 @@ /** | ||
// Create the rule. | ||
var rule$6 = createRule({ | ||
name: name$6, | ||
meta: meta$6, | ||
defaultOptions: defaultOptions$6, | ||
create: function (context) { | ||
var _checkLoop = checkNode(checkLoop, context); | ||
return { | ||
ForStatement: _checkLoop, | ||
ForInStatement: _checkLoop, | ||
ForOfStatement: _checkLoop, | ||
WhileStatement: _checkLoop, | ||
DoWhileStatement: _checkLoop | ||
}; | ||
} | ||
var rule$7 = createRule(name$7, meta$7, defaultOptions$7, function (context, options) { | ||
var _checkLoop = checkNode(checkLoop, context, options); | ||
return { | ||
ForStatement: _checkLoop, | ||
ForInStatement: _checkLoop, | ||
ForOfStatement: _checkLoop, | ||
WhileStatement: _checkLoop, | ||
DoWhileStatement: _checkLoop | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$7 = "no-object-mutation"; | ||
var name$8 = "no-method-signature"; | ||
// The schema for the rule options. | ||
var schema$3 = [ | ||
deepmerge.all([ | ||
ignorePatternOptionSchema, | ||
ignoreAccessorPatternOptionSchema | ||
]) | ||
]; | ||
var schema$8 = []; | ||
// The default options for the rule. | ||
var defaultOptions$7 = [{}]; | ||
var defaultOptions$8 = {}; | ||
// The possible error messages. | ||
var errorMessages$7 = { | ||
generic: "Modifying properties of existing object not allowed." | ||
var errorMessages$8 = { | ||
generic: "Method signature is mutable, use property signature with readonly modifier instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$7 = { | ||
var meta$8 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Disallow mutating objects.", | ||
description: "Prefer property signatures with readonly modifiers over method signatures.", | ||
category: "Best Practices", | ||
recommended: "error" | ||
}, | ||
messages: errorMessages$7, | ||
schema: schema$3 | ||
messages: errorMessages$8, | ||
schema: schema$8 | ||
}; | ||
/** | ||
* Check if the given assignment expression violates this rule. | ||
* Check if the given TSMethodSignature violates this rule. | ||
*/ | ||
function checkAssignmentExpression$1(node, context) { | ||
return { | ||
context: context, | ||
descriptors: isMemberExpression(node.left) && | ||
// Ignore if in a constructor - allow for field initialization. | ||
!inConstructor(node) | ||
? [{ node: node, messageId: "generic" }] | ||
: [] | ||
}; | ||
function checkTSMethodSignature(node, context) { | ||
// All TS method signatures violate this rule. | ||
return { context: context, descriptors: [{ node: node, messageId: "generic" }] }; | ||
} | ||
/** | ||
* Check if the given node violates this rule. | ||
*/ | ||
function checkUnaryExpression$2(node, context) { | ||
// Create the rule. | ||
var rule$8 = createRule(name$8, meta$8, defaultOptions$8, function (context, options) { | ||
var _checkTSMethodSignature = checkNode(checkTSMethodSignature, context, options); | ||
return { | ||
context: context, | ||
descriptors: node.operator === "delete" && isMemberExpression(node.argument) | ||
? [{ node: node, messageId: "generic" }] | ||
: [] | ||
TSMethodSignature: _checkTSMethodSignature | ||
}; | ||
} | ||
}); | ||
// The name of this rule. | ||
var name$9 = "no-mixed-interface"; | ||
// The schema for the rule options. | ||
var schema$9 = []; | ||
// The default options for the rule. | ||
var defaultOptions$9 = {}; | ||
// The possible error messages. | ||
var errorMessages$9 = { | ||
generic: "Only the same kind of members allowed in interfaces." | ||
}; | ||
// The meta data for this rule. | ||
var meta$9 = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Restrict interfaces so that only members of the same kind of are allowed in them.", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
messages: errorMessages$9, | ||
schema: schema$9 | ||
}; | ||
/** | ||
* Check if the given node violates this rule. | ||
* Check if the given TSInterfaceDeclaration violates this rule. | ||
*/ | ||
function checkUpdateExpression$1(node, context) { | ||
function checkTSInterfaceDeclaration(node, context) { | ||
return { | ||
context: context, | ||
descriptors: isMemberExpression(node.argument) | ||
? [{ node: node, messageId: "generic" }] | ||
: [] | ||
descriptors: node.body.body.reduce(function (carry, member) { | ||
var memberType = member.type; | ||
var memberTypeAnnotation = isTSPropertySignature(member) && member.typeAnnotation !== undefined | ||
? member.typeAnnotation.typeAnnotation.type | ||
: undefined; | ||
return { | ||
prevMemberType: memberType, | ||
prevMemberTypeAnnotation: memberTypeAnnotation, | ||
violations: | ||
// Not the first property in the interface. | ||
carry.prevMemberType !== undefined && | ||
// And different property type to previous property. | ||
(carry.prevMemberType !== memberType || | ||
// Or annotationed with a different type annotation. | ||
(carry.prevMemberTypeAnnotation !== memberTypeAnnotation && | ||
// Where one of the properties is a annotationed as a function. | ||
(carry.prevMemberTypeAnnotation === | ||
astNodeTypes.AST_NODE_TYPES.TSFunctionType || | ||
memberTypeAnnotation === astNodeTypes.AST_NODE_TYPES.TSFunctionType))) | ||
? carry.violations.concat([{ node: member, messageId: "generic" }]) : carry.violations | ||
}; | ||
}, { | ||
prevMemberType: undefined, | ||
prevMemberTypeAnnotation: undefined, | ||
violations: [] | ||
}).violations | ||
}; | ||
} | ||
/** | ||
* Check if the given node violates this rule. | ||
*/ | ||
function checkCallExpression$1(node, context) { | ||
// No Object.assign on identifiers. | ||
// Create the rule. | ||
var rule$9 = createRule(name$9, meta$9, defaultOptions$9, function (context, options) { | ||
var _checkTSInterfaceDeclaration = checkNode(checkTSInterfaceDeclaration, context, options); | ||
return { | ||
context: context, | ||
descriptors: isMemberExpression(node.callee) && | ||
isIdentifier(node.callee.property) && | ||
node.callee.property.name === "assign" && | ||
node.arguments.length >= 2 && | ||
(isIdentifier(node.arguments[0]) || | ||
isMemberExpression(node.arguments[0])) && | ||
// Type checking if avaliable? | ||
((parserServicesAvaliable(context) && | ||
isObjectConstructorType(getTypeOfNode(node.callee.object, context))) || | ||
// Type checking not avaliable? | ||
(isIdentifier(node.callee.object) && | ||
node.callee.object.name === "Object")) | ||
? [{ node: node, messageId: "generic" }] | ||
: [] | ||
TSInterfaceDeclaration: _checkTSInterfaceDeclaration | ||
}; | ||
} | ||
// Create the rule. | ||
var rule$7 = createRule({ | ||
name: name$7, | ||
meta: meta$7, | ||
defaultOptions: defaultOptions$7, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkAssignmentExpression = checkNode(checkAssignmentExpression$1, context, ignoreOptions, otherOptions); | ||
var _checkUnaryExpression = checkNode(checkUnaryExpression$2, context, ignoreOptions, otherOptions); | ||
var _checkUpdateExpression = checkNode(checkUpdateExpression$1, context, ignoreOptions, otherOptions); | ||
// This functionality is only avaliable if the parser services are | ||
// avaliable. | ||
var _checkCallExpression = checkNode(checkCallExpression$1, context, ignoreOptions, otherOptions); | ||
return { | ||
AssignmentExpression: _checkAssignmentExpression, | ||
UnaryExpression: _checkUnaryExpression, | ||
UpdateExpression: _checkUpdateExpression, | ||
CallExpression: _checkCallExpression | ||
}; | ||
} | ||
}); | ||
// The name of this rule. | ||
var name$8 = "no-reject"; | ||
var name$a = "no-reject"; | ||
// The schema for the rule options. | ||
var schema$a = []; | ||
// The default options for the rule. | ||
var defaultOptions$8 = []; | ||
var defaultOptions$a = {}; | ||
// The possible error messages. | ||
var errorMessages$8 = { | ||
var errorMessages$a = { | ||
generic: "Unexpected reject, return an error instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$8 = { | ||
var meta$a = { | ||
type: "suggestion", | ||
@@ -1055,4 +1420,4 @@ docs: { | ||
}, | ||
messages: errorMessages$8, | ||
schema: [] | ||
messages: errorMessages$a, | ||
schema: schema$a | ||
}; | ||
@@ -1062,3 +1427,3 @@ /** | ||
*/ | ||
function checkCallExpression$2(node, context) { | ||
function checkCallExpression$1(node, context) { | ||
return { | ||
@@ -1076,24 +1441,21 @@ context: context, | ||
// Create the rule. | ||
var rule$8 = createRule({ | ||
name: name$8, | ||
meta: meta$8, | ||
defaultOptions: defaultOptions$8, | ||
create: function (context) { | ||
var _checkCallExpression = checkNode(checkCallExpression$2, context); | ||
return { | ||
CallExpression: _checkCallExpression | ||
}; | ||
} | ||
var rule$a = createRule(name$a, meta$a, defaultOptions$a, function (context, options) { | ||
var _checkCallExpression = checkNode(checkCallExpression$1, context, options); | ||
return { | ||
CallExpression: _checkCallExpression | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$9 = "no-this"; | ||
var name$b = "no-this"; | ||
// The schema for the rule options. | ||
var schema$b = []; | ||
// The default options for the rule. | ||
var defaultOptions$9 = []; | ||
var defaultOptions$b = {}; | ||
// The possible error messages. | ||
var errorMessages$9 = { | ||
var errorMessages$b = { | ||
generic: "Unexpected this, use functions not classes." | ||
}; | ||
// The meta data for this rule. | ||
var meta$9 = { | ||
var meta$b = { | ||
type: "suggestion", | ||
@@ -1105,4 +1467,4 @@ docs: { | ||
}, | ||
messages: errorMessages$9, | ||
schema: [] | ||
messages: errorMessages$b, | ||
schema: schema$b | ||
}; | ||
@@ -1117,24 +1479,21 @@ /** | ||
// Create the rule. | ||
var rule$9 = createRule({ | ||
name: name$9, | ||
meta: meta$9, | ||
defaultOptions: defaultOptions$9, | ||
create: function (context) { | ||
var _checkThisExpression = checkNode(checkThisExpression, context); | ||
return { | ||
ThisExpression: _checkThisExpression | ||
}; | ||
} | ||
var rule$b = createRule(name$b, meta$b, defaultOptions$b, function (context, options) { | ||
var _checkThisExpression = checkNode(checkThisExpression, context, options); | ||
return { | ||
ThisExpression: _checkThisExpression | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$a = "no-throw"; | ||
var name$c = "no-throw"; | ||
// The schema for the rule options. | ||
var schema$c = []; | ||
// The default options for the rule. | ||
var defaultOptions$a = []; | ||
var defaultOptions$c = {}; | ||
// The possible error messages. | ||
var errorMessages$a = { | ||
var errorMessages$c = { | ||
generic: "Unexpected throw, throwing exceptions is not functional." | ||
}; | ||
// The meta data for this rule. | ||
var meta$a = { | ||
var meta$c = { | ||
type: "suggestion", | ||
@@ -1146,4 +1505,4 @@ docs: { | ||
}, | ||
messages: errorMessages$a, | ||
schema: [] | ||
messages: errorMessages$c, | ||
schema: schema$c | ||
}; | ||
@@ -1158,24 +1517,21 @@ /** | ||
// Create the rule. | ||
var rule$a = createRule({ | ||
name: name$a, | ||
meta: meta$a, | ||
defaultOptions: defaultOptions$a, | ||
create: function (context) { | ||
var _checkThrowStatement = checkNode(checkThrowStatement, context); | ||
return { | ||
ThrowStatement: _checkThrowStatement | ||
}; | ||
} | ||
var rule$c = createRule(name$c, meta$c, defaultOptions$c, function (context, options) { | ||
var _checkThrowStatement = checkNode(checkThrowStatement, context, options); | ||
return { | ||
ThrowStatement: _checkThrowStatement | ||
}; | ||
}); | ||
// The name of this rule. | ||
var name$b = "no-try"; | ||
var name$d = "no-try"; | ||
// The schema for the rule options. | ||
var schema$d = []; | ||
// The default options for the rule. | ||
var defaultOptions$b = []; | ||
var defaultOptions$d = {}; | ||
// The possible error messages. | ||
var errorMessages$b = { | ||
var errorMessages$d = { | ||
generic: "Unexpected try, the try-catch[-finally] and try-finally patterns are not functional." | ||
}; | ||
// The meta data for this rule. | ||
var meta$b = { | ||
var meta$d = { | ||
type: "suggestion", | ||
@@ -1187,4 +1543,4 @@ docs: { | ||
}, | ||
messages: errorMessages$b, | ||
schema: [] | ||
messages: errorMessages$d, | ||
schema: schema$d | ||
}; | ||
@@ -1199,141 +1555,45 @@ /** | ||
// Create the rule. | ||
var rule$b = createRule({ | ||
name: name$b, | ||
meta: meta$b, | ||
defaultOptions: defaultOptions$b, | ||
create: function (context) { | ||
var _checkTryStatement = checkNode(checkTryStatement, context); | ||
return { | ||
TryStatement: _checkTryStatement | ||
}; | ||
} | ||
}); | ||
// The name of this rule. | ||
var name$c = "no-method-signature"; | ||
// The default options for the rule. | ||
var defaultOptions$c = []; | ||
// The possible error messages. | ||
var errorMessages$c = { | ||
generic: "Method signature is mutable, use property signature with readonly modifier instead." | ||
}; | ||
// The meta data for this rule. | ||
var meta$c = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Prefer property signatures with readonly modifiers over method signatures.", | ||
category: "Best Practices", | ||
recommended: "error" | ||
}, | ||
messages: errorMessages$c, | ||
schema: [] | ||
}; | ||
/** | ||
* Check if the given TSMethodSignature violates this rule. | ||
*/ | ||
function checkTSMethodSignature(node, context) { | ||
// All TS method signatures violate this rule. | ||
return { context: context, descriptors: [{ node: node, messageId: "generic" }] }; | ||
} | ||
// Create the rule. | ||
var rule$c = createRule({ | ||
name: name$c, | ||
meta: meta$c, | ||
defaultOptions: defaultOptions$c, | ||
create: function (context) { | ||
var _checkTSMethodSignature = checkNode(checkTSMethodSignature, context); | ||
return { | ||
TSMethodSignature: _checkTSMethodSignature | ||
}; | ||
} | ||
}); | ||
// The name of this rule. | ||
var name$d = "no-mixed-interface"; | ||
// The default options for the rule. | ||
var defaultOptions$d = []; | ||
// The possible error messages. | ||
var errorMessages$d = { | ||
generic: "Only the same kind of members allowed in interfaces." | ||
}; | ||
// The meta data for this rule. | ||
var meta$d = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Restrict interfaces so that only members of the same kind of are allowed in them.", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
messages: errorMessages$d, | ||
schema: [] | ||
}; | ||
/** | ||
* Check if the given TSInterfaceDeclaration violates this rule. | ||
*/ | ||
function checkTSInterfaceDeclaration(node, context) { | ||
var rule$d = createRule(name$d, meta$d, defaultOptions$d, function (context, options) { | ||
var _checkTryStatement = checkNode(checkTryStatement, context, options); | ||
return { | ||
context: context, | ||
descriptors: node.body.body.reduce(function (carry, member) { | ||
var memberType = member.type; | ||
var memberTypeAnnotation = isTSPropertySignature(member) && member.typeAnnotation !== undefined | ||
? member.typeAnnotation.typeAnnotation.type | ||
: undefined; | ||
return { | ||
prevMemberType: memberType, | ||
prevMemberTypeAnnotation: memberTypeAnnotation, | ||
violations: | ||
// Not the first property in the interface. | ||
carry.prevMemberType !== undefined && | ||
// And different property type to previous property. | ||
(carry.prevMemberType !== memberType || | ||
// Or annotationed with a different type annotation. | ||
(carry.prevMemberTypeAnnotation !== memberTypeAnnotation && | ||
// Where one of the properties is a annotationed as a function. | ||
(carry.prevMemberTypeAnnotation === | ||
astNodeTypes.AST_NODE_TYPES.TSFunctionType || | ||
memberTypeAnnotation === astNodeTypes.AST_NODE_TYPES.TSFunctionType))) | ||
? carry.violations.concat([{ node: member, messageId: "generic" }]) : carry.violations | ||
}; | ||
}, { | ||
prevMemberType: undefined, | ||
prevMemberTypeAnnotation: undefined, | ||
violations: [] | ||
}).violations | ||
TryStatement: _checkTryStatement | ||
}; | ||
} | ||
// Create the rule. | ||
var rule$d = createRule({ | ||
name: name$d, | ||
meta: meta$d, | ||
defaultOptions: defaultOptions$d, | ||
create: function (context) { | ||
var _checkTSInterfaceDeclaration = checkNode(checkTSInterfaceDeclaration, context); | ||
return { | ||
TSInterfaceDeclaration: _checkTSInterfaceDeclaration | ||
}; | ||
} | ||
}); | ||
// Polyfill. | ||
// The name of this rule. | ||
var name$e = "readonly-array"; | ||
var name$e = "prefer-readonly-types"; | ||
// The schema for the rule options. | ||
var schema$4 = [ | ||
var schema$e = [ | ||
deepmerge.all([ | ||
ignoreLocalOptionSchema, | ||
ignorePatternOptionSchema, | ||
ignoreReturnTypeOptionSchema | ||
ignoreClassOptionSchema, | ||
ignoreInterfaceOptionSchema, | ||
ignoreReturnTypeOptionSchema, | ||
{ | ||
type: "object", | ||
properties: { | ||
checkImplicit: { | ||
type: "boolean" | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
]) | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions$e = [ | ||
{ | ||
ignoreLocal: false, | ||
ignoreReturnType: false | ||
} | ||
]; | ||
var defaultOptions$e = { | ||
checkImplicit: false, | ||
ignoreClass: false, | ||
ignoreInterface: false, | ||
ignoreLocal: false, | ||
ignoreReturnType: false | ||
}; | ||
// The possible error messages. | ||
var errorMessages$e = { | ||
generic: "Only readonly arrays allowed.", | ||
implicit: "Implicitly a mutable array. Only readonly arrays allowed." | ||
array: "Only readonly arrays allowed.", | ||
implicit: "Implicitly a mutable array. Only readonly arrays allowed.", | ||
property: "A readonly modifier is required.", | ||
tuple: "Only readonly tuples allowed.", | ||
type: "Only readonly types allowed." | ||
}; | ||
@@ -1350,9 +1610,9 @@ // The meta data for this rule. | ||
fixable: "code", | ||
schema: schema$4 | ||
schema: schema$e | ||
}; | ||
var mutableToImmutableTypes = new Map([["Array", "ReadonlyArray"], ["Map", "ReadonlyMap"], ["Set", "ReadonlySet"]]); | ||
/** | ||
* Check if the given ArrayType or TupleType violates this rule. | ||
*/ | ||
function checkArrayOrTupleType(node, context, _a) { | ||
var options = _a[0]; | ||
function checkArrayOrTupleType(node, context, options) { | ||
return { | ||
@@ -1367,3 +1627,3 @@ context: context, | ||
node: node, | ||
messageId: "generic", | ||
messageId: isTSTupleType(node) ? "tuple" : "array", | ||
fix: node.parent && isTSArrayType(node.parent) | ||
@@ -1383,17 +1643,44 @@ ? function (fixer) { return [ | ||
*/ | ||
function checkTypeReference(node, context, _a) { | ||
var options = _a[0]; | ||
function checkTypeReference(node, context, options) { | ||
if (isIdentifier(node.typeName)) { | ||
var immutableType_1 = mutableToImmutableTypes.get(node.typeName.name); | ||
return { | ||
context: context, | ||
descriptors: immutableType_1 && (!options.ignoreReturnType || !isInReturnType(node)) | ||
? [ | ||
{ | ||
node: node, | ||
messageId: "type", | ||
fix: function (fixer) { return fixer.replaceText(node.typeName, immutableType_1); } | ||
} | ||
] | ||
: [] | ||
}; | ||
} | ||
else { | ||
return { | ||
context: context, | ||
descriptors: [] | ||
}; | ||
} | ||
} | ||
/** | ||
* Check if the given property/signature node violates this rule. | ||
*/ | ||
function checkProperty(node, context) { | ||
return { | ||
context: context, | ||
descriptors: isIdentifier(node.typeName) && | ||
node.typeName.name === "Array" && | ||
(!options.ignoreReturnType || !isInReturnType(node)) | ||
? [ | ||
descriptors: node.readonly | ||
? [] | ||
: [ | ||
{ | ||
node: node, | ||
messageId: "generic", | ||
fix: function (fixer) { return fixer.insertTextBefore(node, "Readonly"); } | ||
messageId: "property", | ||
fix: isTSIndexSignature(node) | ||
? function (fixer) { return fixer.insertTextBefore(node, "readonly "); } | ||
: isTSParameterProperty(node) | ||
? function (fixer) { return fixer.insertTextBefore(node.parameter, "readonly "); } | ||
: function (fixer) { return fixer.insertTextBefore(node.key, "readonly "); } | ||
} | ||
] | ||
: [] | ||
}; | ||
@@ -1443,15 +1730,9 @@ } | ||
// Create the rule. | ||
var rule$e = createRule({ | ||
name: name$e, | ||
meta: meta$e, | ||
defaultOptions: defaultOptions$e, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkArrayOrTupleType = checkNode(checkArrayOrTupleType, context, ignoreOptions, otherOptions); | ||
var _checkTypeReference = checkNode(checkTypeReference, context, ignoreOptions, otherOptions); | ||
var _checkImplicitType = checkNode(checkImplicitType, context, ignoreOptions, otherOptions); | ||
return { | ||
TSArrayType: _checkArrayOrTupleType, | ||
TSTupleType: _checkArrayOrTupleType, | ||
TSTypeReference: _checkTypeReference, | ||
var rule$e = createRule(name$e, meta$e, defaultOptions$e, function (context, options) { | ||
var _checkArrayOrTupleType = checkNode(checkArrayOrTupleType, context, options); | ||
var _checkTypeReference = checkNode(checkTypeReference, context, options); | ||
var _checkProperty = checkNode(checkProperty, context, options); | ||
var _checkImplicitType = checkNode(checkImplicitType, context, options); | ||
return tslib_1.__assign({ TSArrayType: _checkArrayOrTupleType, TSTupleType: _checkArrayOrTupleType, TSTypeReference: _checkTypeReference, ClassProperty: _checkProperty, TSIndexSignature: _checkProperty, TSParameterProperty: _checkProperty, TSPropertySignature: _checkProperty }, (options.checkImplicit | ||
? { | ||
VariableDeclaration: _checkImplicitType, | ||
@@ -1461,76 +1742,6 @@ FunctionDeclaration: _checkImplicitType, | ||
ArrowFunctionExpression: _checkImplicitType | ||
}; | ||
} | ||
} | ||
: {})); | ||
}); | ||
// The name of this rule. | ||
var name$f = "readonly-keyword"; | ||
// The schema for the rule options. | ||
var schema$5 = [ | ||
deepmerge.all([ | ||
ignoreLocalOptionSchema, | ||
ignorePatternOptionSchema, | ||
ignoreClassOptionSchema, | ||
ignoreInterfaceOptionSchema | ||
]) | ||
]; | ||
// The default options for the rule. | ||
var defaultOptions$f = [ | ||
{ | ||
ignoreClass: false, | ||
ignoreInterface: false, | ||
ignoreLocal: false | ||
} | ||
]; | ||
// The possible error messages. | ||
var errorMessages$f = { | ||
generic: "A readonly modifier is required." | ||
}; | ||
// The meta data for this rule. | ||
var meta$f = { | ||
type: "suggestion", | ||
docs: { | ||
description: "Enforce readonly modifiers are used where possible.", | ||
category: "Best Practices", | ||
recommended: "error" | ||
}, | ||
messages: errorMessages$f, | ||
fixable: "code", | ||
schema: schema$5 | ||
}; | ||
/** | ||
* Check if the given node violates this rule. | ||
*/ | ||
function check(node, context) { | ||
return { | ||
context: context, | ||
descriptors: node.readonly | ||
? [] | ||
: [ | ||
{ | ||
node: node, | ||
messageId: "generic", | ||
fix: isTSIndexSignature(node) | ||
? function (fixer) { return fixer.insertTextBefore(node, "readonly "); } | ||
: function (fixer) { return fixer.insertTextBefore(node.key, "readonly "); } | ||
} | ||
] | ||
}; | ||
} | ||
// Create the rule. | ||
var rule$f = createRule({ | ||
name: name$f, | ||
meta: meta$f, | ||
defaultOptions: defaultOptions$f, | ||
create: function (context, _a) { | ||
var ignoreOptions = _a[0], otherOptions = _a.slice(1); | ||
var _checkNode = checkNode(check, context, ignoreOptions, otherOptions); | ||
return { | ||
ClassProperty: _checkNode, | ||
TSIndexSignature: _checkNode, | ||
TSPropertySignature: _checkNode | ||
}; | ||
} | ||
}); | ||
var _a; | ||
@@ -1544,8 +1755,5 @@ /** | ||
_a[name$2] = rule$2, | ||
_a[name$3] = rule$3, | ||
_a[name$4] = rule$4, | ||
_a[name$5] = rule$5, | ||
_a[name$6] = rule$6, | ||
_a[name$c] = rule$c, | ||
_a[name$d] = rule$d, | ||
_a[name$7] = rule$7, | ||
@@ -1555,5 +1763,7 @@ _a[name$8] = rule$8, | ||
_a[name$a] = rule$a, | ||
_a[name$3] = rule$3, | ||
_a[name$b] = rule$b, | ||
_a[name$c] = rule$c, | ||
_a[name$d] = rule$d, | ||
_a[name$e] = rule$e, | ||
_a[name$f] = rule$f, | ||
_a); | ||
@@ -1560,0 +1770,0 @@ |
{ | ||
"name": "eslint-plugin-ts-immutable", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "ESLint rules to disable mutation in TypeScript.", | ||
@@ -13,3 +13,8 @@ "main": "lib/index.js", | ||
"eslint", | ||
"immutability" | ||
"eslint plugin", | ||
"immutable", | ||
"immutability", | ||
"functional", | ||
"functional programming", | ||
"fp" | ||
], | ||
@@ -58,2 +63,3 @@ "author": "Jonas Kello", | ||
"eslint-plugin-jest": "^22.7.1", | ||
"eslint-plugin-jsdoc": "^15.5.3", | ||
"eslint-plugin-prettier": "^3.1.0", | ||
@@ -63,2 +69,3 @@ "glob": "^7.1.4", | ||
"jest": "^24.8.0", | ||
"json-schema": "^0.2.3", | ||
"lint-staged": "^8.2.1", | ||
@@ -77,7 +84,10 @@ "prettier": "^1.18.2", | ||
"peerDependencies": { | ||
"eslint": ">=6.0.0" | ||
}, | ||
"optionalDependencies": { | ||
"eslint": ">=6.0.0", | ||
"typescript": ">=3.4.1" | ||
}, | ||
"peerDependenciesMeta": { | ||
"typescript": { | ||
"optional": true | ||
} | ||
}, | ||
"scripts": { | ||
@@ -93,3 +103,3 @@ "compile": "rollup -c", | ||
"test-compiled": "USE_COMPLIED=1 yarn test", | ||
"verify": "yarn build && yarn lint && yarn build-tests && yarn test-compiled", | ||
"verify": "yarn build && yarn lint && yarn build-tests && yarn test-compiled && rimraf build", | ||
"report-coverage": "codecov -f coverage/lcov.info", | ||
@@ -96,0 +106,0 @@ "preversion": "yarn verify", |
@@ -66,2 +66,3 @@ # eslint-plugin-ts-immutable | ||
`@typescript-eslint/parser` is needed to parse TypeScript code; add `@typescript-eslint/parser` to the "parser" filed in your `.eslintrc` configuration file. | ||
Additionally, for this plugin to use type information, you will need to specify a path to your tsconfig.json file in the "project" property of "parserOptions". | ||
@@ -71,14 +72,2 @@ ```json | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": ["ts-immutable"], | ||
"rules": { | ||
"ts-immutable/rule-name": "error" | ||
} | ||
} | ||
``` | ||
For this plugin to use type information, you will need to specify a path to your tsconfig.json file in the "project" property of "parserOptions". | ||
```json | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
@@ -115,25 +104,24 @@ "project": "./tsconfig.json" | ||
| Name | Description | <span title="Recommended">:see_no_evil:</span> | <span title="Functional Lite">:hear_no_evil:</span> | <span title="Functional">:speak_no_evil:</span> | :wrench: | :blue_heart: | | ||
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------- | :--------------------------------------------: | :-------------------------------------------------: | :---------------------------------------------: | :------: | :---------------: | | ||
| [`ts-immutable/readonly-keyword`](./docs/rules/readonly-keyword.md) | Enforce readonly modifiers are used where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| [`ts-immutable/readonly-array`](./docs/rules/readonly-array.md) | Enforce readonly array over mutable arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| [`ts-immutable/no-let`](./docs/rules/no-let.md) | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | | | ||
| [`ts-immutable/no-array-mutation`](./docs/rules/no-array-mutation.md) | Disallow mutating arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
| [`ts-immutable/no-object-mutation`](./docs/rules/no-object-mutation.md) | Disallow mutating objects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: | | ||
| [`ts-immutable/no-method-signature`](./docs/rules/no-method-signature.md) | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
| [`ts-immutable/no-delete`](./docs/rules/no-delete.md) | Disallow delete expressions | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| Name | Description | <span title="Recommended">:see_no_evil:</span> | <span title="Functional Lite">:hear_no_evil:</span> | <span title="Functional">:speak_no_evil:</span> | :wrench: | :blue_heart: | | ||
| ---------------------------------------------------------------- | -------------------------------------------------------------------------- | :--------------------------------------------: | :-------------------------------------------------: | :---------------------------------------------: | :------: | :---------------: | | ||
| [`prefer-readonly-types`](./docs/rules/prefer-readonly-types.md) | Use readonly types and readonly modifiers where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| [`no-let`](./docs/rules/no-let.md) | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`immutable-data`](./docs/rules/immutable-data.md) | Disallow mutating objects and arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: | | ||
| [`no-method-signature`](./docs/rules/no-method-signature.md) | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
### Functional style rules | ||
| Name | Description | <span title="Recommended">:see_no_evil:</span> | <span title="Functional Lite">:hear_no_evil:</span> | <span title="Functional">:speak_no_evil:</span> | :wrench: | :blue_heart: | | ||
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | :--------------------------------------------: | :-------------------------------------------------: | :---------------------------------------------: | :------: | :---------------: | | ||
| [`ts-immutable/no-this`](./docs/rules/no-this.md) | Disallow this access | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-class`](./docs/rules/no-class.md) | Disallow classes | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-mixed-interface`](./docs/rules/no-mixed-interface.md) | Restrict interfaces so that only members of the same kind of are allowed in them | | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
| [`ts-immutable/no-expression-statement`](./docs/rules/no-expression-statement.md) | Using expressions to cause side-effects not allowed | | | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-if-statement`](./docs/rules/no-if-statement.md) | Disallow if statements | | | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-loop-statement`](./docs/rules/no-loop-statement.md) | Disallow imperative loops | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-throw`](./docs/rules/no-throw.md) | Disallow throwing exceptions | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-try`](./docs/rules/no-try.md) | Disallow try-catch[-finally] and try-finally patterns | | | :heavy_check_mark: | | | | ||
| [`ts-immutable/no-reject`](./docs/rules/no-reject.md) | Disallow rejecting Promises | | | | | | | ||
| Name | Description | <span title="Recommended">:see_no_evil:</span> | <span title="Functional Lite">:hear_no_evil:</span> | <span title="Functional">:speak_no_evil:</span> | :wrench: | :blue_heart: | | ||
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------- | :--------------------------------------------: | :-------------------------------------------------: | :---------------------------------------------: | :------: | :---------------: | | ||
| [`no-this`](./docs/rules/no-this.md) | Disallow this access | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`no-class`](./docs/rules/no-class.md) | Disallow classes | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`no-mixed-interface`](./docs/rules/no-mixed-interface.md) | Restrict interfaces so that only members of the same kind are allowed in them | | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
| [`no-expression-statement`](./docs/rules/no-expression-statement.md) | Disallow expressions to cause side-effects | | | :heavy_check_mark: | | | | ||
| [`no-conditional-statement`](./docs/rules/no-conditional-statement.md) | Disallow conditional statements (if and switch statements) | | | :heavy_check_mark: | | | | ||
| [`no-loop-statement`](./docs/rules/no-loop-statement.md) | Disallow imperative loops | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`no-return-void`](./docs/rules/no-return-void.md) | Disallow function that return nothing | | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: | | ||
| [`functional-parameters`](./docs/rules/functional-parameters.md) | Functions must have functional parameter | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`no-throw`](./docs/rules/no-throw.md) | Disallow throwing exceptions | | :heavy_check_mark: | :heavy_check_mark: | | | | ||
| [`no-try`](./docs/rules/no-try.md) | Disallow try-catch[-finally] and try-finally patterns | | | :heavy_check_mark: | | | | ||
| [`no-reject`](./docs/rules/no-reject.md) | Disallow rejecting Promises | | | | | | | ||
@@ -195,3 +183,3 @@ ## Recommended standard rules | ||
[version-image]: https://img.shields.io/npm/v/eslint-plugin-ts-immutable.svg?style=flat | ||
[version-url]: https://www.npmjs.com/packageeslint-plugin-ts-immutable | ||
[version-url]: https://www.npmjs.com/package/eslint-plugin-ts-immutable | ||
[travis-image]: https://travis-ci.com/jonaskello/eslint-plugin-ts-immutable.svg?branch=master&style=flat | ||
@@ -198,0 +186,0 @@ [travis-url]: https://travis-ci.com/jonaskello/eslint-plugin-ts-immutable |
Sorry, the diff of this file is not supported yet
134150
3357
32
191