Skip to content

Commit

Permalink
feat(element-type): new rule
Browse files Browse the repository at this point in the history
  • Loading branch information
d0whc3r committed Sep 15, 2019
1 parent 1bfecdc commit eedbf5d
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/element-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# element-type

Ensures that all `@Element()` are typed correctly

## Config

No config is needed

## Usage

```json
{ "@d0whc3r/stencil/element-type": "error" }
```
49 changes: 49 additions & 0 deletions src/rules/element-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Rule } from 'eslint';
import ts from 'typescript';
import { getDecorator, getType, parseDecorator, stencilComponentContext } from '../utils';

const rule: Rule.RuleModule = {
meta: {
docs: {
description: 'This rule catches Stencil Element type not matching tag name.',
category: 'Possible Errors',
recommended: true
},
schema: []
},

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

function parseTag(tag: string) {
let result = tag[0].toUpperCase() + tag.slice(1);
const tagBody = tag.split('-');
if (tagBody.length > 1) {
result = tagBody.map((tpart) => tpart[0].toUpperCase() + tpart.slice(1)).join('');
}
return result;
}

const parserServices = context.parserServices;
return {
...stencil.rules,
'ClassProperty': (node: any) => {
if (stencil.isComponent() && getDecorator(node, 'Element')) {
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node) as ts.Node;
const tagType = getType(originalNode);
const component = getDecorator(node.parent.parent, 'Component');
const [{ tag }] = parseDecorator(component);
const parsedTag = `HTML${parseTag(tag)}Element`;
if (tagType !== parsedTag) {
context.report({
node: node,
message: `@Element type is not matching tag for component (${parsedTag})`
});
}
}
}
};
}
};

export default rule;
5 changes: 5 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,8 @@ export function stencilComponentContext() {
}
};
}

export function getType(originalNode: ts.Node) {
const type = originalNode.getText().split(':')[1];
return type && type.trim().replace(';', '');
}
11 changes: 11 additions & 0 deletions tests/lib/rules/element-type/element-type.good.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@Component({ tag: 'sample-tag' })
export class TheSampleTag {
/**
* Element: is the element
*/
@Element() theElement!: HTMLSampleTagElement;

render() {
return (<div>test</div>);
}
}
27 changes: 27 additions & 0 deletions tests/lib/rules/element-type/element-type.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import rule from '../../../../src/rules/element-type';
import { ruleTester } from '../rule-tester';
import * as path from 'path';
import * as fs from 'fs';

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

invalid: [
{
code: fs.readFileSync(files.wrong, 'utf8'),
filename: files.wrong,
errors: 1
}
]
});
});
11 changes: 11 additions & 0 deletions tests/lib/rules/element-type/element-type.wrong.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@Component({ tag: 'sample-tag' })
export class TheSampleTag {
/**
* Element: is the element
*/
@Element() theElement!: HTMLElement;

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

0 comments on commit eedbf5d

Please sign in to comment.