@skapxd/eslint-opinionated 0.6.0 → 0.8.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/README.md CHANGED
@@ -510,6 +510,51 @@ export default [
510
510
  Si necesitas una excepción puntual (p. ej. archivos generados), añade después un
511
511
  bloque con `linterOptions: { noInlineConfig: false }` para esos globs.
512
512
 
513
+ ## Configurar y sobrescribir reglas
514
+
515
+ Los presets son flat configs normales de ESLint: **el último config que
516
+ matchea un archivo gana**. Para ajustar una regla encima de un preset, esparce
517
+ sus `rules` y sobrescribe la entrada:
518
+
519
+ ```js
520
+ export default [
521
+ {
522
+ files: ["src/**/*.{ts,tsx}"],
523
+ ...skapxd.configs.shared.frontend,
524
+ rules: {
525
+ ...skapxd.configs.shared.frontend.rules,
526
+ // mismo id, nuevas opciones: esta entrada reemplaza a la del preset
527
+ "skapxd/no-deep-relative-imports": ["error", { maxDepth: 1 }],
528
+ },
529
+ },
530
+ ];
531
+ ```
532
+
533
+ > Las opciones de una regla **se reemplazan completas**, no se mergean: si el
534
+ > preset pasaba opciones y tú la redeclaras, incluye también las que quieras
535
+ > conservar. (Excepción: los patrones *integrados* de `no-default-export` —
536
+ > configs y stories — viven dentro de la regla y nunca se pierden; tus
537
+ > `allowFilePatterns` se suman a ellos.)
538
+
539
+ Referencia rápida de qué se puede configurar (detalle y defaults en la sección
540
+ de cada regla):
541
+
542
+ | Regla | Opciones |
543
+ | --- | --- |
544
+ | `async-functions-return-result` | `allowFilePatterns` (globs), `allowNamePatterns` (regex), `checkMissingReturnType`, `checkMissingReturnTypeWhenCallNames`, `requireCallNames`, `promiseTypeNames`, `resultTypeNames` |
545
+ | `await-requires-result` | `allowFilePatterns` (globs), `trySafeCallNames` |
546
+ | `max-hook-size` | `maxLines`, `maxUseState` |
547
+ | `no-deep-relative-imports` | `maxDepth` |
548
+ | `no-default-export` | `allowFilePatterns` (globs, aditivos a los integrados) |
549
+ | `no-emoji` | `allowFilePatterns` (globs) |
550
+ | `no-functions-inside-components` | `allowJsxCallbacks`, `allowArrayMapCallbacks` (ambas `true` por defecto) |
551
+ | `no-promise-chain` | `methods` |
552
+
553
+ Los `allowFilePatterns` de todas las reglas son **globs** (`*` un segmento,
554
+ `**` cualquier profundidad, `{a,b}` alternativas; un patrón sin prefijo
555
+ matchea en cualquier carpeta). Las 7 reglas restantes no tienen opciones: su
556
+ única configuración es activarlas, apagarlas o cambiar la severidad.
557
+
513
558
  ## Reglas
514
559
 
515
560
  | Regla | Qué protege |
@@ -522,6 +567,8 @@ bloque con `linterOptions: { noInlineConfig: false }` para esos globs.
522
567
  | `skapxd/max-hook-size` | Marca hooks grandes o con demasiados `useState`. |
523
568
  | `skapxd/jsx-return-name-pascal-case` | Funciones que retornan JSX deben nombrarse como componentes. |
524
569
  | `skapxd/no-deep-relative-imports` | Limita la profundidad de los imports relativos (`../`). |
570
+ | `skapxd/no-default-export` | Prohíbe `export default`; el nombre del símbolo es el contrato. Exime configs/stories y, en el preset `next`, los entrypoints del App Router. |
571
+ | `skapxd/no-emoji` | Prohíbe emojis en strings y JSX; cada sistema los renderiza distinto. Usa un icono SVG. |
525
572
  | `skapxd/no-functions-inside-components` | Prohíbe definir funciones dentro de componentes React. |
526
573
  | `skapxd/no-try-catch` | Prohíbe `try/catch`; usa `trySafe` de `@skapxd/result`. |
527
574
  | `skapxd/no-promise-chain` | Prohíbe `.then/.catch/.finally`; usa `await` (+ `trySafe`). |
@@ -610,6 +657,20 @@ async function no(): Promise<Result<number, Error>> {} // se reporta
610
657
  > Sin información de tipos cae a una comprobación por nombre (`resultTypeNames`),
611
658
  > menos estricta.
612
659
 
660
+ Todas las opciones, con sus defaults:
661
+
662
+ ```js
663
+ "skapxd/async-functions-return-result": ["error", {
664
+ allowFilePatterns: [], // globs de archivos exentos, p. ej. ["src/legacy/**"]
665
+ allowNamePatterns: [], // regex de nombres exentos, p. ej. ["^(GET|POST)$"]
666
+ checkMissingReturnType: true, // reportar también funciones SIN anotación de retorno
667
+ checkMissingReturnTypeWhenCallNames: [], // ...o solo si el cuerpo llama a estos nombres
668
+ requireCallNames: [], // acotar la regla a funciones que llamen a estos nombres
669
+ promiseTypeNames: ["Promise"], // wrappers de promesa aceptados (fallback sin tipos)
670
+ resultTypeNames: ["Result"], // nombres de Result aceptados (fallback sin tipos)
671
+ }]
672
+ ```
673
+
613
674
  ### `skapxd/result-error-requires-cause`
614
675
 
615
676
  Evita perder el error original al transformar un `Result` fallido:
@@ -698,6 +759,30 @@ Sirve para código de pegamento, pero deja el error sin modelar (`Result<T,
698
759
  unknown>`). Cuando la misma operación se repite o el error importa, el mensaje
699
760
  de la regla empuja hacia el camino 1.
700
761
 
762
+ ### `skapxd/no-ad-hoc-ok-result`
763
+
764
+ Prohíbe que una función async **exportada** retorne objetos literales con la
765
+ forma `{ ok: ... }` armados a mano. Un contrato casero fragmenta el sistema:
766
+ cada módulo inventa su variante, la exención type-aware de
767
+ `await-requires-result` no lo reconoce, y `match()` pierde la exhaustividad.
768
+
769
+ ```ts
770
+ export async function getUser(id: string) {
771
+ return { ok: false, message: "falló" }; // ❌ contrato inventado
772
+ }
773
+
774
+ export async function getUser(id: string): Promise<Result<User, UserError>> {
775
+ return Result.err({ // ✅ el Result real
776
+ cause: error,
777
+ message: "No pude cargar el usuario.",
778
+ type: "USER_FETCH_FAILED",
779
+ });
780
+ }
781
+ ```
782
+
783
+ Solo mira funciones async exportadas: un helper interno con un objeto `ok`
784
+ cualquiera no es un contrato público y no se reporta.
785
+
701
786
  ### `skapxd/max-hook-size`
702
787
 
703
788
  Marca hooks que crecen demasiado o acumulan muchos `useState`.
@@ -705,6 +790,35 @@ Marca hooks que crecen demasiado o acumulan muchos `useState`.
705
790
  La intención es empujar el diseño hacia `useReducer`, hooks más pequeños o
706
791
  módulos de transición de estado.
707
792
 
793
+ Opciones (los presets `frontend` y `next` usan `maxLines: 120`, `maxUseState: 1`):
794
+
795
+ ```js
796
+ "skapxd/max-hook-size": ["error", {
797
+ maxLines: 120, // líneas máximas del cuerpo del hook
798
+ maxUseState: 1, // useState propios permitidos antes de exigir useReducer
799
+ }]
800
+ ```
801
+
802
+ ### `skapxd/jsx-return-name-pascal-case`
803
+
804
+ Si una función devuelve JSX, es un componente, y debe llamarse como tal:
805
+ PascalCase. El mensaje sugiere el rename concreto.
806
+
807
+ ```tsx
808
+ function renderUserCard(user: User) { // ❌ "render*" devuelve JSX → es un componente
809
+ return <article>{user.name}</article>;
810
+ }
811
+
812
+ function UserCard({ user }: { user: User }) { // ✅ nombre de componente + props
813
+ return <article>{user.name}</article>;
814
+ }
815
+ ```
816
+
817
+ Esta regla es la que mantiene honesto al resto del sistema React: las reglas
818
+ de componentes detectan "componente" por nombre PascalCase, así que una
819
+ función `renderX` que devuelve JSX escaparía de ellas. Esta la captura y
820
+ fuerza el rename — y con el nombre corregido, las demás ya la ven.
821
+
708
822
  ### `skapxd/no-deep-relative-imports`
709
823
 
710
824
  Limita cuántos niveles puede subir un import relativo. Por defecto **prohíbe
@@ -730,20 +844,99 @@ Revisa imports estáticos (`import`), re-exports (`export ... from`) e imports
730
844
  dinámicos (`import(...)`). El remedio habitual es un alias de ruta (`@/...`) o
731
845
  acercar el módulo a quien lo usa.
732
846
 
847
+ ### `skapxd/no-default-export`
848
+
849
+ Prohíbe `export default` (incluida la forma `export { x as default }`). Con
850
+ exports nombrados, el nombre del símbolo es el contrato del módulo: renombrar
851
+ con el IDE actualiza todos los usos, `grep` encuentra definición y consumo, y
852
+ los autoimports no inventan nombres distintos por archivo.
853
+
854
+ ```ts
855
+ export default function getUser() {} // ❌ cada import puede llamarlo distinto
856
+ export function getUser() {} // ✅ un solo nombre canónico
857
+ ```
858
+
859
+ **Dónde sí se permite el default.** Hay entrypoints donde el ecosistema lo
860
+ exige, y la regla los reconoce en capas:
861
+
862
+ 1. **Integrados (siempre activos):** configs de tooling (`*.config.{js,mjs,cjs,ts}`:
863
+ `next.config`, `tailwind.config`, `vitest.config`, `eslint.config`, ...) y
864
+ stories de Storybook (`*.stories.*`).
865
+ 2. **Preset `next` (automático):** los entrypoints del App Router donde Next
866
+ exige el default — `page`, `layout`, `template`, `error`, `loading`,
867
+ `not-found`, `sitemap`, `robots`, `manifest`, `icon`, `opengraph-image`,
868
+ etc. No hay que configurar nada.
869
+ 3. **`allowFilePatterns` (extensible):** si usas un framework o tool que la
870
+ regla aún no contempla, agrega su glob. Los patrones propios se **suman**
871
+ a los integrados, no los reemplazan. Son globs legibles (`*` un segmento,
872
+ `**` cualquier profundidad, `{a,b}` alternativas) y un patrón sin prefijo
873
+ matchea en cualquier carpeta:
874
+
875
+ ```js
876
+ "skapxd/no-default-export": ["error", {
877
+ // p. ej. SvelteKit exige default en +page.ts / +layout.ts
878
+ allowFilePatterns: ["+page.ts", "+layout.ts"],
879
+ }]
880
+ ```
881
+
882
+ Detalle útil con `React.lazy` (que espera `{ default }`): no hace falta volver
883
+ al default export, basta mapear el named en el import dinámico:
884
+
885
+ ```ts
886
+ const Card = lazy(() => import("./card").then((m) => ({ default: m.Card })));
887
+ ```
888
+
889
+ ### `skapxd/no-emoji`
890
+
891
+ Prohíbe emojis en strings, template literals y texto JSX. El problema no es
892
+ estético: un emoji se renderiza con la fuente de emojis del **sistema del
893
+ usuario** — Segoe UI Emoji en Windows, Apple Color Emoji en macOS, Noto en
894
+ Android — así que el mismo carácter se ve distinto en cada plataforma, y en
895
+ un Linux sin fuente de emojis directamente no se renderiza (sale el cuadro
896
+ vacío □). Un SVG se ve idéntico en todas partes.
897
+
898
+ ```tsx
899
+ <button>Enviar 🚀</button> // ❌ depende de la fuente del sistema
900
+ <button>Enviar <Rocket /></button> // ✅ lucide-react: idéntico en todas partes
901
+ ```
902
+
903
+ Detecta por propiedad Unicode (`Extended_Pictographic`), así que los símbolos
904
+ tipográficos normales no se tocan: `→`, `✓`, `©`, `·` pasan sin problema.
905
+
906
+ No revisa comentarios: un emoji en un comentario no llega al navegador. Para
907
+ eximir archivos completos (fixtures, seeds), usa `allowFilePatterns`:
908
+
909
+ ```js
910
+ "skapxd/no-emoji": ["error", {
911
+ allowFilePatterns: ["tests/fixtures/**"],
912
+ }]
913
+ ```
914
+
733
915
  ### `skapxd/no-functions-inside-components`
734
916
 
735
- Prohíbe **cualquier** función definida dentro de un componente React (una función
736
- con nombre PascalCase). Cada render recrea esas funciones, lo que dispara
737
- re-renders innecesarios en hijos memoizados y mezcla lógica con composición.
917
+ Prohíbe definir funciones **con peso propio** dentro de un componente React
918
+ (una función con nombre PascalCase): handlers con nombre, helpers, callbacks de
919
+ `useEffect`. Cada render las recrea, dispara re-renders en hijos memoizados y
920
+ mezcla lógica con composición.
738
921
 
739
922
  ```tsx
740
923
  function Card() {
741
- const onClick = () => save(); // ❌ se recrea en cada render
924
+ const onClick = () => save(); // ❌ handler con nombre en el cuerpo
742
925
  useEffect(() => subscribe(), []); // ❌ callback dentro del componente
743
- return <ul>{items.map((i) => <Li />)}</ul>; // ❌ callback de .map en el render
926
+ return (
927
+ <ul>
928
+ {items.map((i) => <Li key={i} />)} {/* ✅ React idiomático */}
929
+ <button onClick={() => save()}>Guardar</button> {/* ✅ React idiomático */}
930
+ </ul>
931
+ );
744
932
  }
745
933
  ```
746
934
 
935
+ Los dos patrones idiomáticos de React están **permitidos por defecto**: el
936
+ callback anónimo como valor directo de una prop JSX y el callback anónimo de
937
+ `.map(...)` en el render. Forzarlos a salir del componente produce workarounds
938
+ peores que el problema (`.bind(null, ...)`, adapters artificiales).
939
+
747
940
  El cuerpo del componente queda como composición declarativa; **toda** función
748
941
  —handlers, efectos, memos, mapeos— vive fuera:
749
942
 
@@ -764,23 +957,28 @@ function Card() {
764
957
  helper en minúscula **sí** pueden tener funciones dentro — ahí es donde se mueve
765
958
  la lógica.
766
959
 
767
- **Opciones** (desde v0.6.0) para permitir los dos patrones React idiomáticos
768
- que la versión estricta bloqueaba forzarlos a salir del componente producía
769
- workarounds peores que el problema (`.bind(null, ...)`, adapters artificiales):
960
+ **Opciones.** Las exenciones aplican solo a **flechas de expresión** (sin
961
+ cuerpo `{ }`) en esa posición exacta: el valor directo de una prop JSX, o el
962
+ primer argumento de `.map(...)`. La distinción importa: una flecha de expresión
963
+ solo puede contener una expresión — es declarativa por construcción —, mientras
964
+ que un bloque da pie a `if`s, variables y llamadas que pertenecen fuera:
965
+
966
+ ```tsx
967
+ {items.map((i) => <li key={i} />)} // ✅ flecha de expresión
968
+ {items.map((i) => { return <li key={i} />; })} // ❌ bloque: invita a meter lógica
969
+ ```
970
+
971
+ Un handler con nombre en el cuerpo (`const onClick = () => ...`), un callback
972
+ de `useEffect` o un `.forEach` siguen reportándose. Para el modo ultraestricto
973
+ (ninguna función inline, como en v0.6.0 y anteriores), apágalas explícitamente:
770
974
 
771
975
  ```js
772
976
  "skapxd/no-functions-inside-components": ["error", {
773
- allowJsxCallbacks: true, // <button onClick={() => onSelect(id)} />
774
- allowArrayMapCallbacks: true, // {entries.map((entry) => <Entry key={entry.id} />)}
977
+ allowJsxCallbacks: false, // también reporta onClick={() => ...}
978
+ allowArrayMapCallbacks: false, // también reporta items.map((i) => ...)
775
979
  }]
776
980
  ```
777
981
 
778
- Ambas exenciones aplican **solo a funciones anónimas inline en esa posición
779
- exacta**: el valor directo de una prop JSX, o el primer argumento de `.map(...)`.
780
- Un handler con nombre en el cuerpo (`const onClick = () => ...`), un callback de
781
- `useEffect` o un `.forEach` siguen reportándose — la lógica con peso sigue
782
- viviendo fuera del componente.
783
-
784
982
  ### `skapxd/no-try-catch`
785
983
 
786
984
  Prohíbe `try/catch`. La intención es que los errores se modelen como `Result` en
@@ -12,6 +12,8 @@ declare function createAstroConfigs(pluginReference: unknown): ({
12
12
  rules: {
13
13
  "skapxd/no-ad-hoc-ok-result": string;
14
14
  "skapxd/no-deep-relative-imports": string;
15
+ "skapxd/no-default-export": string;
16
+ "skapxd/no-emoji": string;
15
17
  "skapxd/no-promise-chain": string;
16
18
  "skapxd/no-try-catch": string;
17
19
  "skapxd/one-root-function-per-file": string;
@@ -27,6 +29,8 @@ declare function createAstroConfigs(pluginReference: unknown): ({
27
29
  rules: {
28
30
  "skapxd/no-ad-hoc-ok-result": string;
29
31
  "skapxd/no-deep-relative-imports": string;
32
+ "skapxd/no-default-export": string;
33
+ "skapxd/no-emoji": string;
30
34
  "skapxd/no-promise-chain": string;
31
35
  "skapxd/no-try-catch": string;
32
36
  "skapxd/one-root-function-per-file": string;
@@ -12,6 +12,8 @@ declare function createAstroConfigs(pluginReference: unknown): ({
12
12
  rules: {
13
13
  "skapxd/no-ad-hoc-ok-result": string;
14
14
  "skapxd/no-deep-relative-imports": string;
15
+ "skapxd/no-default-export": string;
16
+ "skapxd/no-emoji": string;
15
17
  "skapxd/no-promise-chain": string;
16
18
  "skapxd/no-try-catch": string;
17
19
  "skapxd/one-root-function-per-file": string;
@@ -27,6 +29,8 @@ declare function createAstroConfigs(pluginReference: unknown): ({
27
29
  rules: {
28
30
  "skapxd/no-ad-hoc-ok-result": string;
29
31
  "skapxd/no-deep-relative-imports": string;
32
+ "skapxd/no-default-export": string;
33
+ "skapxd/no-emoji": string;
30
34
  "skapxd/no-promise-chain": string;
31
35
  "skapxd/no-try-catch": string;
32
36
  "skapxd/one-root-function-per-file": string;
@@ -38,6 +38,8 @@ module.exports = __toCommonJS(astro_exports);
38
38
  var baseRules = {
39
39
  "skapxd/no-ad-hoc-ok-result": "error",
40
40
  "skapxd/no-deep-relative-imports": "error",
41
+ "skapxd/no-default-export": "error",
42
+ "skapxd/no-emoji": "error",
41
43
  "skapxd/no-promise-chain": "error",
42
44
  "skapxd/no-try-catch": "error",
43
45
  "skapxd/one-root-function-per-file": "error",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/astro/index.ts","../../src/shared/configs/base-rules.ts","../../src/shared/configs/create-base-language-options.ts","../../src/shared/configs/create-typed-language-options.ts","../../src/astro/configs.ts"],"sourcesContent":["export { createAstroConfigs } from \"./configs\";\n","export const baseRules = {\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n};\n","import tseslint from \"typescript-eslint\";\n\n// Variante sin type-checking: solo el parser, para presets que aplican a TS\n// pero no necesitan projectService (base, package, next/base, next/react).\nexport function createBaseLanguageOptions() {\n return {\n parser: tseslint.parser,\n };\n}\n","import tseslint from \"typescript-eslint\";\n\nexport function createTypedLanguageOptions() {\n return {\n // Sin el parser explícito, un consumidor que use solo estos presets\n // obtiene \"Parsing error\" en cada archivo TS (espree no parsea TS).\n parser: tseslint.parser,\n parserOptions: {\n projectService: true,\n },\n };\n}\n","import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACRA,+BAAqB;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQ,yBAAAA,QAAS;AAAA,EACnB;AACF;;;ACRA,IAAAC,4BAAqB;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,0BAAAC,QAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACLO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":["tseslint","import_typescript_eslint","tseslint"]}
1
+ {"version":3,"sources":["../../src/astro/index.ts","../../src/shared/configs/base-rules.ts","../../src/shared/configs/create-base-language-options.ts","../../src/shared/configs/create-typed-language-options.ts","../../src/astro/configs.ts"],"sourcesContent":["export { createAstroConfigs } from \"./configs\";\n","export const baseRules = {\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n};\n","import tseslint from \"typescript-eslint\";\n\n// Variante sin type-checking: solo el parser, para presets que aplican a TS\n// pero no necesitan projectService (base, package, next/base, next/react).\nexport function createBaseLanguageOptions() {\n return {\n parser: tseslint.parser,\n };\n}\n","import tseslint from \"typescript-eslint\";\n\nexport function createTypedLanguageOptions() {\n return {\n // Sin el parser explícito, un consumidor que use solo estos presets\n // obtiene \"Parsing error\" en cada archivo TS (espree no parsea TS).\n parser: tseslint.parser,\n parserOptions: {\n projectService: true,\n },\n };\n}\n","import {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\nexport function createAstroConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/astro/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n // Los .astro no llevan parser propio: lo aporta eslint-plugin-astro,\n // que el consumidor debe tener configurado.\n {\n files: [\"src/**/*.astro\"],\n name: \"skapxd/astro/astro-files\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n {\n files: [\"src/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/astro/typescript\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACVA,+BAAqB;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQ,yBAAAA,QAAS;AAAA,EACnB;AACF;;;ACRA,IAAAC,4BAAqB;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,0BAAAC,QAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACLO,SAAS,mBAAmB,iBAA0B;AAC3D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA;AAAA,MACE,OAAO,CAAC,gBAAgB;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,OAAO,CAAC,mBAAmB;AAAA,MAC3B,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":["tseslint","import_typescript_eslint","tseslint"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAstroConfigs
3
- } from "../chunk-7H7DRTP2.mjs";
4
- import "../chunk-RWQGOTVZ.mjs";
3
+ } from "../chunk-MSH2BTXH.mjs";
4
+ import "../chunk-Z6CU2N4C.mjs";
5
5
  export {
6
6
  createAstroConfigs
7
7
  };
@@ -1,10 +1,18 @@
1
+ import {
2
+ nextAppMetadataFileStems,
3
+ nextAppRouteSegmentFileStems
4
+ } from "./chunk-HB755SJQ.mjs";
1
5
  import {
2
6
  baseRules,
3
7
  createBaseLanguageOptions,
4
8
  createTypedLanguageOptions
5
- } from "./chunk-RWQGOTVZ.mjs";
9
+ } from "./chunk-Z6CU2N4C.mjs";
6
10
 
7
11
  // src/next/configs.ts
12
+ var nextDefaultExportFileGlob = `{${[
13
+ ...nextAppRouteSegmentFileStems,
14
+ ...nextAppMetadataFileStems
15
+ ].join(",")}}.{js,jsx,ts,tsx}`;
8
16
  function createNextConfigs(pluginReference) {
9
17
  const baseLanguageOptions = createBaseLanguageOptions();
10
18
  const typedLanguageOptions = createTypedLanguageOptions();
@@ -13,7 +21,15 @@ function createNextConfigs(pluginReference) {
13
21
  languageOptions: baseLanguageOptions,
14
22
  name: "skapxd/next/base",
15
23
  plugins: { skapxd: pluginReference },
16
- rules: baseRules
24
+ rules: {
25
+ ...baseRules,
26
+ "skapxd/no-default-export": [
27
+ "error",
28
+ {
29
+ allowFilePatterns: [nextDefaultExportFileGlob]
30
+ }
31
+ ]
32
+ }
17
33
  },
18
34
  {
19
35
  files: ["src/app/api/**/*.{ts,tsx}", "src/server/**/*.{ts,tsx}"],
@@ -53,4 +69,4 @@ function createNextConfigs(pluginReference) {
53
69
  export {
54
70
  createNextConfigs
55
71
  };
56
- //# sourceMappingURL=chunk-QJQ2E2NG.mjs.map
72
+ //# sourceMappingURL=chunk-2IXJXIW5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/next/configs.ts"],"sourcesContent":["import { nextAppMetadataFileStems } from \"#/constants/next-app-metadata-file-stems\";\nimport { nextAppRouteSegmentFileStems } from \"#/constants/next-app-route-segment-file-stems\";\nimport {\n baseRules,\n createBaseLanguageOptions,\n createTypedLanguageOptions,\n} from \"#/shared/configs\";\n\n// Entrypoints donde Next exige `export default` (page, layout, sitemap, ...):\n// la regla no-default-export los exime automáticamente en este preset.\nconst nextDefaultExportFileGlob = `{${[\n ...nextAppRouteSegmentFileStems,\n ...nextAppMetadataFileStems,\n].join(\",\")}}.{js,jsx,ts,tsx}`;\n\nexport function createNextConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return [\n {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/next/base\",\n plugins: { skapxd: pluginReference },\n rules: {\n ...baseRules,\n \"skapxd/no-default-export\": [\n \"error\",\n {\n allowFilePatterns: [nextDefaultExportFileGlob],\n },\n ],\n },\n },\n {\n files: [\"src/app/api/**/*.{ts,tsx}\", \"src/server/**/*.{ts,tsx}\"],\n languageOptions: typedLanguageOptions,\n name: \"skapxd/next/server\",\n plugins: { skapxd: pluginReference },\n rules: {\n ...baseRules,\n // Obligatoria: todo await resuelve en Result. A diferencia de\n // async-functions-return-result (apagada por defecto), no necesita\n // excepciones para los entrypoints de Next: envolver un await en\n // trySafe es compatible con cualquier firma que imponga el framework.\n \"skapxd/await-requires-result\": \"error\",\n },\n },\n {\n files: [\"**/*.tsx\"],\n languageOptions: baseLanguageOptions,\n name: \"skapxd/next/react\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/jsx-return-name-pascal-case\": \"error\",\n \"skapxd/no-functions-inside-components\": \"error\",\n \"skapxd/no-jsx-ternary-null\": \"error\",\n \"skapxd/max-hook-size\": [\n \"error\",\n {\n maxLines: 120,\n maxUseState: 1,\n },\n ],\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;AAUA,IAAM,4BAA4B,IAAI;AAAA,EACpC,GAAG;AAAA,EACH,GAAG;AACL,EAAE,KAAK,GAAG,CAAC;AAEJ,SAAS,kBAAkB,iBAA0B;AAC1D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL;AAAA,MACE,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,GAAG;AAAA,QACH,4BAA4B;AAAA,UAC1B;AAAA,UACA;AAAA,YACE,mBAAmB,CAAC,yBAAyB;AAAA,UAC/C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,6BAA6B,0BAA0B;AAAA,MAC/D,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,gCAAgC;AAAA,MAClC;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,UAAU;AAAA,MAClB,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,sCAAsC;AAAA,QACtC,yCAAyC;AAAA,QACzC,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,UACtB;AAAA,UACA;AAAA,YACE,UAAU;AAAA,YACV,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}