ΠΡΠΎΠ΄Π²ΠΈΠ½ΡΡΡΠΉ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ Π°Π½Π°Π»ΠΈΠ· Π² TypeScript-ΠΏΡΠΎΠ΅ΠΊΡΠ°Ρ : Π²ΡΡ ΠΎΠ΄ΠΈΠΌ Π·Π° ΡΠ°ΠΌΠΊΠΈ tsc ΠΈ ESLint
- ΠΏΡΡΠ½ΠΈΡΠ°, 2 ΠΎΠΊΡΡΠ±ΡΡ 2026β―Π³. Π² 00:00:11
π ΠΡΠΈΠ²Π΅Ρ! ΠΠ΅Π½Ρ Π·ΠΎΠ²ΡΡ ΠΠ»Π΅ΠΊΡΠ°Π½Π΄Ρ, Ρ ΡΠ°Π±ΠΎΡΠ°Ρ ΡΡΠΎΠ½ΡΠ΅Π½Π΄-ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΎΠΌ Π² ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ Β«ΠΠ΅Π³Π°Π€ΠΎΠ½Β». Π‘Π΅Π³ΠΎΠ΄Π½Ρ Ρ ΠΎΡΡ ΠΏΠΎΠ³ΠΎΠ²ΠΎΡΠΈΡΡ ΠΎ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΌ Π°Π½Π°Π»ΠΈΠ·Π΅ TypeScript-ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ² Π·Π° ΠΏΡΠ΅Π΄Π΅Π»Π°ΠΌΠΈ ΠΏΡΠΈΠ²ΡΡΠ½ΠΎΠΉ ΡΠ²ΡΠ·ΠΊΠΈ tsc ΠΈ ESLint.
tsc ΠΏΡΠ΅ΠΆΠ΄Π΅ Π²ΡΠ΅Π³ΠΎ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ ΡΠΈΠΏΠΎΠ², Π° ESLint Π½Π°Ρ
ΠΎΠ΄ΠΈΡ ΡΠΈΡΠΎΠΊΠΈΠΉ ΠΊΡΡΠ³ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΈ Π½Π°ΡΡΡΠ΅Π½ΠΈΠΉ ΠΏΡΠΈΠ½ΡΡΡΡ
Π² ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ». ΠΠΌΠ΅ΡΡΠ΅ ΠΎΠ½ΠΈ Π·Π°ΠΊΡΡΠ²Π°ΡΡ Π±ΠΎΠ»ΡΡΠΈΠ½ΡΡΠ²ΠΎ ΠΏΠΎΠ²ΡΠ΅Π΄Π½Π΅Π²Π½ΡΡ
Π·Π°Π΄Π°Ρ, Π½ΠΎ Π½Π΅ Π²ΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΡΠ΄ΠΎΠ±Π½ΠΎ Π²ΡΡΠ°ΠΆΠ°ΡΡ ΡΠ΅ΡΠ΅Π· ΡΠΈΠΏΡ ΠΈ ΠΏΡΠ°Π²ΠΈΠ»Π° Π»ΠΈΠ½ΡΠ΅ΡΠ°. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Π² ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΠΌΠΎΠΆΠ΅Ρ ΠΏΠΎΡΡΠ΅Π±ΠΎΠ²Π°ΡΡΡΡ Π·Π°ΠΏΡΠ΅ΡΠΈΡΡ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌΠΈ Π°ΡΡ
ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠΌΠΈ ΡΠ»ΠΎΡΠΌΠΈ, Π½Π°ΠΉΡΠΈ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌΡΠΉ ΠΊΠΎΠ΄, ΠΊΠΎΡΠΎΡΡΠΉ Π±ΠΎΠ»ΡΡΠ΅ Π½ΠΈΠ³Π΄Π΅ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ, ΠΈΠ»ΠΈ ΠΏΡΠΎΡΠ»Π΅Π΄ΠΈΡΡ, ΠΊΠ°ΠΊ Π½Π΅ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· HTTP-Π·Π°ΠΏΡΠΎΡΠ° Π΄ΠΎΡ
ΠΎΠ΄ΡΡ Π΄ΠΎ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ. ΠΡΠΎ ΡΠΆΠ΅ ΡΠ°Π·Π½ΡΠ΅ Π·Π°Π΄Π°ΡΠΈ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π°, ΠΈ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΈΠ· Π½ΠΈΡ
Π΅ΡΡΡ ΡΠ²ΠΎΠΉ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄.
ΠΠ΄Π½ΠΈ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ ΡΡΡΠΎΡΡ Π³ΡΠ°Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡΡ ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΡΠ²ΡΠ·ΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ ΠΈ ΠΌΠΎΠ΄ΡΠ»ΡΠΌΠΈ. ΠΡΡΠ³ΠΈΠ΅ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠΊΡΠΏΠΎΡΡΠΎΠ², ΡΠ»Π΅Π΄ΡΡ Π·Π° ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ΠΌ any ΠΈΠ»ΠΈ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΡΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ ΠΏΠΎ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅. Π ΡΡΠ°ΡΡΠ΅ ΡΠ°Π·Π±Π΅ΡΡΠΌ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ Π΄Π»Ρ ΡΡΠΈΡ Π·Π°Π΄Π°Ρ: ΠΎΡ ΠΊΠΎΠ½ΡΡΠΎΠ»Ρ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΡ ΠΈ ΠΏΠΎΠΈΡΠΊΠ° Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° Π΄ΠΎ Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ ΠΈ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΎΠΊ.
Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ½ΡΡΡ, Π·Π°ΡΠ΅ΠΌ Π½ΡΠΆΠ½Ρ ΠΏΡΠΎΠ΄Π²ΠΈΠ½ΡΡΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ, ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΒ tscΒ ΠΈΒ ESLint.
ΠΡΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ΅ TypeScript ΡΠΎΠ·Π΄Π°ΡΡ Π²Π½ΡΡΡΠ΅Π½Π½Π΅Π΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π²ΡΠ΅Π³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ° β ΠΎΠ±ΡΠ΅ΠΊΡ Program. Π Π½ΡΠΌ ΡΠΎΠ±ΡΠ°Π½Ρ ΠΈΡΡ
ΠΎΠ΄Π½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ, ΠΈΠΌΠΏΠΎΡΡΡ ΠΈ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠΈΠΏΠ°Ρ
. ΠΠ° ΠΎΡΠ½ΠΎΠ²Π΅ Program ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ ΡΠ°Π·ΡΠ΅ΡΠ°Π΅Ρ ΠΈΠΌΠΏΠΎΡΡΡ ΠΈ ΡΠΎΠΏΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΠΈΠΏΡ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ. ΠΠΎΡΡΠΎΠΌΡ tsc Π½Π΅Π»ΡΠ·Ρ ΡΡΠΈΡΠ°ΡΡ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΎΠΌ ΡΠΎΠ»ΡΠΊΠΎ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠΉ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ. ΠΠ³ΠΎ Π·Π°Π΄Π°ΡΠ° ΠΏΡΠΎΠ²Π΅ΡΡΡΡ ΠΏΡΠ°Π²ΠΈΠ»Π° ΡΠ·ΡΠΊΠ° ΠΈ ΡΠΈΡΡΠ΅ΠΌΡ ΡΠΈΠΏΠΎΠ², Π° Π½Π΅ Π»ΡΠ±ΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΠ° ΡΠ°ΠΌΠΊΠ°ΠΌΠΈ ΠΎΡΡΠ°ΡΡΡΡ:
ΠΡΡ
ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ: tsc Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ»ΡΠ½ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π° ΡΠ»ΠΎΡΠ², ΠΌΠΎΠ΄ΡΠ»ΡΠ½ΡΡ
Π³ΡΠ°Π½ΠΈΡ ΠΈΠ»ΠΈ Π΄ΠΎΠΏΡΡΡΠΈΠΌΡΡ
Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ.
ΠΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠΊΡΠΏΠΎΡΡΡ ΠΈ ΡΠ°ΠΉΠ»Ρ: ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ noUnusedLocals ΠΈ noUnusedParameters Π½Π΅ Π·Π°ΠΌΠ΅Π½ΡΡΡ ΠΏΠΎΠΈΡΠΊ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌΡΡ
ΡΡΠ½ΠΊΡΠΈΠΉ, ΡΠΈΠΏΠΎΠ² ΠΈ ΠΊΠ»Π°ΡΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π±ΠΎΠ»ΡΡΠ΅ Π½ΠΈΠ³Π΄Π΅ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡΡΡ.
ΠΠ΅ΡΠ΅Π΄Π°ΡΠ° ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΡ
Π΄Π°Π½Π½ΡΡ
: Ρ
ΠΎΡΡ tsc ΠΈ ΡΠΌΠ΅Π΅Ρ ΡΡΠΆΠ°ΡΡ ΡΠΈΠΏΡ Π²Π½ΡΡΡΠΈ ΡΡΠ½ΠΊΡΠΈΠΉ, ΠΎΠ½ Π½Π΅ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅Ρ, ΠΊΠ°ΠΊ Π½Π΅ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΏΡΠΎΡ
ΠΎΠ΄ΡΡ ΡΠ΅ΡΠ΅Π· ΡΠ΅ΠΏΠΎΡΠΊΠΈ Π²ΡΠ·ΠΎΠ²ΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρ ΠΌΠΎΠ΄ΡΠ»ΡΠΌΠΈ.
ESLint Π°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠ΅Ρ Π°Π±ΡΡΡΠ°ΠΊΡΠ½ΠΎΠ΅ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π΄Π΅ΡΠ΅Π²ΠΎ (AST) ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π°. ΠΡΠΎ Π΄Π΅ΡΠ΅Π²ΠΎ ΠΎΠΏΠΈΡΡΠ²Π°Π΅Ρ ΡΡΡΡΠΊΡΡΡΡ ΠΊΠΎΠ΄Π°: ΠΎΠ±ΡΡΠ²Π»Π΅Π½ΠΈΡ, Π²ΡΠ·ΠΎΠ²Ρ, ΡΡΠ»ΠΎΠ²ΠΈΡ ΠΈ Π΄ΡΡΠ³ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ. ΠΡΠΈ ΡΡΠΎΠΌ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ typescript-eslint ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²ΠΈΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠΈΠΏΠ°Ρ
ΠΈΠ· typescript. Π’Π°ΠΊΠ°Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π²ΡΡΠ²Π»ΡΡΡ Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΠΈΠ»ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ, Π½ΠΎ ΠΈ ΠΎΡΠΈΠ±ΠΊΠΈ, Π΄Π»Ρ ΠΏΠΎΠΈΡΠΊΠ° ΠΊΠΎΡΠΎΡΡΡ
Π½ΡΠΆΠ½Ρ ΡΠΈΠΏΡ. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΏΡΠ°Π²ΠΈΠ»Π° no-floating-promises ΠΈ no-misused-promises ΠΏΡΠΎΠ²Π΅ΡΡΡΡ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΡ ΡΠ°Π±ΠΎΡΡ Ρ Promise, Π° ΡΠ΅ΠΌΠ΅ΠΉΡΡΠ²ΠΎ no-unsafe-* Π½Π°Ρ
ΠΎΠ΄ΠΈΡ ΠΎΠΏΠ°ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ Ρ any. ΠΡΠ°Π²ΠΈΠ»ΠΎ ESLint Π²ΡΠ·ΡΠ²Π°Π΅ΡΡΡ Π΄Π»Ρ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π°, Π΄Π°ΠΆΠ΅ Π΅ΡΠ»ΠΈ Π΅ΠΌΡ Π΄ΠΎΡΡΡΠΏΠ½Ρ ΡΠΈΠΏΡ ΠΈ ΡΠΈΠΌΠ²ΠΎΠ»Ρ ΠΈΠ· typescript.
ESLint Π½Π΅ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ Π΄Π»Ρ ΠΏΠΎΠ»Π½ΠΎΡΠ΅Π½Π½ΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π° Π³ΡΠ°ΡΠ° Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ Π²ΡΠ΅Π³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΠ½ ΡΠ°ΠΊΠΆΠ΅ Π½Π΅ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ Π΄Π»Ρ ΡΠ»ΠΎΠΆΠ½ΠΎΠ³ΠΎ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ Π΄Π°Π½Π½ΡΡ
ΠΌΠ΅ΠΆΠ΄Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΠΌΠΈ ΡΡΠ½ΠΊΡΠΈΡΠΌΠΈ ΠΈ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ. ΠΠ»Ρ ΡΠ°ΠΊΠΈΡ
Π·Π°Π΄Π°Ρ ΡΠ΄ΠΎΠ±Π½Π΅Π΅ ΡΠΏΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ.
ΠΠΈΠΆΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½Π° ΡΠ°Π±Π»ΠΈΡΠ° ΡΡΠ°Π²Π½Π΅Π½ΠΈΡ ΡΡΠΎΠ²Π½Π΅ΠΉ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π°:
Π£ΡΠΎΠ²Π΅Π½Ρ Π°Π½Π°Π»ΠΈΠ·Π° | ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ | Π§ΡΠΎ Π²ΠΈΠ΄ΠΈΡ | Π§ΡΠΎ ΡΠΏΡΡΠΊΠ°Π΅Ρ |
|---|---|---|---|
ΠΠ±ΡΡΡΠ°ΠΊΡΠ½ΠΎΠ΅ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π΄Π΅ΡΠ΅Π²ΠΎ (AST) |
| Π‘ΡΡΡΠΊΡΡΡΠ° ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΡΠ°ΠΉΠ»Π°, ΡΡΠΈΠ»ΠΈΡΡΠΈΠΊΠ°, ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΠΈ. | ΠΠ΅Π· Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΠΎΠΉ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠΈΠΏΠ°Ρ . |
ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ² |
| ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ, Π²ΡΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΡΠΈΠΏΠΎΠ², ΡΠΈΠ³Π½Π°ΡΡΡΡ. | ΠΡΠΎΠ΅ΠΊΡΠ½ΡΠ΅ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π°, Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠΊΡΠΏΠΎΡΡΡ, ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΎΠΏΠ°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ . |
ESLint Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠ΅ΠΉ ΠΎ ΡΠΈΠΏΠ°Ρ |
| Π’ΠΈΠΏΡ ΠΈ ΡΠΈΠΌΠ²ΠΎΠ»Ρ ΠΏΡΠΎΠ΅ΠΊΡΠ°, ΠΎΡΠΈΠ±ΠΊΠΈ ΡΠ°Π±ΠΎΡΡ Ρ | ΠΡΠ°Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ Π²ΡΠ΅Π³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ° ΠΈ ΡΠ»ΠΎΠΆΠ½ΡΠ΅ ΡΠ΅ΠΏΠΎΡΠΊΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ Π΄Π°Π½Π½ΡΡ . |
ΠΡΠ°Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΠΈ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ° |
| ΠΡΠ°Ρ ΠΈΠΌΠΏΠΎΡΡΠΎΠ²/ΡΠΊΡΠΏΠΎΡΡΠΎΠ², Π³ΡΠ°Π½ΠΈΡΡ ΡΠ»ΠΎΡΠ², Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΠΌΠΎΠ΄ΡΠ»ΠΈ. | ΠΠΎΡΠΎΠΊ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ Π²Π½ΡΡΡΠΈ ΡΡΠ½ΠΊΡΠΈΠΉ. |
ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ |
| ΠΠ²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΠΎΡ ΠΌΠ΅ΡΡΠ° Π²Π²ΠΎΠ΄Π° Π΄ΠΎ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ. | ΠΠ½Π°Π»ΠΈΠ· ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΠ½ΠΊΡΠΈΡΠΌΠΈ ΠΈ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ Π² Community Edition. |
ΠΡΠΈ ΡΠ°Π·Π²ΠΈΡΠΈΠΈ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π³ΡΠ°Π½ΠΈΡΡ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ»ΠΎΡΠΌΠΈ ΡΠ°ΡΡΠΎ ΡΠ°Π·ΠΌΡΠ²Π°ΡΡΡΡ, ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΠΊΠΎΠ³Π΄Π° Π²Π½Π΅Π΄ΡΡΡΡ Feature-Sliced Design (FSD), Clean Architecture ΠΈΠ»ΠΈ DDD (Domain-Driven Design). Π ΡΠΏΠ΅ΡΠΊΠ΅ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΌΠΎΠΆΠ΅Ρ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ ReactβΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Π² Π±ΠΈΠ·Π½Π΅ΡβΡΠ΅ΡΠ²ΠΈΡ ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΈΠ½ΡΡΠ°ΡΡΡΡΠΊΡΡΡΠ½ΡΠΉ ΠΊΠ»ΠΈΠ΅Π½Ρ ΠΏΡΡΠΌΠΎ Π²Π½ΡΡΡΠΈ Π΄ΠΎΠΌΠ΅Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ.
ΠΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΠΈΠΌΠΏΠΎΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ³ΡΠ°Π½ΠΈΡΠΈΡΡ ΠΈ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌΠΈ ESLint, Π½ΠΎ Π΄Π»Ρ Π΄Π΅ΠΊΠ»Π°ΡΠ°ΡΠΈΠ²Π½ΠΎΠΉ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π³ΡΠ°ΡΠ° Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΡΠ΄ΠΎΠ±Π½Π΅Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΏΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ.
ΠΠ°ΡΠ½ΡΠΌ Ρ dependency-cruiser. ΠΡΠΎ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ CLI-ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ. ΠΠ½ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠ΅Ρ import, require ΠΈ export, ΡΡΡΠΎΠΈΡ Π³ΡΠ°Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΠΏΡΠΎΠ΅ΠΊΡΠ° ΠΈ ΡΠ²Π΅ΡΡΠ΅Ρ Π΅Π³ΠΎ Ρ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌΠΈ ΠΈΠ· ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ:
npm install --save-dev dependency-cruiser
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΎΠ½Π½ΡΠΉ ΡΠ°ΠΉΠ» .dependency-cruiser.cjs. Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ ΠΏΡΠΈΠΌΠ΅Ρ ΠΆΡΡΡΠΊΠΎΠΉ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΡΠ°Π²ΠΈΠ» Π΄Π»Ρ Clean Architecture. ΠΠ½Π°Π»ΠΎΠ³ΠΈΡΠ½ΡΠΉ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΈ Π² FSD, Π°Π΄Π°ΠΏΡΠΈΡΠΎΠ²Π°Π² ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΏΠΎΠ΄ Π΅Ρ ΡΠ»ΠΎΠΈ:
/** @type {import('dependency-cruiser').IConfiguration} */ module.exports = { forbidden: [ { name: 'no-circular', severity: 'error', comment: 'Π¦ΠΈΠΊΠ»ΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ Π·Π°ΠΏΡΠ΅ΡΠ΅Π½Ρ, ΡΠ°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ½ΠΈ ΠΌΠΎΠ³ΡΡ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΡΡ ΠΊ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°ΠΌ Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠ΅ΠΉ ΠΌΠΎΠ΄ΡΠ»Π΅ΠΉ ΠΈ ΡΡΠ»ΠΎΠΆΠ½ΡΡΡ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΡ.', from: {}, to: { circular: true } }, { name: 'domain-isolation', severity: 'error', comment: 'Π‘Π»ΠΎΠΉ Domain Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π·Π°Π²ΠΈΡΠ΅ΡΡ ΠΎΡ Infrastructure ΠΈΠ»ΠΈ Presentation!', from: { path: '^src/domain(?:/|$)' }, to: { path: [ '^src/infrastructure(?:/|$)', '^src/presentation(?:/|$)', '^src/ui(?:/|$)' ] } }, { name: 'no-orphans', severity: 'warn', comment: 'ΠΠ±Π½Π°ΡΡΠΆΠ΅Π½ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠΉ ΡΠ°ΠΉΠ» Π±Π΅Π· Π²Ρ ΠΎΠ΄ΡΡΠΈΡ ΠΈ ΠΈΡΡ ΠΎΠ΄ΡΡΠΈΡ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ.', from: { orphan: true, path: '^src(?:/|$)', pathNot: [ '\\.d\\.ts$', '(^|/)index\\.(?:[cm]?ts|tsx|[cm]?js|jsx)$' ] }, to: {} }, { name: 'restrict-node-builtins-in-ui', severity: 'error', comment: 'UI-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ ΠΌΠΎΠ΄ΡΠ»ΠΈ Node.js (fs, path ΠΈ Ρ.Π΄.)', from: { path: '^src/(?:ui|presentation)(?:/|$)' }, to: { dependencyTypes: ['core'] } } ], options: { tsPreCompilationDeps: true, tsConfig: { fileName: 'tsconfig.json' }, enhancedResolveOptions: { exportsFields: ['exports'], conditionNames: ['import', 'require', 'node', 'default'] } } };
ΠΠ°ΠΏΡΡΡΠΈΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ°ΠΊ:
npx depcruise --config .dependency-cruiser.cjs src
ΠΡΠ»ΠΈ ΠΊΡΠΎ-ΡΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ Π² src/domain/user.ts ΠΈΠΌΠΏΠΎΡΡ Π²ΠΈΠ΄Π° import { HttpClient } from '../infrastructure/http', dependency-cruiser Π·Π°Π²Π΅ΡΡΠΈΡ ΡΠ°Π±ΠΎΡΡ Ρ Π½Π΅Π½ΡΠ»Π΅Π²ΡΠΌ ΠΊΠΎΠ΄ΠΎΠΌ Π²ΡΡ
ΠΎΠ΄Π° ΠΈ Π²ΡΠ²Π΅Π΄Π΅Ρ Π½Π°ΡΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π° domain-isolation.
ΠΡΠ»ΠΈ Π½ΡΠΆΠ½ΠΎ ΡΠ°Π·Π΄Π΅Π»ΠΈΡΡ ΠΌΠΎΠ½ΠΎΠ»ΠΈΡ Π½Π° Π»ΠΎΠ³ΠΈΡΠ΅ΡΠΊΠΈ ΠΈΠ·ΠΎΠ»ΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΠΌΠΎΠ΄ΡΠ»ΠΈ ΠΈ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΠΈΡ
ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠΉ API Π±Π΅Π· Π²ΡΠ΄Π΅Π»Π΅Π½ΠΈΡ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΌΠΎΠ΄ΡΠ»Ρ Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ ΠΏΠ°ΠΊΠ΅Ρ, ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ good-fences.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ ΠΏΠ°ΠΊΠ΅Ρ:
npm install --save-dev @good-fences/api
ΠΠ°ΠΊΠ΅Ρ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅Ρ CLI-ΠΊΠΎΠΌΠ°Π½Π΄Ρ good-fences, ΠΏΠΎΡΡΠΎΠΌΡ Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ ΡΠ΅ΡΠ΅Π· npx ΠΈΠ»ΠΈ npm-ΡΠΊΡΠΈΠΏΡ.
Π‘ ΠΏΠΎΠΌΠΎΡΡΡ good-fences ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°ΡΡ Π² Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΡ
ΠΏΡΠΎΠ΅ΠΊΡΠ° ΡΠ°ΠΉΠ»Ρ fence.json, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΎΠΏΠΈΡΡΠ²Π°ΡΡ Π³ΡΠ°Π½ΠΈΡΡ ΠΌΠΎΠ΄ΡΠ»Π΅ΠΉ. ΠΠΎΠ»Π΅ exports ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ, ΠΊΠ°ΠΊΠΈΠ΅ ΡΠ°ΠΉΠ»Ρ ΠΌΠΎΠ΄ΡΠ»Ρ Π΄ΠΎΡΡΡΠΏΠ½Ρ Π΄Π»Ρ ΠΈΠΌΠΏΠΎΡΡΠ° ΠΈΠ·Π²Π½Π΅, Π° imports ΠΌΠΎΠ΄ΡΠ»ΠΈ Ρ ΠΊΠ°ΠΊΠΈΠΌΠΈ ΡΠ΅Π³Π°ΠΌΠΈ ΡΠ°Π·ΡΠ΅ΡΠ΅Π½ΠΎ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ ΠΈΠ·Π½ΡΡΡΠΈ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΠΌΠΎΠ΄ΡΠ»Ρ.
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΌΠΎΠ΄ΡΠ»Ρ common ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅ΡΠΈΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΠΌ ΡΠ΅Π³ΠΎΠΌ Π² src/modules/common/fence.json:
{ "tags": ["common"] }
Π ΠΌΠΎΠ΄ΡΠ»Ρ auth Π² src/modules/auth/fence.json:
{ "tags": ["auth"] }
Π’ΠΎΠ³Π΄Π° src/modules/billing/fence.json ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠ³Π»ΡΠ΄Π΅ΡΡ ΡΠ°ΠΊ:
{ "tags": ["billing"], "exports": [ "index", "types" ], "imports": [ "common", "auth" ] }
Π’Π°ΠΊΠ°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΡΠ°Π·ΡΠ΅ΡΠ°Π΅Ρ ΠΊΠΎΠ΄Ρ Π²Π½ΡΡΡΠΈ billing ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ ΠΌΠΎΠ΄ΡΠ»ΠΈ, ΠΏΠΎΠΌΠ΅ΡΠ΅Π½Π½ΡΠ΅ ΡΠ΅Π³Π°ΠΌΠΈ common ΠΈ auth, Π° ΡΠ½Π°ΡΡΠΆΠΈ billing Π΄ΠΎΡΡΡΠΏΠ½Ρ ΡΠΎΠ»ΡΠΊΠΎ Π΅Π³ΠΎ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠ΅ ΠΌΠΎΠ΄ΡΠ»ΠΈ index ΠΈ types.
ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:
npx good-fences src
ΠΡΠ»ΠΈ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΏΠΎΠΏΡΡΠ°Π΅ΡΡΡ Π½Π°ΠΏΡΡΠΌΡΡ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠΉ ΡΠ°ΠΉΠ» src/modules/billing/internal/calculator.ts ΠΈΠ· Π΄ΡΡΠ³ΠΎΠ³ΠΎ ΠΌΠΎΠ΄ΡΠ»Ρ, ΡΠ°ΠΊΠΎΠΉ ΠΈΠΌΠΏΠΎΡΡ Π½Π°ΡΡΡΠΈΡ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ exports, ΠΈ good-fences Π·Π°Π²Π΅ΡΡΠΈΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ Ρ ΠΎΡΠΈΠ±ΠΊΠΎΠΉ.
Π‘ΠΎ Π²ΡΠ΅ΠΌΠ΅Π½Π΅ΠΌ ΠΊΠΎΠ΄ΠΎΠ²Π°Ρ Π±Π°Π·Π° ΠΎΠ±ΡΠ°ΡΡΠ°Π΅Ρ ΠΌΡΡΠΎΡΠΎΠΌ: ΡΡΡΠ°ΡΠ΅Π²ΡΠΈΠ΅ ΡΠΊΡΠΏΠΎΡΡΠ½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ, Π½Π΅Π½ΡΠΆΠ½ΡΠ΅ ΡΠΈΠΏΡ, Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ Π² package.json. ΠΠΎΡΡΠ΅ΠΏΠ΅Π½Π½ΡΠ΅ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ ΠΈ Π½Π΅Π±ΡΠ΅ΠΆΠ½ΡΠΉ ΠΊΠΎΠ΄ ΠΏΡΠΈΠ²ΠΎΠ΄ΡΡ ΠΊ ΡΠΎΠΌΡ, ΡΡΠΎ Π² ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½ΡΠ΅ΡΡΡ any, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ½ΠΈΠΆΠ°Π΅Ρ ΠΏΠΎΠ»ΡΠ·Ρ ΠΎΡ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΈΠΏΠΈΠ·Π°ΡΠΈΠΈ.
ΠΡΠ°Π²ΠΈΠ»ΠΎ ESLint no-unused-vars Π°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠ΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
, ΡΡΠ½ΠΊΡΠΈΠΉ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Π²Π½ΡΡΡΠΈ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π°. ΠΠ½ΠΎ Π½Π΅ ΡΡΡΠΎΠΈΡ ΠΏΠΎΠ»Π½ΡΠΉ Π³ΡΠ°Ρ ΠΈΠΌΠΏΠΎΡΡΠΎΠ² ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΠΎΡΡΠΎΠΌΡ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌΠ°Ρ ΡΡΠ½ΠΊΡΠΈΡ ΠΎΠ±ΡΡΠ½ΠΎ Π½Π΅ ΡΡΠΈΡΠ°Π΅ΡΡΡ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠΉ, Π΄Π°ΠΆΠ΅ Π΅ΡΠ»ΠΈ Π΅Ρ Π½ΠΈΠΊΡΠΎ Π½Π΅ ΠΈΠΌΠΏΠΎΡΡΠΈΡΡΠ΅Ρ.
ΠΠ»Ρ ΠΏΠΎΠΈΡΠΊΠ° Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° ΡΠ΄ΠΎΠ±Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Knip. ΠΠ½ ΡΡΡΠΎΠΈΡ Π³ΡΠ°Ρ ΡΠΊΡΠΏΠΎΡΡΠΎΠ², ΠΈΠΌΠΏΠΎΡΡΠΎΠ² ΠΈ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΠΏΡΠΎΠ΅ΠΊΡΠ°, Π²ΠΊΠ»ΡΡΠ°Ρ ΡΠΈΠΏΡ, ΠΈ Π½Π°Ρ ΠΎΠ΄ΠΈΡ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ Ρ ΡΡΡΡΠΎΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΉ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΡΡ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΎΠ² ΠΈ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΎΠ² (Next.js, Vite ΠΈ Ρ. Π΄.).
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°:
npm install -D knip typescript @types/node
ΠΠ»ΠΈ ΡΠ΅ΡΠ΅Π· ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ:
npm init @knip/config
ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ knip.json:
{ "$schema": "https://unpkg.com/knip@6/schema.json", "entry": ["src/index.ts", "src/bin/*.ts"], "project": ["src/**/*.{ts,tsx}"] }
Π ΠΏΠΎΠ»Π΅ entry ΡΠΊΠ°Π·Π°Π½Ρ ΡΠΎΡΠΊΠΈ Π²Ρ
ΠΎΠ΄Π° β ΡΠ°ΠΉΠ»Ρ, Ρ ΠΊΠΎΡΠΎΡΡΡ
Knip Π½Π°ΡΠΈΠ½Π°Π΅Ρ ΠΎΠ±Ρ
ΠΎΠ΄ Π³ΡΠ°ΡΠ° ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΡ ΠΈΡ
ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΠ³ΠΎ Π²ΡΠ±ΠΎΡΠ° Π·Π°Π²ΠΈΡΠΈΡ ΡΠΎΡΠ½ΠΎΡΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ°.
ΠΠΎΠΌΠ°Π½Π΄Π° Π΄Π»Ρ Π·Π°ΠΏΡΡΠΊΠ°:
npx knip
Π§ΡΠΎ ΠΌΠΎΠΆΠ΅Ρ Π½Π°ΠΉΡΠΈ Knip, ΡΠ΅Π³ΠΎ Π½Π΅ Π²ΠΈΠ΄ΠΈΡ ESLint:
ΠΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠ°ΠΉΠ»Ρ (Unused files): ΡΠ°ΠΉΠ»Ρ, ΠΊΠΎΡΠΎΡΡΠ΅ Π½Π΅ Π²Ρ
ΠΎΠ΄ΡΡ Π² Π³ΡΠ°Ρ ΠΈΠΌΠΏΠΎΡΡΠΎΠ² ΠΎΡ Π·Π°Π΄Π°Π½Π½ΡΡ
ΡΠΎΡΠ΅ΠΊ Π²Ρ
ΠΎΠ΄Π°.
ΠΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠΊΡΠΏΠΎΡΡΡ (Unused exports): ΠΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ, ΠΊΠ»Π°ΡΡΡ, ΠΊΠΎΠ½ΡΡΠ°Π½ΡΡ ΠΈ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ Π½ΠΈΠΊΡΠΎ Π½Π΅ ΠΈΠΌΠΏΠΎΡΡΠΈΡΡΠ΅Ρ.
ΠΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌΡΠ΅ ΡΠΈΠΏΡ (Unused export types): Π’ΠΈΠΏΡ ΠΈ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ, ΠΎΡΠ΄Π°Π²Π°Π΅ΠΌΡΠ΅ Π½Π°ΡΡΠΆΡ, Π½ΠΎ Π½Π΅ Π²ΠΎΡΡΡΠ΅Π±ΠΎΠ²Π°Π½Π½ΡΠ΅ Π΄ΡΡΠ³ΠΈΠΌΠΈ ΠΌΠΎΠ΄ΡΠ»ΡΠΌΠΈ.
ΠΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ (Unused dependencies / devDependencies): ΠΠ°ΠΊΠ΅ΡΡ ΠΈΠ· package.json, ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΡΠΎΡΡΡ
Knip Π½Π΅ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ» Π² ΠΊΠΎΠ΄Π΅, ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡΡ
, ΡΠΊΡΠΈΠΏΡΠ°Ρ
ΠΈ Π΄ΡΡΠ³ΠΈΡ
ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΡ
ΡΠΎΡΠΊΠ°Ρ
Π²Ρ
ΠΎΠ΄Π°.
ΠΡΠ±Π»ΠΈΡΡΡΡΠΈΠ΅ΡΡ ΡΠΊΡΠΏΠΎΡΡΡ (Duplicate exports): ΠΎΠ΄ΠΈΠ½ ΠΈ ΡΠΎΡ ΠΆΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΡΡΡ Π±ΠΎΠ»Π΅Π΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΡΠ°Π·Π°, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ ΠΊΠ°ΠΊ ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½ΡΠΉ ΠΈ ΡΠΊΡΠΏΠΎΡΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΈΠ»ΠΈ ΡΠ΅ΡΠ΅Π· ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠΈΠ΅ΡΡ ΡΠ΅ΠΏΠΎΡΠΊΠΈ ΡΠ΅ΡΠΊΡΠΏΠΎΡΡΠΎΠ².
ΠΡΠΎΠ΅ΠΊΡ ΠΌΠΎΠΆΠ΅Ρ ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΠΎΠ²Π°ΡΡΡΡ Π±Π΅Π· ΠΎΡΠΈΠ±ΠΎΠΊ, Π½ΠΎ ΠΏΡΠΈ ΡΡΠΎΠΌ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ ΡΠΎΡΠ½ΠΈ ΡΠ²Π½ΡΡ
any, Π° ΠΏΡΠΈ ΠΎΡΠΊΠ»ΡΡΡΠ½Π½ΠΎΠΌ noImplicitAny ΠΈ Π½Π΅ΡΠ²Π½ΡΡ
. ΠΠ½ΠΈ ΠΏΠΎΡΠ²Π»ΡΡΡΡΡ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΈΠ·-Π·Π° Π²Π½Π΅ΡΠ½ΠΈΡ
Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ Π±Π΅Π· ΡΠΈΠΏΠΎΠ² ΠΈΠ»ΠΈ Π·Π»ΠΎΡΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΡ as any.
ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ type-coverage ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠΈΠΏΠ°Ρ
ΠΈΠ· TypeScript-ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΠ½ ΡΡΠΈΡΠ°Π΅Ρ, ΠΊΠ°ΠΊΠ°Ρ Π΄ΠΎΠ»Ρ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΎΠ² ΠΈΠΌΠ΅Π΅Ρ ΡΠΈΠΏ, ΠΎΡΠ»ΠΈΡΠ½ΡΠΉ ΠΎΡ any, ΠΈ Π²ΡΠ²ΠΎΠ΄ΠΈΡ ΠΏΡΠΎΡΠ΅Π½Ρ ΠΏΠΎΠΊΡΡΡΠΈΡ ΡΠΈΠΏΠ°ΠΌΠΈ.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΈ Π·Π°ΠΏΡΡΠΊ:
npm install -D type-coverage npx type-coverage --at-least 95 --detail --strict
Π€Π»Π°Π³ΠΈ:
--at-least 95 β Π·Π°Π²Π΅ΡΡΠ°Π΅Ρ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Ρ ΠΎΡΠΈΠ±ΠΊΠΎΠΉ, Π΅ΡΠ»ΠΈ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ ΡΠΈΠΏΠ°ΠΌΠΈ Π½ΠΈΠΆΠ΅ 95%. ΠΠ»Π°Π³ΠΎΠ΄Π°ΡΡ ΡΡΠΎΠΌΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π² CI.
--detail β Π²ΡΠ²ΠΎΠ΄ΠΈΡ ΡΠ°ΠΉΠ»Ρ, ΡΡΡΠΎΠΊΠΈ ΠΈ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ ΡΡΠΈΡΠ°Π΅Ρ Π½Π΅ΠΏΠΎΠΊΡΡΡΡΠΌΠΈ ΡΠΈΠΏΠ°ΠΌΠΈ.
--strict β ΡΠ°ΡΡΠΈΡΡΠ΅Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ: ΡΡΠΈΡΡΠ²Π°Π΅Ρ Π²Π»ΠΎΠΆΠ΅Π½Π½ΡΠ΅ any, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ Promise<any> ΠΈ any[], Π° ΡΠ°ΠΊΠΆΠ΅ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ Π½Π΅Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠ΅ ΡΡΠ²Π΅ΡΠΆΠ΄Π΅Π½ΠΈΡ ΡΠΈΠΏΠΎΠ², ΡΡΠ²Π΅ΡΠΆΠ΄Π΅Π½ΠΈΡ ΠΎ ΡΠΎΠΌ, ΡΡΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π½Π΅ ΡΠ°Π²Π½ΠΎ null ΠΈΠ»ΠΈ undefined (!), ΠΈ ΡΠ»ΠΈΡΠΊΠΎΠΌ ΡΠΈΡΠΎΠΊΠΈΠ΅ ΡΠΈΠΏΡ Object ΠΈ {}.
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ type-coverage ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π·Π°ΡΠΈΠΊΡΠΈΡΠΎΠ²Π°ΡΡ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎ Π΄ΠΎΠΏΡΡΡΠΈΠΌΡΠΉ ΡΡΠΎΠ²Π΅Π½Ρ ΠΏΠΎΠΊΡΡΡΠΈΡ ΡΠΈΠΏΠ°ΠΌΠΈ ΠΏΡΠΈ ΡΠ°Π±ΠΎΡΠ΅ Ρ ΡΠ½Π°ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½Π½ΡΠΌ ΠΊΠΎΠ΄ΠΎΠΌ ΠΈ Π½Π΅ Π΄ΠΎΠΏΡΡΡΠΈΡΡ Π΅Π³ΠΎ ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΡ.
ΠΡΠ΅Π΄ΡΡΠ°Π²ΠΈΠΌ, ΡΡΠΎ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΈΡ
ΠΎΠ΄ΠΈΡ ΠΈΠ· req.body, ΠΏΡΠΎΡ
ΠΎΠ΄ΠΈΡ ΡΠ΅ΡΠ΅Π· Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΈ ΠΏΠΎΠΏΠ°Π΄Π°Π΅Ρ Π² ΡΡΠ½ΠΊΡΠΈΡ, Π²ΡΠΏΠΎΠ»Π½ΡΡΡΡΡ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ. ΠΡΠΎΡΡΠΎΠ΅ ΡΠΎΠΏΠΎΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΡ
ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΠΉ ΠΌΠΎΠΆΠ΅Ρ Π·Π°ΠΌΠ΅ΡΠΈΡΡ ΠΎΠΏΠ°ΡΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ², Π½ΠΎ Π½Π΅ Π²ΡΠ΅Π³Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ, ΠΎΡΠΊΡΠ΄Π° ΠΏΡΠΈΡΠ»ΠΈ Π΄Π°Π½Π½ΡΠ΅.
ΠΠ½Π°Π»ΠΈΠ·, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅Ρ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΠΏΠΎ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅, Π½Π°Π·ΡΠ²Π°Π΅ΡΡΡ Π°Π½Π°Π»ΠΈΠ·ΠΎΠΌ ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ (Data Flow Analysis, DFA). ΠΠ΄ΠΈΠ½ ΠΈΠ· Π΅Π³ΠΎ Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ² β Taint Analysis, ΠΈΠ»ΠΈ Π°Π½Π°Π»ΠΈΠ· ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ : ΠΎΠ½ ΠΏΡΠΎΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅Ρ, ΠΊΠ°ΠΊ Π½Π΅ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ Π΄ΠΎΡ ΠΎΠ΄ΡΡ Π΄ΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ, Π² ΠΊΠΎΡΠΎΡΡΡ ΠΌΠΎΠ³ΡΡ ΡΡΠ°ΡΡ ΠΎΠΏΠ°ΡΠ½ΡΠΌΠΈ.
Semgrep β ΠΌΡΠ»ΡΡΠΈΡΠ·ΡΡΠ½ΡΠΉ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π° Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΌΠΈ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌΠΈ Π½Π° YAML, ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡ ΠΊΠΎΡΠΎΡΡΡ ΠΏΠΎΡ ΠΎΠΆ Π½Π° ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌΡΠΉ ΠΊΠΎΠ΄. ΠΡΠΈ ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ ΡΠ΅ΡΠ΅Π· CLI ΠΈ ΠΎΠΏΠΈΡΡΠ²Π°ΡΡ Ρ ΠΈΡ ΠΏΠΎΠΌΠΎΡΡΡ Π°Π½Π°Π»ΠΈΠ· ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ .
Π ΡΠ°ΠΊΠΈΡ
ΠΏΡΠ°Π²ΠΈΠ»Π°Ρ
source β ΠΌΠ΅ΡΡΠΎ, ΠΎΡΠΊΡΠ΄Π° ΠΏΡΠΈΡ
ΠΎΠ΄ΡΡ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅; sink β ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ, Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΎΠ½ΠΈ ΠΌΠΎΠ³ΡΡ ΡΡΠ°ΡΡ ΠΎΠΏΠ°ΡΠ½ΡΠΌΠΈ; sanitizer β ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΠΈΠ»ΠΈ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΏΠΎΡΠ»Π΅ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ Π΄Π°Π½Π½ΡΠ΅ ΡΡΠΈΡΠ°ΡΡΡΡ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠΌΠΈ.
Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΡΠΉ ΡΠΏΠΎΡΠΎΠ± ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ:
pipx install semgrep
ΠΠ° macOS ΡΠ°ΠΊΠΆΠ΅ Π΄ΠΎΡΡΡΠΏΠ΅Π½ Homebrew:
brew install semgrep
ΠΡΡΡΠΎΠΉ catch ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠ²Π°Π΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅, Π½ΠΎ Π½ΠΈΠΊΠ°ΠΊ Π΅Π³ΠΎ Π½Π΅ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ:
async function loadData() { try { await requestData(); } catch (error) { // ΠΡΠΈΠ±ΠΊΠ° Π½Π΅ ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°Π½Π° } }
Π’Π°ΠΊΠΎΠΉ ΠΊΠΎΠ΄ Π½Π΅ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΡ ΠΊ Π½Π΅ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°Π½Π½ΠΎΠΌΡ ΠΎΡΠΊΠ»ΠΎΠ½Π΅Π½ΠΈΡ Promise, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΎΡΠΈΠ±ΠΊΠ° Π±ΡΠ»Π° ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΠ΅Π½Π°. ΠΠ΄Π½Π°ΠΊΠΎ ΡΡΠΎ ΡΠΊΡΡΠ²Π°Π΅Ρ ΡΠ±ΠΎΠΈ, ΡΡΠ»ΠΎΠΆΠ½ΡΠ΅Ρ ΠΎΡΠ»Π°Π΄ΠΊΡ ΠΈ ΠΌΠΎΠΆΠ΅Ρ ΡΠΎΠ·Π΄Π°ΡΡ Π²ΠΏΠ΅ΡΠ°ΡΠ»Π΅Π½ΠΈΠ΅, ΡΡΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠΈΠ»Π°ΡΡ ΡΡΠΏΠ΅ΡΠ½ΠΎ.
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ Π² ΡΠ°ΠΉΠ»Π΅ .semgrep/rules/no-empty-catch.yaml:
rules: - id: javascript-no-empty-catch languages: - javascript - typescript message: >- ΠΡΡΡΠΎΠΉ Π±Π»ΠΎΠΊ catch ΠΏΠΎΠ΄Π°Π²Π»ΡΠ΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅. ΠΠ±ΡΠ°Π±ΠΎΡΠ°ΠΉΡΠ΅ ΠΎΡΠΈΠ±ΠΊΡ, Π·Π°Π»ΠΎΠ³ΠΈΡΡΠΉΡΠ΅ Π΅Ρ ΠΈΠ»ΠΈ ΡΠ²Π½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ Π΄Π°Π»ΡΡΠ΅. severity: MEDIUM pattern-either: - pattern: | try { ... } catch ($ERR) {} - pattern: | try { ... } catch {}
ΠΡΠ°Π²ΠΈΠ»ΠΎ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΠΏΡΡΡΡΠ΅ Π±Π»ΠΎΠΊΠΈ catch ΠΊΠ°ΠΊ Π² ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΠΎΠΌ, ΡΠ°ΠΊ ΠΈ Π² Π°ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅. ΠΠ»ΠΎΠΊΠΈ, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΠ΅ ΡΠΎΠ»ΡΠΊΠΎ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ, ΡΠ°ΠΊΠΆΠ΅ ΡΡΠΈΡΠ°ΡΡΡΡ ΠΏΡΡΡΡΠΌΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΉ Π½Π΅ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅.
ΠΠ°ΠΏΡΡΠΊ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ:
semgrep scan --config .semgrep/rules/no-empty-catch.yaml .
ΠΠ»Ρ ΠΏΠΎΠΈΡΠΊΠ° Π½Π΅ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°Π½Π½ΡΡ
Promise, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² Π²ΡΠ·ΠΎΠ²ΠΎΠ² async-ΡΡΠ½ΠΊΡΠΈΠΉ Π±Π΅Π· await, return ΠΈΠ»ΠΈ ΡΠ²Π½ΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ° Π² TypeScript-ΠΏΡΠΎΠ΅ΠΊΡΠ°Ρ
Π»ΡΡΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΈΠΏΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ ESLint:
'@typescript-eslint/no-floating-promises': 'error'
Semgrep Π² ΡΠ°ΠΊΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ Π΄Π»Ρ Π±ΠΎΠ»Π΅Π΅ ΡΠΏΠ΅ΡΠΈΡΠΈΡΠ½ΡΡ
ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΡ
ΡΠΎΠ³Π»Π°ΡΠ΅Π½ΠΈΠΉ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ Π΄Π»Ρ Π·Π°ΠΏΡΠ΅ΡΠ° ΠΏΡΡΡΡΡ
catch, ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠ³ΠΎ Π»ΠΎΠ³Π³Π΅ΡΠ° ΠΈΠ»ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΡΡΡΡΠΊΡΡΡΡ ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΎΠ² ΠΎΡΠΈΠ±ΠΎΠΊ.
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ Π΄Π»Ρ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΡΠΈΠΏΠ° ΠΎΠΏΠ°ΡΠ½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ: ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΡ
Π΄Π°Π½Π½ΡΡ
Π² unsafeExec(...) Π±Π΅Π· ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎΠΉ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ.
.semgrep/rules/taint-user-input.yaml:
rules: - id: taint-user-input-to-command languages: [typescript, javascript] severity: ERROR message: >- ΠΠ΅ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡΡΡ Π² ΠΎΠΏΠ°ΡΠ½ΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ. mode: taint pattern-sources: - pattern: req.body - pattern: req.query - pattern: req.params pattern-sanitizers: - pattern: validateCommandArgument(...) pattern-sinks: - pattern: unsafeExec(...)
ΠΠ°ΠΏΡΡΠΊ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Semgrep Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ:
semgrep --config=.semgrep/rules/
ΠΡΠ»ΠΈ Π² ΠΊΠΎΠ΄Π΅ ΠΏΠΎΡΠ²ΠΈΡΡΡ ΡΠ΅ΠΏΠΎΡΠΊΠ°:
function handleRequest(req, res) { const rawData = req.body.data; // ... ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΉ ΠΊΠΎΠ΄ ... const formatted = `${rawData}`; // ... unsafeExec(formatted); }
Semgrep ΠΎΡΡΠ»Π΅Π΄ΠΈΡ ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΌΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌΠΈ Π²Π½ΡΡΡΠΈ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΈ ΡΠΊΠ°ΠΆΠ΅Ρ Π½Π° Π²ΡΠ·ΠΎΠ² unsafeExec(formatted).
Π‘ΠΏΠΎΡΠΎΠ± ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΈΠ»ΠΈ ΠΎΡΠΈΡΡΠΊΠΈ Π²ΡΠ΅Π³Π΄Π° Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΉ ΠΎΠΏΠ°ΡΠ½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ: HTML, SQL ΠΈ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΠΎΠ±ΠΎΠ»ΠΎΡΠΊΠΈ ΡΡΠ΅Π±ΡΡΡ ΡΠ°Π·Π½ΡΡ ΡΡΡΠ°ΡΠ΅Π³ΠΈΠΉ.
ΠΠΎΡΠΎΠ²ΡΡ
ΠΏΡΠ°Π²ΠΈΠ» Π»ΠΈΠ½ΡΠ΅ΡΠΎΠ² ΠΈΠ½ΠΎΠ³Π΄Π° Π½Π΅ Ρ
Π²Π°ΡΠ°Π΅Ρ. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Π² ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ ΡΠ²ΠΎΠΈ Π°ΡΡ
ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠ΅ ΡΠΎΠ³Π»Π°ΡΠ΅Π½ΠΈΡ: ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΊΠ»Π°ΡΡ Ρ Π΄Π΅ΠΊΠΎΡΠ°ΡΠΎΡΠΎΠΌ @Injectable() ΠΎΠ±ΡΠ·Π°Π½ ΠΈΠΌΠ΅ΡΡ ΡΠ²Π½ΠΎ Π·Π°Π΄Π°Π½Π½ΡΠΉ Π»ΠΎΠ³Π³Π΅Ρ Π² ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ΅, Π° Π²ΡΠ΅ ΠΊΠ»Π°ΡΡΡ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ Π΄Π°Π½Π½ΡΡ
(Data Transfer Object, DTO) ΠΈΠ· ΠΏΠ°ΠΏΠΊΠΈ src/dto Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΠΎΡΡΠΎΡΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΈΠ· readonly ΠΏΠΎΠ»Π΅ΠΉ ΠΈ Π·Π°ΠΊΠ°Π½ΡΠΈΠ²Π°ΡΡΡΡ Π½Π° Dto.
Π Π΅ΡΠ°ΡΡ ΡΠ°ΠΊΠΈΠ΅ Π·Π°Π΄Π°ΡΠΈ ΡΠ΅Π³ΡΠ»ΡΡΠ½ΡΠΌΠΈ Π²ΡΡΠ°ΠΆΠ΅Π½ΠΈΡΠΌΠΈ Π½Π΅Π½Π°Π΄ΡΠΆΠ½ΠΎ, Π° ΠΏΠΈΡΠ°ΡΡ ΠΏΠ»Π°Π³ΠΈΠ½ Π΄Π»Ρ ESLint ΡΠΎΠ»ΡΠΊΠΎ Π½Π° ΡΡΠΎΠ²Π½Π΅ AST Π³ΡΠΎΠΌΠΎΠ·Π΄ΠΊΠΎ. ΠΠ΄Π΅ΡΡ ΠΏΠΎΠ΄ΠΎΠΉΠ΄ΡΡ TypeScript Compiler API β ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΊ ΡΠ΅ΠΌ ΠΆΠ΅ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΌ Π΄Π΅ΡΠ΅Π²ΡΡΠΌ, ΡΠΈΠΌΠ²ΠΎΠ»Π°ΠΌ ΠΈ ΡΠΈΠΏΠ°ΠΌ, Ρ ΠΊΠΎΡΠΎΡΡΠΌΠΈ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ. ΠΠ»Ρ ΡΠ΄ΠΎΠ±ΡΡΠ²Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ ΠΎΠ±ΡΡΡΠΊΡ ts-morph.
ts-morph ΡΠΏΡΠΎΡΠ°Π΅Ρ ΠΎΠ±Ρ
ΠΎΠ΄ AST β ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΡΡ ΡΠ°Π±ΠΎΡΡ Ρ ΡΠ·Π»Π°ΠΌΠΈ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π΄Π΅ΡΠ΅Π²Π°, Π° ΡΠ°ΠΊΠΆΠ΅ ΡΠ°Π±ΠΎΡΡ Ρ ΡΠΈΠΌΠ²ΠΎΠ»Π°ΠΌΠΈ, ΡΠΈΠΏΠ°ΠΌΠΈ ΠΈ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ ΠΏΡΠΎΠ΅ΠΊΡΠ°.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°:
npm install --save-dev ts-morph ts-node
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΡΠΊΡΠΈΠΏΡ scripts/analyze-architecture.ts. ΠΠ½ Π±ΡΠ΄Π΅Ρ ΠΏΡΠΎΠ²Π΅ΡΡΡΡ Π΄Π²Π° ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΡ
ΠΏΡΠ°Π²ΠΈΠ»Π°:
ΠΠΌΠ΅Π½Π° Π²ΡΠ΅Ρ
DTO-ΠΊΠ»Π°ΡΡΠΎΠ² Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΈΠΌΠ΅ΡΡ ΡΡΡΡΠΈΠΊΡ Dto, Π° Π²ΡΠ΅ ΠΈΡ
ΡΠ²ΠΎΠΉΡΡΠ²Π° Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ readonly.
ΠΠΈ ΠΎΠ΄ΠΈΠ½ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ΡΠ²ΠΈΡΠ° (*Service.ts) Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ any ΠΈΠ»ΠΈ Π½Π΅ΡΠ²Π½ΡΠΉ Promise<any>.
Π ΡΡΠΎΠΌ Π΄Π΅ΠΌΠΎΠ½ΡΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ getProperties() ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΡΠ²ΠΎΠΉΡΡΠ²Π°, ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΠ΅ Π½Π΅ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²Π΅Π½Π½ΠΎ Π² ΠΊΠ»Π°ΡΡΠ΅. Π‘Π²ΠΎΠΉΡΡΠ²Π°, ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΠ΅ ΡΠ΅ΡΠ΅Π· ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ°, ΠΈ ΡΠ½Π°ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π° Π½Π΅ ΡΡΠΈΡΡΠ²Π°ΡΡΡΡ. ΠΠ»Ρ ΠΏΡΠΎΡΡΠΎΡΡ Promise<any> Π½ΠΈΠΆΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΡΡΡ ΡΠ΅ΡΠ΅Π· ΡΡΡΠΎΠΊΠΎΠ²ΠΎΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠΈΠΏΠ°. Π Π°Π½Π°Π»ΠΈΠ·Π°ΡΠΎΡΠ΅ Π΄Π»Ρ ΡΠ΅Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π»ΡΡΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΡΡΡ ΡΡΡΡΠΊΡΡΡΡ ΡΠΈΠΏΠ° ΠΈ Π΅Π³ΠΎ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ ΡΠ΅ΡΠ΅Π· TypeScript Compiler API.
import { Project, ClassDeclaration, PropertyDeclaration } from 'ts-morph'; import * as path from 'path'; // ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠ΅ΠΌ ΠΏΡΠΎΠ΅ΠΊΡ Ρ ΠΏΡΠΈΠ²ΡΠ·ΠΊΠΎΠΉ ΠΊ tsconfig.json const project = new Project({ tsConfigFilePath: path.join(__dirname, '../tsconfig.json'), }); let hasErrors = false; function logError(filePath: string, line: number, message: string) { console.error(`\x1b[31m[ARCH ERROR]\x1b[0m ${filePath}:${line} - ${message}`); hasErrors = true; } console.log('ΠΠ°ΠΏΡΡΠΊ Π°Π½Π°Π»ΠΈΠ·Π° Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΡ ΠΏΡΠ°Π²ΠΈΠ»...'); const sourceFiles = project.getSourceFiles(); sourceFiles.forEach((sourceFile) => { const filePath = sourceFile.getFilePath(); // ------------------------------------------------------------------- // ΠΡΠ°Π²ΠΈΠ»ΠΎ 1: ΠΡΠΎΠ²Π΅ΡΠΊΠ° DTO Π² Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΠΈ src/dto // ------------------------------------------------------------------- if (filePath.includes('/dto/')) { const classes = sourceFile.getClasses(); classes.forEach((cls: ClassDeclaration) => { const className = cls.getName(); if (!className || !className.endsWith('Dto')) { const line = cls.getStartLineNumber(); logError(filePath, line, `ΠΠΌΡ ΠΊΠ»Π°ΡΡΠ° DTO "${className || 'Anonymous'}" Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΎΠΊΠ°Π½ΡΠΈΠ²Π°ΡΡΡΡ Π½Π° "Dto".`); } // ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΊΠ»Π°ΡΡΠ° cls.getProperties().forEach((prop: PropertyDeclaration) => { if (!prop.isReadonly()) { const line = prop.getStartLineNumber(); logError( filePath, line, `Π‘Π²ΠΎΠΉΡΡΠ²ΠΎ "${prop.getName()}" Π² DTO "${className}" Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΈΠΌΠ΅ΡΡ ΠΌΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΎΡ readonly!` ); } }); }); } // ------------------------------------------------------------------- // ΠΡΠ°Π²ΠΈΠ»ΠΎ 2: ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΡΠ΅ΡΠ²ΠΈΡΠΎΠ² (*Service.ts) Π½Π° Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΡΠΉ `any` // ------------------------------------------------------------------- if (filePath.endsWith('Service.ts')) { const classes = sourceFile.getClasses(); classes.forEach((cls) => { cls.getMethods().forEach((method) => { const returnType = method.getReturnType(); const returnTypeString = returnType.getText(); const methodName = method.getName(); const line = method.getStartLineNumber(); if (returnType.isAny()) { logError( filePath, line, `ΠΠ΅ΡΠΎΠ΄ "${methodName}" ΡΠ΅ΡΠ²ΠΈΡΠ° "${cls.getName()}" Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΠΈΠΏΠ° "any". Π£ΠΊΠ°ΠΆΠΈΡΠ΅ ΡΠΎΡΠ½ΡΠΉ ΡΠΈΠΏ!` ); } if (returnTypeString === 'Promise<any>') { logError( filePath, line, `ΠΠ΅ΡΠΎΠ΄ "${methodName}" ΡΠ΅ΡΠ²ΠΈΡΠ° "${cls.getName()}" Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ "${returnTypeString}" Ρ Π²Π»ΠΎΠΆΠ΅Π½Π½ΡΠΌ "any". Π£ΠΊΠ°ΠΆΠΈΡΠ΅ ΡΠΎΡΠ½ΡΠΉ ΡΠΈΠΏ!` ); } }); }); } }); if (hasErrors) { console.error('ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π·Π°Π²Π΅ΡΡΠ΅Π½Π° Ρ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌΠΈ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΡ.'); process.exit(1); } else { console.log('ΠΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠΉ Π°Π½Π°Π»ΠΈΠ· ΡΡΠΏΠ΅ΡΠ½ΠΎ ΠΏΡΠΎΠΉΠ΄Π΅Π½!'); process.exit(0); }
ΠΠ»Ρ Π·Π°ΠΏΡΡΠΊΠ° Π°Π½Π°Π»ΠΈΠ·Π°ΡΠΎΡΠ° Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π² package.json:
{ "scripts": { "lint:arch": "ts-node scripts/analyze-architecture.ts" } }
Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ ΡΠ΄ΠΎΠ±Π΅Π½ Π΄Π»Ρ ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΡ ΠΏΡΠ°Π²ΠΈΠ», ΠΊΠΎΡΠΎΡΡΠ΅ ΡΠ»ΠΎΠΆΠ½ΠΎ ΠΈΠ»ΠΈ Π½Π΅ΡΠ΄ΠΎΠ±Π½ΠΎ Π²ΡΡΠ°Π·ΠΈΡΡ Π³ΠΎΡΠΎΠ²ΡΠΌΠΈ Π»ΠΈΠ½ΡΠ΅ΡΠ°ΠΌΠΈ: ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠΎΠ²Π΅ΡΡΡΡ Π΄Π΅ΠΊΠΎΡΠ°ΡΠΎΡΡ, JSDoc-ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΈ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°ΡΡ Π½Π΅ΠΆΠ΅Π»Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ Π²ΡΠ·ΠΎΠ²Ρ Π² CI ΠΈΠ»ΠΈ Π½Π° ΡΡΠ°ΠΏΠ΅ ΡΠ±ΠΎΡΠΊΠΈ.
StrykerJS ΡΠΎΡΠΌΠ°Π»ΡΠ½ΠΎ Π½Π΅ ΠΎΡΠ½ΠΎΡΠΈΡΡΡ ΠΊ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΌΡ Π°Π½Π°Π»ΠΈΠ·Ρ, Π½ΠΎ Ρ ΠΎΡΠΎΡΠΎ Π΄ΠΎΠΏΠΎΠ»Π½ΡΠ΅Ρ ΠΎΠΏΠΈΡΠ°Π½Π½ΡΠ΅ Π²ΡΡΠ΅ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ. Π‘ Π΅Π³ΠΎ ΠΏΠΎΠΌΠΎΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΠ΅Π½ΠΈΡΡ, Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ Π»ΠΈ ΡΠ΅ΡΡΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ²Π°ΡΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ. ΠΠ±ΡΡΠ½ΠΎΠ΅ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ ΠΊΠΎΠ΄Π° (Code Coverage) ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ ΡΠΎΠ»ΡΠΊΠΎ ΡΠ°ΠΊΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ ΡΡΡΠΎΠΊ Π²ΠΎ Π²ΡΠ΅ΠΌΡ ΡΠ΅ΡΡΠΎΠ², Π½ΠΎ Π½Π΅ Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ ΠΊΠ°ΡΠ΅ΡΡΠ²Π° ΡΠ°ΠΌΠΈΡ ΡΠ΅ΡΡΠΎΠ².
ΠΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡΠ΅ΠΆΠ΄Π΅ Π²ΡΠ΅Π³ΠΎ ΠΎΡΠ΅Π½ΠΈΠ²Π°Π΅Ρ ΠΊΠ°ΡΠ΅ΡΡΠ²ΠΎ ΡΠ΅ΡΡΠΎΠ². ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ Π²Π½ΠΎΡΠΈΡ Π½Π°ΠΌΠ΅ΡΠ΅Π½Π½ΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ (ΠΌΡΡΠ°ΡΠΈΠΈ) Π² ΠΈΡΡ ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄ ΠΈ Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ ΡΠ΅ΡΡΡ. ΠΡΠ»ΠΈ Ρ ΠΎΡΡ Π±Ρ ΠΎΠ΄ΠΈΠ½ ΡΠ΅ΡΡ ΡΠΏΠ°Π», ΠΌΡΡΠ°Π½Ρ ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ ΡΡΠ°ΡΡΡ Killed: ΡΠ΅ΡΡΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅. ΠΡΠ»ΠΈ Π²ΡΠ΅ ΡΠ΅ΡΡΡ ΠΏΡΠΎΡΠ»ΠΈ, ΠΌΡΡΠ°Π½Ρ ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ ΡΡΠ°ΡΡΡ Survived, ΡΡΠΎ ΠΎΠ±ΡΡΠ½ΠΎ ΡΠΊΠ°Π·ΡΠ²Π°Π΅Ρ Π½Π° ΠΏΡΠΎΠ±Π΅Π» Π² ΡΠ΅ΡΡΠ°Ρ .
Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ StrykerJS ΠΈ ΠΏΠ°ΠΊΠ΅Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΈ Ρ Vitest:
npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ stryker.config.json:
{ "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json", "mutate": [ "src/**/*.ts", "!src/**/*.spec.ts", "!src/**/*.dto.ts" ], "testRunner": "vitest", "reporters": ["html", "clear-text", "progress"], "coverageAnalysis": "perTest" }
ΠΡΠΈ Π·Π°ΠΌΠ΅Π½Π΅ ΡΡΠ»ΠΎΠ²ΠΈΠΉ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, if (a > b) Π½Π° if (a >= b) ΠΈΠ»ΠΈ return true Π½Π° return false) Stryker ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π² Π»ΠΎΠ³ΠΈΠΊΠ΅ Π½Π΅ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ²Π°ΡΡΡΡ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΌΠΈ ΡΠ΅ΡΡΠ°ΠΌΠΈ.
Π§Π΅ΠΌ Π±ΠΎΠ»ΡΡΠ΅ Π°Π½Π°Π»ΠΈΠ·Π°ΡΠΎΡΠΎΠ², ΡΠ΅ΠΌ Π²Π°ΠΆΠ½Π΅Π΅ ΡΠΊΠΎΡΠΎΡΡΡ ΠΈΡ ΡΠ°Π±ΠΎΡΡ. ΠΠ½Π°Π»ΠΈΠ· ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ ΠΈΠ»ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° Π²ΡΠ΅Π³ΠΎ Π³ΡΠ°ΡΠ° Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ ΠΌΠΎΠΆΠ΅Ρ Π·Π°Π½ΠΈΠΌΠ°ΡΡ Π·Π°ΠΌΠ΅ΡΠ½ΠΎΠ΅ Π²ΡΠ΅ΠΌΡ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΊΠΎΠ΄ΠΎΠ²ΠΎΠΉ Π±Π°Π·Ρ ΠΈ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠ°.
ΠΠΎΡΡΠΎΠΌΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΡΠ΄ΠΎΠ±Π½ΠΎ ΡΠ°Π·Π΄Π΅Π»ΠΈΡΡ Π½Π° Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠΎΠ²Π½Π΅ΠΉ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΡΡΠΎΠΈΠΌΠΎΡΡΠΈ ΠΈΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ:
ΠΠ΅ΡΠ΅Π΄ ΠΊΠΎΠΌΠΌΠΈΡΠΎΠΌ β Π±ΡΡΡΡΡΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ: ESLint Π΄Π»Ρ ΡΠ°ΠΉΠ»ΠΎΠ², Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π½ΡΡ
Π² ΠΊΠΎΠΌΠΌΠΈΡ, ΠΈ ΠΈΠ½ΠΊΡΠ΅ΠΌΠ΅Π½ΡΠ°Π»ΡΠ½Π°Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° tsc.
ΠΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΈ Π² CI Π΄Π»Ρ Pull Request β ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ: Knip, dependency-cruiser, Semgrep, ts-morph, type-coverage.
ΠΠΎ ΡΠ°ΡΠΏΠΈΡΠ°Π½ΠΈΡ β ΡΠ΅ΡΡΡΡΠΎΡΠΌΠΊΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ: StrykerJS ΠΈ Π±ΠΎΠ»Π΅Π΅ ΡΡΠΆΡΠ»ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΠΈ.
Git-Ρ ΡΠΊ β ΠΊΠΎΠΌΠ°Π½Π΄Π°, ΠΊΠΎΡΠΎΡΡΡ Git Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ ΠΏΠ΅ΡΠ΅Π΄ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΡΠΌ ΡΠΎΠ±ΡΡΠΈΠ΅ΠΌ. ΠΠ΄Π΅ΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Husky ΠΈ lint-staged ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠΌ Π±ΡΡΡΡΡΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΊ ΡΡΠ°ΠΏΡ ΠΊΠΎΠΌΠΌΠΈΡΠ°:
1 - Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Husky ΠΈ lint-staged:
npm install -D husky lint-staged npx husky init
2 - ΠΠ°ΡΡΡΠΎΠΈΠΌ .husky/pre-commit:
npx lint-staged
3 - Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ lint-staged.config.mjs:
export default { '*.{ts,tsx}': [ 'eslint --fix', () => 'tsc --noEmit' ] };
ESLint Π±ΡΠ΄Π΅Ρ Π·Π°ΠΏΡΡΠΊΠ°ΡΡΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ TypeScript-ΡΠ°ΠΉΠ»ΠΎΠ², Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π½ΡΡ
Π² ΠΊΠΎΠΌΠΌΠΈΡ. tsc --noEmit Π·Π΄Π΅ΡΡ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ Π²Π΅ΡΡ TypeScript-ΠΏΡΠΎΠ΅ΠΊΡ, Π° Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΠΈ ΡΠ°ΠΉΠ»Ρ, Π½ΠΎ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ, ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ Π² ΠΊΠΎΠΌΠΌΠΈΡ ΠΏΠΎΠΏΠ°Π»ΠΈ ΡΠ°ΠΉΠ»Ρ .ts ΠΈΠ»ΠΈ .tsx. ΠΠ° Π±ΠΎΠ»ΡΡΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ ΡΠΈΠΏΠΎΠ² ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΡΠ°Π·ΡΠΌΠ½Π΅Π΅ ΠΏΠ΅ΡΠ΅Π½Π΅ΡΡΠΈ Π½Π° ΡΡΠ°ΠΏ ΠΏΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΈΠ»ΠΈ Π² CI.
Π‘Π»Π΅Π΄ΡΡΡΠΈΠΉ ΡΡΠΎΠ²Π΅Π½Ρ β ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ Π²ΡΠΏΠΎΠ»Π½ΡΡΡΡΡ ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΈ Pull Request.
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Π² package.json ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ±ΡΠ°ΡΡ ΠΈΡ
Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ:
{ "scripts": { "lint:arch": "depcruise --config .dependency-cruiser.cjs src && ts-node scripts/analyze-architecture.ts", "lint:semgrep": "semgrep scan --config .semgrep/rules/ .", "lint:knip": "knip", "type-coverage": "type-coverage --at-least 95 --detail --strict", "check:deep": "npm run lint:arch && npm run lint:semgrep && npm run lint:knip && npm run type-coverage" } }
Π ΠΎΠ±ΡΡΠ½ΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅ CI ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ:
npm ci npm run check:deep
Π’Π°ΠΊ ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π½Π΅ Π·Π°ΠΌΠ΅Π΄Π»ΡΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΊΠΎΠΌΠΌΠΈΡ, Π½ΠΎ ΠΏΡΠΈ ΡΡΠΎΠΌ ΠΌΠΎΠ³ΡΡ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°ΡΡ Pull Request ΠΏΡΠΈ Π½Π°ΡΡΡΠ΅Π½ΠΈΠΈ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΡ ΠΏΡΠ°Π²ΠΈΠ», ΠΏΠΎΡΠ²Π»Π΅Π½ΠΈΠΈ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° ΠΈΠ»ΠΈ ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΠΈ ΠΏΠΎΠΊΡΡΡΠΈΡ ΡΠΈΠΏΠΎΠ².
ΠΠ°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΡΠ΅ΡΡΡΡΠΎΡΠΌΠΊΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ StrykerJS ΠΈ Π±ΠΎΠ»Π΅Π΅ ΡΡΠΆΡΠ»ΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΠΈ, ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎ ΠΏΠΎ ΡΠ°ΡΠΏΠΈΡΠ°Π½ΠΈΡ β Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠ°Π· Π² ΡΡΡΠΊΠΈ. Π’Π°ΠΊ ΡΠ΅ΡΡΡΡΠΎΡΠΌΠΊΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π²ΡΠΏΠΎΠ»Π½ΡΡΡΡΡ ΡΠ΅Π³ΡΠ»ΡΡΠ½ΠΎ, Π½ΠΎ Π½Π΅ Π²Π»ΠΈΡΡΡ Π½Π° ΡΠΊΠΎΡΠΎΡΡΡ ΠΏΠΎΠ²ΡΠ΅Π΄Π½Π΅Π²Π½ΠΎΠΉ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ.
Π‘Π²Π΅Π΄ΡΠΌ Π²ΡΠ΅ ΡΠ°Π·ΠΎΠ±ΡΠ°Π½Π½ΡΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΡ Π² Π΅Π΄ΠΈΠ½ΡΡ ΠΌΠ°ΡΡΠΈΡΡ Π²ΡΠ±ΠΎΡΠ°:
ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ | ΠΠ°ΠΊΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΡΠ΅ΡΠ°Π΅Ρ | ΠΠ° ΠΊΠ°ΠΊΠΎΠΌ ΡΡΠ°ΠΏΠ΅ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ | Π‘ΠΊΠΎΡΠΎΡΡΡ ΡΠ°Π±ΠΎΡΡ |
|---|---|---|---|
| ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ | ΠΠ΅ΡΠ΅Π΄ ΠΊΠΎΠΌΠΌΠΈΡΠΎΠΌ / Π² IDE | β‘β‘β‘ ΠΡΡΠΎΠΊΠ°Ρ (ΠΏΡΠΈ incremental) |
| ΠΡΠ°Π²ΠΈΠ»Π° ΠΏΠΎ ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠΌΡ Π΄Π΅ΡΠ΅Π²Ρ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠ΅ΠΉ ΠΎ ΡΠΈΠΏΠ°Ρ | ΠΠ΅ΡΠ΅Π΄ ΠΊΠΎΠΌΠΌΠΈΡΠΎΠΌ / Π² IDE | β‘β‘β‘ ΠΡΡΠΎΠΊΠ°Ρ |
| ΠΠΎΠ½ΡΡΠΎΠ»Ρ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΡ ΡΠ»ΠΎΡΠ² ΠΈ ΡΠΈΠΊΠ»ΠΈΡΠ΅ΡΠΊΠΈΡ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ | ΠΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ / Π² CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
| ΠΠΎΠ½ΡΡΠΎΠ»Ρ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΡ Π³ΡΠ°Π½ΠΈΡ ΠΈ ΠΈΠΌΠΏΠΎΡΡΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρ ΠΌΠΎΠ΄ΡΠ»ΡΠΌΠΈ | ΠΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ / Π² CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
| ΠΠΎΠΈΡΠΊ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°, ΡΠΊΡΠΏΠΎΡΡΠΎΠ² ΠΈ Π»ΠΈΡΠ½ΠΈΡ ΠΏΠ°ΠΊΠ΅ΡΠΎΠ² | ΠΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ / Π² CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
| ΠΠΎΠ½ΡΡΠΎΠ»Ρ ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½Π΅Π½ΠΈΡ | Π CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
Semgrep (CLI) | ΠΠΎΠΈΡΠΊ Π°Π½ΡΠΈΠΏΠ°ΡΡΠ΅ΡΠ½ΠΎΠ² ΠΈ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ | Π CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
| ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠ·ΠΊΠΎΡΠΏΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΡ Π±ΠΈΠ·Π½Π΅Ρ- ΠΈ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΡ ΠΏΡΠ°Π²ΠΈΠ» | Π CI Π΄Π»Ρ PR | β‘β‘ Π‘ΡΠ΅Π΄Π½ΡΡ |
| ΠΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠ°Ρ ΠΎΡΠ΅Π½ΠΊΠ° ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΡΡΠΈ ΡΠ΅ΡΡΠΎΠ² ΠΌΠ΅ΡΠΎΠ΄ΠΎΠΌ ΠΌΡΡΠ°ΡΠΈΠΉ | ΠΠΎ ΡΠ°ΡΠΏΠΈΡΠ°Π½ΠΈΡ | π’ ΠΠΈΠ·ΠΊΠ°Ρ |
Π‘ΠΊΠΎΡΠΎΡΡΡ ΡΠΊΠ°Π·Π°Π½Π° ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΈ ΠΌΠΎΠΆΠ΅Ρ ΡΡΡΠ΅ΡΡΠ²Π΅Π½Π½ΠΎ Π·Π°Π²ΠΈΡΠ΅ΡΡ ΠΎΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΏΡΠΎΠ΅ΠΊΡΠ°, ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π°Π½Π°Π»ΠΈΠ·Π°ΡΠΎΡΠ° ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΡΡΠ°.
Π£Π½ΠΈΠ²Π΅ΡΡΠ°Π»ΡΠ½ΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π°ΡΠΎΡΠ° Π½Π΅Ρ: ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ², Π³ΡΠ°Ρ ΠΈΠΌΠΏΠΎΡΡΠΎΠ², ΠΏΠΎΠΈΡΠΊ Π½Π΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°, ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΎΠΏΠ°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ ΠΈ ΠΊΠ°ΡΠ΅ΡΡΠ²ΠΎ ΡΠ΅ΡΡΠΎΠ² β ΡΠ°Π·Π½ΡΠ΅ ΠΊΠ»Π°ΡΡΡ Π·Π°Π΄Π°Ρ. ΠΠΎΡΡΠΎΠΌΡ Π½Π°Π±ΠΎΡ ΠΏΡΠΎΠ²Π΅ΡΠΎΠΊ ΡΡΠΎΠΈΡ ΠΏΠΎΠ΄Π±ΠΈΡΠ°ΡΡ ΠΏΠΎΠ΄ ΡΠΈΡΠΊΠΈ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ°.
ΠΠ΅ΡΡΠ²ΡΠ΅ ΠΈ Π±ΡΡΡΡΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΡΠ°Π·ΡΠΌΠ½ΠΎ ΠΎΡΡΠ°Π²Π»ΡΡΡ Π² Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠΌ ΡΠΈΠΊΠ»Π΅, ΠΏΡΠΎΠ΅ΠΊΡΠ½ΡΠ΅ β Π²ΡΠΏΠΎΠ»Π½ΡΡΡ Π² CI, Π° ΡΠ΅ΡΡΡΡΠΎΡΠΌΠΊΠΈΠ΅ β ΠΏΠΎ ΡΠ°ΡΠΏΠΈΡΠ°Π½ΠΈΡ. Π’Π°ΠΊ ΡΠ°ΡΡΡ ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠΈΡ ΡΡ Π·Π°ΠΌΠ΅ΡΠ°Π½ΠΈΠΉ ΠΏΠ΅ΡΠ΅Π½ΠΎΡΠΈΡΡΡ ΠΈΠ· ΡΡΡΠ½ΠΎΠΉ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΊΠΎΠ΄Π° Π² Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ, Π° ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ²Π°ΡΡΡΡ Π΄ΠΎ ΡΠ»ΠΈΡΠ½ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ.