Slide 1

Slide 1 text

RainerHahnekamp Rainer Hahnekamp 21. August 2024 Angular Warsaw Low Hanging Fruits ESLint

Slide 2

Slide 2 text

RainerHahnekamp About Me... https://www.youtube.com/ @RainerHahnekamp https://www.ng-news.com https://github.com/softarc-consulting/sheriff ● Rainer Hahnekamp ANGULARarchitects.io NgRx Team (Trusted Collaborator) ● Developer / Trainer / Speaker @RainerHahnekamp Workshops NgRx • Testing • Spring • Quality

Slide 3

Slide 3 text

RainerHahnekamp Low Hanging Fruits

Slide 4

Slide 4 text

RainerHahnekamp Agenda ● Writing simple ESLint rules ● Understanding the Configuration

Slide 5

Slide 5 text

RainerHahnekamp Legacy and Flat Config ● How to know? ○ Flat: eslint.config.[m]js ○ Legacy: .eslintrc.json legacy ● Flat is default since angular-eslint 18

Slide 6

Slide 6 text

RainerHahnekamp MVP: Define Plugin export default { plugins: { custom: { rules: { 'capitalized-classes': { create(context) { return { ClassDeclaration(node) { context.report({node, message: "This is a class"}) } }; } } } } } }

Slide 7

Slide 7 text

RainerHahnekamp https://estools.github.io/esquery/

Slide 8

Slide 8 text

RainerHahnekamp MVP: Activate Plugin export default { plugins: { custom: { rules: { 'capitalized-classes': { create(context) { return { ClassDeclaration(node) { if (node.id.name.match(/^[a-z]/)) { context.report({node, message: "This is a class"}) } } // ...

Slide 9

Slide 9 text

RainerHahnekamp MVP: Do some checks export default { plugins: { custom: { rules: { 'capitalized-classes': { meta: { messages: { capitalizeClass: 'class names have to be capitalized' } }, create(context) { return { ClassDeclaration(node) { if (node.id.name.match(/^[a-z]/)) { context.report({ node, messageId: 'capitalizeClass' }) } // ...

Slide 10

Slide 10 text

RainerHahnekamp TypeScript Support I: Configuration Helper import tseslint from 'typescript-eslint'; export default tseslint.config()

Slide 11

Slide 11 text

RainerHahnekamp TypeScript Support I: Type Checking // @ts-check import tseslint from 'typescript-eslint'; export default tseslint.config()

Slide 12

Slide 12 text

RainerHahnekamp TypeScript Support I: Recommended ESLint rules // @ts-check import tseslint from 'typescript-eslint'; export default tseslint.config( eslint.configs.recommended )

Slide 13

Slide 13 text

RainerHahnekamp TypeScript Support I: Recommended TSESLint rules // @ts-check import tseslint from 'typescript-eslint'; import eslint from '@eslint/js' export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, )

Slide 14

Slide 14 text

RainerHahnekamp TypeScript Support I: Adding Custom Plugin // @ts-check import tseslint from 'typescript-eslint'; import eslint from '@eslint/js' export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, { plugins: { // ... }, rules: { 'custom/capitalized-classes': 'error' } })

Slide 15

Slide 15 text

RainerHahnekamp TypeScript Support I: Disabling other Rules // @ts-check import tseslint from 'typescript-eslint'; import eslint from '@eslint/js' export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, { plugins: { // ... }, rules: { 'custom/capitalized-classes': 'error', '@typescript-eslint/no-unused-vars': 'off' } })

Slide 16

Slide 16 text

RainerHahnekamp TypeScript II: Typed Rule Creator import {ESLintUtils} from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => '');

Slide 17

Slide 17 text

RainerHahnekamp TypeScript II: Typed Plugin import {ESLintUtils} from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => ''); const capitalizeClasses = createRule({ name: 'capitalize-class' })

Slide 18

Slide 18 text

RainerHahnekamp TypeScript II: Typed Plugin import {ESLintUtils} from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => ''); const capitalizeClasses = createRule({ name: 'capitalize-class', defaultOptions: [], meta: { type: 'problem', docs: {description: 'Capitalize classes'}, messages: { capitalizeClass: 'Class names have to be capitalized' }, schema: [] } })

Slide 19

Slide 19 text

RainerHahnekamp TypeScript II: Typed Plugin import {ESLintUtils} from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => ''); const capitalizeClasses = createRule({ name: 'capitalize-class', defaultOptions: [], meta: { type: 'problem', docs: {description: 'Capitalize classes'}, messages: { capitalizeClass: 'Class names have to be capitalized' }, schema: [] }, create(context) {...} })

Slide 20

Slide 20 text

RainerHahnekamp TypeScript II: Typed Plugin import {ESLintUtils} from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => ''); const capitalizeClasses = createRule({ name: 'capitalize-class', defaultOptions: [], meta: { type: 'problem', docs: {description: 'Capitalize classes'}, messages: { capitalizeClass: 'Class names have to be capitalized'

Slide 21

Slide 21 text

RainerHahnekamp TypeScript II: Typed Plugin const plugin = { plugins: { custom: { rules: { 'capitalized-classes': capitalizeClasses } } } } export = plugin

Slide 22

Slide 22 text

RainerHahnekamp TypeScript II: Typed Plugin // @ts-check import tseslint from 'typescript-eslint'; import eslint from '@eslint/js' export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, )

Slide 23

Slide 23 text

RainerHahnekamp Config const plugin = { configs: { recommended: { plugins: { custom: { rules: { 'capitalized-classes': capitalizeClasses } } }, rules: { 'custom/capitalized-classes': 'error', '@typescript-eslint/no-unused-vars': 'off' } } } } export = plugin

Slide 24

Slide 24 text

RainerHahnekamp angular-eslint typescript-eslint eslint

Slide 25

Slide 25 text

RainerHahnekamp Where to go from here? ● ESQuery ○ https://ts-ast-viewer.com/ ● Testing ● Customizations ● Fixes ● Processors ○ Angular Template ○ CSS ○ ...

Slide 26

Slide 26 text

RainerHahnekamp https://ng-poland.pl/#workshops

Slide 27

Slide 27 text

RainerHahnekamp https://github.com/rainerhahnekamp/ng-eslint

Slide 28

Slide 28 text

RainerHahnekamp Thanks