-
-
Notifications
You must be signed in to change notification settings - Fork 653
Code Style Guide
A collection of conventions we follow when coding for core
When contributing new code to the codebase, please follow the following code style guide for consistency and continued ease-of-maintenance! Thank you for your help! 🚀
- Imports
- ToDos
- New libraries
- Code
- Use aliases
- File Structure
- Linting
- Typescript
- Styles
- Naming
- Testing
- Dependencies
- Version Control
-
Keep to the ordered format for imports as follows:
i. External module imports ii. Absolute imports (Use the
@
alias to reference things in the/src
directory) iii. Relative imports
ex:
import React from "react";
import { Heading } from "@mycrypto/ui";
import { AccountContext, StoreContext } from "@services/Store";
import { actions } from "./constants";
import "./Dashboard.scss";
-
@utils
and@components
may only import from@config, @types, @vendor
.Circular dependencies are caused by import loops eg.
A -> B -> C -> A
. They are painful to debug. We avoid them by respecting import hierarchy.
- If you add a ToDo somewhere in the code, please use the format
@todo: <explanation>
so it's easily-searchable in the future.
1. Prefer pure functions
2. Isolate side effects
3. Use conditional spread to extend object properties It's simple and elegant.
/* Don't */
const tx = {
gasLimit: tx.gasLimit.toString()
}
if (isTrue) {
tx.anotherKey = 'value'
}
return tx;
/* Do */
return {
gasLimit: tx.gasLimit.toString(),
...(isTrue && { key: <value> })
}
4. Avoid nested conditionnels It's the Sandi Metz squint test
5. Prefer arguments as object keys If a function has 3 or more arguments, prefer passing them as object keys. It facilitates maintainability by avoiding argument order dependency.
Review Codebase Outline We group .stories and .test with the target component.
To facilitate file switching when working on a component
/components
| Account.tsx
| Account.story.tsx
| Account.test.tsx
We use ESlint, Tslint, Prettier. Activate the rules in your IDE for auto-correction.
We use TS and attempt to stay up to date with the latest version.
1. Place shared typings in @types
Whenever a type is shared place it in the @types
directory. It will allow us to determine which types to place as globals.
2. Favour type composition over redefinition
It emphasises the relation between the types. Read utility-types
to discover the helpers.
/* Don't */
interface Asset {
uuid: TUuid;
name: string;
symbol: TSymbol;
decimal: number;
}
interface ISwapAsset {
uuid: TUuid;
name: string;
symbol: TSymbol;
}
/* Do */
interface Asset {
uuid: TUuid;
name: string;
symbol: TSymbol;
decimal: number;
}
type ISwapAsset = Omit<Asset, 'decimal'>
3. Use Brand<>
types for strings
It allows type safety for strings that we can’t enumerate like uuid
, address
, symbol
etc.
/* Don't */
interface Account {
address: string;
}
/* Do */
import { Brand } from 'utility-types';
type TAddress = Brand<string, 'address'>
interface Account {
address: TAddress;
}
4. Use React.ComponentProps<>
to access a components props So we can reduce the amount of imports
/* Don't */
// Selector.tsx
export interface SelectorProps {...}
export const Selector = (): SelectorProps => {...}
// AssetSelector.tsx
import { Selector, SelectorProps } from '@components'
const AssetSelector = (props: SelectorProps & OwnProps) => (...)
/* Do */
// Selector.tsx
interface Props {...}
export const Selector = (): Props => {...}
// AssetSelector.tsx
import { Selector } from '@components'
const AssetSelector = (props: Pick<React.ComponentProps<Selector>, 'whatever' | 'you' | 'need'>) => (...)
5. Use unknown
over any
(since TS 3.0).
unknown
is safer than any
since it reminds us that we need to perform some sorts of type-checks before operating on our values.
https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch
6. Use 4.0.0 assignment operators
&&=, ||=
and ??=
7. Use discriminate unions https://basarat.gitbook.io/typescript/type-system/discriminated-unions
Currently there is a mix between .css
, .scss
, and style
components.
Use theme over importing constants.
It helps reduce imports and anticipates theme switching.
const Wrapper = styled.div`
color: ${({ theme }) => theme.brand }
`;
Use nested SC To reduce the number of intermediate components in JSX
Use &&
for adding conditional styles
const SContainer = styled('div')`
display: inline-flex;
${(p) => p.fontSize && `font-size: ${p.fontSize}`}
`;
- Rule: Prefix exported types
Rationale: Facilitate maintenance by making them searchable.
Example:
// Don't
interface SwapAssets {}
// Do
interface TSwapAssets {}
- Rule: Prefix SC components with S
Rationale: When parsing JSX it's useful to know what is an SC
Example:
// Don't
const StyledWrapper = styled.div``
const Price = styled(Currency)``
// Do
const SWrapper = styled.div``
const SPrice = styled(Currency)``
-
Use redux and sagas when relevant. They are simpler to test, particularly in regards to side-effects.
-
Reuse selectors. Most selectors already exist. Encourage reuse.
-
actions, selectors, reducers
are keywords reserved for redux.
- For unit and functional tests we use
jest, jsDom, react-testing-library
. - For E2E tests we use Testcafe and Ganache.
- Use
Jest
watch mode when you code. - Test a page redirect with
history.push
seeMigrateLS
. eg. How to spy on a dependency called within an effect. - Test input. eg.
AssetSelector
- Test absence of element in DOM. eg.
Downloader
- Intro to rtl https://kentcdodds.com/blog/introducing-the-react-testing-library/
- Write fewer longer tests. https://kentcdodds.com/blog/write-fewer-longer-tests
- Understand
act
. https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning - Avoid common mistakes when writing tests. https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen
Please avoid adding new libraries. If you do add one, please note it in your PR and a short description of what it is used for so that the person reviewing has a simple time understanding what it is/what it is used for.
- bundle bloat: can the library tree-shake?
- duplication: is there a dep with similar functionnality wihtin the project?
- security: is the dep recent and has a sufficient amount of contributors to not become a supply-chain attack vector?
With that in mind:
- Use react libraries that offer hooks. It simpler to use then overriding default styles
- Use
ramda
overlodash
(Import the method rather then the default [to decrease bundle size]) - Use
datefns
overmoment
- PR reviews
- Atomic commits: https://dev.to/cbillowes/why-i-create-atomic-commits-in-git-kfi
e.g. Release Process
A List of common abbreviations used in the codebase and in our communication
- Tx | TX | tx: transaction
- MM: Metamask
- GHA: Github Actions
- Web3: corresponds to all web3 compatible wallets ie. Trust, MM.