@dsivd/prestations-ng 19.0.6 → 19.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/CHANGELOG.md CHANGED
@@ -6,14 +6,21 @@
6
6
 
7
7
  ---
8
8
 
9
- ## [Unreleased]
9
+ ## [19.0.7]
10
10
 
11
11
  ### Added
12
12
 
13
13
  - ESLint plugin: **shareable configs**, documented in [ESLINT_PLUGIN.md](ESLINT_PLUGIN.md)
14
14
  - add `configs.tsRecommended` and `configs.templateRecommended` to the `extends` of your
15
- `**/*.ts` and `**/*.html` blocks — see [UPGRADING_V19.md](UPGRADING_V19.md). Nothing
16
- else is needed: no `plugins` declaration, no rule list.
15
+ `**/*.ts` and `**/*.html` blocks — see [UPGRADING_V19.md](UPGRADING_V19.md). They carry
16
+ the whole shared setup: the `eslint`, `typescript-eslint` and `angular-eslint`
17
+ baselines, the house conventions, and the rules below. Nothing else is needed: no
18
+ `plugins` declaration, no rule list.
19
+ - `configs.tsRules` and `configs.templateRules` enable only the rules written here, for a
20
+ project that supplies its own style
21
+ - 🚨 do **not** also spread `angular.configs.tsRecommended` or
22
+ `angular.configs.templateRecommended`: they are already included, and declaring the
23
+ same plugin twice makes ESLint fail outright
17
24
  - 🚨 **Prerequisite**: `lintFilePatterns` in your `angular.json` must include
18
25
  `src/**/*.html`, otherwise the template rules never run
19
26
  - 🚨 **A rule added to these configs makes your lint fail on `npm update`.** This is
package/CONTRIBUTING.md CHANGED
@@ -131,8 +131,9 @@ below mandatory:
131
131
 
132
132
  1. Write the rule in `projects/prestations-ng/src/eslint/rules/<rule-name>.mjs`
133
133
  2. Register it in `rules/index.mjs`, keyed by its public kebab-case name
134
- 3. Enable it in `configs/ts-recommended.mjs`, `configs/template-recommended.mjs`, or both
135
- depending on what it inspects
134
+ 3. Enable it in `configs/ts-rules.mjs`, `configs/template-rules.mjs`, or both depending on
135
+ what it inspects — the `*-recommended.mjs` presets pick those layers up. A change to a
136
+ shared convention rather than to a rule of ours goes into `configs/ts-recommended.mjs`.
136
137
  4. Test it in `rules/__tests__/<rule-name>.test.mjs` — use the `@angular-eslint/template-parser`
137
138
  for a template rule
138
139
  5. Run `npm run test:eslint`, then `npm run lint:js` on the whole repository: a rule that
package/ESLINT_PLUGIN.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # ESLint plugin
2
2
 
3
3
  `@dsivd/prestations-ng` ships an ESLint plugin with rules that catch signal misuses the
4
- Angular compiler lets through. It is published as a subpath of the library, so the rules
5
- follow the version of the library.
4
+ Angular compiler lets through, plus the lint setup shared by every project built on the
5
+ library. It is published as a subpath of the library, so both follow the version of the
6
+ library.
6
7
 
7
8
  ## Setup
8
9
 
@@ -17,18 +18,18 @@ import prestationsNg from '@dsivd/prestations-ng/eslint';
17
18
  export default tseslint.config(
18
19
  {
19
20
  files: ['**/*.ts'],
20
- extends: [
21
- ...angular.configs.tsRecommended,
22
- ...prestationsNg.configs.tsRecommended,
23
- ],
21
+ extends: [...prestationsNg.configs.tsRecommended],
22
+ languageOptions: {
23
+ parserOptions: {
24
+ projectService: true,
25
+ tsconfigRootDir: import.meta.dirname,
26
+ },
27
+ },
24
28
  processor: angular.processInlineTemplates,
25
29
  },
26
30
  {
27
31
  files: ['**/*.html'],
28
- extends: [
29
- ...angular.configs.templateRecommended,
30
- ...prestationsNg.configs.templateRecommended,
31
- ],
32
+ extends: [...prestationsNg.configs.templateRecommended],
32
33
  },
33
34
  );
34
35
  ```
@@ -37,17 +38,38 @@ That is all. You do **not** declare `plugins`, and you do **not** list the rules
37
38
  from the config. Inline templates written in a `@Component` are covered too, through
38
39
  `angular.processInlineTemplates`.
39
40
 
41
+ > 🚨 **Do not spread `angular.configs.tsRecommended` or `angular.configs.templateRecommended`
42
+ > alongside these configs.** They already include them. Declaring the same plugin twice from
43
+ > two different copies makes ESLint fail outright, before linting anything.
44
+
40
45
  > 🚨 **Prerequisite**: `lintFilePatterns` in your `angular.json` must include
41
46
  > `src/**/*.html`. Without it `ng lint` never opens your templates, and the template rules
42
47
  > silently never run.
43
48
 
44
- | Config | Target | Rules |
49
+ ## The configs
50
+
51
+ | Config | Target | Contents |
45
52
  | ----------------------------- | ----------- | -------------------------------------------------------------- |
46
- | `configs.tsRecommended` | `**/*.ts` | `no-direct-signal-mutation` |
47
- | `configs.templateRecommended` | `**/*.html` | `no-direct-signal-mutation`, `no-uninvoked-signal-in-template` |
53
+ | `configs.tsRecommended` | `**/*.ts` | the baselines, the house style, and `tsRules` |
54
+ | `configs.templateRecommended` | `**/*.html` | the baselines, the house style, and `templateRules` |
55
+ | `configs.tsRules` | `**/*.ts` | `no-direct-signal-mutation` |
56
+ | `configs.templateRules` | `**/*.html` | `no-direct-signal-mutation`, `no-uninvoked-signal-in-template` |
57
+
58
+ The `*Recommended` presets are what a project normally extends. On top of the rules written
59
+ here they bring:
48
60
 
49
- `no-direct-signal-mutation` appears in both: the rule carries one visitor for TypeScript
50
- and one for the Angular template AST, and each fires on its own kind of file.
61
+ - the baselines `eslint.configs.recommended`, `typescript-eslint` recommended and
62
+ stylistic, `angular-eslint` recommended, and for templates its accessibility set;
63
+ - the house style — naming and selector conventions, import ordering
64
+ (`simple-import-sort`, `import-x`), rxjs conventions, and the signal rules of
65
+ `angular-eslint` (`no-uncalled-signals`, `prefer-signals`, `prefer-signal-model`).
66
+
67
+ Use `tsRules` / `templateRules` instead when a project supplies its own style and only wants
68
+ the rules of this library. They register the plugin and nothing else, so you keep control of
69
+ the baselines — and stay responsible for keeping them aligned.
70
+
71
+ `no-direct-signal-mutation` appears in both targets: the rule carries one visitor for
72
+ TypeScript and one for the Angular template AST, and each fires on its own kind of file.
51
73
 
52
74
  ## What happens when you update the library
53
75
 
@@ -62,10 +84,7 @@ your own `rules` block. It is applied after the shared config, so it wins:
62
84
  ```ts
