Giter Club home page Giter Club logo

eslint-plugin-criteo's Introduction

eslint-plugin-criteo

This ES lint plugin defines custom rules we use at Criteo and exposes a recommended set of rules.

Installation

npm install eslint eslint-plugin-criteo --save-dev

Like any library, keep it updated to make sure your project follows the latest coding recommendations.

Usage

Add criteo to the plugins section of your .eslintrc configuration file and apply:

  • plugin:criteo/recommended-angular-app if the project is an Angular application (formerly, now deprecated, recommended-app)
  • plugin:criteo/recommended-angular-lib if the project is an Angular library (formerly, now deprecated, recommended-lib)
  • plugin:criteo/recommended-react-app if the project is a React application
  • plugin:criteo/recommended-react-lib if the project is a React library
  • plugin:criteo/recommended for general-purpose rules which are included in all the above recommended configs
  • plugin:criteo/recommended-angular-template for Angular HTML templates (formerly, now deprecated, recommended-template)
{
  "plugins": ["criteo"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": ["plugin:criteo/recommended-angular-app"], // or recommended-react-app, recommended-angular-lib...
      [...]
    },
    {
      "files": ["*.html"],
      "extends": ["plugin:criteo/recommended-angular-template"],
      [...]
    }
  ]
}

Then configure/disable the rules under the rules section, following your project's context and constraints.

{
  "rules": {
    "criteo/rule-1": ["error", { "custom-config-key": "custom-config-value" }],
    "criteo/rule-2": "off"
  }
}

Pre-commit Git hook

As a developer, it can be very frustrating to get a change rejected by QA bot because the code does not respect an ES lint rule. Even more if the failing rule has an auto-fix! To avoid it, you can configure your project to run ES lint automatically on pre-commit. Because it will target only staged files, it is quite fast!

  1. Define the pre-commit hook script .git-hook-lint-staged at the root of the first/main UI project of the repository. It will make it available to all developers since Git hooks cannot be pushed directly.
#!/bin/sh
cd project1 && npx lint-staged \
  && cd ../project2 && npx lint-staged # If the repository hosts several projects, add each of them
  1. Also define the postinstall NPM script in the scripts section of package.json:
"scripts": {
  "postinstall": "npx shx cp .git-hook-lint-staged ../.git/hooks/lint-staged"
}

It will enable the hook automatically after npm install.

  1. Still in the same project, install shx to make the copy command work on all environments: npm install --save-dev shx

  2. Install lint-staged in each UI project of the repository: npm install --save-dev lint-staged

  3. In each project, also define its configuration by declaring in package.json:

"lint-staged": {
  "**.{ts,component.html}": "eslint --fix"
}

You can also declare "*.{ts,component.html,json,scss}": "prettier --write" if you use Prettier.

๐Ÿ™Œ

Criteo rules

cypress-no-force

Cypress: disallow using of 'force: true' option.

Why?

The Cypress ESLint plugin provides this rule, with the following explanation:

Using force: true on inputs appears to be confusing rather than helpful. It usually silences the actual problem instead of providing a way to overcome it. See Cypress Core Concepts.

Unfortunately, the Cypress plugin's version only works when calling the function like cy.get(...).click({ force: true }), i.e. with cy in the same line. But in our code we often use helper functions, so the rule does not detect our usages. This new version of the rule leverages TypeScript to work out if the method is called on a Cypress.Chainable object.

filename

Ensure file names are valid and consistent.

Config:

  • pattern: allowed name pattern (default: /^[a-z0-9\.\-]+$/)

Why?

Improve the file tree readability. For example, a component called MyButtonComponent should be defined under the path */my-button/my-button.component.{html,ts,css,scss,...}.

filename-match-export

Ensure file names reflect the named exported members.

Config:

  • removeFromFilename: text removed from file names before check (default: [])

Why?

The name of the file should describe its content for readability.

independent-folders

Ensure feature folders are independent by preventing imports between each other. Features folders imports are also forbidden from the shared modules.

Config:

  • basePath: base path (from the project) applied to all featureFolders and sharedFolders (default: ./)
  • featureFolders: array of the feature folders paths that should remain independent (default: [])
  • sharedFolders: array of the shared folders paths that cannot use any feature folder (default: [])

Sample :

'criteo/independent-folders': [
  'error',
  {
    basePath: './src/app/',
    featureFolders: ['broad-targeting-audience', 'similar-audience'],
    sharedFolders: ['shared'],
  },
],

