@gaia-react/lint 1.2.0 → 1.3.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.js CHANGED
@@ -79,7 +79,7 @@ var typescriptConfig = [
79
79
  }
80
80
  }
81
81
  ];
82
- var tsEslintConfig = [
82
+ var buildTsEslintConfig = (sourceDir) => [
83
83
  {
84
84
  files: ["**/*.ts?(x)"],
85
85
  name: "typescript/config",
@@ -121,14 +121,22 @@ var tsEslintConfig = [
121
121
  }
122
122
  },
123
123
  {
124
- files: ["app/hooks/**/*", "app/routes/**/*", "app/sessions.server/**/*"],
124
+ files: [
125
+ `${sourceDir}/hooks/**/*`,
126
+ `${sourceDir}/routes/**/*`,
127
+ `${sourceDir}/sessions.server/**/*`
128
+ ],
125
129
  name: "typescript/only-throw-error",
126
130
  rules: {
127
131
  "@typescript-eslint/only-throw-error": "off"
128
132
  }
129
133
  },
130
134
  {
131
- files: ["app/utils/**", "app/services/**", "app/hooks/**"],
135
+ files: [
136
+ `${sourceDir}/utils/**`,
137
+ `${sourceDir}/services/**`,
138
+ `${sourceDir}/hooks/**`
139
+ ],
132
140
  name: "typescript/explicit-return-types",
133
141
  rules: {
134
142
  "@typescript-eslint/explicit-module-boundary-types": "error"
@@ -161,7 +169,7 @@ var tsEslintConfig = [
161
169
  }
162
170
  }
163
171
  ];
164
- var importXConfig = [
172
+ var buildImportXConfig = (sourceDir) => [
165
173
  {
166
174
  name: "import-x-all-files",
167
175
  rules: {
@@ -200,14 +208,14 @@ var importXConfig = [
200
208
  }
201
209
  },
202
210
  {
203
- files: ["app/**/!(*.test|*.stories).ts?(x)"],
211
+ files: [`${sourceDir}/**/!(*.test|*.stories).ts?(x)`],
204
212
  name: "import-x/app-test-files",
205
213
  rules: {
206
214
  "import-x/no-unresolved": "error"
207
215
  }
208
216
  },
209
217
  {
210
- files: ["app/**/hooks/*.ts?(x)"],
218
+ files: [`${sourceDir}/**/hooks/*.ts?(x)`],
211
219
  name: "import-x/hooks",
212
220
  rules: {
213
221
  "import-x/no-default-export": "error"
@@ -290,138 +298,438 @@ var lodashUnderscoreConfig = [
290
298
  }
291
299
  }
292
300
  ];
293
- var base = [
301
+ var buildBase = (sourceDir) => [
294
302
  ...jsConfig,
295
303
  ...jsCustomConfig,
296
304
  ...typescriptConfig,
297
- ...tsEslintConfig,
298
- ...importXConfig,
305
+ ...buildTsEslintConfig(sourceDir),
306
+ ...buildImportXConfig(sourceDir),
299
307
  ...eslintCommentsConfig,
300
308
  ...preferArrowFunctionsConfig,
301
309
  ...lodashUnderscoreConfig
302
310
  ];
303
311
 
304
- // src/configs/react.ts
305
- import { configs as configs2, plugins as plugins2 } from "eslint-config-airbnb-extended";
306
- var react = [
307
- // React plugin
308
- plugins2.react,
309
- // React hooks plugin
310
- plugins2.reactHooks,
311
- // React JSX A11y plugin
312
- plugins2.reactA11y,
313
- // Airbnb React recommended config
314
- ...configs2.react.recommended,
312
+ // src/configs/better-tailwind.ts
313
+ import betterTailwindPlugin from "eslint-plugin-better-tailwindcss";
314
+ var betterTailwind = (opts) => [
315
315
  {
316
- name: "react-custom",
316
+ name: "better-tailwindcss",
317
+ plugins: {
318
+ "better-tailwindcss": betterTailwindPlugin
319
+ },
317
320
  rules: {
318
- "jsx-a11y/control-has-associated-label": "off",
319
- "jsx-a11y/no-autofocus": "off",
320
- "react/boolean-prop-naming": [
321
+ "better-tailwindcss/enforce-canonical-classes": "error",
322
+ "better-tailwindcss/enforce-consistent-important-position": "error",
323
+ "better-tailwindcss/enforce-consistent-variable-syntax": "error",
324
+ "better-tailwindcss/enforce-shorthand-classes": "error",
325
+ "better-tailwindcss/no-conflicting-classes": "error",
326
+ "better-tailwindcss/no-deprecated-classes": "error",
327
+ "better-tailwindcss/no-unknown-classes": [
321
328
  "error",
322
- {
323
- propTypeNames: ["bool", "mutuallyExclusiveTrueProps"],
324
- rule: "^((can|has|hide|is|show)[A-Z]([A-Za-z0-9]?)+|(checked|disabled|hide|required|show))"
325
- }
326
- ],
327
- "react/function-component-definition": "off",
328
- "react/jsx-boolean-value": ["error", "always"],
329
- "react/jsx-curly-brace-presence": "error",
330
- "react/jsx-filename-extension": "off",
331
- "react/jsx-fragments": "error",
332
- // off by default because it doesn't handle props onXyz function names correctly
333
- // turn this on from time to time to check for misnamed handlers elsewhere
334
- "react/jsx-handler-names": ["off", { checkLocalVariables: true }],
335
- "react/jsx-newline": ["error", { prevent: true }],
336
- "react/jsx-no-useless-fragment": ["error", { allowExpressions: true }],
337
- "react/no-children-prop": "off",
338
- "react/no-danger": "off",
339
- "react/prop-types": "off",
340
- "react/react-in-jsx-scope": "off",
341
- "react/require-default-props": "off"
329
+ { ignore: opts.ignore ?? [] }
330
+ ]
331
+ },
332
+ settings: {
333
+ "better-tailwindcss": {
334
+ detectComponentClasses: true,
335
+ entryPoint: opts.entryPoint
336
+ }
337
+ }
338
+ }
339
+ ];
340
+
341
+ // src/configs/guardrails.ts
342
+ import noRelativeImportPaths from "eslint-plugin-no-relative-import-paths";
343
+ import sonarjs from "eslint-plugin-sonarjs";
344
+
345
+ // src/plugins/no-enum.ts
346
+ var noEnumRule = {
347
+ create: (context) => ({
348
+ TSEnumDeclaration: (node) => {
349
+ context.report({ messageId: "noEnum", node });
342
350
  }
351
+ }),
352
+ meta: {
353
+ docs: { description: "Disallow TypeScript enums" },
354
+ messages: {
355
+ noEnum: "Do not use TypeScript enums. Use an object with `as const` instead."
356
+ },
357
+ schema: [],
358
+ type: "problem"
359
+ }
360
+ };
361
+ var plugin = {
362
+ meta: {
363
+ name: "no-enum",
364
+ version: "0.1.0"
343
365
  },
344
- {
345
- files: ["**/routes/**/*.tsx"],
346
- name: "react-router/routes",
347
- rules: {
348
- "no-empty-pattern": "off",
349
- "react/display-name": "off"
366
+ rules: {
367
+ "no-enum": noEnumRule
368
+ }
369
+ };
370
+ var no_enum_default = plugin;
371
+
372
+ // src/plugins/no-jsx-iife.ts
373
+ var noJsxIifeRule = {
374
+ create: (context) => ({
375
+ "JSXExpressionContainer > CallExpression > ArrowFunctionExpression.callee": (node) => {
376
+ context.report({ messageId: "noJsxIife", node });
377
+ },
378
+ "JSXExpressionContainer > CallExpression > FunctionExpression.callee": (node) => {
379
+ context.report({ messageId: "noJsxIife", node });
350
380
  }
381
+ }),
382
+ meta: {
383
+ docs: { description: "Disallow IIFEs in JSX expressions" },
384
+ messages: {
385
+ noJsxIife: "Do not use IIFEs in JSX. Use a computed variable before the return or extract a component instead."
386
+ },
387
+ schema: [],
388
+ type: "problem"
389
+ }
390
+ };
391
+ var plugin2 = {
392
+ meta: {
393
+ name: "no-jsx-iife",
394
+ version: "0.1.0"
351
395
  },
352
- {
353
- files: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)"],
354
- name: "react/test-files",
355
- rules: {
356
- "react/jsx-props-no-spreading": "off"
396
+ rules: {
397
+ "no-jsx-iife": noJsxIifeRule
398
+ }
399
+ };
400
+ var no_jsx_iife_default = plugin2;
401
+
402
+ // src/plugins/no-switch.ts
403
+ var noSwitchRule = {
404
+ create: (context) => ({
405
+ SwitchStatement: (node) => {
406
+ context.report({ messageId: "noSwitch", node });
357
407
  }
408
+ }),
409
+ meta: {
410
+ docs: { description: "Disallow switch statements" },
411
+ messages: {
412
+ noSwitch: "Do not use switch statements. Use an object map or if/else instead."
413
+ },
414
+ schema: [],
415
+ type: "problem"
358
416
  }
359
- ];
417
+ };
418
+ var plugin3 = {
419
+ meta: {
420
+ name: "no-switch",
421
+ version: "0.1.0"
422
+ },
423
+ rules: {
424
+ "no-switch": noSwitchRule
425
+ }
426
+ };
427
+ var no_switch_default = plugin3;
360
428
 
361
- // src/configs/style-hygiene.ts
362
- import canonical from "eslint-plugin-canonical";
363
- import checkFile from "eslint-plugin-check-file";
364
- import perfectionist from "eslint-plugin-perfectionist";
365
- import unicorn from "eslint-plugin-unicorn";
366
- import unusedImports from "eslint-plugin-unused-imports";
367
- var canonicalConfig = [
368
- canonical.configs["flat/recommended"],
429
+ // src/configs/guardrails.ts
430
+ var buildSonarConfig = (sourceDir) => [
431
+ sonarjs.configs.recommended,
369
432
  {
370
- name: "canonical",
433
+ name: "sonarjs",
371
434
  rules: {
372
- "canonical/destructuring-property-newline": "off",
373
- "canonical/filename-match-exported": "error",
374
- "canonical/id-match": "off",
375
- "canonical/import-specifier-newline": "off"
435
+ "sonarjs/cognitive-complexity": "error",
436
+ "sonarjs/fixme-tag": "off",
437
+ "sonarjs/no-commented-code": "off",
438
+ "sonarjs/no-nested-conditional": "off",
439
+ "sonarjs/no-nested-functions": "off",
440
+ "sonarjs/no-selector-parameter": "off",
441
+ "sonarjs/regex-complexity": "off",
442
+ "sonarjs/todo-tag": "off"
376
443
  }
377
444
  },
378
445
  {
379
446
  files: ["**/*.tsx", "**/hooks/*.ts?(x)"],
380
- name: "canonical/sort-react-dependencies",
447
+ name: "sonarjs/react-files",
381
448
  rules: {
382
- "canonical/sort-react-dependencies": "error"
449
+ "sonarjs/cognitive-complexity": "off",
450
+ "sonarjs/function-return-type": "off"
383
451
  }
384
452
  },
385
453
  {
386
- files: [
387
- "app/root.tsx",
388
- "app/entry.server.tsx",
389
- "app/**/tests/*",
390
- "test/**/*.ts?(x)",
391
- "**/*.stories.tsx",
392
- "**/routes/**/*.tsx",
393
- "**/hooks/*.ts?(x)",
394
- ".storybook/**/*.ts?(x)",
395
- ".playwright/**/*.ts?(x)"
396
- ],
397
- name: "canonical/filename-match-exported-disabled",
454
+ files: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)"],
455
+ name: "sonarjs/test-files",
398
456
  rules: {
399
- "canonical/filename-match-exported": "off"
457
+ "sonarjs/no-duplicate-string": "off",
458
+ "sonarjs/no-identical-functions": "off"
459
+ }
460
+ },
461
+ {
462
+ files: [`${sourceDir}/languages/**/*.ts`, "eslint.config.mjs"],
463
+ name: "sonarjs/credential-checks",
464
+ rules: {
465
+ "sonarjs/no-hardcoded-credentials": "off",
466
+ "sonarjs/no-hardcoded-passwords": "off"
400
467
  }
401
468
  }
402
469
  ];
403
- var perfectionistConfig = [
404
- perfectionist.configs["recommended-natural"],
470
+ var noEnumConfig = [
405
471
  {
406
- name: "perfectionist",
472
+ files: ["**/*.ts?(x)"],
473
+ name: "no-enum",
474
+ plugins: { "no-enum": no_enum_default },
407
475
  rules: {
408
- "perfectionist/sort-imports": [
476
+ "no-enum/no-enum": "error"
477
+ }
478
+ }
479
+ ];
480
+ var noJsxIifeConfig = [
481
+ {
482
+ files: ["**/*.tsx", "**/*.jsx"],
483
+ name: "no-jsx-iife",
484
+ plugins: { "no-jsx-iife": no_jsx_iife_default },
485
+ rules: { "no-jsx-iife/no-jsx-iife": "error" }
486
+ }
487
+ ];
488
+ var noSwitchConfig = [
489
+ {
490
+ files: ["**/*.ts?(x)", "**/*.js?(x)"],
491
+ name: "no-switch",
492
+ plugins: { "no-switch": no_switch_default },
493
+ rules: { "no-switch/no-switch": "error" }
494
+ }
495
+ ];
496
+ var buildNoRelativeImportPathsConfig = (sourceDir) => [
497
+ {
498
+ name: "no-relative-import-paths",
499
+ plugins: {
500
+ "no-relative-import-paths": noRelativeImportPaths
501
+ },
502
+ rules: {
503
+ "no-relative-import-paths/no-relative-import-paths": [
409
504
  "error",
410
505
  {
411
- customGroups: [
412
- {
413
- elementNamePattern: "^react$",
414
- groupName: "react-type",
415
- selector: "type"
416
- },
417
- {
418
- elementNamePattern: "^react$",
419
- groupName: "react"
420
- },
421
- {
422
- elementNamePattern: "^react-.+",
423
- groupName: "react-other-type",
424
- selector: "type"
506
+ allowedDepth: 2,
507
+ allowSameFolder: true,
508
+ prefix: "~",
509
+ rootDir: sourceDir
510
+ }
511
+ ]
512
+ }
513
+ }
514
+ ];
515
+ var buildGuardrails = (sourceDir) => [
516
+ ...buildSonarConfig(sourceDir),
517
+ ...noEnumConfig,
518
+ ...noJsxIifeConfig,
519
+ ...noSwitchConfig,
520
+ ...buildNoRelativeImportPathsConfig(sourceDir)
521
+ ];
522
+
523
+ // src/configs/ignores.ts
524
+ import { includeIgnoreFile } from "@eslint/config-helpers";
525
+ import fs from "fs";
526
+ import path from "path";
527
+ var defaultIgnores = [
528
+ ".storybook",
529
+ ".playwright",
530
+ "/.react-router/**",
531
+ ".claude/**/*.js",
532
+ ".claude/**/*.cjs",
533
+ "scripts",
534
+ "public/**",
535
+ "**/*.css",
536
+ "**/*.svg",
537
+ "**/*.md"
538
+ ];
539
+ var ignores = (opts) => {
540
+ const out = [];
541
+ const gitignore = opts?.gitignore ?? ".gitignore";
542
+ if (gitignore !== false) {
543
+ const resolved = path.isAbsolute(gitignore) ? gitignore : path.resolve(process.cwd(), gitignore);
544
+ if (fs.existsSync(resolved)) {
545
+ out.push(includeIgnoreFile(resolved));
546
+ }
547
+ }
548
+ out.push({
549
+ ignores: [...defaultIgnores, ...opts?.extra ?? []],
550
+ name: "ignored-files"
551
+ });
552
+ return out;
553
+ };
554
+
555
+ // src/configs/playwright.ts
556
+ import playwrightPlugin from "eslint-plugin-playwright";
557
+ var playwright = [
558
+ {
559
+ name: "playwright",
560
+ ...playwrightPlugin.configs["flat/recommended"],
561
+ files: [".playwright/**/*.ts?(x)"],
562
+ rules: {
563
+ ...playwrightPlugin.configs["flat/recommended"].rules,
564
+ "playwright/expect-expect": [
565
+ "warn",
566
+ { assertFunctionPatterns: ["^expect[A-Z]"] }
567
+ ]
568
+ }
569
+ }
570
+ ];
571
+
572
+ // src/configs/prettier.ts
573
+ import { rules as prettierConfigRules } from "eslint-config-prettier";
574
+ import prettierPlugin from "eslint-plugin-prettier";
575
+ var prettier = [
576
+ {
577
+ name: "prettier/plugin/config",
578
+ plugins: { prettier: prettierPlugin }
579
+ },
580
+ {
581
+ name: "prettier/config",
582
+ rules: {
583
+ ...prettierConfigRules,
584
+ "@stylistic/padding-line-between-statements": [
585
+ "error",
586
+ {
587
+ blankLine: "always",
588
+ next: ["block-like", "export", "return", "throw"],
589
+ prev: "*"
590
+ }
591
+ ],
592
+ "@stylistic/quotes": [
593
+ "error",
594
+ "single",
595
+ {
596
+ allowTemplateLiterals: "avoidEscape",
597
+ avoidEscape: true
598
+ }
599
+ ],
600
+ "@stylistic/spaced-comment": "off",
601
+ "prettier/prettier": ["error", { endOfLine: "auto" }]
602
+ }
603
+ }
604
+ ];
605
+
606
+ // src/configs/react.ts
607
+ import { configs as configs2, plugins as plugins2 } from "eslint-config-airbnb-extended";
608
+ var react = [
609
+ // React plugin
610
+ plugins2.react,
611
+ // React hooks plugin
612
+ plugins2.reactHooks,
613
+ // React JSX A11y plugin
614
+ plugins2.reactA11y,
615
+ // Airbnb React recommended config
616
+ ...configs2.react.recommended,
617
+ {
618
+ name: "react-custom",
619
+ rules: {
620
+ "jsx-a11y/control-has-associated-label": "off",
621
+ "jsx-a11y/no-autofocus": "off",
622
+ "react/boolean-prop-naming": [
623
+ "error",
624
+ {
625
+ propTypeNames: ["bool", "mutuallyExclusiveTrueProps"],
626
+ rule: "^((can|has|hide|is|show)[A-Z]([A-Za-z0-9]?)+|(checked|disabled|hide|required|show))"
627
+ }
628
+ ],
629
+ "react/function-component-definition": "off",
630
+ "react/jsx-boolean-value": ["error", "always"],
631
+ "react/jsx-curly-brace-presence": "error",
632
+ "react/jsx-filename-extension": "off",
633
+ "react/jsx-fragments": "error",
634
+ // off by default because it doesn't handle props onXyz function names correctly
635
+ // turn this on from time to time to check for misnamed handlers elsewhere
636
+ "react/jsx-handler-names": ["off", { checkLocalVariables: true }],
637
+ "react/jsx-newline": ["error", { prevent: true }],
638
+ "react/jsx-no-useless-fragment": ["error", { allowExpressions: true }],
639
+ "react/no-children-prop": "off",
640
+ "react/no-danger": "off",
641
+ "react/prop-types": "off",
642
+ "react/react-in-jsx-scope": "off",
643
+ "react/require-default-props": "off"
644
+ }
645
+ },
646
+ {
647
+ files: ["**/routes/**/*.tsx"],
648
+ name: "react-router/routes",
649
+ rules: {
650
+ "no-empty-pattern": "off",
651
+ "react/display-name": "off"
652
+ }
653
+ },
654
+ {
655
+ files: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)"],
656
+ name: "react/test-files",
657
+ rules: {
658
+ "react/jsx-props-no-spreading": "off"
659
+ }
660
+ }
661
+ ];
662
+
663
+ // src/configs/storybook.ts
664
+ import storybookPlugin from "eslint-plugin-storybook";
665
+ var storybook = [
666
+ ...storybookPlugin.configs["flat/recommended"]
667
+ ];
668
+
669
+ // src/configs/style-hygiene.ts
670
+ import canonical from "eslint-plugin-canonical";
671
+ import checkFile from "eslint-plugin-check-file";
672
+ import perfectionist from "eslint-plugin-perfectionist";
673
+ import unicorn from "eslint-plugin-unicorn";
674
+ import unusedImports from "eslint-plugin-unused-imports";
675
+ var buildCanonicalConfig = (sourceDir) => [
676
+ canonical.configs["flat/recommended"],
677
+ {
678
+ name: "canonical",
679
+ rules: {
680
+ "canonical/destructuring-property-newline": "off",
681
+ "canonical/filename-match-exported": "error",
682
+ "canonical/id-match": "off",
683
+ "canonical/import-specifier-newline": "off"
684
+ }
685
+ },
686
+ {
687
+ files: ["**/*.tsx", "**/hooks/*.ts?(x)"],
688
+ name: "canonical/sort-react-dependencies",
689
+ rules: {
690
+ "canonical/sort-react-dependencies": "error"
691
+ }
692
+ },
693
+ {
694
+ files: [
695
+ `${sourceDir}/root.tsx`,
696
+ `${sourceDir}/entry.server.tsx`,
697
+ `${sourceDir}/**/tests/*`,
698
+ "test/**/*.ts?(x)",
699
+ "**/*.stories.tsx",
700
+ "**/routes/**/*.tsx",
701
+ "**/hooks/*.ts?(x)",
702
+ ".storybook/**/*.ts?(x)",
703
+ ".playwright/**/*.ts?(x)"
704
+ ],
705
+ name: "canonical/filename-match-exported-disabled",
706
+ rules: {
707
+ "canonical/filename-match-exported": "off"
708
+ }
709
+ }
710
+ ];
711
+ var perfectionistConfig = [
712
+ perfectionist.configs["recommended-natural"],
713
+ {
714
+ name: "perfectionist",
715
+ rules: {
716
+ "perfectionist/sort-imports": [
717
+ "error",
718
+ {
719
+ customGroups: [
720
+ {
721
+ elementNamePattern: "^react$",
722
+ groupName: "react-type",
723
+ selector: "type"
724
+ },
725
+ {
726
+ elementNamePattern: "^react$",
727
+ groupName: "react"
728
+ },
729
+ {
730
+ elementNamePattern: "^react-.+",
731
+ groupName: "react-other-type",
732
+ selector: "type"
425
733
  },
426
734
  {
427
735
  elementNamePattern: "^react-.+",
@@ -551,14 +859,14 @@ var unusedImportsConfig = [
551
859
  }
552
860
  }
553
861
  ];
554
- var checkFileConfig = [
862
+ var buildCheckFileConfig = (sourceDir) => [
555
863
  {
556
864
  plugins: {
557
865
  "check-file": checkFile
558
866
  }
559
867
  },
560
868
  {
561
- files: ["app/**/*"],
869
+ files: [`${sourceDir}/**/*`],
562
870
  name: "check-file",
563
871
  rules: {
564
872
  "check-file/filename-naming-convention": [
@@ -566,13 +874,13 @@ var checkFileConfig = [
566
874
  {
567
875
  // React hook files must be camelCase (to match the hook name)
568
876
  "**/hooks/*.{ts,tsx}": "CAMEL_CASE",
569
- "app/state/*.tsx": "KEBAB_CASE",
877
+ [`${sourceDir}/state/*.tsx`]: "KEBAB_CASE",
570
878
  // React component files must be named index.tsx
571
- "app/{components,pages}/**/!(assets|hooks|state|tests|utils)/*.tsx": "index+()",
879
+ [`${sourceDir}/{components,pages}/**/!(assets|hooks|state|tests|utils)/*.tsx`]: "index+()",
572
880
  // Generally, non-component files must be named kebab-case
573
- "app/{components,pages}/**/!(hooks)/*.ts": "KEBAB_CASE",
881
+ [`${sourceDir}/{components,pages}/**/!(hooks)/*.ts`]: "KEBAB_CASE",
574
882
  // Non-component files inside specific components folders must be kebab-case
575
- "app/{components,pages}/**/(assets|state|tests|utils)/*.{ts,tsx}": "KEBAB_CASE",
883
+ [`${sourceDir}/{components,pages}/**/(assets|state|tests|utils)/*.{ts,tsx}`]: "KEBAB_CASE",
576
884
  "test/**/*.ts?(x)": "KEBAB_CASE"
577
885
  },
578
886
  {
@@ -583,14 +891,14 @@ var checkFileConfig = [
583
891
  "error",
584
892
  {
585
893
  // require stories and test files to be inside tests folders
586
- "*.(stories|test).{ts,tsx}": "app/**/tests/"
894
+ "*.(stories|test).{ts,tsx}": `${sourceDir}/**/tests/`
587
895
  }
588
896
  ],
589
897
  "check-file/folder-naming-convention": [
590
898
  "error",
591
899
  {
592
900
  // enforce PascalCase component folders, and allow assets, hooks, tests, and utils subfolders
593
- "app/components/**/": "(assets|hooks|state|tests|utils|[A-Z][a-zA-Z0-9]*)"
901
+ [`${sourceDir}/components/**/`]: "(assets|hooks|state|tests|utils|[A-Z][a-zA-Z0-9]*)"
594
902
  }
595
903
  ]
596
904
  }
@@ -611,194 +919,12 @@ var checkFileConfig = [
611
919
  }
612
920
  }
613
921
  ];
614
- var styleHygiene = [
615
- ...canonicalConfig,
922
+ var buildStyleHygiene = (sourceDir) => [
923
+ ...buildCanonicalConfig(sourceDir),
616
924
  ...perfectionistConfig,
617
925
  ...unicornConfig,
618
926
  ...unusedImportsConfig,
619
- ...checkFileConfig
620
- ];
621
-
622
- // src/configs/guardrails.ts
623
- import noRelativeImportPaths from "eslint-plugin-no-relative-import-paths";
624
- import sonarjs from "eslint-plugin-sonarjs";
625
-
626
- // src/plugins/no-enum.ts
627
- var noEnumRule = {
628
- create: (context) => ({
629
- TSEnumDeclaration: (node) => {
630
- context.report({ messageId: "noEnum", node });
631
- }
632
- }),
633
- meta: {
634
- docs: { description: "Disallow TypeScript enums" },
635
- messages: {
636
- noEnum: "Do not use TypeScript enums. Use an object with `as const` instead."
637
- },
638
- schema: [],
639
- type: "problem"
640
- }
641
- };
642
- var plugin = {
643
- meta: {
644
- name: "no-enum",
645
- version: "0.1.0"
646
- },
647
- rules: {
648
- "no-enum": noEnumRule
649
- }
650
- };
651
- var no_enum_default = plugin;
652
-
653
- // src/plugins/no-jsx-iife.ts
654
- var noJsxIifeRule = {
655
- create: (context) => ({
656
- "JSXExpressionContainer > CallExpression > ArrowFunctionExpression.callee": (node) => {
657
- context.report({ messageId: "noJsxIife", node });
658
- },
659
- "JSXExpressionContainer > CallExpression > FunctionExpression.callee": (node) => {
660
- context.report({ messageId: "noJsxIife", node });
661
- }
662
- }),
663
- meta: {
664
- docs: { description: "Disallow IIFEs in JSX expressions" },
665
- messages: {
666
- noJsxIife: "Do not use IIFEs in JSX. Use a computed variable before the return or extract a component instead."
667
- },
668
- schema: [],
669
- type: "problem"
670
- }
671
- };
672
- var plugin2 = {
673
- meta: {
674
- name: "no-jsx-iife",
675
- version: "0.1.0"
676
- },
677
- rules: {
678
- "no-jsx-iife": noJsxIifeRule
679
- }
680
- };
681
- var no_jsx_iife_default = plugin2;
682
-
683
- // src/plugins/no-switch.ts
684
- var noSwitchRule = {
685
- create: (context) => ({
686
- SwitchStatement: (node) => {
687
- context.report({ messageId: "noSwitch", node });
688
- }
689
- }),
690
- meta: {
691
- docs: { description: "Disallow switch statements" },
692
- messages: {
693
- noSwitch: "Do not use switch statements. Use an object map or if/else instead."
694
- },
695
- schema: [],
696
- type: "problem"
697
- }
698
- };
699
- var plugin3 = {
700
- meta: {
701
- name: "no-switch",
702
- version: "0.1.0"
703
- },
704
- rules: {
705
- "no-switch": noSwitchRule
706
- }
707
- };
708
- var no_switch_default = plugin3;
709
-
710
- // src/configs/guardrails.ts
711
- var sonarConfig = [
712
- sonarjs.configs.recommended,
713
- {
714
- name: "sonarjs",
715
- rules: {
716
- "sonarjs/cognitive-complexity": "error",
717
- "sonarjs/fixme-tag": "off",
718
- "sonarjs/no-commented-code": "off",
719
- "sonarjs/no-nested-conditional": "off",
720
- "sonarjs/no-nested-functions": "off",
721
- "sonarjs/no-selector-parameter": "off",
722
- "sonarjs/regex-complexity": "off",
723
- "sonarjs/todo-tag": "off"
724
- }
725
- },
726
- {
727
- files: ["**/*.tsx", "**/hooks/*.ts?(x)"],
728
- name: "sonarjs/react-files",
729
- rules: {
730
- "sonarjs/cognitive-complexity": "off",
731
- "sonarjs/function-return-type": "off"
732
- }
733
- },
734
- {
735
- files: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)"],
736
- name: "sonarjs/test-files",
737
- rules: {
738
- "sonarjs/no-duplicate-string": "off",
739
- "sonarjs/no-identical-functions": "off"
740
- }
741
- },
742
- {
743
- files: ["app/languages/**/*.ts", "eslint.config.mjs"],
744
- name: "sonarjs/credential-checks",
745
- rules: {
746
- "sonarjs/no-hardcoded-credentials": "off",
747
- "sonarjs/no-hardcoded-passwords": "off"
748
- }
749
- }
750
- ];
751
- var noEnumConfig = [
752
- {
753
- files: ["**/*.ts?(x)"],
754
- name: "no-enum",
755
- plugins: { "no-enum": no_enum_default },
756
- rules: {
757
- "no-enum/no-enum": "error"
758
- }
759
- }
760
- ];
761
- var noJsxIifeConfig = [
762
- {
763
- files: ["**/*.tsx", "**/*.jsx"],
764
- name: "no-jsx-iife",
765
- plugins: { "no-jsx-iife": no_jsx_iife_default },
766
- rules: { "no-jsx-iife/no-jsx-iife": "error" }
767
- }
768
- ];
769
- var noSwitchConfig = [
770
- {
771
- files: ["**/*.ts?(x)", "**/*.js?(x)"],
772
- name: "no-switch",
773
- plugins: { "no-switch": no_switch_default },
774
- rules: { "no-switch/no-switch": "error" }
775
- }
776
- ];
777
- var noRelativeImportPathsConfig = [
778
- {
779
- name: "no-relative-import-paths",
780
- plugins: {
781
- "no-relative-import-paths": noRelativeImportPaths
782
- },
783
- rules: {
784
- "no-relative-import-paths/no-relative-import-paths": [
785
- "error",
786
- {
787
- allowedDepth: 2,
788
- allowSameFolder: true,
789
- prefix: "~",
790
- rootDir: "app"
791
- }
792
- ]
793
- }
794
- }
795
- ];
796
- var guardrails = [
797
- ...sonarConfig,
798
- ...noEnumConfig,
799
- ...noJsxIifeConfig,
800
- ...noSwitchConfig,
801
- ...noRelativeImportPathsConfig
927
+ ...buildCheckFileConfig(sourceDir)
802
928
  ];
803
929
 
804
930
  // src/configs/testing.ts
@@ -837,145 +963,30 @@ var testing = [
837
963
  }
838
964
  ];
839
965
 
840
- // src/configs/storybook.ts
841
- import storybookPlugin from "eslint-plugin-storybook";
842
- var storybook = [
843
- ...storybookPlugin.configs["flat/recommended"]
844
- ];
845
-
846
- // src/configs/playwright.ts
847
- import playwrightPlugin from "eslint-plugin-playwright";
848
- var playwright = [
849
- {
850
- name: "playwright",
851
- ...playwrightPlugin.configs["flat/recommended"],
852
- files: [".playwright/**/*.ts?(x)"],
853
- rules: {
854
- ...playwrightPlugin.configs["flat/recommended"].rules,
855
- "playwright/expect-expect": [
856
- "warn",
857
- { assertFunctionPatterns: ["^expect[A-Z]"] }
858
- ]
859
- }
860
- }
861
- ];
862
-
863
- // src/configs/prettier.ts
864
- import { rules as prettierConfigRules } from "eslint-config-prettier";
865
- import prettierPlugin from "eslint-plugin-prettier";
866
- var prettier = [
867
- {
868
- name: "prettier/plugin/config",
869
- plugins: { prettier: prettierPlugin }
870
- },
871
- {
872
- name: "prettier/config",
873
- rules: {
874
- ...prettierConfigRules,
875
- "@stylistic/padding-line-between-statements": [
876
- "error",
877
- {
878
- blankLine: "always",
879
- next: ["block-like", "export", "return", "throw"],
880
- prev: "*"
881
- }
882
- ],
883
- "@stylistic/quotes": [
884
- "error",
885
- "single",
886
- {
887
- allowTemplateLiterals: "avoidEscape",
888
- avoidEscape: true
889
- }
890
- ],
891
- "@stylistic/spaced-comment": "off",
892
- "prettier/prettier": ["error", { endOfLine: "auto" }]
893
- }
894
- }
895
- ];
896
-
897
- // src/configs/better-tailwind.ts
898
- import betterTailwindPlugin from "eslint-plugin-better-tailwindcss";
899
- var betterTailwind = (opts) => [
900
- {
901
- name: "better-tailwindcss",
902
- plugins: {
903
- "better-tailwindcss": betterTailwindPlugin
904
- },
905
- rules: {
906
- "better-tailwindcss/enforce-canonical-classes": "error",
907
- "better-tailwindcss/enforce-consistent-important-position": "error",
908
- "better-tailwindcss/enforce-consistent-variable-syntax": "error",
909
- "better-tailwindcss/enforce-shorthand-classes": "error",
910
- "better-tailwindcss/no-conflicting-classes": "error",
911
- "better-tailwindcss/no-deprecated-classes": "error",
912
- "better-tailwindcss/no-unknown-classes": [
913
- "error",
914
- { ignore: opts.ignore ?? [] }
915
- ]
916
- },
917
- settings: {
918
- "better-tailwindcss": {
919
- detectComponentClasses: true,
920
- entryPoint: opts.entryPoint
921
- }
922
- }
923
- }
924
- ];
925
-
926
- // src/configs/ignores.ts
927
- import { includeIgnoreFile } from "@eslint/compat";
928
- import path from "path";
929
- var defaultIgnores = [
930
- ".storybook",
931
- ".playwright",
932
- "/.react-router/**",
933
- ".claude/**/*.js",
934
- ".claude/**/*.cjs",
935
- "scripts",
936
- "public/**",
937
- "**/*.css",
938
- "**/*.svg",
939
- "**/*.md"
940
- ];
941
- var ignores = (opts) => {
942
- const out = [];
943
- if (opts?.gitignore) {
944
- const resolved = path.isAbsolute(opts.gitignore) ? opts.gitignore : path.resolve(process.cwd(), opts.gitignore);
945
- out.push(includeIgnoreFile(resolved));
946
- }
947
- out.push({
948
- ignores: [...defaultIgnores, ...opts?.extra ?? []],
949
- name: "ignored-files"
950
- });
951
- return out;
952
- };
953
-
954
966
  // src/index.ts
955
- var gaiaLint = {
956
- base,
957
- react,
958
- styleHygiene,
959
- guardrails,
960
- testing,
961
- storybook,
962
- playwright,
963
- prettier,
964
- betterTailwind,
965
- ignores
967
+ var buildIgnoresAccessor = () => {
968
+ const defaults = ignores();
969
+ const accessor = ((opts) => ignores(opts));
970
+ accessor[Symbol.iterator] = () => defaults[Symbol.iterator]();
971
+ return accessor;
972
+ };
973
+ var gaiaLint = (opts) => {
974
+ const sourceDir = opts?.sourceDir ?? "app";
975
+ return {
976
+ base: buildBase(sourceDir),
977
+ betterTailwind,
978
+ guardrails: buildGuardrails(sourceDir),
979
+ ignores: buildIgnoresAccessor(),
980
+ playwright,
981
+ prettier,
982
+ react,
983
+ storybook,
984
+ styleHygiene: buildStyleHygiene(sourceDir),
985
+ testing
986
+ };
966
987
  };
967
988
  var index_default = gaiaLint;
968
989
  export {
969
- base,
970
- betterTailwind,
971
- index_default as default,
972
- guardrails,
973
- ignores,
974
- playwright,
975
- prettier,
976
- react,
977
- storybook,
978
- styleHygiene,
979
- testing
990
+ index_default as default
980
991
  };
981
992
  //# sourceMappingURL=index.js.map