63
85
  {
64
86
  files: ['**/*.html'],
65
- extends: [
66
- ...angular.configs.templateRecommended,
67
- ...prestationsNg.configs.templateRecommended,
68
- ],
87
+ extends: [...prestationsNg.configs.templateRecommended],
69
88
  rules: {
70
89
  // decided by this project, survives library updates
71
90
  '@dsivd/prestations-ng/no-direct-signal-mutation': ['off'],
@@ -162,13 +181,15 @@ found without declaring anything.
162
181
  | `[(value)]="mySignal"` | a writable signal is two-way bound **without** parentheses, Angular calls `.set()` itself |
163
182
  | `#ref`, `@for` items, `@if … as`, `@let`, `let-x`, `$index`… | template-local names shadow the class members |
164
183
 
165
- ## Also worth enabling
184
+ ## The TypeScript counterpart
166
185
 
167
186
  `@angular-eslint/no-uncalled-signals` covers the same class of bug **in TypeScript**, using
168
187
  the real type checker — far more reliable than what a template rule can do. It cannot run on
169
188
  templates: it requires type information, which a `.html` file has no program for. The two
170
189
  are complementary, not redundant.
171
190
 
191
+ `configs.tsRecommended` already enables it. With `configs.tsRules` you enable it yourself:
192
+
172
193
  ```ts
173
194
  '@angular-eslint/no-uncalled-signals': 'error',
174
195
  ```
package/UPGRADING_V19.md CHANGED
@@ -1085,13 +1085,10 @@ rm .eslintrc.json
1085
1085
 
1086
1086
  ```ts
1087
1087
  // @ts-check
1088
- import eslint from '@eslint/js';
1088
+ import prettier from 'eslint-config-prettier';
1089
1089
  import tseslint from 'typescript-eslint';
1090
1090
  import angular from 'angular-eslint';
1091
- import rxjs from '@smarttools/eslint-plugin-rxjs';
1092
- import simpleImportSort from 'eslint-plugin-simple-import-sort';
1093
- import prettier from 'eslint-config-prettier';
1094
- import importX from 'eslint-plugin-import-x';
1091
+
1095
1092
  import prestationsNg from '@dsivd/prestations-ng/eslint';
1096
1093
 
1097
1094
  export default tseslint.config(
@@ -1102,152 +1099,20 @@ export default tseslint.config(
1102
1099
  // ─── TypeScript ────────────────────────────────────────────────────────────
1103
1100
  {
1104
1101
  files: ['**/*.ts'],
1105
- extends: [
1106
- eslint.configs.recommended,
1107
- ...tseslint.configs.recommended, // covers: no-explicit-any, ban-ts-comment,
1108
- // no-unused-expressions, no-var, no-unused-vars…
1109
- ...tseslint.configs.stylistic, // covers: array-type, consistent-type-assertions,
1110
- // dot-notation, no-inferrable-types (default options),
1111
- // no-empty-function (error), prefer-for-of,
1112
- // prefer-function-type…
1113
- ...angular.configs.tsRecommended, // covers: prefer-inject, no-empty-lifecycle-method,
1114
- // use-lifecycle-interface…
1115
- ...prestationsNg.configs.tsRecommended, // covers: no-direct-signal-mutation
1116
- ],
1102
+ extends: [...prestationsNg.configs.tsRecommended],
1117
1103
  languageOptions: {
1118
1104
  parserOptions: {
1119
1105
  projectService: true,
1120
1106
  tsconfigRootDir: import.meta.dirname,
1121
1107
  },
1122
1108
  },
1123
- plugins: {
1124
- rxjs,
1125
- 'simple-import-sort': simpleImportSort,
1126
- 'import-x': importX,
1127
- },
1128
1109
  processor: angular.processInlineTemplates,
1129
- rules: {
1130
- // ── @angular-eslint ──────────────────────────────────────────────
1131
- '@angular-eslint/component-selector': [
1132
- 'error',
1133
- {
1134
- type: 'element',
1135
- prefix: 'app',
1136
- style: 'kebab-case',
1137
- },
1138
- ],
1139
- '@angular-eslint/directive-selector': [
1140
- 'error',
1141
- {
1142
- type: 'attribute',
1143
- prefix: 'app',
1144
- style: 'camelCase',
1145
- },
1146
- ],
1147
- '@angular-eslint/no-uncalled-signals': ['error'],
1148
- '@angular-eslint/prefer-signal-model': ['error'],
1149
- '@angular-eslint/prefer-signals': ['error'],
1150
-
1151
- // ── @typescript-eslint ───────────────────────────────────────────
1152
- '@typescript-eslint/explicit-function-return-type': [
1153
- 'error',
1154
- {
1155
- allowExpressions: true,
1156
- allowTypedFunctionExpressions: true,
1157
- allowHigherOrderFunctions: true,
1158
- allowDirectConstAssertionInArrowFunctions: true,
1159
- },
1160
- ],
1161
- '@typescript-eslint/explicit-member-accessibility': [
1162
- 'error',
1163
- { accessibility: 'no-public' },
1164
- ],
1165
- '@typescript-eslint/member-delimiter-style': 'error',
1166
- '@typescript-eslint/member-ordering': 'error',
1167
- '@typescript-eslint/no-base-to-string': 'warn',
1168
- // stylistic enables no-inferrable-types with default options (ignoreParameters: false);
1169
- // override here to allow typed parameters explicitly
1170
- '@typescript-eslint/no-inferrable-types': [
1171
- 'error',
1172
- { ignoreParameters: true },
1173
- ],
1174
- // stylistic sets no-empty-function to error; downgrade to warn
1175
- '@typescript-eslint/no-empty-function': 'warn',
1176
- '@typescript-eslint/prefer-includes': 'warn',
1177
- '@typescript-eslint/return-await': ['error', 'never'],
1178
- '@typescript-eslint/typedef': ['error', { parameter: true }],
1179
- '@typescript-eslint/unified-signatures': 'error',
1180
- // off here — @typescript-eslint/no-shadow handles it correctly for TS
1181
- 'no-shadow': 'off',
1182
- '@typescript-eslint/no-shadow': ['error', { hoist: 'all' }],
1183
- '@typescript-eslint/no-unused-vars': [
1184
- 'error',
1185
- {
1186
- varsIgnorePattern: '^_',
1187
- argsIgnorePattern: '^_',
1188
- caughtErrorsIgnorePattern: '^_',
1189
- },
1190
- ],
1191
-
1192
- // ── eslint core ──────────────────────────────────────────────────
1193
- 'arrow-body-style': 'error',
1194
- 'arrow-parens': ['error', 'as-needed'],
1195
- curly: 'error',
1196
- eqeqeq: ['error', 'always'],
1197
- 'guard-for-in': 'error',
1198
- 'no-bitwise': 'error',
1199
- 'no-caller': 'error',
1200
- 'no-console': ['error', { allow: ['log', 'warn', 'error'] }],
1201
- 'no-eval': 'error',
1202
- 'no-extra-boolean-cast': 'off',
1203
- 'no-multiple-empty-lines': 'error',
1204
- 'no-new-wrappers': 'error',
1205
- 'no-restricted-imports': [
1206
- 'error',
1207
- { paths: ['rxjs/Rx', 'primeng/primeng', 'primeng'] },
1208
- ],
1209
- 'no-return-assign': 'error',
1210
- 'no-throw-literal': 'error',
1211
- 'no-undef-init': 'error',
1212
- 'no-useless-concat': 'error',
1213
- 'object-shorthand': ['error', 'always', { avoidQuotes: true }],
1214
- 'one-var': ['error', 'never'],
1215
- 'prefer-arrow-callback': 'error',
1216
- 'prefer-template': 'error',
1217
- radix: 'error',
1218
-
1219
- // ── rxjs ─────────────────────────────────────────────────────────
1220
- 'rxjs/no-implicit-any-catch': ['error', { allowExplicitAny: true }],
1221
- 'rxjs/no-sharereplay': 'off',
1222
-
1223
- // ── import-x ─────────────────────────────────────────────────────
1224
- 'import-x/no-cycle': [
1225
- 'warn',
1226
- { maxDepth: 3, ignoreExternal: true },
1227
- ],
1228
- 'import-x/no-deprecated': 'warn',
1229
- 'import-x/first': 'error',
1230
- 'import-x/newline-after-import': 'error',
1231
- 'import-x/no-duplicates': 'error',
1232
-
1233
- // ── simple-import-sort ───────────────────────────────────────────
1234
- 'simple-import-sort/imports': 'error',
1235
- 'simple-import-sort/exports': 'error',
1236
- },
1237
1110
  },
1238
1111
 
1239
1112
  // ─── HTML templates ────────────────────────────────────────────────────────
1240
1113
  {
1241
1114
  files: ['**/*.html'],
1242
- extends: [
1243
- ...angular.configs.templateRecommended,
1244
- ...angular.configs.templateAccessibility,
1245
- ...prestationsNg.configs.templateRecommended, // covers no-direct-signal-mutation, no-uninvoked-signal-in-template
1246
- ],
1247
- rules: {
1248
- '@angular-eslint/template/no-negated-async': 'off',
1249
- '@angular-eslint/template/button-has-type': 'error',
1250
- },
1115
+ extends: [...prestationsNg.configs.templateRecommended],
1251
1116
  },
1252
1117
 
1253
1118
  // ─── Prettier (must be last — disables formatting rules) ───────────────────
@@ -1255,19 +1120,31 @@ export default tseslint.config(
1255
1120
  );
1256
1121
  ```
1257
1122
 
1123
+ That is the whole file. The two `*Recommended` configs come from the library and carry
1124
+ everything: the `eslint`, `typescript-eslint` and `angular-eslint` baselines, the template
1125
+ accessibility set, the naming and import conventions, and the rules written by the library
1126
+ itself. You declare no `plugins` and list no rule.
1127
+
1128
+ The packages installed above are still required — the library's config imports them from your
1129
+ `node_modules` rather than shipping its own copies. For the same reason, do **not** spread
1130
+ `angular.configs.tsRecommended` or `angular.configs.templateRecommended` yourself: they are
1131
+ already included, and declaring the same plugin twice from two different copies makes ESLint
1132
+ fail outright.
1133
+
1258
1134
  #### prestations-ng ESLint rules
1259
1135
 
1260
1136
  The library ships **shareable custom configs** that are auto-updatable with the library version. Two
1261
1137
  rules are enabled today — `no-direct-signal-mutation` and `no-uninvoked-signal-in-template` — and both
1262
- are documented in [the plugin guide](ESLINT_PLUGIN.md).
1138
+ are documented in [the plugin guide](ESLINT_PLUGIN.md), alongside the leaner `tsRules` / `templateRules`
1139
+ configs for a project that supplies its own style.
1263
1140
 
1264
1141
  One consequence to keep in mind:
1265
1142
 
1266
- - A rule added in a later version of the library lands in the config, so `npm update` **will
1267
- make your lint fail** on the offending code. That is the point — it is how a practice is
1268
- rolled out to every project. Each addition is listed in the [CHANGELOG](CHANGELOG.md), and
1269
- a rule you cannot fix right away can be turned off in your own `rules` block, which is
1270
- applied after the shared config:
1143
+ - A rule added — or tightened — in a later version of the library lands in the config, so
1144
+ `npm update` **will make your lint fail** on the offending code. That is the point — it is
1145
+ how a practice is rolled out to every project. Each addition is listed in the
1146
+ [CHANGELOG](CHANGELOG.md), and a rule you cannot fix right away can be turned off in your
1147
+ own `rules` block, which is applied after the shared config:
1271
1148
 
1272
1149
  ```ts
1273
1150
  rules: {
Binary file
@@ -1,6 +1,6 @@
1
1
  // Registers the plugin without enabling any rule. Meant to be used through
2
- // `templateRecommended`, inside the `extends` of a block that already targets
3
- // `**/*.html` and sets the Angular template parser.
2
+ // `templateRules` or `templateRecommended`, inside the `extends` of a block that
3
+ // already targets `**/*.html` and sets the Angular template parser.
4
4
  const templateBase = (plugin) => ({
5
5
  name: 'prestations-ng/template-base',
6
6
  plugins: {
@@ -1,14 +1,23 @@
1
- import templateBase from './template-base.mjs';
1
+ import angular from 'angular-eslint';
2
2
 
3
+ import templateRules from './template-rules.mjs';
4
+
5
+ /**
6
+ * The house Angular template preset: the recommended and accessibility baselines, this
7
+ * library's own rules, and the conventions every prestations-ng project follows.
8
+ *
9
+ * Meant for the `extends` of a `**\/*.html` block. It brings its own parser and plugins,
10
+ * so do not spread `angular.configs.templateRecommended` alongside it.
11
+ */
3
12
  const templateRecommended = (plugin) => [
4
- templateBase(plugin),
13
+ ...angular.configs.templateRecommended,
14
+ ...angular.configs.templateAccessibility,
15
+ ...templateRules(plugin),
5
16
  {
6
17
  name: 'prestations-ng/template-recommended',
7
18
  rules: {
8
- // Also in `tsRecommended`: the rule carries both an ESTree and an Angular
9
- // template visitor, and each fires on its own kind of file.
10
- '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
11
- '@dsivd/prestations-ng/no-uninvoked-signal-in-template': 'error',
19
+ '@angular-eslint/template/no-negated-async': 'off',
20
+ '@angular-eslint/template/button-has-type': 'error',
12
21
  },
13
22
  },
14
23
  ];
@@ -0,0 +1,17 @@
1
+ import templateBase from './template-base.mjs';
2
+
3
+ // Only the rules written by this library. `templateRecommended` includes it, alongside
4
+ // the house style; use this layer directly to opt out of the latter.
5
+ const templateRules = (plugin) => [
6
+ templateBase(plugin),
7
+ {
8
+ name: 'prestations-ng/template-rules',
9
+ rules: {
10
+ // Also in `tsRules`: the rule carries both an ESTree and an Angular
11
+ // template visitor, and each fires on its own kind of file.
12
+ '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
13
+ '@dsivd/prestations-ng/no-uninvoked-signal-in-template': 'error',
14
+ },
15
+ },
16
+ ];
17
+ export default templateRules;
@@ -1,6 +1,6 @@
1
1
  // Registers the plugin without enabling any rule. Meant to be used through
2
- // `tsRecommended`, inside the `extends` of a block that already targets `**/*.ts`
3
- // and sets the TypeScript parser.
2
+ // `tsRules` or `tsRecommended`, inside the `extends` of a block that already
3
+ // targets `**/*.ts` and sets the TypeScript parser.
4
4
  const tsBase = (plugin) => ({
5
5
  name: 'prestations-ng/ts-base',
6
6
  plugins: {
@@ -1,11 +1,141 @@
1
- import tsBase from './ts-base.mjs';
1
+ import eslint from '@eslint/js';
2
+ import rxjs from '@smarttools/eslint-plugin-rxjs';
3
+ import angular from 'angular-eslint';
4
+ import importX from 'eslint-plugin-import-x';
5
+ import simpleImportSort from 'eslint-plugin-simple-import-sort';
6
+ import tseslint from 'typescript-eslint';
2
7
 
8
+ import tsRules from './ts-rules.mjs';
9
+
10
+ /**
11
+ * The house TypeScript preset: the shared baselines, this library's own rules, and the
12
+ * conventions every prestations-ng project follows.
13
+ *
14
+ * Meant for the `extends` of a `**\/*.ts` block. It brings its own parser and plugins,
15
+ * so do not spread `angular.configs.tsRecommended` alongside it — declaring the same
16
+ * plugin twice from two different copies makes ESLint fail outright.
17
+ */
3
18
  const tsRecommended = (plugin) => [
4
- tsBase(plugin),
19
+ eslint.configs.recommended,
20
+ ...tseslint.configs.recommended, // covers: no-explicit-any, ban-ts-comment,
21
+ // no-unused-expressions, no-var, no-unused-vars…
22
+ ...tseslint.configs.stylistic, // covers: array-type, consistent-type-assertions,
23
+ // dot-notation, no-inferrable-types (default options),
24
+ // no-empty-function (error), prefer-for-of,
25
+ // prefer-function-type…
26
+ ...angular.configs.tsRecommended, // covers: prefer-inject, no-empty-lifecycle-method,
27
+ // use-lifecycle-interface…
28
+ ...tsRules(plugin),
5
29
  {
6
30
  name: 'prestations-ng/ts-recommended',
31
+ plugins: {
32
+ rxjs,
33
+ 'simple-import-sort': simpleImportSort,
34
+ 'import-x': importX,
35
+ },
7
36
  rules: {
8
- '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
37
+ // ── @angular-eslint ──────────────────────────────────────────────
38
+ '@angular-eslint/component-selector': [
39
+ 'error',
40
+ {
41
+ type: 'element',
42
+ prefix: 'app',
43
+ style: 'kebab-case',
44
+ },
45
+ ],
46
+ '@angular-eslint/directive-selector': [
47
+ 'error',
48
+ {
49
+ type: 'attribute',
50
+ prefix: 'app',
51
+ style: 'camelCase',
52
+ },
53
+ ],
54
+ '@angular-eslint/no-uncalled-signals': ['error'],
55
+ '@angular-eslint/prefer-signal-model': ['error'],
56
+ '@angular-eslint/prefer-signals': ['error'],
57
+
58
+ // ── @typescript-eslint ───────────────────────────────────────────
59
+ '@typescript-eslint/explicit-function-return-type': [
60
+ 'error',
61
+ {
62
+ allowExpressions: true,
63
+ allowTypedFunctionExpressions: true,
64
+ allowHigherOrderFunctions: true,
65
+ allowDirectConstAssertionInArrowFunctions: true,
66
+ },
67
+ ],
68
+ '@typescript-eslint/explicit-member-accessibility': [
69
+ 'error',
70
+ { accessibility: 'no-public' },
71
+ ],
72
+ '@typescript-eslint/member-ordering': 'error',
73
+ '@typescript-eslint/no-base-to-string': 'warn',
74
+ // stylistic enables no-inferrable-types with default options (ignoreParameters: false);
75
+ // override here to allow typed parameters explicitly
76
+ '@typescript-eslint/no-inferrable-types': [
77
+ 'error',
78
+ { ignoreParameters: true },
79
+ ],
80
+ // stylistic sets no-empty-function to error; downgrade to warn
81
+ '@typescript-eslint/no-empty-function': 'warn',
82
+ '@typescript-eslint/prefer-includes': 'warn',
83
+ '@typescript-eslint/return-await': ['error', 'never'],
84
+ '@typescript-eslint/typedef': ['error', { parameter: true }],
85
+ '@typescript-eslint/unified-signatures': 'error',
86
+ // off here — @typescript-eslint/no-shadow handles it correctly for TS
87
+ 'no-shadow': 'off',
88
+ '@typescript-eslint/no-shadow': ['error', { hoist: 'all' }],
89
+ '@typescript-eslint/no-unused-vars': [
90
+ 'error',
91
+ {
92
+ varsIgnorePattern: '^_',
93
+ argsIgnorePattern: '^_',
94
+ caughtErrorsIgnorePattern: '^_',
95
+ },
96
+ ],
97
+
98
+ // ── eslint core ──────────────────────────────────────────────────
99
+ 'arrow-body-style': 'error',
100
+ 'arrow-parens': ['error', 'as-needed'],
101
+ curly: 'error',
102
+ eqeqeq: ['error', 'always'],
103
+ 'guard-for-in': 'error',
104
+ 'no-bitwise': 'error',
105
+ 'no-caller': 'error',
106
+ 'no-console': ['error', { allow: ['log', 'warn', 'error'] }],
107
+ 'no-eval': 'error',
108
+ 'no-extra-boolean-cast': 'off',
109
+ 'no-multiple-empty-lines': 'error',
110
+ 'no-new-wrappers': 'error',
111
+ 'no-restricted-imports': [
112
+ 'error',
113
+ { paths: ['rxjs/Rx', 'primeng/primeng', 'primeng'] },
114
+ ],
115
+ 'no-return-assign': 'error',
116
+ 'no-throw-literal': 'error',
117
+ 'no-undef-init': 'error',
118
+ 'no-useless-concat': 'error',
119
+ 'object-shorthand': ['error', 'always', { avoidQuotes: true }],
120
+ 'one-var': ['error', 'never'],
121
+ 'prefer-arrow-callback': 'error',
122
+ 'prefer-template': 'error',
123
+ radix: 'error',
124
+
125
+ // ── rxjs ─────────────────────────────────────────────────────────
126
+ 'rxjs/no-implicit-any-catch': ['error', { allowExplicitAny: true }],
127
+ 'rxjs/no-sharereplay': 'off',
128
+
129
+ // ── import-x ─────────────────────────────────────────────────────
130
+ 'import-x/no-cycle': ['warn', { maxDepth: 3, ignoreExternal: true }],
131
+ 'import-x/no-deprecated': 'warn',
132
+ 'import-x/first': 'error',
133
+ 'import-x/newline-after-import': 'error',
134
+ 'import-x/no-duplicates': 'error',
135
+
136
+ // ── simple-import-sort ───────────────────────────────────────────
137
+ 'simple-import-sort/imports': 'error',
138
+ 'simple-import-sort/exports': 'error',
9
139
  },
10
140
  },
11
141
  ];
@@ -0,0 +1,14 @@
1
+ import tsBase from './ts-base.mjs';
2
+
3
+ // Only the rules written by this library. `tsRecommended` includes it, alongside the
4
+ // house style; use this layer directly to opt out of the latter.
5
+ const tsRules = (plugin) => [
6
+ tsBase(plugin),
7
+ {
8
+ name: 'prestations-ng/ts-rules',
9
+ rules: {
10
+ '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
11
+ },
12
+ },
13
+ ];
14
+ export default tsRules;
package/eslint/index.mjs CHANGED
@@ -1,15 +1,21 @@
1
1
  import templateRecommended from './configs/template-recommended.mjs';
2
+ import templateRules from './configs/template-rules.mjs';
2
3
  import tsRecommended from './configs/ts-recommended.mjs';
4
+ import tsRules from './configs/ts-rules.mjs';
3
5
  import rules from './rules/index.mjs';
4
6
 
5
7
  const plugin = {
6
- meta: { name: '@dsivd/prestations-ng' },
7
- rules,
8
+ meta: { name: '@dsivd/prestations-ng' },
9
+ rules,
8
10
  };
9
11
 
10
12
  const configs = {
11
- tsRecommended: tsRecommended(plugin),
12
- templateRecommended: templateRecommended(plugin),
13
+ // The house presets: baselines, this library's rules, and the shared conventions.
14
+ tsRecommended: tsRecommended(plugin),
15
+ templateRecommended: templateRecommended(plugin),
16
+ // Only this library's own rules, for a project that supplies its own style.
17
+ tsRules: tsRules(plugin),
18
+ templateRules: templateRules(plugin),
13
19
  };
14
20
 
15
21
  export { configs, plugin, rules };
package/package.json CHANGED
@@ -40,7 +40,7 @@
40
40
  "ng-update": {
41
41
  "migrations": "./schematics/migration-collection.json"
42
42
  },
43
- "version": "19.0.6",
43
+ "version": "19.0.7",
44
44
  "module": "fesm2022/dsivd-prestations-ng.mjs",
45
45
  "typings": "types/dsivd-prestations-ng.d.ts",
46
46
  "exports": {
@@ -1,3 +1,4 @@
1
+ import { builtinRules } from 'eslint/use-at-your-own-risk';
1
2
  import { describe, expect, it } from 'vitest';
2
3
 
3
4
  import prestationsNg, { configs, plugin, rules } from '../../index.mjs';
@@ -6,65 +7,129 @@ const PLUGIN_KEY = '@dsivd/prestations-ng';
6
7
 
7
8
  const configNames = Object.keys(configs);
8
9
 
9
- // Rule ids enabled by a config, whatever its severity.
10
+ // Configs that only register this library's rules, and the presets built on top of them.
11
+ const RULE_LAYERS = ['tsRules', 'templateRules'];
12
+ const PRESETS = [
13
+ ['tsRecommended', 'tsRules'],
14
+ ['templateRecommended', 'templateRules'],
15
+ ];
16
+
17
+ const isOff = (severity) =>
18
+ severity === 'off' ||
19
+ severity === 0 ||
20
+ (Array.isArray(severity) && (severity[0] === 'off' || severity[0] === 0));
21
+
22
+ // Rule ids a config turns on. Ids set to 'off' are excluded: ESLint accepts those even
23
+ // when the rule does not exist, which is precisely the escape hatch consumers rely on.
10
24
  const enabledIdsOf = (config) =>
11
- config.flatMap((entry) => Object.keys(entry.rules ?? {}));
25
+ config.flatMap((entry) =>
26
+ Object.entries(entry.rules ?? {})
27
+ .filter(([, severity]) => !isOff(severity))
28
+ .map(([id]) => id),
29
+ );
30
+
31
+ const ownIdsOf = (config) =>
32
+ enabledIdsOf(config).filter((id) => id.startsWith(`${PLUGIN_KEY}/`));
33
+
34
+ const pluginsOf = (config) =>
35
+ Object.assign({}, ...config.map((entry) => entry.plugins ?? {}));
36
+
37
+ // An id is either `plugin-key/rule` (possibly nested, as in `@angular-eslint/template/x`)
38
+ // or a bare core rule.
39
+ const ruleExists = (id, plugins) => {
40
+ for (const [key, declared] of Object.entries(plugins)) {
41
+ if (id.startsWith(`${key}/`)) {
42
+ return Boolean(declared.rules?.[id.slice(key.length + 1)]);
43
+ }
44
+ }
45
+ return builtinRules.has(id);
46
+ };
12
47
 
13
48
  describe('eslint configs', () => {
14
- it('should expose one config per lint target', () => {
15
- expect(configNames).toEqual(['tsRecommended', 'templateRecommended']);
16
- });
49
+ it('should expose a rule layer and a preset per lint target', () => {
50
+ expect(configNames).toEqual([
51
+ 'tsRecommended',
52
+ 'templateRecommended',
53
+ 'tsRules',
54
+ 'templateRules',
55
+ ]);
56
+ });
17
57
 
18
- it.each(configNames)('%s should register the plugin first', (name) => {
19
- const [base, ...rest] = configs[name];
58
+ it.each(RULE_LAYERS)('%s should register the plugin first', (name) => {
59
+ const [base, ...rest] = configs[name];
20
60
 
21
- expect(base.plugins).toEqual({ [PLUGIN_KEY]: plugin });
22
- expect(base.rules).toBeUndefined();
23
- expect(rest.every((entry) => entry.plugins === undefined)).toBe(true);
24
- });
61
+ expect(base.plugins).toEqual({ [PLUGIN_KEY]: plugin });
62
+ expect(base.rules).toBeUndefined();
63
+ expect(rest.every((entry) => entry.plugins === undefined)).toBe(true);
64
+ });
25
65
 
26
- it.each(configNames)('%s should name every entry', (name) => {
27
- for (const entry of configs[name]) {
28
- expect(entry.name).toMatch(/^prestations-ng\//);
29
- }
30
- });
31
-
32
- // A config referencing a rule that does not exist breaks the lint of every
33
- // consuming project at once, so it must never reach a release.
34
- it.each(configNames)('%s should only enable existing rules', (name) => {
35
- for (const id of enabledIdsOf(configs[name])) {
36
- expect(id.startsWith(`${PLUGIN_KEY}/`)).toBe(true);
37
- expect(rules).toHaveProperty(id.slice(PLUGIN_KEY.length + 1));
38
- }
39
- });
66
+ it.each(RULE_LAYERS)('%s should carry no third-party rule', (name) => {
67
+ expect(enabledIdsOf(configs[name])).toEqual(ownIdsOf(configs[name]));
68
+ });
40
69
 
41
- it('should enable every rule in at least one config', () => {
42
- const enabled = new Set(configNames.flatMap((n) => enabledIdsOf(configs[n])));
70
+ it.each(configNames)('%s should name its own entries', (name) => {
71
+ const own = configs[name].filter((entry) =>
72
+ entry.name?.startsWith('prestations-ng/'),
73
+ );
43
74
 
44
- for (const ruleName of Object.keys(rules)) {
45
- expect(enabled).toContain(`${PLUGIN_KEY}/${ruleName}`);
46
- }
47
- });
75
+ expect(own.length).toBeGreaterThan(0);
76
+ });
48
77
 
49
- it('should keep the template-only rule out of the TypeScript config', () => {
50
- expect(enabledIdsOf(configs.tsRecommended)).not.toContain(
51
- `${PLUGIN_KEY}/no-uninvoked-signal-in-template`,
78
+ // A config referencing a rule that does not exist breaks the lint of every consuming
79
+ // project at once, so it must never reach a release. `builtinRules` is an unstable
80
+ // ESLint export, but it is the only way to check core rule ids.
81
+ it.each(configNames)('%s should only enable existing rules', (name) => {
82
+ const plugins = pluginsOf(configs[name]);
83
+
84
+ for (const id of enabledIdsOf(configs[name])) {
85
+ expect(ruleExists(id, plugins), `unknown rule ${id}`).toBe(true);
86
+ }
87
+ });
88
+
89
+ // Two entries declaring the same plugin key from different copies is a hard ESLint
90
+ // failure, so the preset must register ours exactly once.
91
+ it.each(configNames)('%s should register the plugin once', (name) => {
92
+ const declarations = configs[name]
93
+ .map((entry) => entry.plugins?.[PLUGIN_KEY])
94
+ .filter(Boolean);
95
+
96
+ expect(declarations).toEqual([plugin]);
97
+ });
98
+
99
+ it.each(PRESETS)('%s should build on %s', (preset, layer) => {
100
+ expect(ownIdsOf(configs[preset])).toEqual(ownIdsOf(configs[layer]));
101
+ });
102
+
103
+ it('should enable every rule in at least one config', () => {
104
+ const enabled = new Set(configNames.flatMap((n) => ownIdsOf(configs[n])));
105
+
106
+ for (const ruleName of Object.keys(rules)) {
107
+ expect(enabled).toContain(`${PLUGIN_KEY}/${ruleName}`);
108
+ }
109
+ });
110
+
111
+ it.each(['tsRecommended', 'tsRules'])(
112
+ '%s should keep the template-only rule out',
113
+ (name) => {
114
+ expect(enabledIdsOf(configs[name])).not.toContain(
115
+ `${PLUGIN_KEY}/no-uninvoked-signal-in-template`,
116
+ );
117
+ },
52
118
  );
53
- });
54
119
  });
55
120
 
56
121
  describe('eslint plugin', () => {
57
- it('should be named after the npm package', () => {
58
- expect(plugin.meta.name).toBe(PLUGIN_KEY);
59
- });
60
-
61
- it('should mirror the named exports on the default export', () => {
62
- expect(prestationsNg).toEqual({ configs, plugin, rules });
63
- });
64
-
65
- it.each(Object.entries(rules))('%s should be a usable rule', (_, rule) => {
66
- expect(typeof rule.create).toBe('function');
67
- expect(Object.keys(rule.meta.messages).length).toBeGreaterThan(0);
68
- expect(rule.meta.type).toBe('problem');
69
- });
122
+ it('should be named after the npm package', () => {
123
+ expect(plugin.meta.name).toBe(PLUGIN_KEY);
124
+ });
125
+
126
+ it('should mirror the named exports on the default export', () => {
127
+ expect(prestationsNg).toEqual({ configs, plugin, rules });
128
+ });
129
+
130
+ it.each(Object.entries(rules))('%s should be a usable rule', (_, rule) => {
131
+ expect(typeof rule.create).toBe('function');
132
+ expect(Object.keys(rule.meta.messages).length).toBeGreaterThan(0);
133
+ expect(rule.meta.type).toBe('problem');
134
+ });
70
135
  });
@@ -1,6 +1,6 @@
1
1
  // Registers the plugin without enabling any rule. Meant to be used through
2
- // `templateRecommended`, inside the `extends` of a block that already targets
3
- // `**/*.html` and sets the Angular template parser.
2
+ // `templateRules` or `templateRecommended`, inside the `extends` of a block that
3
+ // already targets `**/*.html` and sets the Angular template parser.
4
4
  const templateBase = (plugin) => ({
5
5
  name: 'prestations-ng/template-base',
6
6
  plugins: {
@@ -1,14 +1,23 @@
1
- import templateBase from './template-base.mjs';
1
+ import angular from 'angular-eslint';
2
2
 
3
+ import templateRules from './template-rules.mjs';
4
+
5
+ /**
6
+ * The house Angular template preset: the recommended and accessibility baselines, this
7
+ * library's own rules, and the conventions every prestations-ng project follows.
8
+ *
9
+ * Meant for the `extends` of a `**\/*.html` block. It brings its own parser and plugins,
10
+ * so do not spread `angular.configs.templateRecommended` alongside it.
11
+ */
3
12
  const templateRecommended = (plugin) => [
4
- templateBase(plugin),
13
+ ...angular.configs.templateRecommended,
14
+ ...angular.configs.templateAccessibility,
15
+ ...templateRules(plugin),
5
16
  {
6
17
  name: 'prestations-ng/template-recommended',
7
18
  rules: {
8
- // Also in `tsRecommended`: the rule carries both an ESTree and an Angular
9
- // template visitor, and each fires on its own kind of file.
10
- '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
11
- '@dsivd/prestations-ng/no-uninvoked-signal-in-template': 'error',
19
+ '@angular-eslint/template/no-negated-async': 'off',
20
+ '@angular-eslint/template/button-has-type': 'error',
12
21
  },
13
22
  },
14
23
  ];
@@ -0,0 +1,17 @@
1
+ import templateBase from './template-base.mjs';
2
+
3
+ // Only the rules written by this library. `templateRecommended` includes it, alongside
4
+ // the house style; use this layer directly to opt out of the latter.
5
+ const templateRules = (plugin) => [
6
+ templateBase(plugin),
7
+ {
8
+ name: 'prestations-ng/template-rules',
9
+ rules: {
10
+ // Also in `tsRules`: the rule carries both an ESTree and an Angular
11
+ // template visitor, and each fires on its own kind of file.
12
+ '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
13
+ '@dsivd/prestations-ng/no-uninvoked-signal-in-template': 'error',
14
+ },
15
+ },
16
+ ];
17
+ export default templateRules;
@@ -1,6 +1,6 @@
1
1
  // Registers the plugin without enabling any rule. Meant to be used through
2
- // `tsRecommended`, inside the `extends` of a block that already targets `**/*.ts`
3
- // and sets the TypeScript parser.
2
+ // `tsRules` or `tsRecommended`, inside the `extends` of a block that already
3
+ // targets `**/*.ts` and sets the TypeScript parser.
4
4
  const tsBase = (plugin) => ({
5
5
  name: 'prestations-ng/ts-base',
6
6
  plugins: {
@@ -1,11 +1,141 @@
1
- import tsBase from './ts-base.mjs';
1
+ import eslint from '@eslint/js';
2
+ import rxjs from '@smarttools/eslint-plugin-rxjs';
3
+ import angular from 'angular-eslint';
4
+ import importX from 'eslint-plugin-import-x';
5
+ import simpleImportSort from 'eslint-plugin-simple-import-sort';
6
+ import tseslint from 'typescript-eslint';
2
7
 
8
+ import tsRules from './ts-rules.mjs';
9
+
10
+ /**
11
+ * The house TypeScript preset: the shared baselines, this library's own rules, and the
12
+ * conventions every prestations-ng project follows.
13
+ *
14
+ * Meant for the `extends` of a `**\/*.ts` block. It brings its own parser and plugins,
15
+ * so do not spread `angular.configs.tsRecommended` alongside it — declaring the same
16
+ * plugin twice from two different copies makes ESLint fail outright.
17
+ */
3
18
  const tsRecommended = (plugin) => [
4
- tsBase(plugin),
19
+ eslint.configs.recommended,
20
+ ...tseslint.configs.recommended, // covers: no-explicit-any, ban-ts-comment,
21
+ // no-unused-expressions, no-var, no-unused-vars…
22
+ ...tseslint.configs.stylistic, // covers: array-type, consistent-type-assertions,
23
+ // dot-notation, no-inferrable-types (default options),
24
+ // no-empty-function (error), prefer-for-of,
25
+ // prefer-function-type…
26
+ ...angular.configs.tsRecommended, // covers: prefer-inject, no-empty-lifecycle-method,
27
+ // use-lifecycle-interface…
28
+ ...tsRules(plugin),
5
29
  {
6
30
  name: 'prestations-ng/ts-recommended',
31
+ plugins: {
32
+ rxjs,
33
+ 'simple-import-sort': simpleImportSort,
34
+ 'import-x': importX,
35
+ },
7
36
  rules: {
8
- '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
37
+ // ── @angular-eslint ──────────────────────────────────────────────
38
+ '@angular-eslint/component-selector': [
39
+ 'error',
40
+ {
41
+ type: 'element',
42
+ prefix: 'app',
43
+ style: 'kebab-case',
44
+ },
45
+ ],
46
+ '@angular-eslint/directive-selector': [
47
+ 'error',
48
+ {
49
+ type: 'attribute',
50
+ prefix: 'app',
51
+ style: 'camelCase',
52
+ },
53
+ ],
54
+ '@angular-eslint/no-uncalled-signals': ['error'],
55
+ '@angular-eslint/prefer-signal-model': ['error'],
56
+ '@angular-eslint/prefer-signals': ['error'],
57
+
58
+ // ── @typescript-eslint ───────────────────────────────────────────
59
+ '@typescript-eslint/explicit-function-return-type': [
60
+ 'error',
61
+ {
62
+ allowExpressions: true,
63
+ allowTypedFunctionExpressions: true,
64
+ allowHigherOrderFunctions: true,
65
+ allowDirectConstAssertionInArrowFunctions: true,
66
+ },
67
+ ],
68
+ '@typescript-eslint/explicit-member-accessibility': [
69
+ 'error',
70
+ { accessibility: 'no-public' },
71
+ ],
72
+ '@typescript-eslint/member-ordering': 'error',
73
+ '@typescript-eslint/no-base-to-string': 'warn',
74
+ // stylistic enables no-inferrable-types with default options (ignoreParameters: false);
75
+ // override here to allow typed parameters explicitly
76
+ '@typescript-eslint/no-inferrable-types': [
77
+ 'error',
78
+ { ignoreParameters: true },
79
+ ],
80
+ // stylistic sets no-empty-function to error; downgrade to warn
81
+ '@typescript-eslint/no-empty-function': 'warn',
82
+ '@typescript-eslint/prefer-includes': 'warn',
83
+ '@typescript-eslint/return-await': ['error', 'never'],
84
+ '@typescript-eslint/typedef': ['error', { parameter: true }],
85
+ '@typescript-eslint/unified-signatures': 'error',
86
+ // off here — @typescript-eslint/no-shadow handles it correctly for TS
87
+ 'no-shadow': 'off',
88
+ '@typescript-eslint/no-shadow': ['error', { hoist: 'all' }],
89
+ '@typescript-eslint/no-unused-vars': [
90
+ 'error',
91
+ {
92
+ varsIgnorePattern: '^_',
93
+ argsIgnorePattern: '^_',
94
+ caughtErrorsIgnorePattern: '^_',
95
+ },
96
+ ],
97
+
98
+ // ── eslint core ──────────────────────────────────────────────────
99
+ 'arrow-body-style': 'error',
100
+ 'arrow-parens': ['error', 'as-needed'],
101
+ curly: 'error',
102
+ eqeqeq: ['error', 'always'],
103
+ 'guard-for-in': 'error',
104
+ 'no-bitwise': 'error',
105
+ 'no-caller': 'error',
106
+ 'no-console': ['error', { allow: ['log', 'warn', 'error'] }],
107
+ 'no-eval': 'error',
108
+ 'no-extra-boolean-cast': 'off',
109
+ 'no-multiple-empty-lines': 'error',
110
+ 'no-new-wrappers': 'error',
111
+ 'no-restricted-imports': [
112
+ 'error',
113
+ { paths: ['rxjs/Rx', 'primeng/primeng', 'primeng'] },
114
+ ],
115
+ 'no-return-assign': 'error',
116
+ 'no-throw-literal': 'error',
117
+ 'no-undef-init': 'error',
118
+ 'no-useless-concat': 'error',
119
+ 'object-shorthand': ['error', 'always', { avoidQuotes: true }],
120
+ 'one-var': ['error', 'never'],
121
+ 'prefer-arrow-callback': 'error',
122
+ 'prefer-template': 'error',
123
+ radix: 'error',
124
+
125
+ // ── rxjs ─────────────────────────────────────────────────────────
126
+ 'rxjs/no-implicit-any-catch': ['error', { allowExplicitAny: true }],
127
+ 'rxjs/no-sharereplay': 'off',
128
+
129
+ // ── import-x ─────────────────────────────────────────────────────
130
+ 'import-x/no-cycle': ['warn', { maxDepth: 3, ignoreExternal: true }],
131
+ 'import-x/no-deprecated': 'warn',
132
+ 'import-x/first': 'error',
133
+ 'import-x/newline-after-import': 'error',
134
+ 'import-x/no-duplicates': 'error',
135
+
136
+ // ── simple-import-sort ───────────────────────────────────────────
137
+ 'simple-import-sort/imports': 'error',
138
+ 'simple-import-sort/exports': 'error',
9
139
  },
10
140
  },
11
141
  ];
@@ -0,0 +1,14 @@
1
+ import tsBase from './ts-base.mjs';
2
+
3
+ // Only the rules written by this library. `tsRecommended` includes it, alongside the
4
+ // house style; use this layer directly to opt out of the latter.
5
+ const tsRules = (plugin) => [
6
+ tsBase(plugin),
7
+ {
8
+ name: 'prestations-ng/ts-rules',
9
+ rules: {
10
+ '@dsivd/prestations-ng/no-direct-signal-mutation': 'error',
11
+ },
12
+ },
13
+ ];
14
+ export default tsRules;
@@ -1,15 +1,21 @@
1
1
  import templateRecommended from './configs/template-recommended.mjs';
2
+ import templateRules from './configs/template-rules.mjs';
2
3
  import tsRecommended from './configs/ts-recommended.mjs';
4
+ import tsRules from './configs/ts-rules.mjs';
3
5
  import rules from './rules/index.mjs';
4
6
 
5
7
  const plugin = {
6
- meta: { name: '@dsivd/prestations-ng' },
7
- rules,
8
+ meta: { name: '@dsivd/prestations-ng' },
9
+ rules,
8
10
  };
9
11
 
10
12
  const configs = {
11
- tsRecommended: tsRecommended(plugin),
12
- templateRecommended: templateRecommended(plugin),
13
+ // The house presets: baselines, this library's rules, and the shared conventions.
14
+ tsRecommended: tsRecommended(plugin),
15
+ templateRecommended: templateRecommended(plugin),
16
+ // Only this library's own rules, for a project that supplies its own style.
17
+ tsRules: tsRules(plugin),
18
+ templateRules: templateRules(plugin),
13
19
  };
14
20
 
15
21
  export { configs, plugin, rules };
Binary file