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.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: { description: "Enforce PascalCase for file names" },
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: { type: "array", items: { type: "string" } },
63
- ignoreMiddleExtensions: { type: "boolean" }
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 hasWarned = false;
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 warnOnce() {
473
- if (hasWarned) return;
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
- hasWarned = true;
478
- console.warn(
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: "Warn when the project structure validation is not configured"
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
- warnOnce();
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: "Sort imports by group and line length descending, collapsing multiline imports"
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: "As importa\xE7\xF5es devem estar ordenadas por grupo e tamanho da linha, ocupando uma \xFAnica linha."
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.3.0"
671
+ version: "0.4.0"
636
672
  },
637
673
  rules: {
638
- "filename-pascal-case": filenamePascalCase,
639
- "no-multiline-imports": noMultilineImports,
640
- "project-setup": projectSetup,
641
- "project-structure": projectStructure,
642
- "sort-imports": sortImports
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
- "ai-rules/sort-imports": "error",
664
- "ai-rules/no-multiline-imports": "error",
665
- "ai-rules/filename-pascal-case": "error",
666
- "ai-rules/project-setup": "warn",
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",
@@ -1224,15 +1260,15 @@ var frontendRelations = {
1224
1260
  pageContext: "src/pages/*/contexts",
1225
1261
  pageHelper: "src/pages/*/helpers",
1226
1262
  page: "src/pages/*",
1227
- components: "src/components",
1228
- hooks: "src/hooks",
1229
- contexts: "src/contexts",
1230
- generated: "src/services/generated",
1231
- services: "src/services",
1232
- utils: "src/utils",
1233
- helpers: "src/helpers",
1234
- constants: "src/constants",
1235
- env: "src/env"
1263
+ components: "src/shared/components",
1264
+ hooks: "src/shared/hooks",
1265
+ contexts: "src/shared/contexts",
1266
+ generated: "src/shared/services/generated",
1267
+ services: "src/shared/services",
1268
+ utils: "src/shared/utils",
1269
+ helpers: "src/shared/helpers",
1270
+ constants: "src/shared/constants",
1271
+ env: "src/shared/env"
1236
1272
  },
1237
1273
  scope: {
1238
1274
  capture: "page",
@@ -1441,34 +1477,49 @@ var frontendPreset = {
1441
1477
  "vite-env.d.ts"
1442
1478
  ],
1443
1479
  folders: {
1444
- components: {
1445
- files: [pascalTsx],
1480
+ shared: {
1446
1481
  folders: {
1447
- ui: {
1448
- files: [pascalTsOrTsx]
1482
+ components: {
1483
+ files: [pascalTsx],
1484
+ folders: {
1485
+ ui: {
1486
+ files: [pascalTsOrTsx]
1487
+ }
1488
+ }
1489
+ },
1490
+ constants: {
1491
+ files: [pascalTs]
1492
+ },
1493
+ contexts: {
1494
+ files: [
1495
+ {
1496
+ case: "PascalCase",
1497
+ extensions: [".tsx"]
1498
+ }
1499
+ ]
1500
+ },
1501
+ env: {
1502
+ files: ["index.ts"]
1503
+ },
1504
+ helpers: {
1505
+ files: [pascalTs]
1506
+ },
1507
+ hooks: {
1508
+ files: [pascalTs]
1509
+ },
1510
+ services: {
1511
+ folders: {
1512
+ generated: { ignore: true },
1513
+ utils: {
1514
+ files: [pascalTs]
1515
+ }
1516
+ }
1517
+ },
1518
+ utils: {
1519
+ files: [pascalTs]
1449
1520
  }
1450
1521
  }
1451
1522
  },
1452
- constants: {
1453
- files: [pascalTs]
1454
- },
1455
- contexts: {
1456
- files: [
1457
- {
1458
- case: "PascalCase",
1459
- extensions: [".tsx"]
1460
- }
1461
- ]
1462
- },
1463
- env: {
1464
- files: ["index.ts"]
1465
- },
1466
- hooks: {
1467
- files: [pascalTs]
1468
- },
1469
- helpers: {
1470
- files: [pascalTs]
1471
- },
1472
1523
  pages: {
1473
1524
  files: [pascalTsx],
1474
1525
  folders: {
@@ -1531,17 +1582,6 @@ var frontendPreset = {
1531
1582
  }
1532
1583
  }
1533
1584
  }
1534
- },
1535
- services: {
1536
- folders: {
1537
- generated: { ignore: true },
1538
- utils: {
1539
- files: [pascalTs]
1540
- }
1541
- }
1542
- },
1543
- utils: {
1544
- files: [pascalTs]
1545
1585
  }
1546
1586
  }
1547
1587
  },
@@ -1579,7 +1619,7 @@ var frontendPreset = {
1579
1619
  ...commonIgnoredPaths,
1580
1620
  "playwright-report/**",
1581
1621
  "test-results/**",
1582
- "src/services/generated/**",
1622
+ "src/shared/services/generated/**",
1583
1623
  "scripts/types/**"
1584
1624
  ]
1585
1625
  },
@@ -1876,6 +1916,11 @@ function validateDefinition(definition, index) {
1876
1916
  });
1877
1917
  }
1878
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
+ }
1879
1924
  }
1880
1925
  function resolveStructure(definition, preset) {
1881
1926
  return mergeStructure(preset?.structure, definition.structure);
@@ -1930,15 +1975,16 @@ function projects(definitions) {
1930
1975
  ]));
1931
1976
  const rules = {
1932
1977
  ...baseConfig.rules,
1933
- ...preset?.rules ?? {},
1934
- "ai-rules/project-structure": structureEnabled ? "error" : "off",
1935
- "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",
1936
1980
  ...relations ? {
1937
1981
  "boundaries/dependencies": [
1938
1982
  "error",
1939
1983
  buildRelationsRuleOptions(relations)
1940
1984
  ]
1941
- } : {}
1985
+ } : {},
1986
+ ...preset?.rules ?? {},
1987
+ ...definition.rules ?? {}
1942
1988
  };
1943
1989
  return {
1944
1990
  ...baseConfig,