Skip to content

Commit

Permalink
feat(own-props-must-be-private): add new rule
Browse files Browse the repository at this point in the history
  • Loading branch information
d0whc3r committed Sep 15, 2019
1 parent ce7b1d3 commit cf18c53
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ This rule catches Stencil Methods marked as private or protected.

This rule catches Stencil Watchs with non existing Props or States.

- [`@d0whc3r/stencil/own-props-must-be-private`](./docs/own-props-must-be-private.md)

This rule catches own class properties marked as public.

- [`@d0whc3r/stencil/prefer-vdom-listener`](./docs/prefer-vdom-listener.md)

This rule catches Stencil Listen with vdom events.
Expand Down Expand Up @@ -116,6 +120,7 @@ This rule catches modules that expose more than just the Stencil Component itsel
"@d0whc3r/stencil/host-data-deprecated": "error",
"@d0whc3r/stencil/methods-must-be-public": "error",
"@d0whc3r/stencil/no-unused-watch": "error",
"@d0whc3r/stencil/own-props-must-be-private": "error",
"@d0whc3r/stencil/prefer-vdom-listener": "error",
"@d0whc3r/stencil/props-must-be-public": "error",
"@d0whc3r/stencil/props-must-be-readonly": "error",
Expand Down
15 changes: 15 additions & 0 deletions docs/own-props-must-be-private.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# own-props-must-be-private

Ensures that all own class properties are private.

## Config

No config is needed

## Usage

```json
{ "@d0whc3r/stencil/own-props-must-be-private": "error" }
```

> Fix included
1 change: 1 addition & 0 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default {
'@d0whc3r/stencil/host-data-deprecated': 'error',
'@d0whc3r/stencil/methods-must-be-public': 'error',
'@d0whc3r/stencil/no-unused-watch': 'error',
'@d0whc3r/stencil/own-props-must-be-private': 'error',
'@d0whc3r/stencil/prefer-vdom-listener': 'error',
'@d0whc3r/stencil/props-must-be-public': 'error',
'@d0whc3r/stencil/props-must-be-readonly': 'error',
Expand Down
2 changes: 2 additions & 0 deletions src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import elementType from './element-type';
import hostDataDeprecated from './host-data-deprecated';
import methodsMustBePublic from './methods-must-be-public';
import noUnusedWatch from './no-unused-watch';
import ownPropsMustBePrivate from './own-props-must-be-private';
import preferVdomListener from './prefer-vdom-listener';
import propsMustBePublic from './props-must-be-public';
import propsMustBeReadonly from './props-must-be-readonly';
Expand All @@ -26,6 +27,7 @@ export default {
'host-data-deprecated': hostDataDeprecated,
'methods-must-be-public': methodsMustBePublic,
'no-unused-watch': noUnusedWatch,
'own-props-must-be-private': ownPropsMustBePrivate,
'prefer-vdom-listener': preferVdomListener,
'props-must-be-public': propsMustBePublic,
'props-must-be-readonly': propsMustBeReadonly,
Expand Down
46 changes: 46 additions & 0 deletions src/rules/own-props-must-be-private.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Rule } from 'eslint';
import { isPrivate, stencilComponentContext, stencilDecorators } from '../utils';

const rule: Rule.RuleModule = {
meta: {
docs: {
description: 'This rule catches own class attributes marked as public.',
category: 'Possible Errors',
recommended: true
},
schema: [],
type: 'problem',
fixable: 'code'
},

create(context): Rule.RuleListener {
const stencil = stencilComponentContext();

const parserServices = context.parserServices;
return {
...stencil.rules,
'ClassProperty': (node: any) => {
if (!stencil.isComponent()) {
return;
}
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const stencilDecorator = originalNode.decorators && originalNode.decorators.some(
(dec: any) => stencilDecorators.includes(dec.expression.expression.escapedText));
if (!stencilDecorator && !isPrivate(originalNode)) {
const text = String(originalNode.getFullText());
context.report({
node: node,
message: `Own class properties cannot be public`,
fix(fixer) {
const varName = node.key.name;
const result = text.replace('public ', '').replace(varName, `private ${varName}`);
return fixer.replaceText(node, result);
}
});
}
}
};
}
};

export default rule;
4 changes: 3 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,7 @@ export function stencilComponentContext() {
}

export function getType(node: any) {
return node.typeAnnotation.typeAnnotation.typeName.name
return node.typeAnnotation.typeAnnotation.typeName.name;
}

export const stencilDecorators = ['Component', 'Prop', 'State', 'Watch', 'Element', 'Method', 'Event', 'Listen'];
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@Component({ tag: 'sample-tag' })
export class SampleTag {
private internalProp: string;
@OwnDecorator() private internalDecoratedProp: string;

@Prop() readonly test?: string;
@Prop({ mutable: true }) testMutable?: string;

render() {
return (<div>test</div>);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import rule from '../../../../src/rules/own-props-must-be-private';
import { ruleTester } from '../rule-tester';
import * as path from 'path';
import * as fs from 'fs';

describe('stencil rules', () => {
const files = {
good: path.resolve(__dirname, 'own-props-must-be-private.good.tsx'),
wrong: path.resolve(__dirname, 'own-props-must-be-private.wrong.tsx')
};
ruleTester.run('own-props-must-be-private', rule, {
valid: [
{
code: fs.readFileSync(files.good, 'utf8'),
filename: files.good
}
],

invalid: [
{
code: fs.readFileSync(files.wrong, 'utf8'),
filename: files.wrong,
errors: 4
}
]
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@Component({ tag: 'sample-tag' })
export class SampleTag {
internalProp: string;
public internalProp2 = 1;
@OwnDecorator() public internalDecoratedProp: string;
@OwnDecorator() internalDecoratedProp2: string;

@Prop() readonly test?: string;
@Prop({ mutable: true }) testMutable?: string;


render() {
return (<div>test</div>);
}
}

0 comments on commit cf18c53

Please sign in to comment.