eslint-plugin-ai-rules 0.3.0 → 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/dist/index.d.ts CHANGED
@@ -45,6 +45,7 @@ interface IProjectDefinition {
45
45
  preset?: TProjectPreset;
46
46
  structure?: IProjectStructure;
47
47
  relations?: IRelationsConfig;
48
+ rules?: Linter.RulesRecord;
48
49
  }
49
50
  interface IProjectPreset {
50
51
  structure: IProjectStructure;
@@ -69,14 +70,14 @@ declare function projects(definitions: IProjectDefinition[]): Linter.Config[];
69
70
  declare const plugin: {
70
71
  readonly meta: {
71
72
  readonly name: "eslint-plugin-ai-rules";
72
- readonly version: "0.3.0";
73
+ readonly version: string;
73
74
  };
74
75
  readonly rules: {
75
- readonly 'filename-pascal-case': eslint.Rule.RuleModule;
76
- readonly 'no-multiline-imports': eslint.Rule.RuleModule;
77
- readonly 'project-setup': eslint.Rule.RuleModule;
78
- readonly 'project-structure': eslint.Rule.RuleModule;
79
- readonly 'sort-imports': eslint.Rule.RuleModule;
76
+ readonly "filename-pascal-case": eslint.Rule.RuleModule;
77
+ readonly "no-multiline-imports": eslint.Rule.RuleModule;
78
+ readonly "project-setup": eslint.Rule.RuleModule;
79
+ readonly "project-structure": eslint.Rule.RuleModule;
80
+ readonly "sort-imports": eslint.Rule.RuleModule;
80
81
  };
81
82
  };
82
83
 
package/dist/index.js CHANGED
@@ -3,22 +3,50 @@ import { createNodeResolver, importX } from "eslint-plugin-import-x";
3
3
  import stylistic from "@stylistic/eslint-plugin";
4
4
  import tseslint from "typescript-eslint";
5
5
 
6
+ // src/rules/constants.ts
7
+ var RULE_NAMES = {
8
+ filenamePascalCase: "filename-pascal-case",
9
+ noMultilineImports: "no-multiline-imports",
10
+ projectSetup: "project-setup",
11
+ projectStructure: "project-structure",
12
+ sortImports: "sort-imports"
13
+ };
14
+ var RULE_IDS = {
15
+ filenamePascalCase: `ai-rules/${RULE_NAMES.filenamePascalCase}`,
16
+ noMultilineImports: `ai-rules/${RULE_NAMES.noMultilineImports}`,
17
+ projectSetup: `ai-rules/${RULE_NAMES.projectSetup}`,
18
+ projectStructure: `ai-rules/${RULE_NAMES.projectStructure}`,
19
+ sortImports: `ai-rules/${RULE_NAMES.sortImports}`
20
+ };
21
+
6
22
  // src/rules/FilenamePascalCase.ts
7
23
  import path from "path";
8
24
  var filenamePascalCase = {
9
25
  meta: {
10
26
  type: "suggestion",
11
- docs: { description: "Enforce PascalCase for file names" },
27
+ docs: {
28
+ description: "Enforce PascalCase for file names",
29
+ recommended: true,
30
+ url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesfilename-pascal-case"
31
+ },
12
32
  schema: [
13
33
  {
14
34
  type: "object",
15
35
  properties: {
16
- ignore: { type: "array", items: { type: "string" } },
17
- ignoreMiddleExtensions: { type: "boolean" }
36
+ ignore: {
37
+ type: "array",
38
+ items: { type: "string" },
39
+ description: "Regex strings matched against the file name and relative path."
40
+ },
41
+ ignoreMiddleExtensions: {
42
+ type: "boolean",
43
+ description: "Allow middle extensions such as .test in file names."
44
+ }
18
45
  },
19
46
  additionalProperties: false
20
47
  }
21
48
  ],
49
+ defaultOptions: [{ ignore: [], ignoreMiddleExtensions: true }],
22
50
  messages: {
23
51
  invalid: 'File name "{{basename}}" must be in PascalCase. Rename to "{{expected}}".'
24
52
  }
@@ -76,10 +104,12 @@ var filenamePascalCase = {
76
104
  var noMultilineImports = {
77
105
  meta: {
78
106
  type: "layout",
79
- fixable: "code",
80
107
  docs: {
81
- description: "Disallow multiline imports"
108
+ description: "Disallow multiline imports",
109
+ recommended: true,
110
+ url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesno-multiline-imports"
82
111
  },
112
+ fixable: "code",
83
113
  schema: [],
84
114
  messages: {
85
115
  multilineImport: "Imports must be written on a single line."
@@ -341,7 +371,9 @@ var projectStructure = {
341
371
  meta: {
342
372
  type: "problem",
343
373
  docs: {
344
- description: "Enforce the configured project folder and file structure"
374
+ description: "Enforce the configured project folder and file structure",
375
+ recommended: false,
376
+ url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesproject-structure"
345
377
  },
346
378
  schema: [],
347
379
  messages: {
@@ -415,7 +447,7 @@ function hasStructureRules(structure) {
415
447
  }
416
448
 
417
449
  // src/rules/ProjectSetup.ts
418
- var hasWarned = false;
450
+ var hasReported = false;
419
451
  function isConfiguredProject(project) {
420
452
  if (project.relations) return true;
421
453
  if (project.preset === "frontend" || project.preset === "backend" || project.preset === "other") {
@@ -423,36 +455,38 @@ function isConfiguredProject(project) {
423
455
  }
424
456
  return Boolean(project.structure && hasStructureRules(project.structure));
425
457
  }
426
- function warnOnce() {
427
- if (hasWarned) return;
458
+ function shouldReportOnce() {
459
+ if (hasReported) return false;
428
460
  if (process.env.AI_RULES_SILENT === "1" || process.env.AI_RULES_SILENT === "true") {
429
- return;
461
+ return false;
430
462
  }
431
- hasWarned = true;
432
- console.warn(
433
- [
434
- "[eslint-plugin-ai-rules] Project structure validation is not configured.",
435
- "Configure it with aiRules.configs.projects([{ rootFolder: '.', preset: 'frontend' | 'backend' }]).",
436
- "If this project is not a frontend/backend, use preset: 'other' to disable this warning."
437
- ].join("\n")
438
- );
463
+ hasReported = true;
464
+ return true;
439
465
  }
440
466
  var projectSetup = {
441
467
  meta: {
442
468
  type: "suggestion",
443
469
  docs: {
444
- description: "Warn when the project structure validation is not configured"
470
+ description: "Require project structure validation to be configured",
471
+ recommended: true,
472
+ url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulesproject-setup"
445
473
  },
446
474
  schema: [],
447
- messages: {}
475
+ messages: {
476
+ 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.'
477
+ }
448
478
  },
449
479
  create(context) {
450
480
  return {
451
- Program() {
481
+ Program(node) {
452
482
  const settings = context.settings["ai-rules"];
453
483
  const projects2 = settings?.projects ?? (settings?.project ? [settings.project] : []);
454
484
  if (projects2.some(isConfiguredProject)) return;
455
- warnOnce();
485
+ if (!shouldReportOnce()) return;
486
+ context.report({
487
+ node,
488
+ messageId: "notConfigured"
489
+ });
456
490
  }
457
491
  };
458
492
  }
@@ -463,12 +497,14 @@ var sortImports = {
463
497
  meta: {
464
498
  type: "layout",
465
499
  docs: {
466
- description: "Sort imports by group and line length descending, collapsing multiline imports"
500
+ description: "Enforce sorted imports by group and line length descending, collapsing multiline imports",
501
+ recommended: true,
502
+ url: "https://github.com/lvsouza/eslint-ai-guard/blob/main/README.md#ai-rulessort-imports"
467
503
  },
468
504
  fixable: "code",
469
505
  schema: [],
470
506
  messages: {
471
- sortRequired: "As importa\xE7\xF5es devem estar ordenadas por grupo e tamanho da linha, ocupando uma \xFAnica linha."
507
+ sortRequired: "Imports must be sorted by group and line length, each on a single line."
472
508
  }
473
509
  },
474
510
  create(context) {
@@ -586,14 +622,14 @@ function getSortedContent(content) {
586
622
  var plugin = {
587
623
  meta: {
588
624
  name: "eslint-plugin-ai-rules",
589
- version: "0.3.0"
625
+ version: "0.4.0"
590
626
  },
591
627
  rules: {
592
- "filename-pascal-case": filenamePascalCase,
593
- "no-multiline-imports": noMultilineImports,
594
- "project-setup": projectSetup,
595
- "project-structure": projectStructure,
596
- "sort-imports": sortImports
628
+ [RULE_NAMES.filenamePascalCase]: filenamePascalCase,
629
+ [RULE_NAMES.noMultilineImports]: noMultilineImports,
630
+ [RULE_NAMES.projectSetup]: projectSetup,
631
+ [RULE_NAMES.projectStructure]: projectStructure,
632
+ [RULE_NAMES.sortImports]: sortImports
597
633
  }
598
634
  };
599
635
 
@@ -614,10 +650,10 @@ var recommended = [
614
650
  ]
615
651
  },
616
652
  rules: {
617
- "ai-rules/sort-imports": "error",
618
- "ai-rules/no-multiline-imports": "error",
619
- "ai-rules/filename-pascal-case": "error",
620
- "ai-rules/project-setup": "warn",
653
+ [RULE_IDS.sortImports]: "error",
654
+ [RULE_IDS.noMultilineImports]: "error",
655
+ [RULE_IDS.filenamePascalCase]: "error",
656
+ [RULE_IDS.projectSetup]: "warn",
621
657
  "max-len": "off",
622
658
  "import-x/no-duplicates": "error",
623
659
  "import-x/no-default-export": "off",
@@ -1178,15 +1214,15 @@ var frontendRelations = {
1178
1214
  pageContext: "src/pages/*/contexts",
1179
1215
  pageHelper: "src/pages/*/helpers",
1180
1216
  page: "src/pages/*",
1181
- components: "src/components",
1182
- hooks: "src/hooks",
1183
- contexts: "src/contexts",
1184
- generated: "src/services/generated",
1185
- services: "src/services",
1186
- utils: "src/utils",
1187
- helpers: "src/helpers",
1188
- constants: "src/constants",
1189
- env: "src/env"
1217
+ components: "src/shared/components",
1218
+ hooks: "src/shared/hooks",
1219
+ contexts: "src/shared/contexts",
1220
+ generated: "src/shared/services/generated",
1221
+ services: "src/shared/services",
1222
+ utils: "src/shared/utils",
1223
+ helpers: "src/shared/helpers",
1224
+ constants: "src/shared/constants",
1225
+ env: "src/shared/env"
1190
1226
  },
1191
1227
  scope: {
1192
1228
  capture: "page",
@@ -1395,34 +1431,49 @@ var frontendPreset = {
1395
1431
  "vite-env.d.ts"
1396
1432
  ],
1397
1433
  folders: {
1398
- components: {
1399
- files: [pascalTsx],
1434
+ shared: {
1400
1435
  folders: {
1401
- ui: {
1402
- files: [pascalTsOrTsx]
1436
+ components: {
1437
+ files: [pascalTsx],
1438
+ folders: {
1439
+ ui: {
1440
+ files: [pascalTsOrTsx]
1441
+ }
1442
+ }
1443
+ },
1444
+ constants: {
1445
+ files: [pascalTs]
1446
+ },
1447
+ contexts: {
1448
+ files: [
1449
+ {
1450
+ case: "PascalCase",
1451
+ extensions: [".tsx"]
1452
+ }
1453
+ ]
1454
+ },
1455
+ env: {
1456
+ files: ["index.ts"]
1457
+ },
1458
+ helpers: {
1459
+ files: [pascalTs]
1460
+ },
1461
+ hooks: {
1462
+ files: [pascalTs]
1463
+ },
1464
+ services: {
1465
+ folders: {
1466
+ generated: { ignore: true },
1467
+ utils: {
1468
+ files: [pascalTs]
1469
+ }
1470
+ }
1471
+ },
1472
+ utils: {
1473
+ files: [pascalTs]
1403
1474
  }
1404
1475
  }
1405
1476
  },
1406
- constants: {
1407
- files: [pascalTs]
1408
- },
1409
- contexts: {
1410
- files: [
1411
- {
1412
- case: "PascalCase",
1413
- extensions: [".tsx"]
1414
- }
1415
- ]
1416
- },
1417
- env: {
1418
- files: ["index.ts"]
1419
- },
1420
- hooks: {
1421
- files: [pascalTs]
1422
- },
1423
- helpers: {
1424
- files: [pascalTs]
1425
- },
1426
1477
  pages: {
1427
1478
  files: [pascalTsx],
1428
1479
  folders: {
@@ -1485,17 +1536,6 @@ var frontendPreset = {
1485
1536
  }
1486
1537
  }
1487
1538
  }
1488
- },
1489
- services: {
1490
- folders: {
1491
- generated: { ignore: true },
1492
- utils: {
1493
- files: [pascalTs]
1494
- }
1495
- }
1496
- },
1497
- utils: {
1498
- files: [pascalTs]
1499
1539
  }
1500
1540
  }
1501
1541
  },
@@ -1533,7 +1573,7 @@ var frontendPreset = {
1533
1573
  ...commonIgnoredPaths,
1534
1574
  "playwright-report/**",
1535
1575
  "test-results/**",
1536
- "src/services/generated/**",
1576
+ "src/shared/services/generated/**",
1537
1577
  "scripts/types/**"
1538
1578
  ]
1539
1579
  },
@@ -1830,6 +1870,11 @@ function validateDefinition(definition, index) {
1830
1870
  });
1831
1871
  }
1832
1872
  }
1873
+ if (definition.rules !== void 0 && (typeof definition.rules !== "object" || definition.rules === null || Array.isArray(definition.rules))) {
1874
+ throw new Error(
1875
+ `Invalid project at index ${index}: rules must be an object.`
1876
+ );
1877
+ }
1833
1878
  }
1834
1879
  function resolveStructure(definition, preset) {
1835
1880
  return mergeStructure(preset?.structure, definition.structure);
@@ -1884,15 +1929,16 @@ function projects(definitions) {
1884
1929
  ]));
1885
1930
  const rules = {
1886
1931
  ...baseConfig.rules,
1887
- ...preset?.rules ?? {},
1888
- "ai-rules/project-structure": structureEnabled ? "error" : "off",
1889
- "ai-rules/filename-pascal-case": structureEnabled ? "off" : baseConfig.rules?.["ai-rules/filename-pascal-case"] ?? "error",
1932
+ [RULE_IDS.projectStructure]: structureEnabled ? "error" : "off",
1933
+ [RULE_IDS.filenamePascalCase]: structureEnabled ? "off" : baseConfig.rules?.[RULE_IDS.filenamePascalCase] ?? "error",
1890
1934
  ...relations ? {
1891
1935
  "boundaries/dependencies": [
1892
1936
  "error",
1893
1937
  buildRelationsRuleOptions(relations)
1894
1938
  ]
1895
- } : {}
1939
+ } : {},
1940
+ ...preset?.rules ?? {},
1941
+ ...definition.rules ?? {}
1896
1942
  };
1897
1943
  return {
1898
1944
  ...baseConfig,