Skip to content

Commit

Permalink
fix: proper hostbinding handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mgechev committed Mar 5, 2017
1 parent e27217b commit d10e980
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 14 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@
},
"homepage": "https://github.com/mgechev/codelyzer#readme",
"devDependencies": {
"@angular/common": "^4.0.0-rc.1",
"@angular/compiler": "^4.0.0-rc.1",
"@angular/core": "^4.0.0-rc.1",
"@angular/forms": "^4.0.0-rc.1",
"@angular/platform-browser": "^4.0.0-rc.1",
"@angular/router": "^4.0.0-rc.1",
"@types/chai": "^3.4.33",
"@types/less": "0.0.31",
"@types/mocha": "^2.2.32",
Expand Down
19 changes: 13 additions & 6 deletions src/angular/templates/basicTemplateAstVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export interface TemplateAstVisitorCtr {
}

export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast.TemplateAstVisitor {
private _variables = [];
protected _variables = [];

constructor(sourceFile: ts.SourceFile,
private _originalOptions: Lint.IOptions,
Expand All @@ -101,21 +101,21 @@ export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast
visitNgContent(ast: ast.NgContentAst, context: any): any {}

visitEmbeddedTemplate(ast: ast.EmbeddedTemplateAst, context: any): any {
ast.directives.forEach(d => this.visit(d, context));
ast.variables.forEach(v => this.visit(v, context));
ast.children.forEach(e => this.visit(e, context));
ast.outputs.forEach(o => this.visit(o, context));
ast.attrs.forEach(a => this.visit(a, context));
ast.references.forEach(r => this.visit(r, context));
ast.directives.forEach(d => this.visit(d, context));
}

visitElement(element: ast.ElementAst, context: any): any {
element.directives.forEach(d => this.visit(d, context));
element.references.forEach(r => this.visit(r, context));
element.inputs.forEach(i => this.visit(i, context));
element.outputs.forEach(o => this.visit(o, context));
element.attrs.forEach(a => this.visit(a, context));
element.children.forEach(e => this.visit(e, context));
element.directives.forEach(d => this.visit(d, context));
}

visitReference(ast: ast.ReferenceAst, context: any): any {
Expand Down Expand Up @@ -144,7 +144,6 @@ export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast

visitBoundText(text: ast.BoundTextAst, context: any): any {
if (ExpTypes.ASTWithSource(text.value)) {
// Note that will not be reliable for different interpolation symbols
const ast: any = (<e.ASTWithSource>text.value).ast;
ast.interpolateExpression = (<any>text.value).source;
this.visitNg2TemplateAST(ast,
Expand All @@ -156,8 +155,16 @@ export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast

visitDirective(ast: ast.DirectiveAst, context: any): any {
ast.inputs.forEach(o => this.visit(o, context));
ast.hostProperties.forEach(p => this.visit(p, context));
ast.hostEvents.forEach(e => this.visit(e, context));
const bindingCollector = p => {
if (p.value) {
const val = p.value as any;
if (typeof val.source === 'string') {
this._variables.push(val.source.split('.')[0]);
}
}
};
ast.hostProperties.forEach(bindingCollector);
ast.hostEvents.forEach(bindingCollector);
}

visitDirectiveProperty(prop: ast.BoundDirectivePropertyAst, context: any): any {
Expand Down
137 changes: 132 additions & 5 deletions test/noAccessMissingMemberRule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,6 @@ describe('no-access-missing-member', () => {
});
});


it('should not throw when template ref used outside component scope', () => {
let source = `
import { Component, NgModule } from '@angular/core';
Expand Down Expand Up @@ -435,6 +434,7 @@ describe('no-access-missing-member', () => {
it('should not throw when routerLinkActive template ref is used in component', () => {
let source = `
import { Component, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
selector: 'foobar',
Expand All @@ -443,6 +443,7 @@ describe('no-access-missing-member', () => {
export class Test {}
@NgModule({
imports: [RouterModule.forRoot([])],
declarations: [Test],
exports: [Test]
})
Expand All @@ -451,7 +452,6 @@ describe('no-access-missing-member', () => {
assertSuccess('no-access-missing-member', source);
});


it('should not throw when ngModel template ref is used in component', () => {
let source = `
import { Component, NgModule } from '@angular/core';
Expand Down Expand Up @@ -528,6 +528,42 @@ describe('no-access-missing-member', () => {
assertSuccess('no-access-missing-member', source);
});

it('should fail with nonexisting @HostBinding', () => {
let source = `
import { Directive, HostBinding, Component, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@Directive({
selector: '[foo]'
})
export class FooDirective {}
@Component({
template: \`<div foo [attr.title]="foo"></div>\`
})
export class Test {
}
@NgModule({
imports: [CommonModule],
declarations: [Test, FooDirective],
exports: [Test]
})
export class MainModule {}
`;
assertFailure('no-access-missing-member', source, {
message: 'The property "foo" that you\'re trying to access does not exist in the class declaration.',
startPosition: {
line: 10,
character: 44
},
endPosition: {
line: 10,
character: 47
}
});
});

});

describe('valid expressions', () => {
Expand Down Expand Up @@ -649,6 +685,99 @@ describe('no-access-missing-member', () => {
`;
assertSuccess('no-access-missing-member', source);
});

it('should succeed with existing @HostBinding', () => {
let source = `
import { Directive, HostBinding, Component, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@Directive({
selector: '[foo]',
host: {
'[attr.title]': 'foo'
}
})
export class FooDirective {
foo = 42;
}
@Component({
template: \`<div foo [attr.title]="foo"></div>\`
})
export class Test {
}
@NgModule({
imports: [CommonModule],
declarations: [Test, FooDirective],
exports: [Test]
})
export class MainModule {}
`;
assertSuccess('no-access-missing-member', source);
});

it('should succeed with existing @HostBinding', () => {
let source = `
import { Directive, HostBinding, Component, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@Directive({
selector: '[foo]',
host: {
'[attr.title]': 'foo.bar'
}
})
export class FooDirective {
foo = {
bar: 42
};
}
@Component({
template: \`<div foo [attr.title]="foo"></div>\`
})
export class Test {
}
@NgModule({
imports: [CommonModule],
declarations: [Test, FooDirective],
exports: [Test]
})
export class MainModule {}
`;
assertSuccess('no-access-missing-member', source);
});

it('should succeed with existing @HostBinding', () => {
let source = `
import { Directive, HostBinding, Component, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@Directive({
selector: '[foo]'
})
export class FooDirective {
@HostBinding('attr.title') foo = 42;
}
@Component({
template: \`<div foo [attr.title]="foo"></div>\`
})
export class Test {
}
@NgModule({
imports: [CommonModule],
declarations: [Test, FooDirective],
exports: [Test]
})
export class MainModule {}
`;
assertSuccess('no-access-missing-member', source);
});

});

describe('nested properties and pipes', () => {
Expand Down Expand Up @@ -1017,8 +1146,7 @@ describe('no-access-missing-member', () => {
@Component({
template: '<div *ngIf="context"></div>'
})
export class Test {
}
export class Test {}
@NgModule({
imports: [CommonModule],
Expand All @@ -1040,7 +1168,6 @@ describe('no-access-missing-member', () => {
});
});


it('should succeed with array element access', () => {
let source = `
import { Component, NgModule } from '@angular/core';
Expand Down
26 changes: 23 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
# yarn lockfile v1


"@angular/common@^4.0.0-rc.1":
version "4.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.0-rc.2.tgz#69f68639270d71b2e8c552e4fa939975fcb88304"

"@angular/compiler-cli@^4.0.0-rc.1":
version "4.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.0.0-rc.1.tgz#48cdcfb691eac2152602f296fb9fa7ffc4bfa5bd"
Expand All @@ -18,6 +22,18 @@
version "4.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.0-rc.1.tgz#7f87b7696b407476e45d6d3c1880a50d5afbb6e3"

"@angular/forms@^4.0.0-rc.1":
version "4.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.0-rc.2.tgz#6d9df97783b6023d652d97369db13d6ad6c7fa9e"

"@angular/platform-browser@^4.0.0-rc.1":
version "4.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.0-rc.2.tgz#bcca05ce85d320ee0b257640f15479b59fed20f0"

"@angular/router@^4.0.0-rc.1":
version "4.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.0.0-rc.2.tgz#66fc5be012caa38441314d0a0b9c9b6a723c471a"

"@angular/[email protected]":
version "4.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.0.0-rc.1.tgz#767ce64d70c66b9d4bd8ba1ef99a47bc02836336"
Expand Down Expand Up @@ -46,6 +62,10 @@
version "6.0.63"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.63.tgz#e08acbbd5946e0e95990b1c76f3ce5b7882a48eb"

"@types/[email protected]":
version "0.0.28"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-0.0.28.tgz#5562519bc7963caca8abf7f128cae3b594d41d06"

"@types/source-map@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@types/source-map/-/source-map-0.5.0.tgz#dd34bbd8e32fe4e74f2e3d8ac07f8aa5b45a47ac"
Expand Down Expand Up @@ -973,9 +993,9 @@ nan@^2.3.2:
version "2.5.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"

ngast@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/ngast/-/ngast-0.0.1.tgz#a63f599dbc8270773f68d4afd9d10397515d2db3"
ngast@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/ngast/-/ngast-0.0.2.tgz#faf989566b0ff608dbbe427f0cd79d77ad92836a"

node-gyp@^3.3.1:
version "3.5.0"
Expand Down

0 comments on commit d10e980

Please sign in to comment.