Skip to content

Commit

Permalink
Add rule in order to enforce the declaration of a selector (#557)
Browse files Browse the repository at this point in the history
* Add a new rule in order to enforce the declaration of a selector
  • Loading branch information
wKoza authored Apr 19, 2018
1 parent 3e10013 commit b9c899b
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/enforceComponentSelectorRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as Lint from 'tslint';
import * as ts from 'typescript';
import { sprintf } from 'sprintf-js';
import { NgWalker } from './angular/ngWalker';
import { ComponentMetadata } from './angular/metadata';

export class Rule extends Lint.Rules.AbstractRule {

public static metadata: Lint.IRuleMetadata = {
ruleName: 'enforce-component-selector',
type: 'style',
description: 'Component selector must be declared.',
rationale: 'Omit the component selector makes debugging difficult.',
options: null,
optionsDescription: 'Not configurable.',
typescriptOnly: true
};


static SELECTOR_FAILURE: string = 'The selector of the component "%s" is mandatory.';

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(
new EnforceComponentSelectorValidatorWalker(sourceFile, this));
}
}

export class EnforceComponentSelectorValidatorWalker extends NgWalker {

constructor(sourceFile: ts.SourceFile, private rule: Rule) {
super(sourceFile, rule.getOptions());
}

visitNgComponent(metadata: ComponentMetadata) {
if (!metadata.selector) {
const failureConfig: string[] = [metadata.controller.name.text];
failureConfig.unshift(Rule.SELECTOR_FAILURE);
this.generateFailure(metadata.decorator.getStart(), metadata.decorator.getWidth(), failureConfig);
}
super.visitNgComponent(metadata);
}

private generateFailure(start: number, width: number, failureConfig: string[]) {
this.addFailure(
this.createFailure(
start,
width,
sprintf.apply(this, failureConfig)));
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { Rule as ContextualLifeCycleRule } from './contextualLifeCycleRule';
export { Rule as DecoratorNotAllowedRule } from './decoratorNotAllowedRule';
export { Rule as DirectiveClassSuffixRule } from './directiveClassSuffixRule';
export { Rule as DirectiveSelectorRule } from './directiveSelectorRule';
export { Rule as EnforceComponentSelectorRule} from './enforceComponentSelectorRule';
export { Rule as I18nRule } from './i18nRule';
export { Rule as ImportDestructuringSpacingRule } from './importDestructuringSpacingRule';
export { Rule as MaxInlineDeclarationsRule } from './maxInlineDeclarationsRule';
Expand All @@ -31,6 +32,7 @@ export { Rule as UseOutputPropertyDecoratorRule } from './useOutputPropertyDecor
export { Rule as UsePipeDecoratorRule } from './usePipeDecoratorRule';
export { Rule as UsePipeTransformInterfaceRule } from './usePipeTransformInterfaceRule';
export { Rule as UseViewEncapsulationRule } from './useViewEncapsulationRule';

export * from './angular/config';

// this file exists for tslint to resolve the rules directory
Expand Down
70 changes: 70 additions & 0 deletions test/enforceComponentSelectorRule.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { assertAnnotated, assertSuccess } from './testHelper';

describe('enforceComoponentSelectorRule', () => {
it('should fail when selector is not given in @Component', () => {
let source = `
@Component()
~~~~~~~~~~~~
class Test {}`;
assertAnnotated({
ruleName: 'enforce-component-selector',
message: 'The selector of the component "Test" is mandatory.',
source
});
});

it('should fail when selector is empty in @Component', () => {
let source = `
@Component({
~~~~~~~~~~~~
selector: ''
})
~~
class Test {}`;
assertAnnotated({
ruleName: 'enforce-component-selector',
message: 'The selector of the component "Test" is mandatory.',
source
});
});


it('should fail when selector equals 0 in @Component', () => {
let source = `
@Component({
~~~~~~~~~~~~
selector: 0
})
~~
class Test {}`;
assertAnnotated({
ruleName: 'enforce-component-selector',
message: 'The selector of the component "Test" is mandatory.',
source
});
});

it('should fail when selector equals null in @Component', () => {
let source = `
@Component({
~~~~~~~~~~~~
selector: null
})
~~
class Test {}`;
assertAnnotated({
ruleName: 'enforce-component-selector',
message: 'The selector of the component "Test" is mandatory.',
source
});
});

it('should succeed when selector is given in @Component', () => {
let source = `
@Component({
selector: 'sg-bar-foo'
})
class Test {}`;
assertSuccess('enforce-component-selector', source);
});
});

0 comments on commit b9c899b

Please sign in to comment.