Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Linting Angular

Linting Angular

Following best practices is essential for the final success of any project. Unfortunately, the process of enforcing them is manual and error-prone - through code reviews. In the talk I will show how to automatically encourage best practices and verify common style by using static code analysis with codelyzer.

Minko Gechev

March 10, 2017
Tweet

More Decks by Minko Gechev

Other Decks in Programming

Transcript

  1. Enforcing Style • Fork the official style guide • Modify

    the styles according to your needs Introduce the style guide to your team Verify that each individual code change follows it
  2. Enforcing Style • Fork the official style guide • Modify

    the styles according to your needs • Introduce the style guide to your team Verify that each individual code change follows it
  3. Enforcing Style • Fork the official style guide • Modify

    the styles according to your needs • Introduce the style guide to your team • Verify that each individual code change follows it
  4. Codelyzer Rules • Components / directives name suffix • Components

    / directives selectors • Implement life cycle hook interfaces • Bind to public members • No dead CSS styles • …
  5. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  6. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  7. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  8. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  9. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  10. Codelyzer Rules Components / directives name suffix Components / directives

    selectors Implement life cycle hook interfaces Bind to public members No dead CSS styles …
  11. Lexical Analysis tokenize('const product = a * b;') [ {

    lexeme: 'const', type: 'keyword' }, { lexeme: 'product', type: 'identifier' }, { lexeme: '=', type: 'operator' }, ... ]
  12. checkConst(node) { return checkExpression(node.initializer) } checkExpression(node) { if (node instanceof

    BinOp) { return checkBinOperation(node); } ... } checkBinOperation(node) { if (isMultiplication(node)) { if (!isNumber(node.left) || !isNumber(node.right)) { throw new Error("Node must be 'number'"); } ... } }
  13. Static program analysis is the analysis of computer software that

    is performed without actually executing programs. Wikipedia
  14. Codelyzer Goals • Analyze the source code statically • Show

    warning when a style is violated • Extensible • Reuse as much as possible
  15. tsc

  16. tokenize(` @Component({ selector: '[tooltip]', template: '...' }) class TooltipComponent {

    ... } `) ); CLASS DECORATORS[0] OBJ LITERAL SELECTOR [TOOLTIP] TEMPLATE ‘…’ PROPERTY[0] PROPERTY[1]
  17. Validate Selector visitClass(node) { node.decorators.forEach(visitDecorator); } visitDecorator(node) { if (node.name

    === 'Component') { visitComponentMetadata(readMetadata(node)) } } visitComponentMetadata(metadata) { if (!parseSelector(metadata.selector).element) { tslint.report('Components should have element selectors'); } }
  18. @Component({ selector: 'header-cmp', template: ` <header> <h1>{{ title }}</h1> <div

    class="greeting">Hello <span>{{ name }}</span>!</div> </header> `, styles: [` h1 { font-size: 25px; } .greeting spam { color: red; } `] }) class HeaderComponent { ... }
  19. @Component({ selector: 'header-cmp', template: ` <header> <h1>{{ title }}</h1> <div

    class="greeting">Hello <span>{{ name }}</span>!</div> </header> `, styles: [` h1 { font-size: 25px; } .greeting spam { color: red; } `] }) class HeaderComponent { ... }
  20. CLASS DECORATORS[0] OBJ LITERAL SELECTOR HEADER-CMP TEMPLATE TEMPLATE AST PROPERTY[0]

    PROPERTY[1] PROPERTY[2] STYLE ASTS STYLES ROOTS CSS RULES
  21. Current Limitations • Linting per file - not entire program

    • Doesn’t perform type checking • Will fail when selector is variable
  22. import { Component as NgComponent } from '@angular/core'; const selector

    = '[tool' + 'tip]'; @NgComponent({ selector, template: '...' }) class TooltipComponent { ... }
  23. import { Component as NgComponent } from '@angular/core'; const selector

    = '[tool' + 'tip]'; @NgComponent({ selector, template: '...' }) class TooltipComponent { ... }
  24. import { Component as NgComponent } from '@angular/core'; const selector

    = '[tool' + 'tip]'; @NgComponent({ selector, template: '...' }) class TooltipComponent { ... }
  25. ngast • Provides high-level API to the compiler • Allows:

    • Deep metadata collection • Static evaluation of foldable expressions
  26. import { Component as NgComponent } from '@angular/core'; const selector

    = '[tool' + 'tip]'; @NgComponent({ selector, template: '...' }) class TooltipComponent { ... }
  27. import { Component as NgComponent } from '@angular/core'; const selector

    = '[tool' + 'tip]'; @NgComponent({ selector, template: '...' }) class TooltipComponent { ... } { "isHost": false, "isComponent": true, "selector": "[tooltip]", "changeDetection": 1, "inputs": {}, "outputs": {}, "template": { "template": "...", ... }, ... }