Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
292 lines
8.9 KiB
Plaintext
292 lines
8.9 KiB
Plaintext
---
|
|
title: Ghid de stil
|
|
icon: paintbrush
|
|
---
|
|
|
|
Acest document include regulile ce trebuie urmate la scrierea codului.
|
|
|
|
Scopul aici este de a avea o bază de cod coerentă, ușor de citit și ușor de întreținut.
|
|
|
|
Pentru aceasta, este mai bine să fie mai detaliat decât prea concis.
|
|
|
|
Ține mereu minte că oamenii citesc codul mai des decât îl scriu, mai ales într-un proiect open-source, unde oricine poate contribui.
|
|
|
|
Există multe reguli care nu sunt definite aici, dar care sunt verificate automat de linters.
|
|
|
|
## React
|
|
|
|
### Folosește componente funcționale
|
|
|
|
Utilizează întotdeauna componente funcționale TSX.
|
|
|
|
Nu folosi `import` implicit cu `const`, deoarece este mai greu de citit și mai greu de importat cu completarea de cod.
|
|
|
|
```tsx
|
|
// ❌ Bad, harder to read, harder to import with code completion
|
|
const MyComponent = () => {
|
|
return <div>Hello World</div>;
|
|
};
|
|
|
|
export default MyComponent;
|
|
|
|
// ✅ Good, easy to read, easy to import with code completion
|
|
export function MyComponent() {
|
|
return <div>Hello World</div>;
|
|
};
|
|
```
|
|
|
|
### Proprietăți
|
|
|
|
Creează tipul proprietăților și numește-l `(NumeComponentă)Props` dacă nu este necesar să o exporți.
|
|
|
|
Folosește destructurarea props.
|
|
|
|
```tsx
|
|
// ❌ Bad, no type
|
|
export const MyComponent = (props) => <div>Hello {props.name}</div>;
|
|
|
|
// ✅ Good, type
|
|
type MyComponentProps = {
|
|
name: string;
|
|
};
|
|
|
|
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
|
|
```
|
|
|
|
#### Ferește-te de utilizarea `React.FC` sau `React.FunctionComponent` pentru a defini tipurile props
|
|
|
|
```tsx
|
|
/* ❌ - Bad, defines the component type annotations with `FC`
|
|
* - With `React.FC`, the component implicitly accepts a `children` prop
|
|
* even if it's not defined in the prop type. This might not always be
|
|
* desirable, especially if the component doesn't intend to render
|
|
* children.
|
|
*/
|
|
const EmailField: React.FC<{
|
|
value: string;
|
|
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
|
|
```
|
|
|
|
```tsx
|
|
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
|
|
* component's props
|
|
* - This method doesn't automatically include the children prop. If
|
|
* you want to include it, you have to specify it in OwnProps.
|
|
*/
|
|
type EmailFieldProps = {
|
|
value: string;
|
|
};
|
|
|
|
const EmailField = ({ value }: EmailFieldProps) => (
|
|
<TextInput value={value} disabled fullWidth />
|
|
);
|
|
```
|
|
|
|
#### Fără Împrăștierea de Proprietăți cu o singură variabilă în Elemente JSX
|
|
|
|
Evită utilizarea împrăștierii de proprietăți cu o singură variabilă în elemente JSX, cum ar fi `{...props}`. Această practică adesea duce la cod mai puțin lizibil și mai greu de întreținut deoarece nu este clar ce proprietăți primește componenta.
|
|
|
|
```tsx
|
|
/* ❌ - Bad, spreads a single variable prop into the underlying component
|
|
*/
|
|
const MyComponent = (props: OwnProps) => {
|
|
return <OtherComponent {...props} />;
|
|
}
|
|
```
|
|
|
|
```tsx
|
|
/* ✅ - Good, Explicitly lists all props
|
|
* - Enhances readability and maintainability
|
|
*/
|
|
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
|
|
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
|
|
};
|
|
```
|
|
|
|
Raționament:
|
|
|
|
* La o privire rapidă, este mai evident ce proprietăți sunt transmise de cod, făcându-l mai ușor de înțeles și întreținut.
|
|
* Ajută la prevenirea unei cuplări strânse între componente prin proprietățile lor.
|
|
* Instrumentele de linting fac mai ușor să identifici proprietăți sprellate greșit sau neutilizate atunci când enumeri explicit proprietățile.
|
|
|
|
## JavaScript
|
|
|
|
### Utilizează operatorul de coalescență nulă `??`
|
|
|
|
```tsx
|
|
// ❌ Bad, can return 'default' even if value is 0 or ''
|
|
const value = process.env.MY_VALUE || 'default';
|
|
|
|
// ✅ Good, will return 'default' only if value is null or undefined
|
|
const value = process.env.MY_VALUE ?? 'default';
|
|
```
|
|
|
|
### Folosește accesarea opțională `?.`
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
onClick && onClick();
|
|
|
|
// ✅ Good
|
|
onClick?.();
|
|
```
|
|
|
|
## TypeScript
|
|
|
|
### Utilizați `type` în loc de `interface`
|
|
|
|
Utilizați întotdeauna `type` în loc de `interface`, deoarece aproape întotdeauna se suprapun, iar `type` este mai flexibil.
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
interface MyInterface {
|
|
name: string;
|
|
}
|
|
|
|
// ✅ Good
|
|
type MyType = {
|
|
name: string;
|
|
};
|
|
```
|
|
|
|
### Folosește litere de șir în loc de enum
|
|
|
|
[Literele de șir](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) sunt metoda standard pentru manipularea valorilor similare cu enum în TypeScript. Sunt mai ușor de extins cu Pick și Omit, oferind o experiență mai bună dezvoltatorilor, mai ales cu completarea codului.
|
|
|
|
Poți vedea de ce TypeScript recomandă evitarea enumurilor [aici](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums).
|
|
|
|
```tsx
|
|
// ❌ Bad, utilizes an enum
|
|
enum Color {
|
|
Red = "red",
|
|
Green = "green",
|
|
Blue = "blue",
|
|
}
|
|
|
|
let color = Color.Red;
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, utilizes a string literal
|
|
|
|
let color: "red" | "green" | "blue" = "red";
|
|
```
|
|
|
|
#### GraphQL și biblioteci interne
|
|
|
|
Ar trebui să folosești enumurile generate de GraphQL codegen.
|
|
|
|
Este de asemenea mai bine să folosești un enum atunci când utilizezi o bibliotecă internă, astfel încât biblioteca internă să nu trebuiască să expose un tip de literă de șir care nu este legată de API-ul intern.
|
|
|
|
Exemplu:
|
|
|
|
```TSX
|
|
const {
|
|
setHotkeyScopeAndMemorizePreviousScope,
|
|
goBackToPreviousHotkeyScope,
|
|
} = usePreviousHotkeyScope();
|
|
|
|
setHotkeyScopeAndMemorizePreviousScope(
|
|
RelationPickerHotkeyScope.RelationPicker,
|
|
);
|
|
```
|
|
|
|
## Stilizare
|
|
|
|
### Folosește ComponentaStilizată
|
|
|
|
Stilizează componentele cu [Linaria styled](https://github.com/callstack/linaria).
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
<div className="my-class">Hello World</div>
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good
|
|
const StyledTitle = styled.div`
|
|
color: red;
|
|
`;
|
|
```
|
|
|
|
Prefixează componentele stilizate cu „Styled” pentru a le diferenția de componentele „reale”.
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
const Title = styled.div`
|
|
color: red;
|
|
`;
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good
|
|
const StyledTitle = styled.div`
|
|
color: red;
|
|
`;
|
|
```
|
|
|
|
### Tematica
|
|
|
|
Utilizarea temei pentru majoritatea stilizării componentelor este abordarea preferată.
|
|
|
|
#### Unități de măsură
|
|
|
|
Evită utilizarea directă a valorilor `px` sau `rem` în componentele stilizate. Valorile necesare sunt de regulă deja definite în temă, așa că este recomandat să utilizezi tema pentru aceste scopuri.
|
|
|
|
#### Culori
|
|
|
|
Abține-te să introduci culori noi; în schimb, folosește paleta existentă din temă. În situația în care paleta nu se potrivește, te rugăm să lași un comentariu pentru ca echipa să poată remedia acest aspect.
|
|
|
|
```tsx
|
|
// ❌ Bad, directly specifies style values without utilizing the theme
|
|
const StyledButton = styled.button`
|
|
color: #333333;
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
margin-left: 4px;
|
|
border-radius: 50px;
|
|
`;
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, utilizes the theme
|
|
const StyledButton = styled.button`
|
|
color: ${({ theme }) => theme.font.color.primary};
|
|
font-size: ${({ theme }) => theme.font.size.md};
|
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
|
margin-left: ${({ theme }) => theme.spacing(1)};
|
|
border-radius: ${({ theme }) => theme.border.rounded};
|
|
`;
|
|
```
|
|
|
|
## Impunerea Neprecizării Importurilor de Tip
|
|
|
|
Evită importurile de tip. Pentru a impune acest standard, o regulă Oxlint verifică și raportează orice importuri de tip. Acest lucru ajută la menținerea consistenței și lizibilității în codul TypeScript.
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
|
|
// ❌ Bad
|
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
|
|
// ✅ Good
|
|
import { Meta, StoryObj } from '@storybook/react';
|
|
```
|
|
|
|
### De ce Fără Importuri de Tip
|
|
|
|
* **Consistență**: Evitând importurile de tip și folosind o singură abordare atât pentru importurile de tip cât și de valoare, baza de cod rămâne consistentă în stilul său de import module.
|
|
|
|
* **Lizibilitate**: Importurile doar de tip îmbunătățesc lizibilitatea codului, făcând clar când importați valori sau tipuri. Aceasta reduce ambiguitatea și face mai ușor de înțeles scopul simbolurilor importate.
|
|
|
|
* **Întreținere**: Îmbunătățește întreținerea bazei de cod, deoarece dezvoltatorii pot identifica și localiza importurile doar de tip când revizuiesc sau modifică codul.
|
|
|
|
### Regula Oxlint
|
|
|
|
O regulă Oxlint, `typescript/consistent-type-imports`, impune standardul fără importuri de tip. Această regulă va genera erori sau avertismente pentru orice încălcare a importurilor de tip.
|
|
|
|
Rețineți că această regulă abordează în mod specific cazurile-limită rare în care apar importuri de tip neintenționate. TypeScript descurajează el însuși această practică, așa cum este menționat în notele de lansare [TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). În majoritatea situațiilor, nu ar trebui să aveți nevoie de importuri doar de tip.
|
|
|
|
Pentru a vă asigura că codul respectă această regulă, rulați Oxlint ca parte a fluxului de lucru de dezvoltare.
|