Skip to content

Commit

Permalink
fix(templateRules): look for property declaration in constructor
Browse files Browse the repository at this point in the history
Fixes #153
  • Loading branch information
mgechev committed Nov 19, 2016
1 parent 52ba382 commit 23fe633
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/noAccessMissingMemberRule.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Lint from 'tslint/lib/lint';
import * as ts from 'typescript';
import {sprintf} from 'sprintf-js';
import { stringDistance } from './util/utils';
import {stringDistance} from './util/utils';
import {Ng2Walker} from './angular/ng2Walker';
import {RecursiveAngularExpressionVisitor} from './angular/templates/recursiveAngularExpressionVisitor';
import {getDeclaredMethodNames, getDeclaredPropertyNames} from './util/classDeclarationUtils';
Expand Down
4 changes: 3 additions & 1 deletion src/templatesUsePublicRule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as Lint from 'tslint/lib/lint';
import * as ts from 'typescript';
import {stringDistance} from './util/utils';
import {getDeclaredProperties, getDeclaredMethods} from './util/classDeclarationUtils';
import {Ng2Walker} from './angular/ng2Walker';
import {RecursiveAngularExpressionVisitor} from './angular/templates/recursiveAngularExpressionVisitor';
import * as e from '@angular/compiler/src/expression_parser/ast';
Expand Down Expand Up @@ -33,7 +34,8 @@ class SymbolAccessValidator extends RecursiveAngularExpressionVisitor {
}
ast = <e.PropertyRead>receiver;
}
const member = this.context.members.filter((m: any) => m.name && m.name.text === ast.name).pop();
const allMembers = getDeclaredMethods(this.context).concat(getDeclaredProperties(this.context));
const member = allMembers.filter((m: any) => m.name && m.name.text === ast.name).pop();
if (member) {
let isPublic = !member.modifiers;
if (member.modifiers) {
Expand Down
23 changes: 19 additions & 4 deletions src/util/classDeclarationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@ import { current } from './syntaxKind';

const SyntaxKind = current();

export const getDeclaredProperties = (declaration: ts.ClassDeclaration) => {
const m = declaration.members;
const ctr = m.filter((m: any) => m.kind === SyntaxKind.Constructor).pop();
let params: any = [];
if (ctr) {
params = (((<ts.ConstructorDeclaration>ctr).parameters || []) as any)
.filter((p: any) => p.kind === SyntaxKind.Parameter);
}
return m.filter((m: any) => m.kind === SyntaxKind.PropertyDeclaration ||
m.kind === SyntaxKind.GetAccessor || m.kind === SyntaxKind.SetAccessor).concat(params);
};

export const getDeclaredPropertyNames = (declaration: ts.ClassDeclaration) => {
return declaration.members.filter((m: any) => m.kind === SyntaxKind.PropertyDeclaration ||
m.kind === SyntaxKind.GetAccessor || m.kind === SyntaxKind.SetAccessor)
.map((d: any) => (<ts.Identifier>d.name).text);
return getDeclaredProperties(declaration).filter((p: any) => p && p.name).map((p: any) => p.name.text);
};

export const getDeclaredMethods = (declaration: ts.ClassDeclaration) => {
return declaration.members.filter((m: any) => m.kind === SyntaxKind.MethodDeclaration);
};

export const getDeclaredMethodNames = (declaration: ts.ClassDeclaration) => {
return declaration.members.filter((m: any) => m.kind === SyntaxKind.MethodDeclaration)
return getDeclaredMethods(declaration)
.map((d: any) => (<ts.Identifier>d.name).text);
};

12 changes: 12 additions & 0 deletions test/noAccessMissingMemberRule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,18 @@ describe('no-access-missing-member', () => {
assertSuccess('no-access-missing-member', source);
});

it('should succeed with inline property declaration', () => {
let source = `
@Component({
selector: 'foobar',
template: '{{ foo }}'
})
class Test {
constructor(public foo: number) {}
}`;
assertSuccess('no-access-missing-member', source);
});

it('should succeed with declared property', () => {
let source = `
@Component({
Expand Down
37 changes: 36 additions & 1 deletion test/templatesUsePublicRule.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import {assertFailure, assertSuccess} from './testHelper';

describe('access-missing-declaration', () => {
describe('templates-use-public', () => {
describe('invalid expressions', () => {
it('should fail inline property private declaration', () => {
let source = `
@Component({
selector: 'foobar',
template: '{{ foo }}'
})
class Test {
constructor(private foo: number) {}
}`;
assertFailure('templates-use-public', source, {
message: 'You can bind only to public class members.',
startPosition: {
line: 3,
character: 24
},
endPosition: {
line: 3,
character: 27
}
});
});

it('should fail when interpolating private property', () => {
let source = `
@Component({
Expand Down Expand Up @@ -115,6 +137,19 @@ describe('access-missing-declaration', () => {
});

describe('valid expressions', () => {

it('should succeed inline property public declaration', () => {
let source = `
@Component({
selector: 'foobar',
template: '{{ foo }}'
})
class Test {
constructor(public foo: number) {}
}`;
assertSuccess('templates-use-public', source);
});

it('should succeed with public property', () => {
let source = `
@Component({
Expand Down

0 comments on commit 23fe633

Please sign in to comment.