eslint-plugin-tachyon
v32.0.0
Published
Custom ESLint rules for the Tachyon Monorepo
Downloads
3
Readme
Tachyon Lint Rules
Lint rules developed for both the Tachyon repo and Twitch in general.
Exports Configs / Presets
While this plugin contains some rules that are mostly specific to the Tachyon
monorepo, some of the rules are generally applicable to any modern Twitch
project. These general rules are exported under the tachyon:recommended config
(aka preset). It includes the following rules:
- no-bare-react-relay-import
- no-mock-clear
- no-react-import
- no-use-immutable-callback
- styled-components-prefix
To use this config, add it the extends list of an ESLint config:
extends: [
'plugin:tachyon/recommended',
],Rules
bowser-parser-fallback
This rule forces all usages of Bowser.getParser to include a fallback to the
value BOWSER_USER_AGENT_FALLBACK. Bowser will throw an error when getParser
receives a falsy value (including empty strings), which can happen on the
server, in test environments, or on weird devices. This makes usage safer and
eliminates the need for any other special casing or error handling.
Bowser.getParser(window.navigator.userAgent); // error
Bowser.getParser(window.navigator.userAgent || BOWSER_USER_AGENT_FALLBACK); // allowedno-aliased-absolute-imports
This rule is meant to help ensure valid imports in Tachyon apps and packages, which prefer relative imports and barrels instead of aliased absolute paths. It does still allow relative imports from outside of the main code directory, which is useful for things like integration tests outside of that directory.
It takes a configuration of an array of the names of any root directories you want to target:
'tachyon/no-aliased-absolute-imports': ['error', ['src']]which would result in:
import { foo } from 'src/foo'; // error
import { foo } from '../foo'; // allowed
import { foo } from '../src/foo'; // allowed (for things like tests outside of src)no-bare-react-relay-import (recommended)
This rule is meant to help with the transition from the legacy React-Relay
container HOCs to the new hooks APIs. It forces imports to either come from
react-relay/hooks or react-relay/legacy so you can more easily track
conversion progress.
import { useFragment, createFragmentContainer } from 'react-relay'; // error
import { useFragment } from 'react-relay/hooks'; // allowed
import { createFragmentContainer } from 'react-relay/legacy'; // allowedno-disable-rules
This rule disables the ability to disable a target set of other ESLint rules. It is meant to either ban disabling certain rules entirely, or to force such overrides to happen via allowlisting in the central ESLint config (which can then more easily interact with things like codeowners to escalate reviews). It works for all of the various ESLint magic comments.
It takes a configuration of an object, with the rules key specifying the names
of any lint rules you want to target:
'tachyon/no-disable-rules': ['error', {
rules: ['react/no-danger'],
}]which would result in:
// eslint-disable-next-line react/no-danger // errorIt has an additional configuration option to prevent comments that disable all rules:
'tachyon/no-disable-rules': ['error', {
noEmptyDisables: true,
rules: ...,
}]which would result in:
/* eslint-disable */ // error
// eslint-disable-next-line // error
// eslint-disable-line // errorno-mock-clear (recommended)
This rule prevents the unnecessary usage of mockFoo.mockClear() or
jest.clearAllMocks() when you have clearMocks: true in your
Jest config, as they
are unnecessary noise in your tests at that point and also add to the confusion
between clearing and resetting mocks. The lint error messages include tips
explaining why you might want to reset mocks and how to do that if that was the
intention.
const mockFoo = jest.fn();
mockFoo.mockClear(); // error
jest.clearAllMocks(); // errorno-react-import (recommended)
This rule is meant to help the transition to the new React JSX transform.
This rules prevents importing the "default" React import as it is no longer necessary:
import React from 'react'; // errorUse import destructuring instead:
import { Component, ReactNode, ... } from 'react'; // allowedThis also prevents using the React qualifier (since some type/lint setups
consider React to be ambiently available due to how the old transform works):
const Foo: React.FC = () => {...}; // errorUse the destructured import directly:
const Foo: FC = () => {...}; // allowedno-rollup-package-import
This rule prevents importing rollup convenience packages. This is useful when
you have a suite of sub-packages (like in a monorepo) that you also re-expose
via a single rollup package for apps that use all of them together. An example
of this is the tachyon-utils suite that contains specific sub-packages, and
other packages shouldn't consume the rollup to avoid bringing in unnecessary
transitive dependencies. This is the opposite of
tachyon/no-sub-package-import.
It takes a configuration of an array of the names of any rollup packages you want to target:
'tachyon/no-rollup-package-import': ['error', ['tachyon-utils']],which would result in:
import { once } from 'tachyon-utils'; // error
import { once } from 'tachyon-utils-stdlib'; // allowedno-single-child-directional-navs (auto-fix)
This rule prevents the usage of directional nav areas (Horizontal, Vertical,
Grid, etc) with an elementCount of 1, preventing arbitrary choice and making
code structure more semantic. This rule comes with an auto-fix that replaces any
such directional nav areas with NodeNav and cleans up the imports as well.
<HorizontalNav focusIndex={2} elementCount={1} /> // error
<NodeNav focusIndex={2} /> // allowedNote: The autofixer relies on prettier to clean up whitespace after it applies its fixes, so it is intended to be used alongside that plugin.
no-sub-package-import (auto-fix)
This rule prevents importing rollup convenience packages. This is useful when
you have a suite of sub-packages (like in a monorepo) that you also re-expose
via a single rollup package for apps that use all of them together. An example
of this is the tachyon-utils suite that contains specific sub-packages, which
is used throughout the Tachyon apps since they use all of the underlying
packages (and their dependencies). This rule comes with an auto-fix that will
truncate the sub-package extension. This is the opposite of
tachyon/no-rollup-package-import.
It takes a configuration of an array of the names of any rollup packages you want to target:
'tachyon/no-sub-package-import': ['error', ['tachyon-utils']],import { once } from 'tachyon-utils-stdlib'; // error
import { once } from 'tachyon-utils'; // allowedprefer-use-const (recommended)
This rule encourages the use of the useConst hook over useState for saved
data that will not change, as useConst is functionally equivalent but more
self-documenting. This does not affect useState usage that utilizes the
updater function.
const [foo] = useState(() => getSomeState()); // error
const foo = useConst(() => getSomeState()); // allowed
const [bar, setBar] = useState(() => ...); // still allowedstyled-components-prefix (recommended, auto-fix)
This rule enforces that the names given to styled components adopt a Sc prefix
to help ease the quick understanding of component functionality/source while
looking through JSX. This rule comes with an auto-fix to apply the prefix
appropriately:
const Foo = styled.div`...`;
...
<Foo>
<Foo />
</Foo>will be turned into
const ScFoo = styled.div`...`;
...
<ScFoo>
<ScFoo />
</ScFoo>Creating New Rules
To create a new rule, make a new JS file in the rules/ directory and a
matching entry in index.js. Then either add the rule to the recommended
config (if it is general enough) or enable it in the // tachyon section of
.eslintrc.js (or an appropriate app's override section if it is highly
targeted).
For help creating rules, use the AST Explorer with
the parser set to @typescript-eslint/parser and transform set to ESLint v4.
Make sure that these settings are reflected in the upper right of the window
before proceeding because the site has some unexpected order-based side effects.
You can then add sample code in the top left pane, view the AST in the top right
pane, write your rule in the bottom left, and view the output in the bottom
right. Note that ESLint is now on v7, so there are minor API differences but for
the most part you can get a long way using this tool. The main difference that
comes up is the range API, which was an object in v4 and is a tuple in v7.
The ESLint docs on rule development are pretty useful for revealing the APIs, especially when working on autofix logic, but they don't offer much guidance so it can useful to combine them with AST Explorer and looking at other rules' source to get started. There's also a recorded demo from Tachyon office hours showing getting started with lint rule development.