Why?

To avoid messy and circular imports:

  • Feature folders should be independent or use each others using the public API.
  • Shared folders should not rely on feature folders.

ngx-component-display

Ensure Angular components have a display property set.

Config:

  • ignore: classes whose names match this regular expression (defined as string) will be ignored (default: '^.*DialogComponent$')
  • propertyName: name of the display property (default: 'cdsDisplay')

Why?

  • By default, custom components are displayed as inline by the browser which is rarely what we expect. For example, it makes impossible to define a width or margins on them. E.g. <my-component class="w-100 cds-mb-3"></my-component> would have no effect.
  • The workaround of wrapping them in <div></div> should be avoided to not make the DOM and the bundle file heavier.

ngx-no-styles-in-component

Forbid using styles or styleUrls in a component's metadata: favour the Criteo design system instead.

Why?

To maintain a coherent user experience, we aim to use the classes and components from the shared component library as much as possible, as opposed to custom styles.

no-ngxs-select-decorator

Forbid using the NGXS @Select() decorator: @ViewSelectSnapshot() should be preferred.

Why?

@Select() exposes an Observable that must be subscribed using the async pipe in the template. It makes it verbose and creates one subscription per usage. However, the @ViewSelectSnapshot() exposes the raw value directly and refreshes the view on every change ; making it more concise and easier to use.

no-null-undefined-comparison

Forbid comparisons with null and undefined.

Why?

The difference between null and undefined is specific to Javascript and can be tricky for juniors/backend developers. Most of the time, we don't need to distinguish these 2 values, so using isNil from lodash is safer.

ngxs-selector-array-length

Ensure that when using the @Selector() decorator, the number of selectors passed in matches the number of arguments passed to the selector function.

Why?

It can be tricky to pin down the source of an error when using the @Selector() decorator. While this rule can't make sure you put all the parameters in the right order, it does avoid the most obvious mistakes.

no-spreading-accumulators

Ensure that reducers do not mistakenly have O(n^2) complexity.

Why?

When using .reduce(), it may be tempting to do something like this for the sake of brevity:

const mappedById = myArray.reduce((acc, entity) => ({ ...acc, [entity.id]: entity }), {});

However, spreading the accumulator at every iteration results in an operation with O(n^2) time & spatial complexity.

This rule helps ensure that .reduce() is an O(n) operation. For example:

const mappedById = myArray.reduce((acc, entity) => { acc[entity.id] = entity; return acc; }, {});

no-todo-without-ticket

Ensure that comments with TODO or FIXME specify a JIRA ticket in which the work will be completed.

Why?

Commits with TODO comments indicating that a portion of functionality has yet to be implemented are easy to overlook later on. This rule encourages all outstanding work to be tracked by an external ticket as well as in code comments.

prefer-readonly-decorators

Ensure that appropriate decorated properties are readonly.

Config:

  • decorators: array of decorator names that should always be readonly (default: ['Output', 'ViewSelectSnapshot'])

Why?

Some decorated properties should be readonly because their decorator handles the value assignment and should never be assigned by hand (ex: @ViewSelectSnapshot()), or their value should never change (ex: @Output()) to prevent errors.

until-destroy

Ensure @UntilDestroy() decorator is not forgotten, nor applied when it is not necessary.

Why?

@UntilDestroy() defines mandatory properties in decorated class to make the untilDestroyed operator work. Nevertheless, the decorator should not be applied when it is not necessary because it would inject useless code.

External rules

In addition to the rules defined above, we have chosen some rules from external libraries which we activate by default. Some of these have custom config to better address our specific use cases.

