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. twitter.com/mgechev
    github.com/mgechev
    blog.mgechev.com
    Linting Angular

    View Slide

  2. View Slide

  3. twitter.com/mgechev
    github.com/mgechev

    View Slide

  4. View Slide

  5. View Slide

  6. Agenda
    History
    Codelyzer
    How codelyzer works?
    Compilers 101
    Future plans

    View Slide

  7. History

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. Applying The Style Guide
    In Your Project

    View Slide

  12. Enforcing Style

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. Core Review Process

    View Slide

  17. View Slide

  18. ‣ Manual
    ‣ Boring
    ‣ Error Prone

    View Slide

  19. codelyzer

    View Slide

  20. Codelyzer is a project which aims to
    enforce common style using static
    code analysis

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. View Slide

  29. View Slide

  30. How Codelyzer Works?

    View Slide

  31. Compiler
    COMPILER
    Input Output

    View Slide

  32. Compiler
    COMPILER




    Input Output
    FRONT-END BACK-END

    View Slide

  33. FRONT-END
    Compiler
    Input Output

    View Slide

  34. FRONT-END




    Compiler
    Input Output
    LEXICAL
    ANALYSIS
    SYNTAX
    ANALYSIS
    … SEMANTIC
    ANALYSIS

    View Slide

  35. Lexical Analysis
    tokenize('const product = a * b;')

    View Slide

  36. Lexical Analysis
    tokenize('const product = a * b;')

    View Slide

  37. Lexical Analysis
    tokenize('const product = a * b;')
    [
    { lexeme: 'const', type: 'keyword' },
    { lexeme: 'product', type: 'identifier' },
    { lexeme: '=', type: 'operator' },
    ...
    ]

    View Slide

  38. Syntax Analysis
    parse(tokenize('...'))

    View Slide

  39. Syntax Analysis
    parse(tokenize('...'))

    View Slide

  40. Syntax Analysis
    parse(tokenize('...'))
    CONST
    PRODUCT *
    A B

    View Slide

  41. Syntax Analysis
    parse(tokenize('...'))
    CONST
    PRODUCT *
    A B
    AST

    View Slide

  42. Semantic Analysis
    typeCheck(parse(tokenize('...')))

    View Slide

  43. 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'");
    }
    ...
    }
    }

    View Slide

  44. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  45. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  46. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  47. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  48. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  49. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  50. checkConst(node)
    checkExpression(node)
    checkBinOperation(node)
    isNumber(node)
    CONST
    PRODUCT *
    A B

    View Slide

  51. View Slide

  52. Static program analysis is the analysis of computer
    software that is performed without actually executing
    programs.
    Wikipedia

    View Slide

  53. codelyzer

    View Slide

  54. Codelyzer Goals
    • Analyze the source code statically
    • Show warning when a style is violated
    • Extensible
    • Reuse as much as possible

    View Slide

  55. tsc

    View Slide

  56. tslint

    View Slide

  57. Scott Wu
    Alex Eagle Martin Probst

    View Slide

  58. View Slide

  59. tsc.parse(
    tokenize(`
    @Component({
    selector: '[tooltip]',
    template: '...'
    })
    class TooltipComponent { ... }
    `)
    );

    View Slide

  60. ast = tsc.parse(
    tsc.tokenize(`
    @Component({
    selector: '[tooltip]',
    template: '...'
    })
    class TooltipComponent { ... }
    `)
    );

    View Slide

  61. ast = tsc.parse(
    tsc.tokenize(`
    @Component({
    selector: '[tooltip]',
    template: '...'
    })
    class TooltipComponent { ... }
    `)
    );

    View Slide

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

    View Slide

  63. tokenize(`
    @Component({
    selector: '[tooltip]',
    template: '...'
    })
    class TooltipComponent { ... }
    `)
    );
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  64. 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');
    }
    }

    View Slide

  65. Validate Selector
    visitClass(node)
    visitDecorator(node)
    visitComponentMetadata(metadata)
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  66. Validate Selector
    visitClass(node)
    visitDecorator(node)
    visitComponentMetadata(metadata)
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  67. Validate Selector
    visitClass(node)
    visitDecorator(node)
    visitComponentMetadata(metadata)
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  68. Validate Selector
    visitClass(node)
    visitDecorator(node)
    visitComponentMetadata(metadata)
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  69. Validate Selector
    visitClass(node)
    visitDecorator(node)
    visitComponentMetadata(metadata)
    CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR [TOOLTIP] TEMPLATE ‘…’
    PROPERTY[0] PROPERTY[1]

    View Slide

  70. No Dead CSS

    View Slide

  71. @Component({
    selector: 'header-cmp',
    template: `

    {{ title }}
    Hello {{ name }}!

    `,
    styles: [`
    h1 {
    font-size: 25px;
    }
    .greeting spam {
    color: red;
    }
    `]
    })
    class HeaderComponent { ... }

    View Slide

  72. @Component({
    selector: 'header-cmp',
    template: `

    {{ title }}
    Hello {{ name }}!

    `,
    styles: [`
    h1 {
    font-size: 25px;
    }
    .greeting spam {
    color: red;
    }
    `]
    })
    class HeaderComponent { ... }

    View Slide

  73. CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR
    HEADER-CMP
    TEMPLATE ‘…’
    PROPERTY[0]
    PROPERTY[1]
    PROPERTY[2]
    [‘…’]
    STYLES

    View Slide

  74. CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR
    HEADER-CMP
    TEMPLATE ‘…’
    PROPERTY[0]
    PROPERTY[1]
    PROPERTY[2]
    [‘…’]
    STYLES

    View Slide

  75. Strings Are Hard
    To Analyze

    View Slide

  76. CLASS
    DECORATORS[0]
    OBJ LITERAL
    SELECTOR
    HEADER-CMP
    TEMPLATE TEMPLATE
    AST
    PROPERTY[0]
    PROPERTY[1]
    PROPERTY[2]
    STYLE
    ASTS
    STYLES
    ROOTS
    CSS RULES

    View Slide

  77. View Slide

  78. View Slide

  79. Current Limitations
    • Linting per file - not entire program
    • Doesn’t perform type checking
    • Will fail when selector is variable

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  83. ngast
    github.com/mgechev/ngast

    View Slide

  84. ngast
    • Provides high-level API to the compiler
    • Allows:
    • Deep metadata collection
    • Static evaluation of foldable expressions

    View Slide

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

    View Slide

  86. 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": "...",
    ...
    },
    ...
    }

    View Slide

  87. Codelyzer 3
    • Deep metadata collection
    • Autofixes
    • Much more!

    View Slide

  88. Thank you!
    twitter.com/mgechev
    github.com/mgechev
    blog.mgechev.com

    View Slide