From e9f4d2303c5b95cf8623ac91b114c6428e832ef0 Mon Sep 17 00:00:00 2001 From: Rafael Santana Date: Mon, 23 Apr 2018 02:53:23 -0300 Subject: [PATCH] feat(rule): add no-queries-parameter rule (#571) --- src/index.ts | 1 + src/noQueriesParameterRule.ts | 33 ++++++++++++++ test/noQueriesParameterRule.spec.ts | 69 +++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 src/noQueriesParameterRule.ts create mode 100644 test/noQueriesParameterRule.spec.ts diff --git a/src/index.ts b/src/index.ts index f8a530189..ccf0d01d9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,7 @@ export { Rule as NoLifeCycleCallRule } from './noLifeCycleCallRule'; export { Rule as NoOutputNamedAfterStandardEventRule } from './noOutputNamedAfterStandardEventRule'; export { Rule as NoOutputOnPrefixRule } from './noOutputOnPrefixRule'; export { Rule as NoOutputRenameRule } from './noOutputRenameRule'; +export { Rule as NoQueriesParameterRule } from './noQueriesParameterRule'; export { Rule as NoUnusedCssRule } from './noUnusedCssRule'; export { Rule as PipeImpureRule } from './pipeImpureRule'; export { Rule as PipeNamingRule } from './pipeNamingRule'; diff --git a/src/noQueriesParameterRule.ts b/src/noQueriesParameterRule.ts new file mode 100644 index 000000000..18b9c941a --- /dev/null +++ b/src/noQueriesParameterRule.ts @@ -0,0 +1,33 @@ +import * as Lint from 'tslint'; +import { UsePropertyDecorator } from './propertyDecoratorBase'; + +export class Rule extends UsePropertyDecorator { + static metadata: Lint.IRuleMetadata = { + description: + 'Use @ContentChild, @ContentChildren, @ViewChild or @ViewChildren instead of the `queries` property of ' + + '`@Component` or `@Directive` metadata.', + options: null, + optionsDescription: 'Not configurable.', + rationale: + 'The property associated with `@ContentChild`, `@ContentChildren`, `@ViewChild` or `@ViewChildren` ' + + `can be modified only in a single place: in the directive's class. If you use the \`queries\` metadata ` + + 'property, you must modify both the property declaration inside the controller, and the metadata ' + + 'associated with the directive.', + ruleName: 'no-queries-parameter', + type: 'style', + typescriptOnly: true + }; + + static FAILURE_STRING = 'Use @ContentChild, @ContentChildren, @ViewChild or @ViewChildren instead of the queries property'; + + constructor(options: Lint.IOptions) { + super( + { + decoratorName: ['ContentChild', 'ContentChildren', 'ViewChild', 'ViewChildren'], + propertyName: 'queries', + errorMessage: Rule.FAILURE_STRING + }, + options + ); + } +} diff --git a/test/noQueriesParameterRule.spec.ts b/test/noQueriesParameterRule.spec.ts new file mode 100644 index 000000000..77f7923f6 --- /dev/null +++ b/test/noQueriesParameterRule.spec.ts @@ -0,0 +1,69 @@ +import { Rule } from '../src/noQueriesParameterRule'; +import { assertAnnotated, assertSuccess } from './testHelper'; + +type MetadataType = 'Component' | 'Directive'; + +const { FAILURE_STRING, metadata: { ruleName } } = Rule; +const metadataTypes = new Set(['Component', 'Directive']); + +describe(ruleName, () => { + metadataTypes.forEach(metadataType => { + describe(metadataType, () => { + describe('failure', () => { + it('should fail when "queries" is used', () => { + const source = ` + @${metadataType}({ + queries: { + ~~~~~~~~ + contentChildren: new ContentChildren(ChildDirective), + viewChildren: new ViewChildren(ChildDirective) + } + ~ + }) + class Test {} + `; + assertAnnotated({ + message: FAILURE_STRING, + ruleName, + source + }); + }); + + it('should fail when "queries" is used', () => { + const source = ` + @${metadataType}({ + queries: { + ~~~~~~~~ + contentChild: new ContentChild(ChildDirective), + viewChild: new ViewChild(ChildDirective) + } + ~ + }) + class Test {} + `; + assertAnnotated({ message: FAILURE_STRING, ruleName, source }); + }); + }); + + describe('success', () => { + it('should succeed when "queries" is not used', () => { + const source = ` + @${metadataType}({}) + class Test {} + `; + assertSuccess(ruleName, source); + }); + + it('should succeed when content decorators are used', () => { + const source = ` + @${metadataType}({}) + class Test { + @ContentChild(ChildDirective) contentChild: ChildDirective; + } + `; + assertSuccess(ruleName, source); + }); + }); + }); + }); +});