lint-suite 1.0.5 → 1.0.7

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 CHANGED
@@ -1,121 +1,135 @@
1
- # Lint Suite
2
-
3
- A comprehensive collection of ESLint Flat configurations for modern web applications.
4
-
5
- ## Features
6
-
7
- - **TypeScript Linting**: Strict typing rules, consistent imports, and code organization
8
- - **Angular Support**: Component best practices and template accessibility rules
9
- - **RxJS Guidelines**: Observable patterns, Finnish notation, and operator safety
10
- - **Code Style**: Formatting rules, line limits, and structural consistency
11
- - **Accessibility**: ARIA validation, keyboard events, and semantic HTML
12
- - **Testing**: Jest configurations with appropriate relaxations for test files
13
- - **Additional Support**: JSON, Storybook, and more
14
-
15
- ## Installation
16
-
17
- ```bash
18
- npm install --save-dev lint-suite
19
- ```
20
-
21
- ## Usage
22
-
23
- Create a `.eslintrc.config.mjs` file in your project root:
24
-
25
- ```js
26
- import { base, javascript, typescript } from 'lint-suite';
27
-
28
- export default [
29
- ...base,
30
- ...javascript,
31
- ...typescript,
32
- // Rest of the required configurations
33
- ];
34
- ```
35
-
36
- You can selectively include configurations you need.
37
-
38
- ## Available Configurations
39
-
40
- - `base`: Core JavaScript rules and formatting
41
- - `typescript`: TypeScript-specific rules
42
- - `angular`: Angular component best practices
43
- - `angularTemplate`: HTML template rules with accessibility focus
44
- - `rxjs`: Observable patterns and operator safety
45
- - `jest`: Testing configurations
46
- - `json`: JSON file linting
47
- - `storybook`: Storybook support
48
-
49
- ## Customization
50
-
51
- You can override any rules by adding a `rules` section to your ESLint config:
52
-
53
- ```js
54
- import { typescript } from 'lint-suite';
55
-
56
- export default [
57
- ...typescript,
58
- {
59
- rules: {
60
- '@typescript-eslint/explicit-function-return-type': 'off',
61
- },
62
- },
63
- ];
64
- ```
65
-
66
- ## Available Rules (you can add more as you prefer)
67
-
68
- ### nx/eslint-plugin
69
-
70
- - `@nx/enforce-module-boundaries`: Enforces module boundary restrictions
71
- - `@nx/dependency-checks`: Validates dependencies in workspace projects
72
- - ...
73
-
74
- ### eslint-plugin-jest
75
-
76
- - `jest/no-disabled-tests`: Disallows disabled tests
77
- - `jest/expect-expect`: Ensures test cases contain at least one expect statement
78
- - `jest/valid-expect`: Validates expect() usage
79
- - ...
80
-
81
- ### eslint-plugin-json
82
-
83
- - `json/json`: Validates JSON syntax
84
- - `json/sort-package-json`: Enforces consistent ordering in package.json
85
- - ...
86
-
87
- ### @smarttools/eslint-plugin-rxjs
88
-
89
- - `@rxjs/finnish`: Enforces Finnish notation for observables
90
- - `@rxjs/no-ignored-subscription`: Prevents ignoring returned subscriptions
91
- - `@rxjs/no-unsafe-takeuntil`: Ensures proper usage of takeUntil operator
92
- - ...
93
-
94
- ### eslint-plugin-storybook
95
-
96
- - `storybook/no-redundant-story-name`: Prevents redundant story names
97
- - `storybook/csf-component`: Enforces Component Story Format standards
98
- - `storybook/hierarchy-separator`: Enforces story hierarchy separators
99
- - ...
100
-
101
- ### eslint-plugin-import
102
-
103
- - `import/no-unresolved`: Ensures imports point to valid modules
104
- - `import/order`: Enforces a consistent order of import statements
105
- - `import/no-duplicates`: Prevents duplicate imports
106
- - ...
107
-
108
- ### @stylistic/eslint-plugin
109
-
110
- - `@stylistic/max-len`: Enforces maximum line length
111
- - `@stylistic/indent`: Enforces consistent indentation
112
- - `@stylistic/quotes`: Enforces consistent quote style
113
- - ...
114
-
115
- ## Contributing
116
-
117
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.
118
-
119
- ## License
120
-
121
- MIT
1
+ # Lint Suite
2
+
3
+ A comprehensive collection of ESLint Flat configurations for modern web applications.
4
+
5
+ ## Features
6
+
7
+ - **TypeScript Linting**: Strict typing rules, consistent imports, and code organization
8
+ - **Angular Support**: Component best practices and template accessibility rules
9
+ - **RxJS Guidelines**: Observable patterns, Finnish notation, and operator safety
10
+ - **Code Style**: Formatting rules, line limits, and structural consistency
11
+ - **Accessibility**: ARIA validation, keyboard events, and semantic HTML
12
+ - **Testing**: Jest configurations with appropriate relaxations for test files
13
+ - **Additional Support**: JSON, Storybook, and more
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install --save-dev lint-suite
19
+ ```
20
+
21
+ ## Dependencies
22
+
23
+ ```bash
24
+ npm install --save-dev eslint typescript-eslint eslint-config-prettier
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Create a `.eslintrc.config.mjs` file in your project root:
30
+
31
+ ```js
32
+ import { base, javascript, typescript } from 'lint-suite';
33
+
34
+ export default [
35
+ ...base,
36
+ ...javascript,
37
+ ...typescript
38
+ // Rest of the required configurations
39
+ ];
40
+ ```
41
+
42
+ You can selectively include configurations you need.
43
+
44
+ ## Available Configurations
45
+
46
+ - `base`: Core JavaScript rules and formatting
47
+ - `typescript`: TypeScript-specific rules
48
+ - `angular`: Angular component best practices
49
+ - `angularTemplate`: HTML template rules with accessibility focus
50
+ - `rxjs`: Observable patterns and operator safety
51
+ - `jest`: Testing configurations
52
+ - `json`: JSON file linting
53
+ - `storybook`: Storybook support
54
+
55
+ ## Customization
56
+
57
+ You can override any rules by adding a `rules` section to your ESLint config:
58
+
59
+ ```js
60
+ import { typescript } from 'lint-suite';
61
+
62
+ export default [
63
+ ...typescript,
64
+ {
65
+ rules: {
66
+ '@typescript-eslint/explicit-function-return-type': 'off'
67
+ }
68
+ }
69
+ ];
70
+ ```
71
+
72
+ ## Available Rules (you can add more as you prefer)
73
+
74
+ ### nx/eslint-plugin
75
+
76
+ - `@nx/enforce-module-boundaries`: Enforces module boundary restrictions
77
+ - `@nx/dependency-checks`: Validates dependencies in workspace projects
78
+ - ...
79
+
80
+ ### eslint-plugin-jest
81
+
82
+ - `jest/no-disabled-tests`: Disallows disabled tests
83
+ - `jest/expect-expect`: Ensures test cases contain at least one expect statement
84
+ - `jest/valid-expect`: Validates expect() usage
85
+ - ...
86
+
87
+ ### eslint-plugin-json
88
+
89
+ - `json/json`: Validates JSON syntax
90
+ - `json/sort-package-json`: Enforces consistent ordering in package.json
91
+ - ...
92
+
93
+ ### @smarttools/eslint-plugin-rxjs
94
+
95
+ - `@rxjs/finnish`: Enforces Finnish notation for observables
96
+ - `@rxjs/no-ignored-subscription`: Prevents ignoring returned subscriptions
97
+ - `@rxjs/no-unsafe-takeuntil`: Ensures proper usage of takeUntil operator
98
+ - ...
99
+
100
+ ### eslint-plugin-storybook
101
+
102
+ - `storybook/no-redundant-story-name`: Prevents redundant story names
103
+ - `storybook/csf-component`: Enforces Component Story Format standards
104
+ - `storybook/hierarchy-separator`: Enforces story hierarchy separators
105
+ - ...
106
+
107
+ ### eslint-plugin-import
108
+
109
+ - `import/no-unresolved`: Ensures imports point to valid modules
110
+ - `import/order`: Enforces a consistent order of import statements
111
+ - `import/no-duplicates`: Prevents duplicate imports
112
+ - ...
113
+
114
+ ### @stylistic/eslint-plugin
115
+
116
+ - `@stylistic/max-len`: Enforces maximum line length
117
+ - `@stylistic/indent`: Enforces consistent indentation
118
+ - `@stylistic/quotes`: Enforces consistent quote style
119
+ - ...
120
+
121
+ ## Contributing
122
+
123
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.
124
+
125
+ ## License
126
+
127
+ MIT
128
+
129
+ ```
130
+
131
+ ```
132
+
133
+ ```
134
+
135
+ ```
package/index.cjs CHANGED
@@ -32,6 +32,7 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  angular: () => angular,
34
34
  angularTemplate: () => angularTemplate,
