eslint-plugin-ai-rules 0.3.1 → 0.4.0
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/LICENSE +1 -1
- package/README.md +170 -57
- package/dist/index.cjs +80 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +80 -38
- package/dist/index.js.map +1 -1
- package/package.json +19 -8
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -94,6 +94,7 @@ npx eslint --fix .
|
|
|
94
94
|
- [Structure DSL](#structure-dsl)
|
|
95
95
|
- [Limitations](#limitations)
|
|
96
96
|
- [Rules](#rules)
|
|
97
|
+
- [Disabling or changing rules](#disabling-or-changing-rules)
|
|
97
98
|
- [What runs automatically](#what-runs-automatically)
|
|
98
99
|
- [Recommended preset](#recommended-preset)
|
|
99
100
|
- [Troubleshooting](#troubleshooting)
|
|
@@ -101,19 +102,19 @@ npx eslint --fix .
|
|
|
101
102
|
- [Migrating from 0.2.x](#migrating-from-02x)
|
|
102
103
|
- [Development](#development)
|
|
103
104
|
- [Publishing](#publishing)
|
|
104
|
-
- [
|
|
105
|
+
- [Articles](#articles)
|
|
106
|
+
- [Contributing](#contributing)
|
|
107
|
+
- [Security](#security)
|
|
105
108
|
|
|
106
109
|
## Configuration warning
|
|
107
110
|
|
|
108
|
-
If you install the plugin and use `aiRules.configs.recommended` without configuring the project structure,
|
|
111
|
+
If you install the plugin and use `aiRules.configs.recommended` without configuring the project structure, `ai-rules/project-setup` reports a warning the first time it lints a file:
|
|
109
112
|
|
|
110
113
|
```text
|
|
111
|
-
|
|
112
|
-
Configure it with aiRules.configs.projects([{ rootFolder: '.', preset: 'frontend' | 'backend' }]).
|
|
113
|
-
If this project is not a frontend/backend, use preset: 'other' to disable this warning.
|
|
114
|
+
Project structure validation is not configured. Configure it with aiRules.configs.projects([{ rootFolder: ".", preset: "frontend" | "backend" }]). If this project is not a frontend/backend, use preset: "other" to disable this warning. ai-rules/project-setup
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
The warning exists only to make sure the new structure rules are actually configured. It is
|
|
117
|
+
The warning exists only to make sure the new structure rules are actually configured. It is reported once per process.
|
|
117
118
|
|
|
118
119
|
It disappears when you provide a valid configuration:
|
|
119
120
|
|
|
@@ -123,7 +124,7 @@ It disappears when you provide a valid configuration:
|
|
|
123
124
|
- a custom non-empty `structure`
|
|
124
125
|
- a `relations` configuration
|
|
125
126
|
|
|
126
|
-
A project containing only `rootFolder` and an empty `structure` is still considered unconfigured and will
|
|
127
|
+
A project containing only `rootFolder` and an empty `structure` is still considered unconfigured and will report.
|
|
127
128
|
|
|
128
129
|
For CI or temporary runs, silence the warning without changing the config:
|
|
129
130
|
|
|
@@ -131,6 +132,16 @@ For CI or temporary runs, silence the warning without changing the config:
|
|
|
131
132
|
AI_RULES_SILENT=1 npx eslint .
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
You can also disable the rule or change its severity like any other ESLint rule:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
{
|
|
139
|
+
rules: {
|
|
140
|
+
'ai-rules/project-setup': 'off',
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
134
145
|
The warning is still recommended as a setup check, so prefer `preset: 'other'` for projects that intentionally do not use structure validation.
|
|
135
146
|
|
|
136
147
|
## Features
|
|
@@ -167,6 +178,7 @@ interface IProjectDefinition {
|
|
|
167
178
|
preset?: 'frontend' | 'backend' | 'other'
|
|
168
179
|
structure?: IProjectStructure
|
|
169
180
|
relations?: IRelationsConfig
|
|
181
|
+
rules?: Linter.RulesRecord
|
|
170
182
|
}
|
|
171
183
|
```
|
|
172
184
|
|
|
@@ -178,6 +190,7 @@ interface IProjectDefinition {
|
|
|
178
190
|
- `preset` provides a base structure.
|
|
179
191
|
- `structure` is merged on top of the preset.
|
|
180
192
|
- `relations` enables architecture dependency policies with `eslint-plugin-boundaries`.
|
|
193
|
+
- `rules` overrides rule severities and options for that project using the standard ESLint rule format.
|
|
181
194
|
- `preset: 'other'` disables structure validation unless a custom `structure` is provided.
|
|
182
195
|
|
|
183
196
|
`aiRules.configs.projects([...])` generates one flat config per project. Each config:
|
|
@@ -186,9 +199,10 @@ interface IProjectDefinition {
|
|
|
186
199
|
- sets `settings['ai-rules'].project`;
|
|
187
200
|
- enables `ai-rules/project-structure`;
|
|
188
201
|
- adds `ignores` from `ignoredPaths`;
|
|
189
|
-
- includes the recommended rules
|
|
202
|
+
- includes the recommended rules, overridable by `rules`;
|
|
190
203
|
- disables `ai-rules/filename-pascal-case`, because file naming is handled by `project-structure`;
|
|
191
|
-
- when `relations` is present, registers `boundaries` and enables `boundaries/dependencies
|
|
204
|
+
- when `relations` is present, registers `boundaries` and enables `boundaries/dependencies`;
|
|
205
|
+
- applies `rules` last, so a project can disable or change any rule.
|
|
192
206
|
|
|
193
207
|
## Examples
|
|
194
208
|
|
|
@@ -961,12 +975,12 @@ Validates the configured hierarchy, folder names and file names.
|
|
|
961
975
|
|
|
962
976
|
### `ai-rules/project-setup`
|
|
963
977
|
|
|
964
|
-
|
|
978
|
+
Reports the configuration warning described above as a regular ESLint diagnostic.
|
|
965
979
|
|
|
966
980
|
- Type: `suggestion`
|
|
967
981
|
- Enabled as `warn` in `recommended`
|
|
968
|
-
-
|
|
969
|
-
-
|
|
982
|
+
- Reports once per process
|
|
983
|
+
- Respects the configured severity (`off`, `warn`, `error`)
|
|
970
984
|
- Suppressed by `preset: 'other'`, `frontend`, `backend` or a non-empty custom structure
|
|
971
985
|
|
|
972
986
|
### `ai-rules/filename-pascal-case`
|
|
@@ -1038,20 +1052,82 @@ The plugin also configures the `import-x` resolver with:
|
|
|
1038
1052
|
extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx']
|
|
1039
1053
|
```
|
|
1040
1054
|
|
|
1041
|
-
|
|
1055
|
+
## Disabling or changing rules
|
|
1056
|
+
|
|
1057
|
+
Every rule is controlled through the standard ESLint `rules` object. Any entry accepts `'off'`, `'warn'`, `'error'`, or `['error', options]`.
|
|
1058
|
+
|
|
1059
|
+
### Option 1: override after the generated config
|
|
1060
|
+
|
|
1061
|
+
Flat config is evaluated in order, so a config placed after `projects()` or `recommended` wins:
|
|
1042
1062
|
|
|
1043
1063
|
```ts
|
|
1044
1064
|
export default defineConfig([
|
|
1045
|
-
...
|
|
1065
|
+
...tseslint.configs.recommended,
|
|
1066
|
+
...aiRules.configs.projects([
|
|
1067
|
+
{ rootFolder: '.', preset: 'frontend' },
|
|
1068
|
+
]),
|
|
1046
1069
|
{
|
|
1047
1070
|
rules: {
|
|
1048
|
-
'
|
|
1071
|
+
'ai-rules/sort-imports': 'off',
|
|
1072
|
+
'ai-rules/project-structure': 'warn',
|
|
1073
|
+
'ai-rules/filename-pascal-case': 'error',
|
|
1074
|
+
'@stylistic/semi': ['warn', 'always'],
|
|
1049
1075
|
'import-x/no-default-export': 'off',
|
|
1076
|
+
'boundaries/dependencies': 'off',
|
|
1050
1077
|
},
|
|
1051
1078
|
},
|
|
1052
1079
|
])
|
|
1053
1080
|
```
|
|
1054
1081
|
|
|
1082
|
+
To apply an override only to part of the repository, use `files`:
|
|
1083
|
+
|
|
1084
|
+
```ts
|
|
1085
|
+
{
|
|
1086
|
+
files: ['scripts/**/*', '*.config.*'],
|
|
1087
|
+
rules: {
|
|
1088
|
+
'ai-rules/filename-pascal-case': 'off',
|
|
1089
|
+
},
|
|
1090
|
+
}
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
### Option 2: per-project `rules`
|
|
1094
|
+
|
|
1095
|
+
When using `projects()`, pass `rules` in the project definition. These rules are applied after the recommended rules, the preset rules and the automatic structure/relations rules:
|
|
1096
|
+
|
|
1097
|
+
```ts
|
|
1098
|
+
aiRules.configs.projects([
|
|
1099
|
+
{
|
|
1100
|
+
rootFolder: 'apps/api',
|
|
1101
|
+
preset: 'backend',
|
|
1102
|
+
rules: {
|
|
1103
|
+
'ai-rules/sort-imports': 'off',
|
|
1104
|
+
'ai-rules/project-structure': 'warn',
|
|
1105
|
+
'@stylistic/semi': ['warn', 'always'],
|
|
1106
|
+
'boundaries/dependencies': 'off',
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
1109
|
+
])
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
Precedence inside `projects()` is:
|
|
1113
|
+
|
|
1114
|
+
1. `recommended` rules
|
|
1115
|
+
2. automatic `project-structure`, `filename-pascal-case` and `boundaries/dependencies` rules
|
|
1116
|
+
3. `preset.rules`
|
|
1117
|
+
4. project `rules`
|
|
1118
|
+
5. any flat config placed after `projects()` in the ESLint config array
|
|
1119
|
+
|
|
1120
|
+
The `rules` option uses the same shape as ESLint, including options arrays:
|
|
1121
|
+
|
|
1122
|
+
```ts
|
|
1123
|
+
rules: {
|
|
1124
|
+
'ai-rules/filename-pascal-case': [
|
|
1125
|
+
'error',
|
|
1126
|
+
{ ignore: ['\\.config\\.'], ignoreMiddleExtensions: false },
|
|
1127
|
+
],
|
|
1128
|
+
}
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1055
1131
|
## What runs automatically
|
|
1056
1132
|
|
|
1057
1133
|
| You use | What is enabled |
|
|
@@ -1107,6 +1183,16 @@ Or explicitly opt out:
|
|
|
1107
1183
|
|
|
1108
1184
|
A project with only `rootFolder` and an empty `structure` still warns.
|
|
1109
1185
|
|
|
1186
|
+
If you intentionally do not want this check, disable the rule:
|
|
1187
|
+
|
|
1188
|
+
```ts
|
|
1189
|
+
{
|
|
1190
|
+
rules: {
|
|
1191
|
+
'ai-rules/project-setup': 'off',
|
|
1192
|
+
},
|
|
1193
|
+
}
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1110
1196
|
For temporary runs or CI, use:
|
|
1111
1197
|
|
|
1112
1198
|
```bash
|
|
@@ -1210,34 +1296,36 @@ If you intentionally do not want structure validation:
|
|
|
1210
1296
|
## Development
|
|
1211
1297
|
|
|
1212
1298
|
```bash
|
|
1213
|
-
git clone
|
|
1299
|
+
git clone https://github.com/lvsouza/eslint-ai-guard.git
|
|
1214
1300
|
cd eslint-ai-guard
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1301
|
+
corepack enable
|
|
1302
|
+
yarn install --frozen-lockfile
|
|
1303
|
+
|
|
1304
|
+
yarn typecheck # tsc --noEmit
|
|
1305
|
+
yarn test # node:test against dist
|
|
1306
|
+
yarn test:watch # node:test in watch mode
|
|
1307
|
+
yarn lint # eslint .
|
|
1308
|
+
yarn lint --fix
|
|
1309
|
+
yarn build # tsup -> ESM + CJS + declarations
|
|
1310
|
+
yarn dev # tsup --watch
|
|
1311
|
+
yarn verify # typecheck + test + lint + pack
|
|
1312
|
+
yarn test:coverage # c8 coverage with thresholds
|
|
1313
|
+
yarn test:consumer # install packed tarball in a temp project
|
|
1314
|
+
yarn test:package # publint + arethetypeswrong
|
|
1315
|
+
yarn test:release # full validation before release
|
|
1316
|
+
npm pack --dry-run # verify published files
|
|
1229
1317
|
```
|
|
1230
1318
|
|
|
1231
|
-
`test:consumer` and `test:package` are release checks and are not part of `verify`. They may use the network.
|
|
1319
|
+
`test:consumer` and `test:package` are release checks and are not part of `verify`. They may use the network. `test:release` runs the complete validation: `verify`, coverage, consumer and package checks.
|
|
1232
1320
|
|
|
1233
1321
|
The consumer smoke test covers structure validation, relations with aliases and type-only imports, warning/suppression, ESM and CJS. It accepts `ESLINT_VERSION`:
|
|
1234
1322
|
|
|
1235
1323
|
```bash
|
|
1236
|
-
ESLINT_VERSION=9.22.0
|
|
1237
|
-
ESLINT_VERSION='^10.0.0'
|
|
1324
|
+
ESLINT_VERSION=9.22.0 yarn test:consumer
|
|
1325
|
+
ESLINT_VERSION='^10.0.0' yarn test:consumer
|
|
1238
1326
|
```
|
|
1239
1327
|
|
|
1240
|
-
|
|
1328
|
+
The full validation runs in `.github/workflows/release.yml` on version tags: `verify` on Node 20, 22 and 24, coverage and package checks on Node 24, and consumer tests on Node 20/24 with ESLint 9.22 and 10. Pull requests keep dependency review and CodeQL.
|
|
1241
1329
|
|
|
1242
1330
|
### Project structure
|
|
1243
1331
|
|
|
@@ -1260,13 +1348,9 @@ eslint-ai-guard/
|
|
|
1260
1348
|
│ │ ├── validateRelations.ts
|
|
1261
1349
|
│ │ ├── validateStructure.ts
|
|
1262
1350
|
│ │ └── presets/
|
|
1263
|
-
│ │ ├── frontend.ts
|
|
1264
|
-
│ │ ├── backend.ts
|
|
1265
|
-
│ │ ├── other.ts
|
|
1266
|
-
│ │ ├── helpers.ts
|
|
1267
|
-
│ │ └── index.ts
|
|
1268
1351
|
│ ├── utils/
|
|
1269
1352
|
│ └── rules/
|
|
1353
|
+
│ ├── constants.ts
|
|
1270
1354
|
│ ├── FilenamePascalCase.ts
|
|
1271
1355
|
│ ├── NoMultilineImports.ts
|
|
1272
1356
|
│ ├── ProjectSetup.ts
|
|
@@ -1276,11 +1360,18 @@ eslint-ai-guard/
|
|
|
1276
1360
|
├── test/
|
|
1277
1361
|
├── scripts/
|
|
1278
1362
|
│ └── TestConsumer.mjs
|
|
1363
|
+
├── docs/
|
|
1279
1364
|
├── .github/
|
|
1280
|
-
│
|
|
1281
|
-
│
|
|
1282
|
-
├──
|
|
1365
|
+
│ ├── workflows/
|
|
1366
|
+
│ │ ├── codeql.yml
|
|
1367
|
+
│ │ ├── dependency-review.yml
|
|
1368
|
+
│ │ └── release.yml
|
|
1369
|
+
│ ├── ISSUE_TEMPLATE/
|
|
1370
|
+
│ └── dependabot.yml
|
|
1283
1371
|
├── CHANGELOG.md
|
|
1372
|
+
├── CONTRIBUTING.md
|
|
1373
|
+
├── SECURITY.md
|
|
1374
|
+
├── CODE_OF_CONDUCT.md
|
|
1284
1375
|
├── eslint.config.mjs
|
|
1285
1376
|
├── tsconfig.json
|
|
1286
1377
|
├── tsup.config.ts
|
|
@@ -1290,31 +1381,53 @@ eslint-ai-guard/
|
|
|
1290
1381
|
|
|
1291
1382
|
### Adding a new rule
|
|
1292
1383
|
|
|
1293
|
-
1. Create `src/rules/MyRule.ts
|
|
1294
|
-
2.
|
|
1295
|
-
3.
|
|
1296
|
-
4.
|
|
1297
|
-
5. Add
|
|
1298
|
-
6.
|
|
1384
|
+
1. Create `src/rules/MyRule.ts` in PascalCase.
|
|
1385
|
+
2. Add the rule name to `src/rules/constants.ts`.
|
|
1386
|
+
3. Export it from `src/rules/index.ts`.
|
|
1387
|
+
4. Register it in `src/plugin.ts`.
|
|
1388
|
+
5. Add it to `src/configs/recommended.ts` if it should be enabled by default.
|
|
1389
|
+
6. Provide `meta.docs.description`, `meta.docs.url`, `meta.docs.recommended`, `meta.schema` and `meta.messages`.
|
|
1390
|
+
7. Add `meta.defaultOptions` when the rule accepts options.
|
|
1391
|
+
8. Add tests in `test/`.
|
|
1392
|
+
9. Run `yarn verify`.
|
|
1299
1393
|
|
|
1300
1394
|
## Publishing
|
|
1301
1395
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1396
|
+
Releases are driven by a version tag. The workflow validates that the tag matches `package.json`, runs all checks, publishes to npm with provenance and creates a GitHub release.
|
|
1397
|
+
|
|
1398
|
+
1. Update `version` in `package.json`.
|
|
1399
|
+
2. Update `CHANGELOG.md`.
|
|
1400
|
+
3. Run `yarn test:release`.
|
|
1401
|
+
4. Commit and push the changes.
|
|
1402
|
+
5. Create and push a tag:
|
|
1305
1403
|
|
|
1306
|
-
|
|
1404
|
+
```bash
|
|
1405
|
+
git tag v0.4.0
|
|
1406
|
+
git push origin v0.4.0
|
|
1407
|
+
```
|
|
1307
1408
|
|
|
1308
|
-
|
|
1409
|
+
The release workflow requires the `NPM_TOKEN` repository secret. Provenance also requires `id-token: write`, which is already configured in `.github/workflows/release.yml`.
|
|
1410
|
+
|
|
1411
|
+
The workflow runs all checks before publishing. If any check fails, the publish job is skipped. Fix the problem, delete the tag and recreate it before pushing again:
|
|
1309
1412
|
|
|
1310
1413
|
```bash
|
|
1311
|
-
|
|
1312
|
-
|
|
1414
|
+
git tag -d v0.4.0
|
|
1415
|
+
git push origin :refs/tags/v0.4.0
|
|
1313
1416
|
```
|
|
1314
1417
|
|
|
1315
|
-
|
|
1418
|
+
`prepack` runs `yarn build` automatically, so published packages always contain a fresh `dist`. The plugin version is injected from `package.json` during the build, so you do not need to edit `src/plugin.ts`.
|
|
1419
|
+
|
|
1420
|
+
## Contributing
|
|
1421
|
+
|
|
1422
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, testing and rule-authoring guidelines. All participants are expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
1423
|
+
|
|
1424
|
+
## Security
|
|
1425
|
+
|
|
1426
|
+
See [SECURITY.md](SECURITY.md) for supported versions and private vulnerability reporting.
|
|
1427
|
+
|
|
1428
|
+
## Articles
|
|
1316
1429
|
|
|
1317
|
-
|
|
1430
|
+
- [How I forced AI to follow the project standard with ESLint](docs/tabnews-eslint-plugin-ai-rules.md)
|
|
1318
1431
|
|
|
1319
1432
|
## License
|
|
1320
1433
|
|
package/dist/index.cjs
CHANGED
|
@@ -49,22 +49,50 @@ var import_eslint_plugin_import_x = require("eslint-plugin-import-x");
|
|
|
49
49
|
var import_eslint_plugin = __toESM(require("@stylistic/eslint-plugin"), 1);
|
|
50
50
|
var import_typescript_eslint = __toESM(require("typescript-eslint"), 1);
|
|
51
51
|
|
|
52
|
+
// src/rules/constants.ts
|
|
53
|
+
var RULE_NAMES = {
|
|
54
|
+
filenamePascalCase: "filename-pascal-case",
|
|
55
|
+
noMultilineImports: "no-multiline-imports",
|
|
56
|
+
projectSetup: "project-setup",
|
|
57
|
+
projectStructure: "project-structure",
|
|
58
|
+
sortImports: "sort-imports"
|
|
59
|
+
};
|
|
60
|
+
var RULE_IDS = {
|
|
61
|
+
filenamePascalCase: `ai-rules/${RULE_NAMES.filenamePascalCase}`,
|
|
62
|
+
noMultilineImports: `ai-rules/${RULE_NAMES.noMultilineImports}`,
|
|
63
|
+
projectSetup: `ai-rules/${RULE_NAMES.projectSetup}`,
|
|
64
|
+
projectStructure: `ai-rules/${RULE_NAMES.projectStructure}`,
|
|
65
|
+
sortImports: `ai-rules/${RULE_NAMES.sortImports}`
|
|
66
|
+
};
|
|
67
|
+
|
|
52
68
|
// src/rules/FilenamePascalCase.ts
|
|
53
69
|
var import_node_path = __toESM(require("path"), 1);
|
|
54
70
|
var filenamePascalCase = {
|
|
55
71
|
meta: {
|
|
56
72
|
type: "suggestion",
|
|
57
|
-
docs: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: "Enforce PascalCase for file names",
|
|
75
|
+
recommended: true,
|
|
76
|
+
url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesfilename-pascal-case"
|
|
77
|
+
},
|
|
58
78
|
schema: [
|
|
59
79
|
{
|
|
60
80
|
type: "object",
|
|
61
81
|
properties: {
|
|
62
|
-
ignore: {
|
|
63
|
-
|
|
82
|
+
ignore: {
|
|
83
|
+
type: "array",
|
|
84
|
+
items: { type: "string" },
|
|
85
|
+
description: "Regex strings matched against the file name and relative path."
|
|
86
|
+
},
|
|
87
|
+
ignoreMiddleExtensions: {
|
|
88
|
+
type: "boolean",
|
|
89
|
+
description: "Allow middle extensions such as .test in file names."
|
|
90
|
+
}
|
|
64
91
|
},
|
|
65
92
|
additionalProperties: false
|
|
66
93
|
}
|
|
67
94
|
],
|
|
95
|
+
defaultOptions: [{ ignore: [], ignoreMiddleExtensions: true }],
|
|
68
96
|
messages: {
|
|
69
97
|
invalid: 'File name "{{basename}}" must be in PascalCase. Rename to "{{expected}}".'
|
|
70
98
|
}
|
|
@@ -122,10 +150,12 @@ var filenamePascalCase = {
|
|
|
122
150
|
var noMultilineImports = {
|
|
123
151
|
meta: {
|
|
124
152
|
type: "layout",
|
|
125
|
-
fixable: "code",
|
|
126
153
|
docs: {
|
|
127
|
-
description: "Disallow multiline imports"
|
|
154
|
+
description: "Disallow multiline imports",
|
|
155
|
+
recommended: true,
|
|
156
|
+
url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesno-multiline-imports"
|
|
128
157
|
},
|
|
158
|
+
fixable: "code",
|
|
129
159
|
schema: [],
|
|
130
160
|
messages: {
|
|
131
161
|
multilineImport: "Imports must be written on a single line."
|
|
@@ -387,7 +417,9 @@ var projectStructure = {
|
|
|
387
417
|
meta: {
|
|
388
418
|
type: "problem",
|
|
389
419
|
docs: {
|
|
390
|
-
description: "Enforce the configured project folder and file structure"
|
|
420
|
+
description: "Enforce the configured project folder and file structure",
|
|
421
|
+
recommended: false,
|
|
422
|
+
url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesproject-structure"
|
|
391
423
|
},
|
|
392
424
|
schema: [],
|
|
393
425
|
messages: {
|
|
@@ -461,7 +493,7 @@ function hasStructureRules(structure) {
|
|
|
461
493
|
}
|
|
462
494
|
|
|
463
495
|
// src/rules/ProjectSetup.ts
|
|
464
|
-
var
|
|
496
|
+
var hasReported = false;
|
|
465
497
|
function isConfiguredProject(project) {
|
|
466
498
|
if (project.relations) return true;
|
|
467
499
|
if (project.preset === "frontend" || project.preset === "backend" || project.preset === "other") {
|
|
@@ -469,36 +501,38 @@ function isConfiguredProject(project) {
|
|
|
469
501
|
}
|
|
470
502
|
return Boolean(project.structure && hasStructureRules(project.structure));
|
|
471
503
|
}
|
|
472
|
-
function
|
|
473
|
-
if (
|
|
504
|
+
function shouldReportOnce() {
|
|
505
|
+
if (hasReported) return false;
|
|
474
506
|
if (process.env.AI_RULES_SILENT === "1" || process.env.AI_RULES_SILENT === "true") {
|
|
475
|
-
return;
|
|
507
|
+
return false;
|
|
476
508
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
[
|
|
480
|
-
"[eslint-plugin-ai-rules] Project structure validation is not configured.",
|
|
481
|
-
"Configure it with aiRules.configs.projects([{ rootFolder: '.', preset: 'frontend' | 'backend' }]).",
|
|
482
|
-
"If this project is not a frontend/backend, use preset: 'other' to disable this warning."
|
|
483
|
-
].join("\n")
|
|
484
|
-
);
|
|
509
|
+
hasReported = true;
|
|
510
|
+
return true;
|
|
485
511
|
}
|
|
486
512
|
var projectSetup = {
|
|
487
513
|
meta: {
|
|
488
514
|
type: "suggestion",
|
|
489
515
|
docs: {
|
|
490
|
-
description: "
|
|
516
|
+
description: "Require project structure validation to be configured",
|
|
517
|
+
recommended: true,
|
|
518
|
+
url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesproject-setup"
|
|
491
519
|
},
|
|
492
520
|
schema: [],
|
|
493
|
-
messages: {
|
|
521
|
+
messages: {
|
|
522
|
+
notConfigured: 'Project structure validation is not configured. Configure it with aiRules.configs.projects([{ rootFolder: ".", preset: "frontend" | "backend" }]). If this project is not a frontend/backend, use preset: "other" to disable this warning.'
|
|
523
|
+
}
|
|
494
524
|
},
|
|
495
525
|
create(context) {
|
|
496
526
|
return {
|
|
497
|
-
Program() {
|
|
527
|
+
Program(node) {
|
|
498
528
|
const settings = context.settings["ai-rules"];
|
|
499
529
|
const projects2 = settings?.projects ?? (settings?.project ? [settings.project] : []);
|
|
500
530
|
if (projects2.some(isConfiguredProject)) return;
|
|
501
|
-
|
|
531
|
+
if (!shouldReportOnce()) return;
|
|
532
|
+
context.report({
|
|
533
|
+
node,
|
|
534
|
+
messageId: "notConfigured"
|
|
535
|
+
});
|
|
502
536
|
}
|
|
503
537
|
};
|
|
504
538
|
}
|
|
@@ -509,12 +543,14 @@ var sortImports = {
|
|
|
509
543
|
meta: {
|
|
510
544
|
type: "layout",
|
|
511
545
|
docs: {
|
|
512
|
-
description: "
|
|
546
|
+
description: "Enforce sorted imports by group and line length descending, collapsing multiline imports",
|
|
547
|
+
recommended: true,
|
|
548
|
+
url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulessort-imports"
|
|
513
549
|
},
|
|
514
550
|
fixable: "code",
|
|
515
551
|
schema: [],
|
|
516
552
|
messages: {
|
|
517
|
-
sortRequired: "
|
|
553
|
+
sortRequired: "Imports must be sorted by group and line length, each on a single line."
|
|
518
554
|
}
|
|
519
555
|
},
|
|
520
556
|
create(context) {
|
|
@@ -632,14 +668,14 @@ function getSortedContent(content) {
|
|
|
632
668
|
var plugin = {
|
|
633
669
|
meta: {
|
|
634
670
|
name: "eslint-plugin-ai-rules",
|
|
635
|
-
version: "0.
|
|
671
|
+
version: "0.4.0"
|
|
636
672
|
},
|
|
637
673
|
rules: {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
674
|
+
[RULE_NAMES.filenamePascalCase]: filenamePascalCase,
|
|
675
|
+
[RULE_NAMES.noMultilineImports]: noMultilineImports,
|
|
676
|
+
[RULE_NAMES.projectSetup]: projectSetup,
|
|
677
|
+
[RULE_NAMES.projectStructure]: projectStructure,
|
|
678
|
+
[RULE_NAMES.sortImports]: sortImports
|
|
643
679
|
}
|
|
644
680
|
};
|
|
645
681
|
|
|
@@ -660,10 +696,10 @@ var recommended = [
|
|
|
660
696
|
]
|
|
661
697
|
},
|
|
662
698
|
rules: {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
699
|
+
[RULE_IDS.sortImports]: "error",
|
|
700
|
+
[RULE_IDS.noMultilineImports]: "error",
|
|
701
|
+
[RULE_IDS.filenamePascalCase]: "error",
|
|
702
|
+
[RULE_IDS.projectSetup]: "warn",
|
|
667
703
|
"max-len": "off",
|
|
668
704
|
"import-x/no-duplicates": "error",
|
|
669
705
|
"import-x/no-default-export": "off",
|
|
@@ -1880,6 +1916,11 @@ function validateDefinition(definition, index) {
|
|
|
1880
1916
|
});
|
|
1881
1917
|
}
|
|
1882
1918
|
}
|
|
1919
|
+
if (definition.rules !== void 0 && (typeof definition.rules !== "object" || definition.rules === null || Array.isArray(definition.rules))) {
|
|
1920
|
+
throw new Error(
|
|
1921
|
+
`Invalid project at index ${index}: rules must be an object.`
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1883
1924
|
}
|
|
1884
1925
|
function resolveStructure(definition, preset) {
|
|
1885
1926
|
return mergeStructure(preset?.structure, definition.structure);
|
|
@@ -1934,15 +1975,16 @@ function projects(definitions) {
|
|
|
1934
1975
|
]));
|
|
1935
1976
|
const rules = {
|
|
1936
1977
|
...baseConfig.rules,
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
"ai-rules/filename-pascal-case": structureEnabled ? "off" : baseConfig.rules?.["ai-rules/filename-pascal-case"] ?? "error",
|
|
1978
|
+
[RULE_IDS.projectStructure]: structureEnabled ? "error" : "off",
|
|
1979
|
+
[RULE_IDS.filenamePascalCase]: structureEnabled ? "off" : baseConfig.rules?.[RULE_IDS.filenamePascalCase] ?? "error",
|
|
1940
1980
|
...relations ? {
|
|
1941
1981
|
"boundaries/dependencies": [
|
|
1942
1982
|
"error",
|
|
1943
1983
|
buildRelationsRuleOptions(relations)
|
|
1944
1984
|
]
|
|
1945
|
-
} : {}
|
|
1985
|
+
} : {},
|
|
1986
|
+
...preset?.rules ?? {},
|
|
1987
|
+
...definition.rules ?? {}
|
|
1946
1988
|
};
|
|
1947
1989
|
return {
|
|
1948
1990
|
...baseConfig,
|