tslint-immutable
Advanced tools
Comparing version 3.1.2 to 3.2.0
@@ -9,2 +9,9 @@ # Change Log | ||
## [v3.2.0] - 2017-04-10 | ||
### Fixed | ||
- readonly-array does not check shorthand syntax in return types with ignore-local option [#21](https://github.com/jonaskello/tslint-immutable/issues/21) | ||
### Added | ||
- Fixer for the `readonly-array` rule. | ||
## [v3.1.2] - 2017-04-09 | ||
@@ -69,3 +76,4 @@ ### Fixed | ||
[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v3.1.2...master | ||
[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v3.2.0...master | ||
[v3.2.0]: https://github.com/jonaskello/tslint-immutable/compare/v3.1.2...v3.2.0 | ||
[v3.1.2]: https://github.com/jonaskello/tslint-immutable/compare/v3.1.1...v3.1.2 | ||
@@ -72,0 +80,0 @@ [v3.1.1]: https://github.com/jonaskello/tslint-immutable/compare/v3.1.0...v3.1.1 |
{ | ||
"name": "tslint-immutable", | ||
"version": "3.1.2", | ||
"version": "3.2.0", | ||
"description": "TSLint rules to disable mutation in TypeScript.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -113,7 +113,10 @@ # tslint-immutable | ||
Options: | ||
##### Has Fixer | ||
Yes | ||
##### Options: | ||
- [ignore-local](#using-the-ignore-local-option) | ||
- [ignore-prefix](#using-the-ignore-prefix-option) | ||
Example config: | ||
##### Example config: | ||
```javascript | ||
@@ -120,0 +123,0 @@ "readonly-array": true |
@@ -35,4 +35,5 @@ "use strict"; | ||
var myOptions = getMyOptions(this.getOptions()); | ||
if (!myOptions) | ||
if (!myOptions) { | ||
return; | ||
} | ||
var containmentPath = myOptions.containmentPath, allowedExternalFileNames = myOptions.allowedExternalFileNames, disallowedInternalFileNames = myOptions.disallowedInternalFileNames; | ||
@@ -43,3 +44,3 @@ var sourceFileRelativePath = getSourceFilePathRelativeToContainmentPath(this.getSourceFile().path, containmentPath); | ||
// Remove the file name to get the path | ||
var sourceDirRelativePath = sourceFileRelativePath.substring(0, sourceFileRelativePath.lastIndexOf('/')); | ||
var sourceDirRelativePath = sourceFileRelativePath.substring(0, sourceFileRelativePath.lastIndexOf("/")); | ||
// Check that it is a sub directory under the containment path | ||
@@ -52,3 +53,3 @@ if (sourceDirRelativePath.length > containmentPath.length) { | ||
// Get the file name being imported | ||
var importFileName = importRelativePath.substring(importRelativePath.lastIndexOf('/') + 1); | ||
var importFileName = importRelativePath.substring(importRelativePath.lastIndexOf("/") + 1); | ||
// Get how many levels below the containment path the file resides | ||
@@ -61,6 +62,9 @@ var levelsBelowPath = getLevelsBelowPath(containmentPath, sourceDirRelativePath); | ||
// Relative import paths are not allowed to reach up to the containment path | ||
//throw Error("sourceDirPath: " + sourceDirRelativePath + ", importRelativePath: " + importRelativePath + ", highestParentLevel: " + highestParentLevel + ", levelsBelowPath: " + levelsBelowPath + ", sourceFileName" + sourceFileName); | ||
//throw Error("sourceDirPath: " + sourceDirRelativePath + ", importRelativePath: " | ||
//+ importRelativePath + ", highestParentLevel: " + highestParentLevel + ", levelsBelowPath: " + | ||
//levelsBelowPath + ", sourceFileName" + sourceFileName); | ||
// create a failure at the current position | ||
if (allowedExternalFileNames.indexOf(importFileName) === -1) | ||
if (allowedExternalFileNames.indexOf(importFileName) === -1) { | ||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); | ||
} | ||
} | ||
@@ -70,4 +74,5 @@ else { | ||
// are not allowed to import certain file names | ||
if (disallowedInternalFileNames.indexOf(importFileName) !== -1) | ||
if (disallowedInternalFileNames.indexOf(importFileName) !== -1) { | ||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING2)); | ||
} | ||
} | ||
@@ -96,7 +101,9 @@ } | ||
var myOptions = options[0]; // "src/app"; | ||
if (!myOptions) | ||
if (!myOptions) { | ||
return undefined; | ||
} | ||
var containmentPath = myOptions.containmentPath, allowedExternalFileNames = myOptions.allowedExternalFileNames, disallowedInternalFileNames = myOptions.disallowedInternalFileNames; | ||
if (!containmentPath) | ||
if (!containmentPath) { | ||
return undefined; | ||
} | ||
return { | ||
@@ -103,0 +110,0 @@ containmentPath: containmentPath, |
@@ -29,2 +29,5 @@ "use strict"; | ||
var OPTION_IGNORE_PREFIX = "ignore-prefix"; | ||
function createInvalidNode(node, replacement) { | ||
return { node: node, replacement: replacement }; | ||
} | ||
function parseOptions(options) { | ||
@@ -35,3 +38,3 @@ var ignoreLocal = options.indexOf(OPTION_IGNORE_LOCAL) !== -1; | ||
var o = options_1[_i]; | ||
if (typeof o === "object" && o[OPTION_IGNORE_PREFIX] != null) { | ||
if (typeof o === "object" && o[OPTION_IGNORE_PREFIX] !== null) { | ||
ignorePrefix = o[OPTION_IGNORE_PREFIX]; | ||
@@ -50,9 +53,9 @@ break; | ||
var functionNode = node; //tslint:disable-line | ||
checkFunctionNode(functionNode, ctx); | ||
var invalidNodes = checkIgnoreLocalFunctionNode(functionNode, ctx); | ||
invalidNodes.forEach(function (n) { return reportInvalidNode(n, ctx); }); | ||
// Now skip this whole branch | ||
return; | ||
} | ||
// Check the node | ||
checkArrayTypeReference(node, ctx); | ||
checkArrayType(node, ctx); | ||
checkArrayLiteralExpression(node, ctx); | ||
reportInvalidNode(checkNode(node, ctx), ctx); | ||
// Use return becuase performance hints docs say it optimizes the function using tail-call recursion | ||
@@ -62,64 +65,96 @@ return ts.forEachChild(node, cb); | ||
} | ||
function checkFunctionNode(node, ctx) { | ||
// Check the parameters | ||
for (var _i = 0, _a = node.parameters; _i < _a.length; _i++) { | ||
var parameter = _a[_i]; | ||
if (parameter.type) { | ||
checkArrayTypeReference(parameter.type, ctx); | ||
checkArrayType(parameter.type, ctx); | ||
function checkNode(node, ctx) { | ||
return checkArrayTypeOrReference(node, ctx) || checkVariableOrParameterImplicitType(node, ctx); | ||
} | ||
function reportInvalidNode(invalidNode, ctx) { | ||
if (invalidNode) { | ||
ctx.addFailureAtNode(invalidNode.node, Rule.FAILURE_STRING, invalidNode.replacement); | ||
} | ||
} | ||
function checkIgnoreLocalFunctionNode(functionNode, ctx) { | ||
var invalidNodes = []; | ||
// Check either the parameter's explicit type if it has one, or itself for implict type | ||
for (var _i = 0, _a = functionNode.parameters.map(function (p) { return p.type ? p.type : p; }); _i < _a.length; _i++) { | ||
var n = _a[_i]; | ||
var invalidNode = checkNode(n, ctx); | ||
if (invalidNode) { | ||
invalidNodes.push(invalidNode); | ||
} | ||
else if (parameter.initializer) { | ||
checkArrayLiteralExpression(parameter.initializer, ctx); | ||
} | ||
} | ||
// Check the return type | ||
if (node.type) { | ||
checkArrayTypeReference(node.type, ctx); | ||
if (functionNode.type) { | ||
var invalidNode = checkNode(functionNode.type, ctx); | ||
if (invalidNode) { | ||
invalidNodes.push(invalidNode); | ||
} | ||
} | ||
return invalidNodes; | ||
} | ||
function checkArrayType(node, ctx) { | ||
if (node.kind === ts.SyntaxKind.ArrayType) { | ||
if (ctx.options.ignorePrefix) { | ||
var variableDeclarationNode = node.parent; | ||
if (variableDeclarationNode.name.getText(ctx.sourceFile).substr(0, ctx.options.ignorePrefix.length) === ctx.options.ignorePrefix) { | ||
return; | ||
function checkArrayTypeOrReference(node, ctx) { | ||
// We need to check both shorthand syntax "number[]" and type reference "Array<number>" | ||
if (node.kind === ts.SyntaxKind.ArrayType | ||
|| (node.kind === ts.SyntaxKind.TypeReference && node.typeName.getText(ctx.sourceFile) === "Array")) { | ||
if (node.parent && shouldIgnorePrefix(node.parent, ctx.options, ctx.sourceFile)) { | ||
return undefined; | ||
} | ||
var typeArgument = "T"; | ||
if (node.kind === ts.SyntaxKind.ArrayType) { | ||
var typeNode = node; | ||
typeArgument = typeNode.elementType.getFullText(ctx.sourceFile).trim(); | ||
} | ||
else if (node.kind === ts.SyntaxKind.TypeReference) { | ||
var typeNode = node; | ||
if (typeNode.typeArguments) { | ||
typeArgument = typeNode.typeArguments[0].getFullText(ctx.sourceFile).trim(); | ||
} | ||
} | ||
ctx.addFailureAtNode(node, Rule.FAILURE_STRING); | ||
return; | ||
var length_1 = node.getWidth(ctx.sourceFile); | ||
return createInvalidNode(node, new Lint.Replacement(node.end - length_1, length_1, "ReadonlyArray<" + typeArgument + ">")); | ||
} | ||
return undefined; | ||
} | ||
function checkArrayTypeReference(node, ctx) { | ||
if (node.kind === ts.SyntaxKind.TypeReference) { | ||
var typeRefNode = node; | ||
if (typeRefNode.typeName.getText(ctx.sourceFile) === "Array") { | ||
if (ctx.options.ignorePrefix) { | ||
var variableDeclarationNode = node.parent; | ||
if (variableDeclarationNode.name.getText(ctx.sourceFile).substr(0, ctx.options.ignorePrefix.length) === ctx.options.ignorePrefix) { | ||
return; | ||
} | ||
function checkVariableOrParameterImplicitType(node, ctx) { | ||
if (node.kind === ts.SyntaxKind.VariableDeclaration || node.kind === ts.SyntaxKind.Parameter) { | ||
// The initializer is used to set and implicit type | ||
var varOrParamNode = node; | ||
if (shouldIgnorePrefix(node, ctx.options, ctx.sourceFile)) { | ||
return undefined; | ||
} | ||
if (!varOrParamNode.type) { | ||
if (varOrParamNode.initializer && varOrParamNode.initializer.kind === ts.SyntaxKind.ArrayLiteralExpression) { | ||
var length_2 = varOrParamNode.name.getWidth(ctx.sourceFile); | ||
var nameText = varOrParamNode.name.getText(ctx.sourceFile); | ||
var typeArgument = "any"; | ||
// Not sure it is a good idea to guess what the element types are... | ||
// const arrayLiteralNode = varOrParamNode.initializer as ts.ArrayLiteralExpression; | ||
// if (arrayLiteralNode.elements.length > 0) { | ||
// const element = arrayLiteralNode.elements[0]; | ||
// if (element.kind === ts.SyntaxKind.NumericLiteral) { | ||
// typeArgument = "number"; | ||
// } else if (element.kind === ts.SyntaxKind.StringLiteral) { | ||
// typeArgument = "string"; | ||
// } else if (element.kind === ts.SyntaxKind.TrueKeyword || element.kind === ts.SyntaxKind.FalseKeyword) { | ||
// typeArgument = "boolean"; | ||
// } | ||
// } | ||
return createInvalidNode(varOrParamNode.name, new Lint.Replacement(varOrParamNode.name.end - length_2, length_2, nameText + ": ReadonlyArray<" + typeArgument + ">")); | ||
} | ||
ctx.addFailureAtNode(node, Rule.FAILURE_STRING); | ||
} | ||
return; | ||
} | ||
return undefined; | ||
} | ||
function checkArrayLiteralExpression(node, ctx) { | ||
if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) { | ||
// If the array literal is used in a variable declaration, the variable | ||
// must have a type spcecified, otherwise it will implicitly be of mutable Array type | ||
// It could also be a function parameter that has an array literal as default value | ||
if (node.parent && (node.parent.kind === ts.SyntaxKind.VariableDeclaration || node.parent.kind === ts.SyntaxKind.Parameter)) { | ||
var parent_1 = node.parent; | ||
if (!parent_1.type) { | ||
if (ctx.options.ignorePrefix && | ||
parent_1.name.getText(ctx.sourceFile).substr(0, ctx.options.ignorePrefix.length) === ctx.options.ignorePrefix) { | ||
return; | ||
} | ||
var variableDeclarationNode = node.parent; | ||
ctx.addFailureAtNode(variableDeclarationNode.name, Rule.FAILURE_STRING); | ||
function shouldIgnorePrefix(node, options, sourceFile) { | ||
// Check ignore-prefix for VariableDeclaration, PropertySignature, TypeAliasDeclaration, Parameter | ||
if (options.ignorePrefix) { | ||
if (node && (node.kind === ts.SyntaxKind.VariableDeclaration | ||
|| node.kind === ts.SyntaxKind.Parameter | ||
|| node.kind === ts.SyntaxKind.PropertySignature | ||
|| node.kind === ts.SyntaxKind.TypeAliasDeclaration)) { | ||
var variableDeclarationNode = node; | ||
if (variableDeclarationNode.name.getText(sourceFile).substr(0, options.ignorePrefix.length) === options.ignorePrefix) { | ||
return true; | ||
} | ||
} | ||
return; | ||
} | ||
return false; | ||
} |
124
tslint.json
{ | ||
"rules": { | ||
"no-any": true | ||
"no-any": true, | ||
"no-internal-module": true, | ||
"no-namespace": true, | ||
"no-reference": true, | ||
"no-var-requires": true, | ||
"typedef": [ | ||
true, | ||
"call-signature", | ||
"parameter", | ||
"property-declaration" | ||
], | ||
"typedef-whitespace": [ | ||
true, | ||
{ | ||
"call-signature": "nospace", | ||
"index-signature": "nospace", | ||
"parameter": "nospace", | ||
"property-declaration": "nospace", | ||
"variable-declaration": "nospace" | ||
}, | ||
{ | ||
"call-signature": "onespace", | ||
"index-signature": "onespace", | ||
"parameter": "onespace", | ||
"property-declaration": "onespace", | ||
"variable-declaration": "onespace" | ||
} | ||
], | ||
"curly": true, | ||
"forin": true, | ||
"label-position": true, | ||
"no-arg": true, | ||
"no-bitwise": true, | ||
"no-conditional-assignment": true, | ||
// "no-console": [ | ||
// true, | ||
// "log" | ||
// ], | ||
"no-construct": true, | ||
"no-debugger": true, | ||
"no-duplicate-variable": true, | ||
"no-empty": true, | ||
"no-eval": true, | ||
"no-invalid-this": true, | ||
"no-null-keyword": true, | ||
"no-shadowed-variable": true, | ||
// "no-string-literal": true, | ||
"no-switch-case-fall-through": true, | ||
"no-unsafe-finally": true, | ||
"no-unused-expression": true, | ||
"no-var-keyword": true, | ||
"radix": true, | ||
"switch-default": true, | ||
"triple-equals": true, | ||
"use-isnan": true, | ||
"eofline": true, | ||
"indent": [ | ||
true, | ||
"spaces" | ||
], | ||
"max-file-line-count": [ | ||
true, | ||
500 | ||
], | ||
"max-line-length": [ | ||
true, | ||
160 | ||
], | ||
"no-default-export": true, | ||
"no-mergeable-namespace": true, | ||
"no-require-imports": true, | ||
"align": [ | ||
true, | ||
"statements" | ||
], | ||
"arrow-parens": true, | ||
"class-name": true, | ||
"jsdoc-format": true, | ||
"new-parens": true, | ||
"no-angle-bracket-type-assertion": true, | ||
"no-consecutive-blank-lines": [ | ||
true, | ||
1 | ||
], | ||
"no-parameter-properties": true, | ||
// "object-literal-key-quotes": [ | ||
// true, | ||
// "as-needed" | ||
// ], | ||
"one-line": [ | ||
true, | ||
"check-catch", | ||
"check-finally", | ||
"check-else", | ||
"check-open-brace", | ||
"check-whitespace" | ||
], | ||
"one-variable-per-declaration": [true], | ||
"quotemark": [ | ||
true, | ||
"double", | ||
"avoid-escape" | ||
], | ||
"semicolon": [ | ||
true, | ||
"always", | ||
"ignore-interfaces" | ||
], | ||
"variable-name": [ | ||
true, | ||
"ban-keywords", | ||
"check-format" | ||
], | ||
"whitespace": [ | ||
true, | ||
"check-branch", | ||
"check-decl", | ||
"check-operator", | ||
"check-module", | ||
"check-separator", | ||
"check-type", | ||
"check-typecast" | ||
] | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
66474
978
343