Library Rule name Documentation Applies to Angular applications Applies to Angular libraries Applies to React applications Applies to React library
ESLint All recommended ESLint rules https://eslint.org/docs/rules/ โœ… โœ… โœ… โœ…
TypeScript All recommended rules from @typescript-eslint https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin#supported-rules โœ… โœ… โœ… โœ…
Angular All recommended rules from @angular-eslint https://github.com/angular-eslint/angular-eslint/tree/master/packages/eslint-plugin/docs/rules โœ… โœ…
Angular @angular-eslint/no-lifecycle-call https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin/docs/rules/no-lifecycle-call.md โœ… โœ…
Angular @angular-eslint/no-pipe-impure https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin/docs/rules/no-pipe-impure.md โœ… โœ…
Angular @angular-eslint/prefer-on-push-component-change-detection https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md โœ… โœ…
Angular @angular-eslint/relative-url-prefix https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin/docs/rules/relative-url-prefix.md โœ… โœ…
Angular @angular-eslint/use-component-view-encapsulation https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin/docs/rules/use-component-view-encapsulation.md โœ… โœ…
Angular All recommended rules from @angular-eslint/template https://github.com/angular-eslint/angular-eslint/tree/master/packages/eslint-plugin-template/docs/rules โœ… โœ…
Angular @angular-eslint/template/accessibility-alt-text https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/accessibility-alt-text.md โœ… โœ…
Angular @angular-eslint/template/accessibility-elements-content https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md โœ… โœ…
Angular @angular-eslint/template/accessibility-label-has-associated-control https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/accessibility-label-has-associated-control.md โœ… โœ…
Angular @angular-eslint/template/accessibility-valid-aria https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/accessibility-valid-aria.md โœ… โœ…
Angular @angular-eslint/template/no-call-expression https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/no-call-expression.md โœ… โœ…
Angular @angular-eslint/template/no-duplicate-attributes https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md โœ… โœ…
Angular @angular-eslint/template/no-positive-tabindex https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/no-positive-tabindex.md โœ… โœ…
Angular @angular-eslint/template/use-track-by-function https://github.com/angular-eslint/angular-eslint/blob/master/packages/eslint-plugin-template/docs/rules/use-track-by-function.md โœ… โœ…
Cypress All recommended rules from cypress https://github.com/cypress-io/eslint-plugin-cypress#rules โœ… โœ…
eslint-comments All recommended rules from eslint-comments https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/ โœ… โœ… โœ… โœ…
eslint-comments eslint-comments/require-description https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/require-description.html โœ… โœ… โœ… โœ…
eslint-comments eslint-comments/disable-enable-pair (custom config to allow whole-file disables) https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/disable-enable-pair.html โœ… โœ… โœ… โœ…
RxJS All recommended rules from rxjs https://github.com/cartant/eslint-plugin-rxjs#rules โœ… โœ…
RxJS rxjs-angular/prefer-takeuntil (with custom config) https://github.com/cartant/eslint-plugin-rxjs-angular/blob/main/docs/rules/prefer-takeuntil.md โœ… โœ…
RxJS rxjs/finnish (with custom config) https://github.com/cartant/eslint-plugin-rxjs/blob/main/docs/rules/finnish.md โœ… โœ…
RxJS rxjs/no-unsafe-takeuntil (with custom config) https://github.com/cartant/eslint-plugin-rxjs/blob/main/docs/rules/no-unsafe-takeuntil.md โœ… โœ…
no-only-tests no-only-tests/no-only-tests https://github.com/levibuzolic/eslint-plugin-no-only-tests#usage โœ… โœ… โœ… โœ…
simple-import-sort simple-import-sort/exports https://github.com/lydell/eslint-plugin-simple-import-sort โœ… โœ… โœ… โœ…
simple-import-sort simple-import-sort/imports (with custom config) https://github.com/lydell/eslint-plugin-simple-import-sort โœ… โœ… โœ… โœ…
React Hooks All rules https://github.com/lydell/eslint-plugin-simple-import-sort โœ… โœ…
React All recommended rules from react https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/hook-use-state https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/no-arrow-function-lifecycle https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/no-invalid-html-attribute https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/self-closing-comp https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/jsx-no-useless-fragment https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/jsx-closing-bracket-location https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/jsx-boolean-value https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/no-unused-state https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…
React react/jsx-key. https://github.com/jsx-eslint/eslint-plugin-react โœ… โœ…

eslint-plugin-criteo's People

Contributors

benjaminchad avatar connorullmann avatar joepikowski avatar kamaradclimber avatar s-lee avatar xavierdupessey avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-plugin-criteo's Issues

Move dependent ESLint plugins to peerDependencies

In some cases, plugins referenced in a config file cannot be found because the plugin is not present in the top level of node_modules.

I'm not clear on the specifics of when a dependency ends up directly in node_modules, and when it ends up in node_modules/eslint-plugin-criteo/node_modules. But we can fix this using by moving our dependent ESLint plugins to peerDependencies, instead of dependencies. This way they should always be installed directly in the top level.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.