35
+ base: () => base,
35
36
  javascript: () => javascript,
36
37
  jest: () => jest,
37
38
  json: () => json,
@@ -43,12 +44,14 @@ __export(src_exports, {
43
44
  module.exports = __toCommonJS(src_exports);
44
45
 
45
46
  // packages/lint-suite/src/lib/angular-template.ts
46
- var import_eslint_plugin = __toESM(require("@nx/eslint-plugin"), 1);
47
- var angularTemplate = [
48
- ...import_eslint_plugin.default.configs["flat/angular-template"],
47
+ var import_angular_eslint = require("angular-eslint");
48
+ var import_typescript_eslint = require("typescript-eslint");
49
+ var angularTemplate = (0, import_typescript_eslint.config)([
49
50
  {
50
51
  files: ["**/*.html"],
52
+ extends: [...import_angular_eslint.configs.templateAccessibility, ...import_angular_eslint.configs.templateRecommended],
51
53
  rules: {
54
+ "@angular-eslint/template/prefer-template-literal": "error",
52
55
  "@angular-eslint/template/no-any": "error",
53
56
  "@angular-eslint/template/no-duplicate-attributes": "error",
54
57
  "@angular-eslint/template/button-has-type": "error",
@@ -86,26 +89,40 @@ var angularTemplate = [
86
89
  ]
87
90
  }
88
91
  }
89
- ];
92
+ ]);
90
93
 
91
94
  // packages/lint-suite/src/lib/angular.ts
92
- var import_eslint_plugin2 = __toESM(require("@nx/eslint-plugin"), 1);
93
- var angular = [
94
- ...import_eslint_plugin2.default.configs["flat/angular"],
95
+ var import_globals = __toESM(require("globals"), 1);
96
+ var import_angular_eslint2 = require("angular-eslint");
97
+ var import_typescript_eslint2 = require("typescript-eslint");
98
+ var angular = (0, import_typescript_eslint2.config)([
95
99
  {
96
100
  files: ["**/*.ts"],
101
+ extends: [...import_angular_eslint2.configs.tsRecommended],
102
+ languageOptions: {
103
+ globals: {
104
+ ...import_globals.default.browser,
105
+ ...import_globals.default.es2025,
106
+ ...import_globals.default.node
107
+ }
108
+ },
109
+ processor: import_angular_eslint2.processInlineTemplates,
110
+ plugins: { "@angular-eslint": import_angular_eslint2.tsPlugin },
97
111
  rules: {
98
- "@angular-eslint/prefer-on-push-component-change-detection": "error"
112
+ "@angular-eslint/prefer-on-push-component-change-detection": "error",
113
+ "@angular-eslint/prefer-signals": "error",
114
+ "@angular-eslint/use-injectable-provided-in": "error"
99
115
  }
100
116
  }
101
- ];
117
+ ]);
102
118
 
103
119
  // packages/lint-suite/src/lib/base.ts
104
- var import_eslint_plugin3 = __toESM(require("@nx/eslint-plugin"), 1);
105
- var import_eslint_plugin4 = __toESM(require("@stylistic/eslint-plugin"), 1);
120
+ var import_eslint_plugin = require("@nx/eslint-plugin");
121
+ var import_eslint_plugin2 = __toESM(require("@stylistic/eslint-plugin"), 1);
106
122
  var import_eslint_plugin_import_x = __toESM(require("eslint-plugin-import-x"), 1);
107
- var base = [
108
- ...import_eslint_plugin3.default.configs["flat/base"],
123
+ var import_typescript_eslint3 = require("typescript-eslint");
124
+ var base = (0, import_typescript_eslint3.config)([
125
+ { extends: [...import_eslint_plugin.configs["flat/base"]] },
109
126
  {
110
127
  files: [
111
128
  "**/*.js",
@@ -117,7 +134,7 @@ var base = [
117
134
  "**/*.cts",
118
135
  "**/*.mts"
119
136
  ],
120
- ...import_eslint_plugin_import_x.default.flatConfigs.recommended
137
+ extends: [import_eslint_plugin_import_x.default.flatConfigs.recommended]
121
138
  },
122
139
  {
123
140
  files: [
@@ -130,7 +147,7 @@ var base = [
130
147
  "**/*.cts",
131
148
  "**/*.mts"
132
149
  ],
133
- ...import_eslint_plugin4.default.configs.recommended
150
+ extends: [import_eslint_plugin2.default.configs.recommended]
134
151
  },
135
152
  {
136
153
  files: [
@@ -149,7 +166,7 @@ var base = [
149
166
  "no-console": ["warn", { allow: ["warn", "error"] }],
150
167
  "no-debugger": "error",
151
168
  eqeqeq: ["error", "always", { null: "ignore" }],
152
- curly: ["error", "all"],
169
+ curly: ["error", "multi"],
153
170
  "prefer-const": ["error", { destructuring: "all" }],
154
171
  "no-var": "error",
155
172
  "object-shorthand": "error",
@@ -179,7 +196,6 @@ var base = [
179
196
  "grouped-accessor-pairs": ["error", "getBeforeSet"],
180
197
  // Enforce consistency for getters/setters
181
198
  "class-methods-use-this": "error",
182
- "no-void": "error",
183
199
  "no-underscore-dangle": "error",
184
200
  "max-len": [
185
201
  "error",
@@ -197,6 +213,38 @@ var base = [
197
213
  }
198
214
  }
199
215
  ],
216
+ "@stylistic/padding-line-between-statements": [
217
+ "error",
218
+ {
219
+ blankLine: "always",
220
+ prev: "export",
221
+ next: ["export", "const"]
222
+ },
223
+ {
224
+ blankLine: "always",
225
+ prev: ["const", "let", "var"],
226
+ next: "*"
227
+ },
228
+ {
229
+ blankLine: "any",
230
+ prev: ["const", "let", "var"],
231
+ next: ["const", "let", "var"]
232
+ },
233
+ {
234
+ blankLine: "always",
235
+ prev: "*",
236
+ next: [
237
+ "for",
238
+ "if",
239
+ "return",
240
+ "throw",
241
+ "switch",
242
+ "try",
243
+ "class",
244
+ "function"
245
+ ]
246
+ }
247
+ ],
200
248
  "@stylistic/comma-dangle": [
201
249
  "error",
202
250
  {
@@ -258,7 +306,7 @@ var base = [
258
306
  {
259
307
  files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
260
308
  rules: {
261
- "max-line-per-function": "off",
309
+ "max-lines-per-function": "off",
262
310
  complexity: ["warn", { max: 1 }],
263
311
  "max-lines": [
264
312
  "error",
@@ -282,37 +330,45 @@ var base = [
282
330
  "class-methods-use-this": "off"
283
331
  }
284
332
  }
285
- ];
333
+ ]);
286
334
 
287
335
  // packages/lint-suite/src/lib/javascript.ts
288
- var import_eslint_plugin5 = __toESM(require("@nx/eslint-plugin"), 1);
289
- var javascript = import_eslint_plugin5.default.configs["flat/javascript"];
336
+ var import_eslint_plugin3 = require("@nx/eslint-plugin");
337
+ var import_typescript_eslint4 = require("typescript-eslint");
338
+ var javascript = (0, import_typescript_eslint4.config)({
339
+ files: ["**/*.js", "**/*.jsx", "**/*.cjs", "**/*.mjs"],
340
+ extends: [...import_eslint_plugin3.configs["flat/javascript"]],
341
+ rules: {}
342
+ });
290
343
 
291
344
  // packages/lint-suite/src/lib/jest.ts
292
345
  var import_eslint_plugin_jest = __toESM(require("eslint-plugin-jest"), 1);
293
- var jest = [
346
+ var import_typescript_eslint5 = require("typescript-eslint");
347
+ var jest = (0, import_typescript_eslint5.config)([
294
348
  {
295
349
  files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
296
- ...import_eslint_plugin_jest.default.configs["flat/all"],
350
+ extends: [import_eslint_plugin_jest.default.configs["flat/all"]],
297
351
  rules: {
298
- "jest/no-done-callback": "off",
299
- "jest/no-unbound-method": "error"
352
+ "jest/no-done-callback": "off"
300
353
  }
301
354
  }
302
- ];
355
+ ]);
303
356
 
304
357
  // packages/lint-suite/src/lib/json.ts
305
358
  var import_eslint_plugin_json = __toESM(require("eslint-plugin-json"), 1);
306
- var json = [
359
+ var import_typescript_eslint6 = require("typescript-eslint");
360
+ var json = (0, import_typescript_eslint6.config)([
307
361
  {
308
362
  files: ["**/*.json"],
309
- ...import_eslint_plugin_json.default.configs.recommended
363
+ extends: [import_eslint_plugin_json.default.configs.recommended],
364
+ rules: {}
310
365
  }
311
- ];
366
+ ]);
312
367
 
313
368
  // packages/lint-suite/src/lib/rxjs.ts
314
369
  var import_eslint_plugin_rxjs = __toESM(require("@smarttools/eslint-plugin-rxjs"), 1);
315
- var rxjs = [
370
+ var import_typescript_eslint7 = require("typescript-eslint");
371
+ var rxjs = (0, import_typescript_eslint7.config)([
316
372
  {
317
373
  files: [
318
374
  "**/*.js",
@@ -329,7 +385,7 @@ var rxjs = [
329
385
  projectService: true
330
386
  }
331
387
  },
332
- ...import_eslint_plugin_rxjs.default.configs.recommended
388
+ extends: [import_eslint_plugin_rxjs.default.configs.recommended]
333
389
  },
334
390
  {
335
391
  files: [
@@ -360,6 +416,7 @@ var rxjs = [
360
416
  rules: {
361
417
  "rxjs/prefer-observer": "error",
362
418
  "rxjs/finnish": "warn",
419
+ "rxjs/no-ignored-subscription": "warn",
363
420
  "rxjs/no-ignored-error": "warn",
364
421
  "rxjs/no-unsafe-switchmap": "error",
365
422
  "rxjs/no-unsafe-takeuntil": "error",
@@ -376,20 +433,22 @@ var rxjs = [
376
433
  "rxjs/no-ignored-subscription": "off"
377
434
  }
378
435
  }
379
- ];
436
+ ]);
380
437
 
381
438
  // packages/lint-suite/src/lib/storybook.ts
382
439
  var import_eslint_plugin_storybook = __toESM(require("eslint-plugin-storybook"), 1);
383
- var storybook = [
384
- ...import_eslint_plugin_storybook.default.configs["flat/recommended"],
440
+ var import_typescript_eslint8 = require("typescript-eslint");
441
+ var storybook = (0, import_typescript_eslint8.config)([
385
442
  {
386
443
  ignores: ["!.storybook"],
387
- files: ["**/*.stories.ts"]
444
+ files: ["**/*.stories.ts"],
445
+ extends: [import_eslint_plugin_storybook.default.configs["flat/recommended"]],
446
+ rules: {}
388
447
  }
389
- ];
448
+ ]);
390
449
 
391
450
  // packages/lint-suite/src/lib/typescript.ts
392
- var import_eslint_plugin6 = __toESM(require("@nx/eslint-plugin"), 1);
451
+ var import_eslint_plugin4 = require("@nx/eslint-plugin");
393
452
  var import_eslint_import_resolver_typescript = require("eslint-import-resolver-typescript");
394
453
  var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"), 1);
395
454
 
@@ -531,15 +590,21 @@ var explicit_accessibility_default = createRule({
531
590
  });
532
591
 
533
592
  // packages/lint-suite/src/lib/typescript.ts
593
+ var import_typescript_eslint9 = require("typescript-eslint");
534
594
  var localTypescriptRules = {
535
595
  rules: {
536
596
  "explicit-accessibility": explicit_accessibility_default
537
597
  }
538
598
  };
539
- var typescript = [
599
+ var typescript = (0, import_typescript_eslint9.config)([
540
600
  // NOTE: Checks for 'prettier' and 'eslint-plugin-prettier'
541
- import_eslint_plugin_import_x2.default.flatConfigs.typescript,
542
- ...import_eslint_plugin6.default.configs["flat/typescript"],
601
+ {
602
+ files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
603
+ extends: [
604
+ import_eslint_plugin_import_x2.default.flatConfigs.typescript,
605
+ ...import_eslint_plugin4.configs["flat/typescript"]
606
+ ]
607
+ },
543
608
  {
544
609
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
545
610
  plugins: { local: localTypescriptRules },
@@ -562,12 +627,14 @@ var typescript = [
562
627
  // Consider enabling if maximum explicitness is desired, even if verbose:
563
628
  // C. Null/Undefined Safety (Requires strictNullChecks)
564
629
  "@typescript-eslint/no-non-null-assertion": "error",
630
+ "@typescript-eslint/prefer-nullish-coalescing": "error",
631
+ "@typescript-eslint/prefer-optional-chain": "error",
565
632
  // D. Promise Handling (Supplementing plugin:promise/recommended)
566
633
  "@typescript-eslint/no-floating-promises": [
567
634
  "error",
568
635
  { ignoreVoid: true }
569
636
  ],
570
- "@typescript-eslint/no-misused-promises": "error",
637
+ "@typescript-eslint/no-misused-promises": "warn",
571
638
  // Already in recommended-type-checked
572
639
  "@typescript-eslint/promise-function-async": "error",
573
640
  "@typescript-eslint/await-thenable": "error",
@@ -648,7 +715,16 @@ var typescript = [
648
715
  "unknown"
649
716
  ],
650
717
  pathGroups: [
651
- { group: "external", pattern: "@angular/**", position: "before" },
718
+ {
719
+ pattern: "@angular/**",
720
+ group: "builtin",
721
+ position: "before"
722
+ },
723
+ {
724
+ pattern: "@nestjs/**",
725
+ group: "builtin",
726
+ position: "before"
727
+ },
652
728
  {
653
729
  pattern: "@*/shared/**",
654
730
  group: "internal",
@@ -665,7 +741,7 @@ var typescript = [
665
741
  position: "after"
666
742
  }
667
743
  ],
668
- pathGroupsExcludedImportTypes: ["@angular/**"],
744
+ pathGroupsExcludedImportTypes: ["@angular/**", "@nestjs/**"],
669
745
  "newlines-between": "always",
670
746
  alphabetize: { order: "asc", caseInsensitive: true }
671
747
  }
@@ -708,10 +784,18 @@ var typescript = [
708
784
  files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
709
785
  rules: {
710
786
  "@typescript-eslint/no-empty-function": "off",
711
- "@typescript-eslint/no-unbound-method": "off"
787
+ "@typescript-eslint/no-unbound-method": "off",
788
+ "@typescript-eslint/consistent-type-assertions": "off"
789
+ }
790
+ },
791
+ {
792
+ files: ["**/*.stories.ts"],
793
+ rules: {
794
+ "@typescript-eslint/consistent-type-assertions": "off",
795
+ "@typescript-eslint/naming-convention": "off"
712
796
  }
713
797
  }
714
- ];
798
+ ]);
715
799
 
716
800
  // packages/lint-suite/src/index.ts
717
801
  var recommended = [
@@ -729,6 +813,7 @@ var recommended = [
729
813
  0 && (module.exports = {
730
814
  angular,
731
815
  angularTemplate,
816
+ base,
732
817
  javascript,
733
818
  jest,
734
819
  json,