diff --git a/README.md b/README.md
index 571f1f74d..b4a03e83d 100644
--- a/README.md
+++ b/README.md
@@ -208,6 +208,7 @@ Below you can find a recommended configuration which is based on the [Angular St
"use-host-property-decorator": true,
"no-attribute-parameter-decorator": true,
"no-input-rename": true,
+ "no-on-prefix-output-name": true,
"no-output-rename": true,
"no-forward-ref": true,
"use-life-cycle-interface": true,
@@ -292,6 +293,8 @@ module.exports = {
[](https://github.com/leosvelperez) |[](https://github.com/rtfpessoa) |[](https://github.com/scttcper) |[](https://github.com/laco0416) |[](https://github.com/tmair) |[](https://github.com/cexbrayat) |
:---: |:---: |:---: |:---: |:---: |:---: |
[leosvelperez](https://github.com/leosvelperez) |[rtfpessoa](https://github.com/rtfpessoa) |[scttcper](https://github.com/scttcper) |[laco0416](https://github.com/laco0416) |[tmair](https://github.com/tmair) |[cexbrayat](https://github.com/cexbrayat) |
+[](https://github.com/eromano) | | | | | |
+[eromano](https://github.com/eromano) | | | | ||
## License
diff --git a/docs/src/worker.ts b/docs/src/worker.ts
index 61cec9838..7ef10cd8c 100644
--- a/docs/src/worker.ts
+++ b/docs/src/worker.ts
@@ -10,6 +10,7 @@ const rulesConfig = {
'use-host-property-decorator': true,
'no-input-rename': true,
'no-output-rename': true,
+ 'no-on-prefix-output-name': true,
'use-life-cycle-interface': true,
'use-pipe-transform-interface': true,
'component-class-suffix': true,
diff --git a/package.json b/package.json
index 275747ef3..e8e9c65c3 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,8 @@
"contributors": [
"Minko Gechev ",
"Preslav Semov ",
- "William Koza "
+ "William Koza ",
+ "Eugenio Romano "
],
"repository": {
"type": "git",
diff --git a/src/index.ts b/src/index.ts
index 5ba35a8ad..bf23fa263 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -11,6 +11,7 @@ export { Rule as ImportDestructuringSpacingRule } from './importDestructuringSpa
export { Rule as NoAttributeParameterDecoratorRule } from './noAttributeParameterDecoratorRule';
export { Rule as NoForwardRefRule } from './noForwardRefRule';
export { Rule as NoInputRenameRule } from './noInputRenameRule';
+export { Rule as NoOutputOnPrefixNameRule } from './noOutputOnPrefixNameRule';
export { Rule as NoOutputRenameRule } from './noOutputRenameRule';
export { Rule as NoUnusedCssRule } from './noUnusedCssRule';
export { Rule as PipeImpureRule } from './pipeImpureRule';
diff --git a/src/noOutputOnPrefixNameRule.ts b/src/noOutputOnPrefixNameRule.ts
new file mode 100644
index 000000000..ceb35181d
--- /dev/null
+++ b/src/noOutputOnPrefixNameRule.ts
@@ -0,0 +1,44 @@
+import * as Lint from 'tslint';
+import * as ts from 'typescript';
+import { sprintf } from 'sprintf-js';
+import { NgWalker } from './angular/ngWalker';
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static metadata: Lint.IRuleMetadata = {
+ ruleName: 'no-on-prefix-output-name',
+ type: 'maintainability',
+ description: `Name events without the prefix on`,
+ descriptionDetails: `See more at https://angular.io/guide/styleguide#dont-prefix-output-properties`,
+ rationale: `Angular allows for an alternative syntax on-*. If the event itself was prefixed with on
+ this would result in an on-onEvent binding expression`,
+ options: null,
+ optionsDescription: `Not configurable.`,
+ typescriptOnly: true,
+ };
+
+ static FAILURE_STRING: string = 'In the class "%s", the output ' +
+ 'property "%s" should not be prefixed with on';
+
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithWalker(
+ new OutputWalker(sourceFile,
+ this.getOptions()));
+ }
+}
+
+export class OutputWalker extends NgWalker {
+ visitNgOutput(property: ts.PropertyDeclaration, output: ts.Decorator, args: string[]) {
+ let className = (property).parent.name.text;
+ let memberName = (property.name).text;
+
+ if (memberName && memberName.startsWith('on')) {
+ let failureConfig: string[] = [className, memberName];
+ failureConfig.unshift(Rule.FAILURE_STRING);
+ this.addFailure(
+ this.createFailure(
+ property.getStart(),
+ property.getWidth(),
+ sprintf.apply(this, failureConfig)));
+ }
+ }
+}
diff --git a/test/noOutputOnPrefixNameRule.spec.ts b/test/noOutputOnPrefixNameRule.spec.ts
new file mode 100644
index 000000000..e1f2a1bcd
--- /dev/null
+++ b/test/noOutputOnPrefixNameRule.spec.ts
@@ -0,0 +1,27 @@
+import { assertSuccess, assertAnnotated } from './testHelper';
+
+describe('no-on-prefix-output-name', () => {
+ describe('invalid directive output property', () => {
+ it(`should fail, when a directive output property is named with on prefix`, () => {
+ let source = `
+ class ButtonComponent {
+ @Output() onChange = new EventEmitter();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ }`;
+ assertAnnotated({
+ ruleName: 'no-on-prefix-output-name',
+ source
+ });
+ });
+ });
+
+ describe('valid directive output property', () => {
+ it('should succeed, when a directive output property is properly named', () => {
+ let source = `
+ class ButtonComponent {
+ @Output() change = new EventEmitter();
+ }`;
+ assertSuccess('no-on-prefix-output-name', source);
+ });
+ });
+});