tool-snippets 0.0.0-stage → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -2
- package/esm2020/public-api.mjs +5 -0
- package/esm2020/tool-snippets.mjs +5 -0
- package/fesm2015/tool-snippets.mjs +4 -0
- package/fesm2015/tool-snippets.mjs.map +1 -0
- package/fesm2020/tool-snippets.mjs +8 -0
- package/fesm2020/tool-snippets.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/package.json +25 -3
- package/public-api.d.ts +1 -0
- package/vscode/install-snippets.js +36 -0
- package/vscode/tool-frontend.code-snippets +435 -0
- package/webstorm/_tool-frontend.xml +211 -0
- package/webstorm/install-snippets.js +88 -0
package/README.md
CHANGED
|
@@ -1,3 +1,132 @@
|
|
|
1
|
-
|
|
1
|
+
Для WebStorm и VS Code:
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```markdown
|
|
4
|
+
# ToolSnippets
|
|
5
|
+
|
|
6
|
+
Библиотека сниппетов для **WebStorm** и **Visual Studio Code** (Angular/TypeScript).
|
|
7
|
+
|
|
8
|
+
## Установка
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -D tool-snippets
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
После установки выполните команду для нужной IDE:
|
|
15
|
+
|
|
16
|
+
### WebStorm
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run SNIPPETS:webstorm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Скрипт находит все установленные версии WebStorm и копирует файл `_tool-frontend.xml` в папку `templates` каждой из них.
|
|
23
|
+
|
|
24
|
+
> ⚠️ После установки перезапустите WebStorm, чтобы сниппеты применились.
|
|
25
|
+
|
|
26
|
+
### VS Code
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run SNIPPETS:vscode
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Скрипт копирует файл `tool-frontend.code-snippets` в пользовательскую папку сниппетов VS Code:
|
|
33
|
+
- **Windows:** `%APPDATA%\Code\User\snippets\`
|
|
34
|
+
- **macOS:** `~/Library/Application Support/Code/User/snippets/`
|
|
35
|
+
- **Linux:** `~/.config/Code/User/snippets/`
|
|
36
|
+
|
|
37
|
+
> ⚠️ После установки перезагрузите окно VS Code: `Ctrl+Shift+P` → **Developer: Reload Window**.
|
|
38
|
+
|
|
39
|
+
## Использование
|
|
40
|
+
|
|
41
|
+
В редакторе начните вводить префикс сниппета (например `tool_deb`, `tool#forEach`, `tool___subscribe`) — IntelliSense предложит подстановку.
|
|
42
|
+
|
|
43
|
+
## Структура пакета
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
tool-snippets/
|
|
47
|
+
├── webstorm/
|
|
48
|
+
│ ├── _tool-frontend.xml
|
|
49
|
+
│ └── install-snippets.js
|
|
50
|
+
├── vscode/
|
|
51
|
+
│ ├── tool-frontend.code-snippets
|
|
52
|
+
│ └── install-snippets.js
|
|
53
|
+
├── package.json
|
|
54
|
+
└── README.md
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Разработка
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# сборка
|
|
61
|
+
ng build tool-snippets
|
|
62
|
+
|
|
63
|
+
# публикация
|
|
64
|
+
cd dist/tool-snippets
|
|
65
|
+
npm publish
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Требования
|
|
69
|
+
|
|
70
|
+
- Node.js (для скриптов установки)
|
|
71
|
+
|
|
72
|
+
### Группы:
|
|
73
|
+
|
|
74
|
+
**1. Массивы / циклы**
|
|
75
|
+
- `tool#forEach`, `tool#switch`, `tool#switch_NEVER`
|
|
76
|
+
|
|
77
|
+
**2. Angular хуки**
|
|
78
|
+
- `tool_comp_1_ngOnChanges`, `tool_comp_2_ngOnInit`, `tool_comp_3_ngAfterViewInit`, `tool_comp_4_ngOnDestroy`
|
|
79
|
+
|
|
80
|
+
**3. Angular подписки / DI / ViewChild**
|
|
81
|
+
- `tool___subscribe`, `tool___Injector`, `tool____ViewChild_Child`, `tool____ViewChild_Element`
|
|
82
|
+
|
|
83
|
+
**4. Angular getter/setter**
|
|
84
|
+
- `tool____get_set`, `tool____get_set@Input()`
|
|
85
|
+
|
|
86
|
+
**5. TypeScript типы**
|
|
87
|
+
- `tool--typePickPartialOmit`, `tool--type_keyof`, `tool--typeTNullable`, `tool--typePartial`, `tool#type_never`, `tool--Interface with readonly`
|
|
88
|
+
|
|
89
|
+
**6. TypeScript const / Record**
|
|
90
|
+
- `tool-as_const_1`, `tool-as_const_2_Record`, `tool-as_const_3_satisfies_Record`
|
|
91
|
+
|
|
92
|
+
**7. try/catch**
|
|
93
|
+
- `tool__tryCatch`, `tool__tryCatch.await`
|
|
94
|
+
|
|
95
|
+
**8. Отладка**
|
|
96
|
+
- `tool_deb`, `tool---globalErrorListeners`, `tool---Proxy_Логирование`
|
|
97
|
+
|
|
98
|
+
**9. Глобальные объекты**
|
|
99
|
+
- `tool----windowГлобальный_объект`
|
|
100
|
+
|
|
101
|
+
### Примеры сниппетов
|
|
102
|
+
|
|
103
|
+
| Префикс | Описание |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `tool#forEach` | forEach по массиву |
|
|
106
|
+
| `tool_deb` | `debugger;` |
|
|
107
|
+
| `tool_comp_1_ngOnChanges` | хук `ngOnChanges` |
|
|
108
|
+
| `tool_comp_2_ngOnInit` | хук `ngOnInit` |
|
|
109
|
+
| `tool_comp_3_ngAfterViewInit` | хук `ngAfterViewInit` |
|
|
110
|
+
| `tool_comp_4_ngOnDestroy` | хук `ngOnDestroy` |
|
|
111
|
+
| `tool___subscribe` | подписка на Observable с `takeUntil` |
|
|
112
|
+
| `tool__tryCatch` | блок `try-catch-finally` |
|
|
113
|
+
| `tool__tryCatch.await` | tryCatch для promise |
|
|
114
|
+
| `tool-as_const_1` | объект `as const` |
|
|
115
|
+
| `tool-as_const_2_Record` | `Record<K, V>` + `as const` |
|
|
116
|
+
| `tool-as_const_3_satisfies_Record` | `as const satisfies Record` |
|
|
117
|
+
| `tool--typePickPartialOmit` | `Pick & Partial<Omit>` |
|
|
118
|
+
| `tool--type_keyof` | `keyof` |
|
|
119
|
+
| `tool--typeTNullable` | `TNullable<T>` |
|
|
120
|
+
| `tool--typePartial` | `Partial<T>` |
|
|
121
|
+
| `tool--Interface with readonly` | interface с `readonly` |
|
|
122
|
+
| `tool___Injector` | сервис через `Injector` |
|
|
123
|
+
| `tool____ViewChild_Child` | `@ViewChild` (компонент) |
|
|
124
|
+
| `tool____ViewChild_Element` | `@ViewChild` (ElementRef) |
|
|
125
|
+
| `tool____get_set` | getter/setter |
|
|
126
|
+
| `tool____get_set@Input()` | getter/setter с `@Input()` |
|
|
127
|
+
| `tool---Proxy_Логирование` | Proxy-логирование изменений |
|
|
128
|
+
| `tool#type_never` | `switch` + `never` (exhaustive check) |
|
|
129
|
+
| `tool#switch` | `switch` блок |
|
|
130
|
+
| `tool#switch_NEVER` | `switch` + `never` |
|
|
131
|
+
| `tool----windowГлобальный_объект` | declare global window |
|
|
132
|
+
| `tool---globalErrorListeners` | global error listeners |
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public API Surface of tool-snippets
|
|
3
|
+
*/
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Rvb2wtc25pcHBldHMvc3JjL3B1YmxpYy1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUciLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIHRvb2wtc25pcHBldHNcbiAqL1xuXG5leHBvcnQge307XG4iXX0=
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated bundle index. Do not edit.
|
|
3
|
+
*/
|
|
4
|
+
export * from './public-api';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbC1zbmlwcGV0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Rvb2wtc25pcHBldHMvc3JjL3Rvb2wtc25pcHBldHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9wdWJsaWMtYXBpJztcbiJdfQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-snippets.mjs","sources":["../../../projects/tool-snippets/src/tool-snippets.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAAA;;AAEG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-snippets.mjs","sources":["../../../projects/tool-snippets/src/public-api.ts","../../../projects/tool-snippets/src/tool-snippets.ts"],"sourcesContent":["/*\n * Public API Surface of tool-snippets\n */\n\nexport {};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAAA;;AAEG;;ACFH;;AAEG"}
|
package/index.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tool-snippets",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.8",
|
|
4
|
+
"peerDependencies": {},
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"tslib": "^2.3.0"
|
|
7
|
+
},
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"module": "fesm2015/tool-snippets.mjs",
|
|
10
|
+
"es2020": "fesm2020/tool-snippets.mjs",
|
|
11
|
+
"esm2020": "esm2020/tool-snippets.mjs",
|
|
12
|
+
"fesm2020": "fesm2020/tool-snippets.mjs",
|
|
13
|
+
"fesm2015": "fesm2015/tool-snippets.mjs",
|
|
14
|
+
"typings": "index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./package.json": {
|
|
17
|
+
"default": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"esm2020": "./esm2020/tool-snippets.mjs",
|
|
22
|
+
"es2020": "./fesm2020/tool-snippets.mjs",
|
|
23
|
+
"es2015": "./fesm2015/tool-snippets.mjs",
|
|
24
|
+
"node": "./fesm2015/tool-snippets.mjs",
|
|
25
|
+
"default": "./fesm2020/tool-snippets.mjs"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
6
28
|
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const log = m => console.log(`[tool] ${m}`);
|
|
7
|
+
|
|
8
|
+
function getVscodeSnippetsDir() {
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
if (process.platform === 'darwin') return path.join(home, 'Library/Application Support/Code/User/snippets');
|
|
11
|
+
if (process.platform === 'win32') return path.join(process.env.APPDATA, 'Code/User/snippets');
|
|
12
|
+
return path.join(home, '.config/Code/User/snippets');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function run() {
|
|
16
|
+
try {
|
|
17
|
+
log('Installing VS Code snippets...');
|
|
18
|
+
const dir = getVscodeSnippetsDir();
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
const source = path.join(__dirname, '../vscode/tool-frontend.code-snippets');
|
|
22
|
+
const destination = path.join(dir, 'tool-frontend.code-snippets');
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(source)) throw new Error('Snippets file not found in package');
|
|
25
|
+
|
|
26
|
+
log(`Copy from: ${source}`);
|
|
27
|
+
log(`Copy to: ${destination}`);
|
|
28
|
+
fs.copyFileSync(source, destination);
|
|
29
|
+
log(`✅ Installed`);
|
|
30
|
+
log('⚠️ Reload VS Code window (Ctrl+Shift+P -> Reload Window)');
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error('[tool] ❌ Error:', e.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
run();
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tool#forEach": {
|
|
3
|
+
"prefix": "tool#forEach",
|
|
4
|
+
"body": [
|
|
5
|
+
"$1.forEach((item, i, arr) => {",
|
|
6
|
+
" console.log( arr, i, item);",
|
|
7
|
+
"});",
|
|
8
|
+
"$0"
|
|
9
|
+
],
|
|
10
|
+
"description": "forEach - метод массива",
|
|
11
|
+
"scope": "javascript,typescript"
|
|
12
|
+
},
|
|
13
|
+
"tool_deb": {
|
|
14
|
+
"prefix": "tool_deb",
|
|
15
|
+
"body": ["debugger;"],
|
|
16
|
+
"description": "debugger",
|
|
17
|
+
"scope": "javascript,typescript"
|
|
18
|
+
},
|
|
19
|
+
"tool_comp_2_ngOnInit": {
|
|
20
|
+
"prefix": "tool_comp_2_ngOnInit",
|
|
21
|
+
"body": [
|
|
22
|
+
"import { OnInit } from '@angular/core';",
|
|
23
|
+
"",
|
|
24
|
+
"implements OnInit",
|
|
25
|
+
"",
|
|
26
|
+
"public ngOnInit() {",
|
|
27
|
+
" $1",
|
|
28
|
+
"}",
|
|
29
|
+
"$0"
|
|
30
|
+
],
|
|
31
|
+
"description": "хук, вызывается один раз после создания компонента, когда инициализировались все данные (например, @Input свойства) - при первой загрузке",
|
|
32
|
+
"scope": "javascript,typescript"
|
|
33
|
+
},
|
|
34
|
+
"tool_comp_4_ngOnDestroy": {
|
|
35
|
+
"prefix": "tool_comp_4_ngOnDestroy",
|
|
36
|
+
"body": [
|
|
37
|
+
"import {OnDestroy} from '@angular/core';",
|
|
38
|
+
"",
|
|
39
|
+
"implements OnDestroy",
|
|
40
|
+
"",
|
|
41
|
+
"public ngOnDestroy() {",
|
|
42
|
+
" $1",
|
|
43
|
+
"}",
|
|
44
|
+
"$0"
|
|
45
|
+
],
|
|
46
|
+
"description": "хук, вызывается перед уничтожением компонента - при уничтожении",
|
|
47
|
+
"scope": "javascript,typescript"
|
|
48
|
+
},
|
|
49
|
+
"tool_comp_1_ngOnChanges": {
|
|
50
|
+
"prefix": "tool_comp_1_ngOnChanges",
|
|
51
|
+
"body": [
|
|
52
|
+
"import { OnChanges, SimpleChanges } from '@angular/core';",
|
|
53
|
+
"",
|
|
54
|
+
"implements OnChanges",
|
|
55
|
+
"",
|
|
56
|
+
"public ngOnChanges(changes: SimpleChanges): void {",
|
|
57
|
+
" console.log(changes);",
|
|
58
|
+
"}",
|
|
59
|
+
"$0"
|
|
60
|
+
],
|
|
61
|
+
"description": "хук, который срабатывает каждый раз, когда меняются входные данные (@Input) компонента - при первой загрузке (если есть @Input)",
|
|
62
|
+
"scope": "javascript,typescript"
|
|
63
|
+
},
|
|
64
|
+
"tool_comp_3_ngAfterViewInit": {
|
|
65
|
+
"prefix": "tool_comp_3_ngAfterViewInit",
|
|
66
|
+
"body": [
|
|
67
|
+
"import { AfterViewInit } from '@angular/core';",
|
|
68
|
+
"",
|
|
69
|
+
"implements AfterViewInit",
|
|
70
|
+
"",
|
|
71
|
+
"public ngAfterViewInit() {",
|
|
72
|
+
" $1",
|
|
73
|
+
"}",
|
|
74
|
+
"$0"
|
|
75
|
+
],
|
|
76
|
+
"description": "хук, вызывается после полной инициализации представления (view) компонента - при первой загрузке",
|
|
77
|
+
"scope": "javascript,typescript"
|
|
78
|
+
},
|
|
79
|
+
"tool___subscribe": {
|
|
80
|
+
"prefix": "tool___subscribe",
|
|
81
|
+
"body": [
|
|
82
|
+
"// --------------------------------------",
|
|
83
|
+
"import { OnDestroy } from '@angular/core';",
|
|
84
|
+
"import { Subject } from 'rxjs/Subject';",
|
|
85
|
+
"import { takeUntil } from 'rxjs/operators';",
|
|
86
|
+
"// --------------------------------------",
|
|
87
|
+
"private _destroyedSubscribe$: Subject<void> = new Subject<void>();",
|
|
88
|
+
"// --------------------------------------",
|
|
89
|
+
" $1",
|
|
90
|
+
" .pipe(takeUntil(this._destroyedSubscribe$))",
|
|
91
|
+
" .subscribe({",
|
|
92
|
+
" next: (result) => { console.log('result', result); },",
|
|
93
|
+
" error: (err) => { console.error('Ошибка:', err); // здесь можно показать уведомление или выполнить fallback-логику },",
|
|
94
|
+
" complete: () => { console.log('Поток завершён'); }",
|
|
95
|
+
" });",
|
|
96
|
+
"// --------------------------------------",
|
|
97
|
+
"public ngOnDestroy(): void {",
|
|
98
|
+
" this._destroyedSubscribe$.next();",
|
|
99
|
+
" this._destroyedSubscribe$.complete();",
|
|
100
|
+
"}",
|
|
101
|
+
"// --------------------------------------",
|
|
102
|
+
"$0"
|
|
103
|
+
],
|
|
104
|
+
"description": "подписка на Observable и автоматическая отписка при уничтожении компонента (ngOnDestroy).",
|
|
105
|
+
"scope": "javascript,typescript"
|
|
106
|
+
},
|
|
107
|
+
"tool__tryCatch.await": {
|
|
108
|
+
"prefix": "tool__tryCatch.await",
|
|
109
|
+
"body": ["await evo.awaitTryCatch.send($1, $2)", "$0"],
|
|
110
|
+
"description": "tryCatch для promise",
|
|
111
|
+
"scope": "javascript,typescript"
|
|
112
|
+
},
|
|
113
|
+
"tool-as_const_1": {
|
|
114
|
+
"prefix": "tool-as_const_1",
|
|
115
|
+
"body": ["const obj = { $1: $2 } as const; // readonly", "$0"],
|
|
116
|
+
"description": "объект константа",
|
|
117
|
+
"scope": "javascript,typescript"
|
|
118
|
+
},
|
|
119
|
+
"tool-as_const_2_Record": {
|
|
120
|
+
"prefix": "tool-as_const_2_Record",
|
|
121
|
+
"body": [
|
|
122
|
+
"// type UserId = 'id1' | 'id2' | 'id3';",
|
|
123
|
+
"// const users: Record<UserId, string> = { id1: 'Alice', id2: 'Bob', id3: 'Charlie' // id4: 'David' // ❌ ошибка: ключ не из UserId } as const;;",
|
|
124
|
+
"",
|
|
125
|
+
"const obj: Record<$1, $2> = {$0} as const; // readonly"
|
|
126
|
+
],
|
|
127
|
+
"description": "типизация объектов через Record",
|
|
128
|
+
"scope": "javascript,typescript"
|
|
129
|
+
},
|
|
130
|
+
"tool--typePickPartialOmit": {
|
|
131
|
+
"prefix": "tool--typePickPartialOmit",
|
|
132
|
+
"body": [
|
|
133
|
+
"// type UserUpdate = Pick<User, 'id'> & Partial<Omit<User, 'id'>>;",
|
|
134
|
+
"type $1 = Pick<$2, '$3'> & Partial<Omit<$2, '$3'>>;",
|
|
135
|
+
"$0"
|
|
136
|
+
],
|
|
137
|
+
"description": "обязательное поле, остальные — опциональные",
|
|
138
|
+
"scope": "javascript,typescript"
|
|
139
|
+
},
|
|
140
|
+
"tool--type_keyof": {
|
|
141
|
+
"prefix": "tool--type_keyof",
|
|
142
|
+
"body": [
|
|
143
|
+
"// interface Person { name: string; age: number }; type PersonKeys = keyof Person",
|
|
144
|
+
"type $1Keys = keyof $1;",
|
|
145
|
+
"$0"
|
|
146
|
+
],
|
|
147
|
+
"description": "динамические ключи объектов",
|
|
148
|
+
"scope": "javascript,typescript"
|
|
149
|
+
},
|
|
150
|
+
"tool--typeTNullable": {
|
|
151
|
+
"prefix": "tool--typeTNullable",
|
|
152
|
+
"body": [
|
|
153
|
+
"// let currentUser: TNullable<User> = { id: 1, name: \"Иван\" };",
|
|
154
|
+
"type TNullable<T> = T | null;",
|
|
155
|
+
"$0"
|
|
156
|
+
],
|
|
157
|
+
"description": "Дженерик-тип, который расширяет тип T, позволяя ему также принимать значение null.",
|
|
158
|
+
"scope": "javascript,typescript"
|
|
159
|
+
},
|
|
160
|
+
"tool--typePartial": {
|
|
161
|
+
"prefix": "tool--typePartial",
|
|
162
|
+
"body": [
|
|
163
|
+
"// const car: Partial<Car> = {};",
|
|
164
|
+
"const $1: Partial<$2> = {};",
|
|
165
|
+
"$0"
|
|
166
|
+
],
|
|
167
|
+
"description": "объект временно пуст",
|
|
168
|
+
"scope": "javascript,typescript"
|
|
169
|
+
},
|
|
170
|
+
"tool__tryCatch": {
|
|
171
|
+
"prefix": "tool__tryCatch",
|
|
172
|
+
"body": [
|
|
173
|
+
"try {",
|
|
174
|
+
" $0",
|
|
175
|
+
"} catch (error) {",
|
|
176
|
+
" console.error(error);",
|
|
177
|
+
"} finally {",
|
|
178
|
+
" // код, который выполнится всегда",
|
|
179
|
+
"}"
|
|
180
|
+
],
|
|
181
|
+
"description": "обертка кода в блок try-catch-finally",
|
|
182
|
+
"scope": "javascript,typescript"
|
|
183
|
+
},
|
|
184
|
+
"tool___Injector": {
|
|
185
|
+
"prefix": "tool___Injector",
|
|
186
|
+
"body": [
|
|
187
|
+
"// в \"рабочем компоненте\" - public constructor(protected injector: Injector) { super(injector); }",
|
|
188
|
+
"",
|
|
189
|
+
"import { Injector, Type } from '@angular/core';",
|
|
190
|
+
"",
|
|
191
|
+
"protected $1: $2;",
|
|
192
|
+
"",
|
|
193
|
+
"protected constructor(",
|
|
194
|
+
" protected injector: Injector,",
|
|
195
|
+
") {",
|
|
196
|
+
" this.$1 = injector.get<$2>($2 as Type<$2>);",
|
|
197
|
+
"}",
|
|
198
|
+
"$0"
|
|
199
|
+
],
|
|
200
|
+
"description": "Подключение сервиса в базовом компоненте через injector",
|
|
201
|
+
"scope": "javascript,typescript"
|
|
202
|
+
},
|
|
203
|
+
"tool____ViewChild_Child": {
|
|
204
|
+
"prefix": "tool____ViewChild_Child",
|
|
205
|
+
"body": [
|
|
206
|
+
"import { ViewChild } from '@angular/core'",
|
|
207
|
+
"",
|
|
208
|
+
"@ViewChild($1) private _childComponent!: $1;",
|
|
209
|
+
"$0"
|
|
210
|
+
],
|
|
211
|
+
"description": "дочерний компонент",
|
|
212
|
+
"scope": "javascript,typescript"
|
|
213
|
+
},
|
|
214
|
+
"tool____ViewChild_Element": {
|
|
215
|
+
"prefix": "tool____ViewChild_Element",
|
|
216
|
+
"body": [
|
|
217
|
+
"import { ViewChild, ElementRef } from '@angular/core'",
|
|
218
|
+
"",
|
|
219
|
+
"@ViewChild('element') private _element: ElementRef; // <div #rootElement></div>",
|
|
220
|
+
"$0"
|
|
221
|
+
],
|
|
222
|
+
"description": "html элемент",
|
|
223
|
+
"scope": "javascript,typescript"
|
|
224
|
+
},
|
|
225
|
+
"tool____get_set": {
|
|
226
|
+
"prefix": "tool____get_set",
|
|
227
|
+
"body": [
|
|
228
|
+
"private _$1: $2;",
|
|
229
|
+
"",
|
|
230
|
+
"public get $1(): $2 {",
|
|
231
|
+
" return this._$1;",
|
|
232
|
+
"}",
|
|
233
|
+
"",
|
|
234
|
+
"public set $1(value: $2) {",
|
|
235
|
+
" if (this._$1 !== value) {",
|
|
236
|
+
" this._$1 = value;",
|
|
237
|
+
" // this.cdr.markForCheck(); // если используется ChangeDetectionStrategy.OnPush",
|
|
238
|
+
" }",
|
|
239
|
+
"}",
|
|
240
|
+
"$0"
|
|
241
|
+
],
|
|
242
|
+
"description": "getter/setter типизированный",
|
|
243
|
+
"scope": "javascript,typescript"
|
|
244
|
+
},
|
|
245
|
+
"tool____get_set@Input()": {
|
|
246
|
+
"prefix": "tool____get_set@Input()",
|
|
247
|
+
"body": [
|
|
248
|
+
"private _$1: $2;",
|
|
249
|
+
"",
|
|
250
|
+
"@Input()",
|
|
251
|
+
"public get $1(): $2 {",
|
|
252
|
+
" return this._$1;",
|
|
253
|
+
"}",
|
|
254
|
+
"",
|
|
255
|
+
"public set $1(value: $2) {",
|
|
256
|
+
" if (this._$1 !== value) {",
|
|
257
|
+
" this._$1 = value;",
|
|
258
|
+
" // this.cdr.markForCheck(); // если используется ChangeDetectionStrategy.OnPush",
|
|
259
|
+
" }",
|
|
260
|
+
"}",
|
|
261
|
+
"$0"
|
|
262
|
+
],
|
|
263
|
+
"description": "",
|
|
264
|
+
"scope": "javascript,typescript"
|
|
265
|
+
},
|
|
266
|
+
"tool---Proxy_Логирование": {
|
|
267
|
+
"prefix": "tool---Proxy_Логирование",
|
|
268
|
+
"body": [
|
|
269
|
+
"public createProxyLog(target, onChange) {",
|
|
270
|
+
" // Обработка null и примитивов",
|
|
271
|
+
" if (target === null || typeof target !== 'object') {",
|
|
272
|
+
" return target;",
|
|
273
|
+
" }",
|
|
274
|
+
" ",
|
|
275
|
+
" return new Proxy(target, {",
|
|
276
|
+
" set(obj, prop, value) {",
|
|
277
|
+
" const oldValue = obj[prop];",
|
|
278
|
+
" ",
|
|
279
|
+
" // Рекурсивно обрабатываем новые объекты",
|
|
280
|
+
" const newValue = this.createProxyLog(value, onChange);",
|
|
281
|
+
" ",
|
|
282
|
+
" obj[prop] = newValue;",
|
|
283
|
+
" ",
|
|
284
|
+
" // Проверяем реальное изменение (учитываем NaN)",
|
|
285
|
+
" if (oldValue !== value && !(Number.isNaN(oldValue) && Number.isNaN(value))) {",
|
|
286
|
+
" onChange(prop, newValue, oldValue);",
|
|
287
|
+
" }",
|
|
288
|
+
" ",
|
|
289
|
+
" return true;",
|
|
290
|
+
" },",
|
|
291
|
+
" ",
|
|
292
|
+
" get(obj, prop) {",
|
|
293
|
+
" const value = obj[prop];",
|
|
294
|
+
" ",
|
|
295
|
+
" // Рекурсивно создаем прокси для вложенных объектов",
|
|
296
|
+
" if (value !== null && typeof value === 'object') {",
|
|
297
|
+
" return this.createProxyLog(value, onChange);",
|
|
298
|
+
" }",
|
|
299
|
+
" ",
|
|
300
|
+
" return value;",
|
|
301
|
+
" },",
|
|
302
|
+
" ",
|
|
303
|
+
" deleteProperty(obj, prop) {",
|
|
304
|
+
" const oldValue = obj[prop];",
|
|
305
|
+
" const deleted = delete obj[prop];",
|
|
306
|
+
" ",
|
|
307
|
+
" if (deleted) { onChange(prop, undefined, oldValue); }",
|
|
308
|
+
" ",
|
|
309
|
+
" return deleted;",
|
|
310
|
+
" }",
|
|
311
|
+
" });",
|
|
312
|
+
"}",
|
|
313
|
+
"",
|
|
314
|
+
"",
|
|
315
|
+
"// Использование:",
|
|
316
|
+
"//let state = this.createProxyLog(",
|
|
317
|
+
"//{ user: { name: 'Alice', age: 30 } settings: { theme: 'dark', notifications: true } }, ",
|
|
318
|
+
"//(prop, value, oldValue) => { console.log(`Property \"${prop}\" changed: ${oldValue} → ${value}`); });",
|
|
319
|
+
"//",
|
|
320
|
+
"// delete state.user.email; // Property \"email\" changed: alice@example.com → undefined",
|
|
321
|
+
"$0"
|
|
322
|
+
],
|
|
323
|
+
"description": "Функция создания Proxy Логирование",
|
|
324
|
+
"scope": "javascript,typescript"
|
|
325
|
+
},
|
|
326
|
+
"tool-as_const_3_satisfies_Record": {
|
|
327
|
+
"prefix": "tool-as_const_3_satisfies_Record",
|
|
328
|
+
"body": [
|
|
329
|
+
"// type UserId = 'id1' | 'id2' | 'id3';",
|
|
330
|
+
"// const users = { id1: 'Alice', id2: 'Bob', id3: 'Charlie' // id4: 'David' // ❌ ошибка: ключ не из UserId } as const satisfies Record<UserId, string>;",
|
|
331
|
+
"",
|
|
332
|
+
"const obj = {$0} as const satisfies Record<$1, $2>;"
|
|
333
|
+
],
|
|
334
|
+
"description": "константный объект с as const и satisfies Record",
|
|
335
|
+
"scope": "javascript,typescript"
|
|
336
|
+
},
|
|
337
|
+
"tool#type_never": {
|
|
338
|
+
"prefix": "tool#type_never",
|
|
339
|
+
"body": [
|
|
340
|
+
"// Если union‑тип (Direction) изменится, TypeScript выдаст ошибку компиляции, если switch не обновлён.",
|
|
341
|
+
"type Direction = 'up' | 'down';",
|
|
342
|
+
"",
|
|
343
|
+
"public move(direction: Direction): number {",
|
|
344
|
+
" switch (direction) {",
|
|
345
|
+
" case 'up': return 1;",
|
|
346
|
+
" case 'down': return -1;",
|
|
347
|
+
" default:",
|
|
348
|
+
" //!!! Если добавлен новый вариант в `Direction` и не обработан — ошибка компиляции",
|
|
349
|
+
" const _: never = direction;",
|
|
350
|
+
" throw new Error(`Неизвестный ключ: ${_}`);",
|
|
351
|
+
" }",
|
|
352
|
+
"}",
|
|
353
|
+
"$0"
|
|
354
|
+
],
|
|
355
|
+
"description": "пример обеспечения типовой безопасности при работе с union‑тип и switch",
|
|
356
|
+
"scope": "javascript,typescript"
|
|
357
|
+
},
|
|
358
|
+
"tool--Interface with readonly": {
|
|
359
|
+
"prefix": "tool--Interface with readonly",
|
|
360
|
+
"body": [
|
|
361
|
+
"// interface UserId { readonly id: number; }; const user: UserId = { id: 12345 };",
|
|
362
|
+
"// Ошибка компиляции - user.id = 67890; // Cannot assign to 'id' because it is a read-only property",
|
|
363
|
+
"interface I$1 {",
|
|
364
|
+
" readonly $2: $3;",
|
|
365
|
+
"}",
|
|
366
|
+
"$0"
|
|
367
|
+
],
|
|
368
|
+
"description": "Interface with readonly properties",
|
|
369
|
+
"scope": "javascript,typescript"
|
|
370
|
+
},
|
|
371
|
+
"tool#switch": {
|
|
372
|
+
"prefix": "tool#switch",
|
|
373
|
+
"body": [
|
|
374
|
+
"switch ($1) {",
|
|
375
|
+
" case $2: $3; break;",
|
|
376
|
+
" case $4: $5; break;",
|
|
377
|
+
" default:",
|
|
378
|
+
" $6;",
|
|
379
|
+
"}",
|
|
380
|
+
"$0"
|
|
381
|
+
],
|
|
382
|
+
"description": "create SWITCH block",
|
|
383
|
+
"scope": "javascript,typescript"
|
|
384
|
+
},
|
|
385
|
+
"tool#switch_NEVER": {
|
|
386
|
+
"prefix": "tool#switch_NEVER",
|
|
387
|
+
"body": [
|
|
388
|
+
"// Если union‑тип (type Direction = 'up' | 'down';) изменится, TypeScript выдаст ошибку компиляции, если switch не обновлён.",
|
|
389
|
+
"",
|
|
390
|
+
"switch ($1) {",
|
|
391
|
+
" case $2:",
|
|
392
|
+
" $3;",
|
|
393
|
+
" break;",
|
|
394
|
+
" case $4:",
|
|
395
|
+
" $5;",
|
|
396
|
+
" break;",
|
|
397
|
+
" default:",
|
|
398
|
+
" const _exhaustiveCheck: never = $1;",
|
|
399
|
+
" throw new Error(`Необработанное значение: ${$1}`);",
|
|
400
|
+
"}",
|
|
401
|
+
"$0"
|
|
402
|
+
],
|
|
403
|
+
"description": "create SWITCH block with NEVER",
|
|
404
|
+
"scope": "javascript,typescript"
|
|
405
|
+
},
|
|
406
|
+
"tool----windowГлобальный_объект": {
|
|
407
|
+
"prefix": "tool----windowГлобальный_объект",
|
|
408
|
+
"body": [
|
|
409
|
+
"declare global {",
|
|
410
|
+
" interface Window { $1: any; }",
|
|
411
|
+
" ",
|
|
412
|
+
" const $1: any; // Добавляем глобальную переменную",
|
|
413
|
+
"}",
|
|
414
|
+
"",
|
|
415
|
+
"(window as any).$1 = $0;",
|
|
416
|
+
"",
|
|
417
|
+
"export {};"
|
|
418
|
+
],
|
|
419
|
+
"description": "Declare global window object and assign value",
|
|
420
|
+
"scope": "javascript,typescript"
|
|
421
|
+
},
|
|
422
|
+
"tool---globalErrorListeners": {
|
|
423
|
+
"prefix": "tool---globalErrorListeners",
|
|
424
|
+
"body": [
|
|
425
|
+
"window.addEventListener('error', (event) => { console.warn('Global error:', event.error); }); // для обычных ошибок",
|
|
426
|
+
"",
|
|
427
|
+
"window.addEventListener('unhandledrejection', (event) => {",
|
|
428
|
+
" console.warn('Unhandled promise rejection:', event.reason); // для ошибок (например, async / Promise)",
|
|
429
|
+
"});",
|
|
430
|
+
"$0"
|
|
431
|
+
],
|
|
432
|
+
"description": "Debug: global error & promise rejection listeners",
|
|
433
|
+
"scope": "javascript,typescript"
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
<templateSet group="#tool-frontend">
|
|
2
|
+
<template name="tool#forEach" value="$ARR$.forEach((item, i, arr) => { console.log( arr, i, item); });$END$" description="forEach - метод массива" toReformat="true" toShortenFQNames="true">
|
|
3
|
+
<variable name="ARR" expression="" defaultValue="" alwaysStopAt="true" />
|
|
4
|
+
<context>
|
|
5
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
6
|
+
<option name="TypeScript" value="true" />
|
|
7
|
+
</context>
|
|
8
|
+
</template>
|
|
9
|
+
<template name="tool_deb" value="debugger;" description="debugger" toReformat="true" toShortenFQNames="true">
|
|
10
|
+
<context>
|
|
11
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
12
|
+
<option name="TypeScript" value="true" />
|
|
13
|
+
</context>
|
|
14
|
+
</template>
|
|
15
|
+
<template name="tool_comp_2_ngOnInit" value="import { OnInit } from '@angular/core'; implements OnInit public ngOnInit() { $TEXT$ }$END$ " description="хук, вызывается один раз после создания компонента, когда инициализировались все данные (например, @Input свойства) - при первой загрузке" toReformat="false" toShortenFQNames="true">
|
|
16
|
+
<variable name="TEXT" expression="" defaultValue="" alwaysStopAt="true" />
|
|
17
|
+
<context>
|
|
18
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
19
|
+
<option name="TypeScript" value="true" />
|
|
20
|
+
</context>
|
|
21
|
+
</template>
|
|
22
|
+
<template name="tool_comp_4_ngOnDestroy" value="import {OnDestroy} from '@angular/core'; implements OnDestroy public ngOnDestroy() { $TEXT$ }$END$" description="хук, вызывается перед уничтожением компонента - при уничтожении" toReformat="false" toShortenFQNames="true">
|
|
23
|
+
<variable name="TEXT" expression="" defaultValue="" alwaysStopAt="true" />
|
|
24
|
+
<context>
|
|
25
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
26
|
+
<option name="TypeScript" value="true" />
|
|
27
|
+
</context>
|
|
28
|
+
</template>
|
|
29
|
+
<template name="tool_comp_1_ngOnChanges" value="import { OnChanges, SimpleChanges } from '@angular/core'; implements OnChanges public ngOnChanges(changes: SimpleChanges): void { console.log(changes); }$END$" description="хук, который срабатывает каждый раз, когда меняются входные данные (@Input) компонента - при первой загрузке (если есть @Input)" toReformat="true" toShortenFQNames="true">
|
|
30
|
+
<context>
|
|
31
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
32
|
+
<option name="TypeScript" value="true" />
|
|
33
|
+
</context>
|
|
34
|
+
</template>
|
|
35
|
+
<template name="tool_comp_3_ngAfterViewInit" value="import { AfterViewInit } from '@angular/core'; implements AfterViewInit public ngAfterViewInit() { $TEXT$ }$END$" description="хук, вызывается после полной инициализации представления (view) компонента - при первой загрузке" toReformat="true" toShortenFQNames="true">
|
|
36
|
+
<variable name="TEXT" expression="" defaultValue="" alwaysStopAt="true" />
|
|
37
|
+
<context>
|
|
38
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
39
|
+
<option name="TypeScript" value="true" />
|
|
40
|
+
</context>
|
|
41
|
+
</template>
|
|
42
|
+
<template name="tool___subscribe" value="// -------------------------------------- import { OnDestroy } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { takeUntil } from 'rxjs/operators'; // -------------------------------------- private _destroyedSubscribe$: Subject<void> = new Subject<void>(); // -------------------------------------- $TEXT$ .pipe(takeUntil(this._destroyedSubscribe$)) .subscribe({ next: (result) => { console.log('result', result); }, error: (err) => { console.error('Ошибка:', err); // здесь можно показать уведомление или выполнить fallback-логику }, complete: () => { console.log('Поток завершён'); } }); // -------------------------------------- public ngOnDestroy(): void { this._destroyedSubscribe$.next(); this._destroyedSubscribe$.complete(); } // --------------------------------------" description="подписка на Observable и автоматическая отписка при уничтожении компонента (ngOnDestroy)." toReformat="false" toShortenFQNames="true">
|
|
43
|
+
<variable name="TEXT" expression="" defaultValue="" alwaysStopAt="true" />
|
|
44
|
+
<context>
|
|
45
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
46
|
+
<option name="TypeScript" value="true" />
|
|
47
|
+
</context>
|
|
48
|
+
</template>
|
|
49
|
+
<template name="tool__tryCatch.await" value="await evo.awaitTryCatch.send($PROMISE$, $FROM$)" description="tryCatch для promise" toReformat="false" toShortenFQNames="true">
|
|
50
|
+
<variable name="PROMISE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
51
|
+
<variable name="FROM" expression="" defaultValue="" alwaysStopAt="true" />
|
|
52
|
+
<context>
|
|
53
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
54
|
+
<option name="TypeScript" value="true" />
|
|
55
|
+
</context>
|
|
56
|
+
</template>
|
|
57
|
+
<template name="tool-as_const_1" value="const obj = { $PROP$: $VAL$} as const; // readonly" description="объект константа" toReformat="false" toShortenFQNames="true">
|
|
58
|
+
<variable name="PROP" expression="" defaultValue="" alwaysStopAt="true" />
|
|
59
|
+
<variable name="VAL" expression="" defaultValue="" alwaysStopAt="true" />
|
|
60
|
+
<context>
|
|
61
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
62
|
+
<option name="TypeScript" value="true" />
|
|
63
|
+
</context>
|
|
64
|
+
</template>
|
|
65
|
+
<template name="tool-as_const_2_Record" value="// type UserId = 'id1' | 'id2' | 'id3'; // const users: Record<UserId, string> = { id1: 'Alice', id2: 'Bob', id3: 'Charlie' // id4: 'David' // ❌ ошибка: ключ не из UserId } as const;; const obj: Record<$PROP$, $VAL$> = {$END$} as const; // readonly" description="типизация объектов через Record" toReformat="false" toShortenFQNames="true">
|
|
66
|
+
<variable name="PROP" expression="" defaultValue="" alwaysStopAt="true" />
|
|
67
|
+
<variable name="VAL" expression="" defaultValue="" alwaysStopAt="true" />
|
|
68
|
+
<context>
|
|
69
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
70
|
+
<option name="TypeScript" value="true" />
|
|
71
|
+
</context>
|
|
72
|
+
</template>
|
|
73
|
+
<template name="tool--typePickPartialOmit" value="// type UserUpdate = Pick<User, 'id'> & Partial<Omit<User, 'id'>>; type $TYPE$ = Pick<$INTERFACE$, '$PROP$'> & Partial<Omit<$INTERFACE$, '$PROP$'>>;" description="обязательное поле, остальные — опциональные" toReformat="false" toShortenFQNames="true">
|
|
74
|
+
<variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
75
|
+
<variable name="INTERFACE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
76
|
+
<variable name="PROP" expression="" defaultValue="" alwaysStopAt="true" />
|
|
77
|
+
<context>
|
|
78
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
79
|
+
<option name="TypeScript" value="true" />
|
|
80
|
+
</context>
|
|
81
|
+
</template>
|
|
82
|
+
<template name="tool--type_keyof" value="// interface Person { name: string; age: number }; type PersonKeys = keyof Person type $TYPE$Keys = keyof $TYPE$;" description="динамические ключи объектов" toReformat="false" toShortenFQNames="true">
|
|
83
|
+
<variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
84
|
+
<context>
|
|
85
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
86
|
+
<option name="TypeScript" value="true" />
|
|
87
|
+
</context>
|
|
88
|
+
</template>
|
|
89
|
+
<template name="tool--typeTNullable" value="// let currentUser: TNullable<User> = { id: 1, name: "Иван" }; type TNullable<T> = T | null;" description="Дженерик-тип, который расширяет тип T, позволяя ему также принимать значение null." toReformat="false" toShortenFQNames="true">
|
|
90
|
+
<context>
|
|
91
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
92
|
+
<option name="TypeScript" value="true" />
|
|
93
|
+
</context>
|
|
94
|
+
</template>
|
|
95
|
+
<template name="tool--typePartial" value="// const car: Partial<Car> = {}; const $CAR$: Partial<$CARINTERFACE$> = {};" description="объект временно пуст" toReformat="false" toShortenFQNames="true">
|
|
96
|
+
<variable name="CAR" expression="" defaultValue="" alwaysStopAt="true" />
|
|
97
|
+
<variable name="CARINTERFACE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
98
|
+
<context>
|
|
99
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
100
|
+
<option name="TypeScript" value="true" />
|
|
101
|
+
</context>
|
|
102
|
+
</template>
|
|
103
|
+
<template name="tool__tryCatch" value="try { $END$ } catch (error) { console.error(error); } finally { // код, который выполнится всегда }" description="обертка кода в блок try-catch-finally" toReformat="false" toShortenFQNames="true">
|
|
104
|
+
<context>
|
|
105
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
106
|
+
<option name="TypeScript" value="true" />
|
|
107
|
+
</context>
|
|
108
|
+
</template>
|
|
109
|
+
<template name="tool___Injector" value="// в "рабочем компоненте" - public constructor(protected injector: Injector) { super(injector); } import { Injector, Type } from '@angular/core'; protected $INSTANS$: $SERVICE$; protected constructor( protected injector: Injector, ) { this.$INSTANS$ = injector.get<$SERVICE$>($SERVICE$ as Type<$SERVICE$>); }" description="Подключение сервиса в базовом компоненте через injector" toReformat="false" toShortenFQNames="true">
|
|
110
|
+
<variable name="INSTANS" expression="" defaultValue="" alwaysStopAt="true" />
|
|
111
|
+
<variable name="SERVICE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
112
|
+
<context>
|
|
113
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
114
|
+
<option name="TypeScript" value="true" />
|
|
115
|
+
</context>
|
|
116
|
+
</template>
|
|
117
|
+
<template name="tool____ViewChild_Child" value="import { ViewChild } from '@angular/core' @ViewChild($COMPONENT$) private _childComponent!: $COMPONENT$;" description="дочерний компонент" toReformat="false" toShortenFQNames="true">
|
|
118
|
+
<variable name="COMPONENT" expression="" defaultValue="" alwaysStopAt="true" />
|
|
119
|
+
<context>
|
|
120
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
121
|
+
<option name="TypeScript" value="true" />
|
|
122
|
+
</context>
|
|
123
|
+
</template>
|
|
124
|
+
<template name="tool____ViewChild_Element" value="import { ViewChild, ElementRef } from '@angular/core' @ViewChild('element') private _element: ElementRef; // <div #rootElement></div>" description="html элемент" toReformat="false" toShortenFQNames="true">
|
|
125
|
+
<context>
|
|
126
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
127
|
+
<option name="TypeScript" value="true" />
|
|
128
|
+
</context>
|
|
129
|
+
</template>
|
|
130
|
+
<template name="tool____get_set" value="private _$VAR$: $TYPE$; public get $VAR$(): $TYPE$ { return this._$VAR$; } public set $VAR$(value: $TYPE$) { if (this._$VAR$ !== value) { this._$VAR$ = value; // this.cdr.markForCheck(); // если используется ChangeDetectionStrategy.OnPush } }$END$" description="getter/setter типизированный" toReformat="false" toShortenFQNames="true">
|
|
131
|
+
<variable name="VAR" expression="" defaultValue="" alwaysStopAt="true" />
|
|
132
|
+
<variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
133
|
+
<context>
|
|
134
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
135
|
+
<option name="TypeScript" value="true" />
|
|
136
|
+
</context>
|
|
137
|
+
</template>
|
|
138
|
+
<template name="tool____get_set@Input()" value="private _$VAR$: $TYPE$; @Input() public get $VAR$(): $TYPE$ { return this._$VAR$; } public set $VAR$(value: $TYPE$) { if (this._$VAR$ !== value) { this._$VAR$ = value; // this.cdr.markForCheck(); // если используется ChangeDetectionStrategy.OnPush } }$END$" description="" toReformat="false" toShortenFQNames="true">
|
|
139
|
+
<variable name="VAR" expression="" defaultValue="" alwaysStopAt="true" />
|
|
140
|
+
<variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
141
|
+
<context>
|
|
142
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
143
|
+
<option name="TypeScript" value="true" />
|
|
144
|
+
</context>
|
|
145
|
+
</template>
|
|
146
|
+
<template name="tool---Proxy_Логирование" value="public createProxyLog(target, onChange) { // Обработка null и примитивов if (target === null || typeof target !== 'object') { return target; } return new Proxy(target, { set(obj, prop, value) { const oldValue = obj[prop]; // Рекурсивно обрабатываем новые объекты const newValue = this.createProxyLog(value, onChange); obj[prop] = newValue; // Проверяем реальное изменение (учитываем NaN) if (oldValue !== value && !(Number.isNaN(oldValue) && Number.isNaN(value))) { onChange(prop, newValue, oldValue); } return true; }, get(obj, prop) { const value = obj[prop]; // Рекурсивно создаем прокси для вложенных объектов if (value !== null && typeof value === 'object') { return this.createProxyLog(value, onChange); } return value; }, deleteProperty(obj, prop) { const oldValue = obj[prop]; const deleted = delete obj[prop]; if (deleted) { onChange(prop, undefined, oldValue); } return deleted; } }); } // Использование: //let state = this.createProxyLog( //{ user: { name: 'Alice', age: 30 } settings: { theme: 'dark', notifications: true } }, //(prop, value, oldValue) => { console.log(`Property "${prop}" changed: ${oldValue} → ${value}`); }); // // delete state.user.email; // Property "email" changed: alice@example.com → undefined" description="Функция создания Proxy Логирование" toReformat="false" toShortenFQNames="true">
|
|
147
|
+
<context>
|
|
148
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
149
|
+
<option name="TypeScript" value="true" />
|
|
150
|
+
</context>
|
|
151
|
+
</template>
|
|
152
|
+
<template name="tool-as_const_3_satisfies_Record" value="// type UserId = 'id1' | 'id2' | 'id3'; // const users = { id1: 'Alice', id2: 'Bob', id3: 'Charlie' // id4: 'David' // ❌ ошибка: ключ не из UserId } as const satisfies Record<UserId, string>; const obj = {$END$} as const satisfies Record<$KEY_TYPE$, $VALUE_TYPE$>;" description="константный объект с as const и satisfies Record" toReformat="false" toShortenFQNames="true">
|
|
153
|
+
<variable name="KEY_TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
154
|
+
<variable name="VALUE_TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
155
|
+
<context>
|
|
156
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
157
|
+
<option name="TypeScript" value="true" />
|
|
158
|
+
</context>
|
|
159
|
+
</template>
|
|
160
|
+
<template name="tool#type_never" value="// Если union‑тип (Direction) изменится, TypeScript выдаст ошибку компиляции, если switch не обновлён. type Direction = 'up' | 'down'; public move(direction: Direction): number { switch (direction) { case 'up': return 1; case 'down': return -1; default: //!!! Если добавлен новый вариант в `Direction` и не обработан — ошибка компиляции const _: never = direction; throw new Error(`Неизвестный ключ: ${_}`); } }" description="пример обеспечения типовой безопасности при работе с union‑тип и switch" toReformat="false" toShortenFQNames="true" deactivated="true">
|
|
161
|
+
<context>
|
|
162
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
163
|
+
<option name="TypeScript" value="true" />
|
|
164
|
+
</context>
|
|
165
|
+
</template>
|
|
166
|
+
<template name="tool--Interface with readonly" value="// interface UserId { readonly id: number; }; const user: UserId = { id: 12345 }; // Ошибка компиляции - user.id = 67890; // Cannot assign to 'id' because it is a read-only property interface I$NAME$ { readonly $PROPERTY$: $TYPE$; }" description="Interface with readonly properties" toReformat="false" toShortenFQNames="true">
|
|
167
|
+
<variable name="NAME" expression="" defaultValue="" alwaysStopAt="true" />
|
|
168
|
+
<variable name="PROPERTY" expression="" defaultValue="" alwaysStopAt="true" />
|
|
169
|
+
<variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
|
|
170
|
+
<context>
|
|
171
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
172
|
+
<option name="TypeScript" value="true" />
|
|
173
|
+
</context>
|
|
174
|
+
</template>
|
|
175
|
+
<template name="tool#switch" value="switch ($EXPRESSION$) { case $CASE1$: $BODY1$; break; case $CASE2$: $BODY2$; break; default: $DEFAULT_BODY$; } " description="create SWITCH block" toReformat="false" toShortenFQNames="true">
|
|
176
|
+
<variable name="EXPRESSION" expression="" defaultValue="" alwaysStopAt="true" />
|
|
177
|
+
<variable name="CASE1" expression="" defaultValue="" alwaysStopAt="true" />
|
|
178
|
+
<variable name="BODY1" expression="" defaultValue="" alwaysStopAt="true" />
|
|
179
|
+
<variable name="CASE2" expression="" defaultValue="" alwaysStopAt="true" />
|
|
180
|
+
<variable name="BODY2" expression="" defaultValue="" alwaysStopAt="true" />
|
|
181
|
+
<variable name="DEFAULT_BODY" expression="" defaultValue="" alwaysStopAt="true" />
|
|
182
|
+
<context>
|
|
183
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
184
|
+
<option name="TypeScript" value="true" />
|
|
185
|
+
</context>
|
|
186
|
+
</template>
|
|
187
|
+
<template name="tool#switch_NEVER" value="// Если union‑тип (type Direction = 'up' | 'down';) изменится, TypeScript выдаст ошибку компиляции, если switch не обновлён. switch ($EXPRESSION$) { case $CASE1$: $BODY1$; break; case $CASE2$: $BODY2$; break; default: const _exhaustiveCheck: never = $EXPRESSION$; throw new Error(`Необработанное значение: ${$EXPRESSION$}`); } " description="create SWITCH block with NEVER" toReformat="false" toShortenFQNames="true">
|
|
188
|
+
<variable name="EXPRESSION" expression="" defaultValue="" alwaysStopAt="true" />
|
|
189
|
+
<variable name="CASE1" expression="" defaultValue="" alwaysStopAt="true" />
|
|
190
|
+
<variable name="BODY1" expression="" defaultValue="" alwaysStopAt="true" />
|
|
191
|
+
<variable name="CASE2" expression="" defaultValue="" alwaysStopAt="true" />
|
|
192
|
+
<variable name="BODY2" expression="" defaultValue="" alwaysStopAt="true" />
|
|
193
|
+
<context>
|
|
194
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
195
|
+
<option name="TypeScript" value="true" />
|
|
196
|
+
</context>
|
|
197
|
+
</template>
|
|
198
|
+
<template name="tool----windowГлобальный_объект" value="declare global { interface Window { $NAME$: any; } const $NAME$: any; // Добавляем глобальную переменную } (window as any).$NAME$ = $END$; export {};" description="Declare global window object and assign value" toReformat="false" toShortenFQNames="true">
|
|
199
|
+
<variable name="NAME" expression="" defaultValue="" alwaysStopAt="true" />
|
|
200
|
+
<context>
|
|
201
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
202
|
+
<option name="TypeScript" value="true" />
|
|
203
|
+
</context>
|
|
204
|
+
</template>
|
|
205
|
+
<template name="tool---globalErrorListeners" value="window.addEventListener('error', (event) => { console.warn('Global error:', event.error); }); // для обычных ошибок window.addEventListener('unhandledrejection', (event) => { console.warn('Unhandled promise rejection:', event.reason); // для ошибок (например, async / Promise) });" description="Debug: global error & promise rejection listeners" toReformat="false" toShortenFQNames="true">
|
|
206
|
+
<context>
|
|
207
|
+
<option name="JAVA_SCRIPT" value="true" />
|
|
208
|
+
<option name="TypeScript" value="true" />
|
|
209
|
+
</context>
|
|
210
|
+
</template>
|
|
211
|
+
</templateSet>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function log(msg) {
|
|
8
|
+
console.log(`[tool] ${msg}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getJetBrainsBaseDir() {
|
|
12
|
+
const home = os.homedir();
|
|
13
|
+
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
return path.join(home, 'Library/Application Support/JetBrains');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (process.platform === 'win32') {
|
|
19
|
+
return path.join(process.env.APPDATA, 'JetBrains');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return path.join(home, '.config/JetBrains');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findAllWebStormTemplatesDirs(baseDir) {
|
|
26
|
+
if (!fs.existsSync(baseDir)) {
|
|
27
|
+
throw new Error('JetBrains directory not found');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const dirs = fs.readdirSync(baseDir);
|
|
31
|
+
|
|
32
|
+
const wsDirs = dirs.filter(d => d.startsWith('WebStorm'));
|
|
33
|
+
|
|
34
|
+
if (!wsDirs.length) {
|
|
35
|
+
throw new Error('WebStorm not found');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return wsDirs.map(dir => {
|
|
39
|
+
const templatesDir = path.join(baseDir, dir, 'templates');
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(templatesDir)) {
|
|
42
|
+
fs.mkdirSync(templatesDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return templatesDir;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function copySnippets(targetDir) {
|
|
50
|
+
const source = path.join(__dirname, '../webstorm/_tool-frontend.xml');
|
|
51
|
+
|
|
52
|
+
if (!fs.existsSync(source)) {
|
|
53
|
+
console.error('[tool] Snippets not found. Is the library installed?');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const destination = path.join(targetDir, '_tool-frontend.xml');
|
|
58
|
+
|
|
59
|
+
log(`Copy from: ${source}`);
|
|
60
|
+
log(`Copy to: ${destination}`);
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(source)) {
|
|
63
|
+
throw new Error('Snippets file not found in package');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fs.copyFileSync(source, destination);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function run() {
|
|
70
|
+
try {
|
|
71
|
+
log('Installing WebStorm snippets...');
|
|
72
|
+
|
|
73
|
+
const baseDir = getJetBrainsBaseDir();
|
|
74
|
+
const templateDirs = findAllWebStormTemplatesDirs(baseDir);
|
|
75
|
+
|
|
76
|
+
templateDirs.forEach(dir => {
|
|
77
|
+
copySnippets(dir);
|
|
78
|
+
log(`✅ Installed to: ${dir}`);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
log('⚠️ Restart WebStorm to apply snippets');
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error('[evo-lib] ❌ Error:', e.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
run();
|