tslint-immutable
Advanced tools
Comparing version 3.3.1 to 3.3.2
@@ -9,2 +9,6 @@ # Change Log | ||
## [v3.3.2] - 2017-05-13 | ||
### Fixed | ||
- Functions in interfaces cannot have readonly specified but are still checked [#28](https://github.com/jonaskello/tslint-immutable/issues/28) | ||
## [v3.3.1] - 2017-05-09 | ||
@@ -87,3 +91,4 @@ ### Fixed | ||
[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v3.3.1...master | ||
[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v3.3.2...master | ||
[v3.3.2]: https://github.com/jonaskello/tslint-immutable/compare/v3.3.1...v3.3.2 | ||
[v3.3.1]: https://github.com/jonaskello/tslint-immutable/compare/v3.3.0...v3.3.1 | ||
@@ -90,0 +95,0 @@ [v3.3.0]: https://github.com/jonaskello/tslint-immutable/compare/v3.2.0...v3.3.0 |
{ | ||
"name": "tslint-immutable", | ||
"version": "3.3.1", | ||
"version": "3.3.2", | ||
"description": "TSLint rules to disable mutation in TypeScript.", | ||
@@ -5,0 +5,0 @@ "main": "tslint-immutable.json", |
@@ -15,2 +15,3 @@ "use strict"; | ||
var Lint = require("tslint"); | ||
var Shared = require("./readonly-shared"); | ||
var Rule = (function (_super) { | ||
@@ -22,71 +23,12 @@ __extends(Rule, _super); | ||
Rule.prototype.apply = function (sourceFile) { | ||
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments)); | ||
return this.applyWithFunction(sourceFile, function (ctx) { return Shared.walk(ctx, checkNode, "Only ReadonlyArray allowed."); }, Shared.parseOptions(this.ruleArguments)); | ||
}; | ||
return Rule; | ||
}(Lint.Rules.AbstractRule)); | ||
Rule.FAILURE_STRING = "Only ReadonlyArray allowed."; | ||
exports.Rule = Rule; | ||
var OPTION_IGNORE_LOCAL = "ignore-local"; | ||
var OPTION_IGNORE_PREFIX = "ignore-prefix"; | ||
function createInvalidNode(node, replacement) { | ||
return { node: node, replacement: replacement }; | ||
} | ||
function parseOptions(options) { | ||
var ignoreLocal = options.indexOf(OPTION_IGNORE_LOCAL) !== -1; | ||
var ignorePrefix; | ||
for (var _i = 0, options_1 = options; _i < options_1.length; _i++) { | ||
var o = options_1[_i]; | ||
if (typeof o === "object" && o[OPTION_IGNORE_PREFIX] !== null) { | ||
ignorePrefix = o[OPTION_IGNORE_PREFIX]; | ||
break; | ||
} | ||
} | ||
return { ignoreLocal: ignoreLocal, ignorePrefix: ignorePrefix }; | ||
} | ||
function walk(ctx) { | ||
return ts.forEachChild(ctx.sourceFile, cb); | ||
function cb(node) { | ||
// Skip checking in functions if ignore-local is set | ||
if (ctx.options.ignoreLocal && (node.kind === ts.SyntaxKind.FunctionDeclaration | ||
|| node.kind === ts.SyntaxKind.ArrowFunction || node.kind === ts.SyntaxKind.FunctionExpression)) { | ||
// We still need to check the parameters and return type | ||
var functionNode = node; //tslint:disable-line | ||
var invalidNodes = checkIgnoreLocalFunctionNode(functionNode, ctx); | ||
invalidNodes.forEach(function (n) { return reportInvalidNode(n, ctx); }); | ||
// Now skip this whole branch | ||
return; | ||
} | ||
// Check the node | ||
reportInvalidNode(checkNode(node, ctx), ctx); | ||
// Use return becuase performance hints docs say it optimizes the function using tail-call recursion | ||
return ts.forEachChild(node, cb); | ||
} | ||
} | ||
function checkNode(node, ctx) { | ||
return checkArrayTypeOrReference(node, ctx) || checkVariableOrParameterImplicitType(node, ctx); | ||
var explicitTypeFailures = checkArrayTypeOrReference(node, ctx); | ||
var implicitTypeFailures = checkVariableOrParameterImplicitType(node, ctx); | ||
return explicitTypeFailures.concat(implicitTypeFailures); | ||
} | ||
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); | ||
} | ||
} | ||
// Check the return type | ||
if (functionNode.type) { | ||
var invalidNode = checkNode(functionNode.type, ctx); | ||
if (invalidNode) { | ||
invalidNodes.push(invalidNode); | ||
} | ||
} | ||
return invalidNodes; | ||
} | ||
function checkArrayTypeOrReference(node, ctx) { | ||
@@ -96,4 +38,4 @@ // We need to check both shorthand syntax "number[]" and type reference "Array<number>" | ||
|| (node.kind === ts.SyntaxKind.TypeReference && node.typeName.getText(ctx.sourceFile) === "Array")) { | ||
if (node.parent && shouldIgnorePrefix(node.parent, ctx.options, ctx.sourceFile)) { | ||
return undefined; | ||
if (node.parent && Shared.shouldIgnorePrefix(node.parent, ctx.options, ctx.sourceFile)) { | ||
return []; | ||
} | ||
@@ -112,5 +54,5 @@ var typeArgument = "T"; | ||
var length_1 = node.getWidth(ctx.sourceFile); | ||
return createInvalidNode(node, new Lint.Replacement(node.end - length_1, length_1, "ReadonlyArray<" + typeArgument + ">")); | ||
return [Shared.createInvalidNode(node, new Lint.Replacement(node.end - length_1, length_1, "ReadonlyArray<" + typeArgument + ">"))]; | ||
} | ||
return undefined; | ||
return []; | ||
} | ||
@@ -121,4 +63,4 @@ function checkVariableOrParameterImplicitType(node, ctx) { | ||
var varOrParamNode = node; | ||
if (shouldIgnorePrefix(node, ctx.options, ctx.sourceFile)) { | ||
return undefined; | ||
if (Shared.shouldIgnorePrefix(node, ctx.options, ctx.sourceFile)) { | ||
return []; | ||
} | ||
@@ -142,22 +84,7 @@ if (!varOrParamNode.type) { | ||
// } | ||
return createInvalidNode(varOrParamNode.name, new Lint.Replacement(varOrParamNode.name.end - length_2, length_2, nameText + ": ReadonlyArray<" + typeArgument + ">")); | ||
return [Shared.createInvalidNode(varOrParamNode.name, new Lint.Replacement(varOrParamNode.name.end - length_2, length_2, nameText + ": ReadonlyArray<" + typeArgument + ">"))]; | ||
} | ||
} | ||
} | ||
return undefined; | ||
return []; | ||
} | ||
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 false; | ||
} |
@@ -15,2 +15,3 @@ "use strict"; | ||
var Lint = require("tslint"); | ||
var Shared = require("./readonly-shared"); | ||
var Rule = (function (_super) { | ||
@@ -22,38 +23,28 @@ __extends(Rule, _super); | ||
Rule.prototype.apply = function (sourceFile) { | ||
var walker = new ReadonlyInterfaceWalker(sourceFile, this.getOptions()); | ||
return this.applyWithWalker(walker); | ||
return this.applyWithFunction(sourceFile, function (ctx) { return Shared.walk(ctx, checkNode, "Interface members must have readonly modifier."); }, Shared.parseOptions(this.ruleArguments)); | ||
}; | ||
return Rule; | ||
}(Lint.Rules.AbstractRule)); | ||
Rule.FAILURE_STRING = "Interface members must have readonly modifier."; | ||
Rule.FAILURE_STRING_ARRAY = "Interface members of array type must be ReadonlyArray."; | ||
exports.Rule = Rule; | ||
var ReadonlyInterfaceWalker = (function (_super) { | ||
__extends(ReadonlyInterfaceWalker, _super); | ||
function ReadonlyInterfaceWalker() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
ReadonlyInterfaceWalker.prototype.visitInterfaceDeclaration = function (node) { | ||
for (var _i = 0, _a = node.members; _i < _a.length; _i++) { | ||
function checkNode(node, ctx) { | ||
return checkInterfaceDeclaration(node, ctx); | ||
} | ||
function checkInterfaceDeclaration(node, ctx) { | ||
if (node.kind === ts.SyntaxKind.InterfaceDeclaration) { | ||
var interfaceDeclarationNode = node; | ||
var invalidNodes = []; | ||
for (var _i = 0, _a = interfaceDeclarationNode.members; _i < _a.length; _i++) { | ||
var member = _a[_i]; | ||
if (!(member.modifiers && member.modifiers.filter(function (m) { return m.kind === ts.SyntaxKind.ReadonlyKeyword; }).length > 0)) { | ||
this.addFailure(this.createFailure(member.getStart(), member.getWidth(), Rule.FAILURE_STRING)); | ||
// readonly modifier is only allowed for PropertySignature and IndexSignature | ||
if (member.kind === ts.SyntaxKind.PropertySignature || member.kind === ts.SyntaxKind.IndexSignature) { | ||
if (!(member.modifiers && member.modifiers.filter(function (m) { return m.kind === ts.SyntaxKind.ReadonlyKeyword; }).length > 0)) { | ||
var length_1 = member.getWidth(ctx.sourceFile); | ||
var memberName = member.name; | ||
invalidNodes.push(Shared.createInvalidNode(member, new Lint.Replacement(node.end - length_1, length_1, "readonly " + memberName))); | ||
} | ||
} | ||
// interface Foo { | ||
// readonly bar: number, | ||
// readonly zoo: () => string | ||
// readonly loo: Array<string>, | ||
// readonly <T1>(): Array<T1> | ||
// } | ||
// let f: Foo; | ||
// f.zoo = () => ""; | ||
// console.log("member.type", (member as any).type); | ||
// | ||
// if (member.kind === ts.SyntaxKind.ArrayType) { | ||
// this.addFailure(this.createFailure(member.getStart(), member.getWidth(), Rule.FAILURE_STRING_ARRAY)); | ||
// } | ||
} | ||
_super.prototype.visitInterfaceDeclaration.call(this, node); | ||
}; | ||
return ReadonlyInterfaceWalker; | ||
}(Lint.RuleWalker)); | ||
return invalidNodes; | ||
} | ||
return []; | ||
} |
68622
24
1003