@skapxd/eslint-opinionated 0.14.0 → 0.15.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
@@ -98,7 +98,7 @@ la que defiende el axioma más fundamental (el orden es jerárquico).
98
98
 
99
99
  | # | Axioma | Reglas que lo ejecutan |
100
100
  | --- | --- | --- |
101
- | A1 | **Los estados imposibles son irrepresentables.** El tipo modela exactamente los estados válidos; lo inválido no compila. | `prefer-tagged-union-state`, `no-runtime-state-guard`, `requires-strict-tsconfig`, `@typescript-eslint/no-unnecessary-condition`, `no-explicit-any`, `consistent-type-definitions` |
101
+ | A1 | **Los estados imposibles son irrepresentables.** El tipo modela exactamente los estados válidos; lo inválido no compila. | `prefer-tagged-union-state`, `no-runtime-state-guard`, `requires-strict-tsconfig`, `no-impossible-branch`, `no-explicit-any`, `consistent-type-definitions` |
102
102
  | A2 | **Ningún efecto es invisible al tipo.** Si una operación puede fallar, su firma lo confiesa — no una convención oral ni un `throw` sorpresa. | `await-requires-result`, `no-try-catch`, `no-promise-chain`, `no-ad-hoc-ok-result`, `@typescript-eslint/no-floating-promises` |
103
103
  | A3 | **La información no se destruye.** Un error que se transforma conserva su `cause`; uno que se detecta llega a alguien. Nadie decide "esto no importa" en silencio. | `result-error-requires-cause`, `result-error-requires-handling` |
104
104
  | A4 | **Una unidad, una responsabilidad, un nombre semántico.** El árbol de archivos cuenta una historia; una clase expone una intención. | `one-root-function-per-file`, `max-public-methods`, `no-default-export`, `jsx-return-name-pascal-case`, `max-hook-size` |
@@ -274,6 +274,149 @@ hay errores, sale con código `1` (apto para CI). Como acota por **archivo
274
274
  completo**, también dispara las reglas estructurales (p. ej.
275
275
  `one-root-function-per-file`), que un filtrado por línea se perdería.
276
276
 
277
+ ## Adopción en proyectos legacy: de `off` a `error`, por olas
278
+
279
+ El CLI de arriba acota **qué archivos** se juzgan. Este apartado acota **qué
280
+ reglas** — el camino para meter el preset completo en un proyecto legacy
281
+ escrito por humanos, sin que el primer `pnpm lint` escupa 2.000 errores y el
282
+ equipo apague el linter para siempre.
283
+
284
+ ### Las reglas del juego
285
+
286
+ 1. **`off` o `error`, nunca `warn`.** Un warn se ignora desde el día dos y
287
+ solo entrena al equipo a ignorar amarillo. Una regla está adoptada
288
+ (`error`) o todavía no (`off`) — no hay estado intermedio.
289
+ 2. **Una regla a la vez, y a cero.** Se activa una regla, se arreglan TODOS
290
+ sus hallazgos, se mergea en verde. Nunca actives una regla con pendientes:
291
+ el CI rojo permanente es la ventana rota que normaliza ignorar el linter.
292
+ 3. **Ratchet: lo que se enciende no se apaga.** El bloque de `off` solo puede
293
+ encoger. El diff de ese bloque ES la métrica de progreso del equipo.
294
+ 4. **Mide antes de activar.** En una rama, borra el `off` de una regla y corre
295
+ el lint: el número de hallazgos es el precio. **Activa siempre la más
296
+ barata pendiente** — el momentum importa más que el orden perfecto.
297
+ 5. **Deja que el mensaje enseñe.** Los mensajes de error de estas reglas
298
+ explican el porqué y el cómo (qué patrón usar, cómo se llama, dónde va).
299
+ Para un equipo sin seniors, el linter es el code review que nadie tiene
300
+ tiempo de hacer: no resumas las reglas en un documento aparte — el
301
+ documento es el error en pantalla.
302
+
303
+ ### El mecanismo: la lista de pendientes
304
+
305
+ El preset completo es la meta; un bloque posterior apaga lo que el equipo aún
306
+ no cumple. **Adoptar una regla = borrar su línea y arreglar lo que aflore:**
307
+
308
+ ```js
309
+ // eslint.config.js
310
+ import skapxd from "@skapxd/eslint-opinionated";
311
+
312
+ export default [
313
+ ...skapxd.configs.nest, // la meta: el preset completo, desde el día uno
314
+
315
+ // ─── Lista de pendientes ───────────────────────────────────────────
316
+ // Todo lo que el proyecto aún no cumple, apagado y a la vista.
317
+ // Este bloque SOLO ENCOGE: se borra una línea, se arregla, se mergea.
318
+ {
319
+ rules: {
320
+ "skapxd/await-requires-result": "off",
321
+ "skapxd/no-try-catch": "off",
322
+ // ...
323
+ },
324
+ },
325
+ ];
326
+ ```
327
+
328
+ ### El orden de las olas
329
+
330
+ El orden no es arbitrario: va de "cada hallazgo es un bug que ya tienes" hacia
331
+ "esto exige rediseñar tipos", y cada ola deja el suelo que la siguiente pisa.
332
+
333
+ **Ola 1 — bugs gratis y fixes únicos.** Señal pura, arreglo puntual, cero
334
+ rediseño. Aquí el equipo aprende que el linter encuentra cosas reales:
335
+
336
+ - `@typescript-eslint/no-floating-promises` — cada hallazgo es un error que
337
+ hoy muere sin que nadie lo vea (en un backend real en producción: 12).
338
+ - `skapxd/nest-requires-swagger-plugin` y `skapxd/nest-validation-pipe-config`
339
+ — un hallazgo por proyecto, un fix de configuración, y quedan vigiladas las
340
+ premisas de las olas siguientes.
341
+ - `skapxd/requires-strict-tsconfig` con la exigencia mínima:
342
+ `{ requiredCompilerOptions: ["strict"] }`. Es el trinquete del tsconfig —
343
+ cada ola le sube un flag (ver abajo).
344
+ - `skapxd/no-emoji`, `skapxd/no-deep-relative-imports` — fixes mecánicos.
345
+ - `skapxd/prefer-abort-signal` (front) — cada hallazgo es un leak.
346
+
347
+ **Ola 2 — la forma del código.** Refactors locales, archivo por archivo, sin
348
+ tocar contratos. Es la ola que más enseña por repetición:
349
+
350
+ - `skapxd/no-nested-if` y `skapxd/no-else` — guard clauses. El refactor más
351
+ formativo que existe para un junior: aplana la lógica o confiesa que la
352
+ función hace demasiado.
353
+ - `skapxd/one-root-function-per-file` y `skapxd/no-default-export` — el árbol
354
+ de archivos empieza a contar la historia.
355
+ - `skapxd/no-accessors`, `skapxd/max-public-methods` — clases con una
356
+ intención (partir un god-object es la cirugía mayor de esta ola: déjala de
357
+ última).
358
+ - Front: `skapxd/jsx-return-name-pascal-case`, `skapxd/max-hook-size`,
359
+ `skapxd/no-functions-inside-components`, `skapxd/no-jsx-ternary-null`,
360
+ `skapxd/no-tunnel-props`.
361
+ - Nest: `skapxd/nest-no-swagger-in-controllers`,
362
+ `skapxd/nest-dto-requires-api-property`,
363
+ `skapxd/nest-dto-requires-validation`,
364
+ `skapxd/nest-no-inline-query-params`,
365
+ `skapxd/nest-no-direct-instantiation` — mover decoradores y dependencias a
366
+ donde pertenecen.
367
+
368
+ **Ola 3 — el contrato de errores.** La migración de paradigma
369
+ (`@skapxd/result` + `ts-pattern`; ver "Cómo encaja todo" abajo). Aquí NO se va
370
+ regla por regla sino **módulo por módulo**: las seis reglas entran juntas
371
+ (son un solo sistema) pero acotadas por carpeta, y el primer módulo migrado se
372
+ vuelve el ejemplo canónico que el resto copia:
373
+
374
+ ```js
375
+ // Ola 3: el pipeline de Result entra carpeta por carpeta.
376
+ {
377
+ files: ["src/modules/payments/**"],
378
+ rules: {
379
+ "skapxd/await-requires-result": "error",
380
+ "skapxd/no-try-catch": "error",
381
+ "skapxd/no-promise-chain": "error",
382
+ "skapxd/no-ad-hoc-ok-result": "error",
383
+ "skapxd/prefer-ts-pattern": "error",
384
+ "skapxd/result-error-requires-cause": "error",
385
+ "skapxd/result-error-requires-handling": "error",
386
+ },
387
+ },
388
+ ```
389
+
390
+ (En Nest, suma `skapxd/nest-no-result-response` al grupo: el controller del
391
+ módulo migrado traduce el Result, no lo serializa.) Cuando todos los módulos
392
+ migraron, las líneas salen del bloque por-carpeta y entran globales: se borran
393
+ de la lista de pendientes.
394
+
395
+ **Ola 4 — el modelado de estados.** Lo más profundo: exige criterio de
396
+ diseño, no solo disciplina. Para cuando el equipo ya vio el patrón en la ola 3:
397
+
398
+ - `requires-strict-tsconfig` al máximo: `["strict", "noImplicitReturns",
399
+ "noUncheckedIndexedAccess"]`. Sube un flag a la vez — cada uno aflora
400
+ errores de compilación que son bugs latentes, no burocracia.
401
+ - `@typescript-eslint/no-explicit-any`, `no-non-null-assertion` y
402
+ `ban-ts-comment` — se cierran las tres puertas de escape del compilador.
403
+ - `skapxd/class-properties-require-readonly` — el cambio se modela con
404
+ instancias nuevas.
405
+ - `skapxd/prefer-tagged-union-state` y `skapxd/no-runtime-state-guard` — los
406
+ booleanos co-dependientes se vuelven uniones etiquetadas.
407
+ - `skapxd/no-impossible-branch` — **la última de todas**: solo es sólida
408
+ cuando el tsconfig ya está al máximo (sin `noUncheckedIndexedAccess`,
409
+ acusaría guards necesarios).
410
+
411
+ ### Los dos ejes se combinan
412
+
413
+ Mientras la lista de pendientes encoge, `skapxd-lint-changed` aplica lo ya
414
+ activado solo a los archivos tocados: el código nuevo nace cumpliendo y el
415
+ legacy se corrige cuando alguien lo visita (regla del boy scout), no en un
416
+ big-bang. Un proyecto mediano recorre las cuatro olas en semanas, no en
417
+ trimestres — y cada semana el lint encuentra menos, porque el equipo ya
418
+ escribe distinto.
419
+
277
420
  ## Cómo encaja todo: `@skapxd/result` + `ts-pattern`
278
421
 
279
422
  Este plugin no es una colección de reglas sueltas: es el guardián de un
@@ -676,6 +819,7 @@ de cada regla):
676
819
  | `no-default-export` | `allowFilePatterns` (globs, aditivos a los integrados) |
677
820
  | `no-else` | `allowFilePatterns` (globs) |
678
821
  | `no-emoji` | `allowFilePatterns` (globs) |
822
+ | `no-impossible-branch` | las de la regla original de typescript-eslint (`allowConstantLoopConditions`, ...) |
679
823
  | `no-functions-inside-components` | `allowJsxCallbacks`, `allowArrayMapCallbacks` (ambas `true` por defecto) |
680
824
  | `no-nested-if` | `allowFilePatterns` (globs) |
681
825
  | `no-promise-chain` | `methods` |
@@ -719,6 +863,7 @@ matchea en cualquier carpeta). Las 7 reglas restantes no tienen opciones: su
719
863
  | `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. |
720
864
  | `skapxd/no-else` | Prohíbe `else`/`else if`: el else es el estado sin nombre. Retorno anticipado, ternario simple o `match()`. |
721
865
  | `skapxd/no-emoji` | Prohíbe emojis en strings y JSX; cada sistema los renderiza distinto. Usa un icono SVG. |
866
+ | `skapxd/no-impossible-branch` | Condiciones que el type-checker demuestra constantes: la pregunta ya tiene respuesta. Es `@typescript-eslint/no-unnecessary-condition` con nombre semántico y mensajes que enseñan el fix. |
722
867
  | `skapxd/no-nested-if` | Prohíbe `if` anidados: retorno anticipado o `match()`. Menos carga cognitiva y sin puntos ciegos para las demás reglas. |
723
868
  | `skapxd/no-runtime-state-guard` | Prohíbe `if (this.x) throw` en métodos: el estado inválido se hace irrepresentable en el tipo, no se vigila en runtime. |
724
869
  | `skapxd/no-tunnel-props` | Ninguna prop viaja más de un nivel: quien la recibe no puede reenviarla a otro componente. Mata el prop drilling. |
@@ -831,8 +976,12 @@ Todas las opciones, con sus defaults:
831
976
  Todo el sistema descansa en que el compilador pueda hacer irrepresentables
832
977
  los estados inválidos — y eso exige un `tsconfig` implacable. Esta regla lee
833
978
  el `tsconfig.json` **real** del proyecto (con la API de TypeScript: soporta
834
- JSONC y resuelve la cadena de `extends`) y exige los flags, anclada a un
835
- entrypoint para reportar una vez por proyecto:
979
+ JSONC y resuelve la cadena de `extends`) y exige los flags, reportando una
980
+ vez por proyecto: si existe un archivo ancla (`anchorFilePatterns`, default
981
+ `src/main.ts(x)`/`src/index.ts(x)`), el reporte le pertenece a ese archivo;
982
+ si el proyecto no tiene entrypoint clásico (Astro, librerías), reporta sobre
983
+ el primer archivo del run y los demás callan — un proyecto sin ancla no se
984
+ queda sin guardián:
836
985
 
837
986
  - `strict` — sin él, el sistema de tipos está apagado a medias.
838
987
  - `noImplicitReturns` — una rama que sale sin valor deja de ser silenciosa
@@ -862,12 +1011,14 @@ Además, los **presets tipados activan reglas curadas de typescript-eslint**
862
1011
  que tú" dicho al compilador. Si no puede ser nulo, que lo diga el tipo.
863
1012
  (En `nest/tests` queda apagada: el `!` sobre un fixture cuya existencia el
864
1013
  propio test garantiza es el arrange, no una mentira.)
865
- - `@typescript-eslint/no-unnecessary-condition` — la generalización
866
- type-aware de `no-runtime-state-guard`: si el tipo dice que un estado es
867
- imposible, el guard defensivo sobra; si el guard hace falta, lo que está
868
- mal es el tipo. Por eso esta regla y `requires-strict-tsconfig` van
869
- juntas: sin `noUncheckedIndexedAccess`, `array[i]` miente y la regla
870
- acusaría guards necesarios.
1014
+ - `skapxd/no-impossible-branch` — la generalización type-aware de
1015
+ `no-runtime-state-guard`: si el tipo dice que un estado es imposible, el
1016
+ guard defensivo sobra; si el guard hace falta, lo que está mal es el tipo.
1017
+ Es `@typescript-eslint/no-unnecessary-condition` **re-registrada bajo
1018
+ nuestro namespace** (ver su sección): mismo motor, nombre que dice lo que
1019
+ defiende y mensajes que enseñan el fix. Por eso esta regla y
1020
+ `requires-strict-tsconfig` van juntas: sin `noUncheckedIndexedAccess`,
1021
+ `array[i]` miente y la regla acusaría guards necesarios.
871
1022
  - `@typescript-eslint/ban-ts-comment` — un error de tipos se arregla
872
1023
  modelando mejor, no silenciando la alarma: `@ts-ignore` y `@ts-nocheck`
873
1024
  prohibidos. `@ts-expect-error` **con descripción** queda permitido: es la
@@ -1726,6 +1877,36 @@ en un `<input>` es la frontera con el DOM). Para wrappers legítimos de un
1726
1877
  design system, exime props por nombre (`allowPropPatterns: ["^className$"]`)
1727
1878
  o archivos completos (`allowFilePatterns`).
1728
1879
 
1880
+ ### `skapxd/no-impossible-branch`
1881
+
1882
+ La rama imposible: una condición que el type-checker demuestra constante. Si
1883
+ el tipo dice que un valor siempre es truthy, ese `if` no decide nada; si un
1884
+ `?.` cuelga de algo que nunca es nullish, finge una duda que el modelo ya
1885
+ resolvió. La pregunta ya tiene respuesta — y un `if` que no pregunta es
1886
+ código muerto disfrazado de prudencia.
1887
+
1888
+ ```ts
1889
+ const sheet = workbook.Sheets[name]; // tipo: WorkSheet (¿seguro?)
1890
+ if (!sheet) continue; // ❌ "always falsy"... ¿o el tipo miente?
1891
+ ```
1892
+
1893
+ El mensaje de error enseña la lección completa: **si la comprobación hace
1894
+ falta en runtime, lo que está mal es el tipo**. El caso clásico es el acceso
1895
+ por índice sin `noUncheckedIndexedAccess` — `array[i]` y `obj[key]` juran que
1896
+ nunca son `undefined`, y esta regla, creyéndoles, acusaría guards necesarios.
1897
+ Por eso va de la mano de `skapxd/requires-strict-tsconfig`, que exige ese
1898
+ flag: primero el tsconfig dice la verdad, después esta regla opina.
1899
+
1900
+ Bajo el capó es `@typescript-eslint/no-unnecessary-condition`
1901
+ ([doc original](https://typescript-eslint.io/rules/no-unnecessary-condition/))
1902
+ **re-registrada bajo nuestro namespace**: mismo motor y mismas opciones, pero
1903
+ con un nombre que dice lo que defiende (axioma A1: los estados imposibles son
1904
+ irrepresentables — es la generalización type-aware de
1905
+ `no-runtime-state-guard`) y mensajes en español que explican el fix en vez
1906
+ del críptico "Unnecessary conditional". Los presets tipados activan este
1907
+ nombre y **no** el original: una sola fuente de verdad para configurarla,
1908
+ silenciarla o buscarla.
1909
+
1729
1910
  ### `skapxd/no-functions-inside-components`
1730
1911
 
1731
1912
  Prohíbe definir funciones **con peso propio** dentro de un componente React
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAstroConfigs
3
- } from "../chunk-C5JP4FQN.mjs";
4
- import "../chunk-QR35MHTW.mjs";
3
+ } from "../chunk-DJDXQWCP.mjs";
4
+ import "../chunk-F6GJW5A4.mjs";
5
5
  export {
6
6
  createAstroConfigs
7
7
  };
@@ -2,7 +2,7 @@ import {
2
2
  baseRules,
3
3
  createBaseLanguageOptions,
4
4
  createTypedLanguageOptions
5
- } from "./chunk-QR35MHTW.mjs";
5
+ } from "./chunk-F6GJW5A4.mjs";
6
6
 
7
7
  // src/astro/configs.ts
8
8
  function createAstroConfigs(pluginReference) {
@@ -41,4 +41,4 @@ function createAstroConfigs(pluginReference) {
41
41
  export {
42
42
  createAstroConfigs
43
43
  };
44
- //# sourceMappingURL=chunk-C5JP4FQN.mjs.map
44
+ //# sourceMappingURL=chunk-DJDXQWCP.mjs.map
@@ -78,7 +78,9 @@ var typeDrivenRules = {
78
78
  // hace falta, lo que está mal es el tipo. Requiere el tsconfig de
79
79
  // requires-strict-tsconfig para ser sólida: sin noUncheckedIndexedAccess,
80
80
  // `array[i]` miente y esta regla acusaría guards necesarios.
81
- "@typescript-eslint/no-unnecessary-condition": "error"
81
+ // Es @typescript-eslint/no-unnecessary-condition re-registrada bajo un
82
+ // nombre que dice lo que defiende, con mensajes que enseñan el fix.
83
+ "skapxd/no-impossible-branch": "error"
82
84
  };
83
85
 
84
86
  // src/shared/configs/create-shared-configs.ts
@@ -172,4 +174,4 @@ export {
172
174
  createSharedConfigs,
173
175
  strictConfig
174
176
  };
175
- //# sourceMappingURL=chunk-QR35MHTW.mjs.map
177
+ //# sourceMappingURL=chunk-F6GJW5A4.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/configs/create-typed-language-options.ts","../src/shared/configs/create-shared-configs.ts","../src/shared/configs/base-rules.ts","../src/shared/configs/create-base-language-options.ts","../src/shared/configs/type-driven-rules.ts","../src/shared/configs/strict-config.ts"],"sourcesContent":["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 tseslint from \"typescript-eslint\";\nimport { baseRules } from \"./base-rules\";\nimport { createBaseLanguageOptions } from \"./create-base-language-options\";\nimport { createTypedLanguageOptions } from \"./create-typed-language-options\";\nimport { typeDrivenRules } from \"./type-driven-rules\";\n\nexport function createSharedConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return {\n backend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/backend\",\n plugins: {\n \"@typescript-eslint\": tseslint.plugin,\n skapxd: pluginReference,\n },\n rules: {\n ...baseRules,\n ...typeDrivenRules,\n // La regla obligatoria del sistema de errores es await-requires-result\n // (todo await resuelve en Result). async-functions-return-result queda\n // apagada por defecto: exigir la firma por decreto choca con los bordes\n // del framework y bloquea la adopción incremental; la presión sobre los\n // awaits produce el mismo estado final. Ver \"¿Por qué está apagada por\n // defecto?\" en el README.\n \"skapxd/await-requires-result\": \"error\",\n },\n },\n base: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n frontend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/frontend\",\n plugins: {\n \"@typescript-eslint\": tseslint.plugin,\n skapxd: pluginReference,\n },\n rules: {\n ...baseRules,\n ...typeDrivenRules,\n // En el front no se obliga a retornar Result, pero todo await debe\n // resolver en uno: o la función llamada ya retorna Promise<Result>\n // (camino preferido: errores modelados en el dominio) o se envuelve\n // en trySafe en el sitio.\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/jsx-return-name-pascal-case\": \"error\",\n // Anti prop-drilling: ninguna prop viaja más de un nivel. Quien la\n // crea puede pasarla a UN hijo; quien la recibe no la reenvía —\n // estado y acciones a un store global o custom hook.\n \"skapxd/no-functions-inside-components\": \"error\",\n \"skapxd/no-tunnel-props\": \"error\",\n // Listeners en efectos: un AbortController por efecto, cleanup con\n // un solo abort() en vez de removeEventListener por listener.\n \"skapxd/prefer-abort-signal\": \"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 package: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/package\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/one-root-function-per-file\": \"error\",\n },\n },\n };\n}\n","export const baseRules = {\n \"skapxd/class-properties-require-readonly\": \"error\",\n \"skapxd/max-public-methods\": \"error\",\n \"skapxd/no-accessors\": \"error\",\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-else\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-nested-if\": \"error\",\n \"skapxd/no-runtime-state-guard\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-tagged-union-state\": \"error\",\n \"skapxd/requires-strict-tsconfig\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"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","// Reglas de typescript-eslint que el diseño guiado por tipos exige y que no\n// tiene sentido reimplementar — typescript-eslint ya es peer dependency.\n//\n// Ausencias deliberadas (no son olvidos):\n// - switch-exhaustiveness-check: prefer-ts-pattern prohíbe el switch entero;\n// match().exhaustive() da la misma garantía sin él.\n// - prefer-readonly: superada por class-properties-require-readonly, que\n// exige readonly en la declaración (no solo en privados nunca reasignados).\n// - strict-boolean-expressions: castiga narrowing legítimo por cientos\n// (560 hallazgos en un backend real) sin hacer irrepresentable ningún\n// estado nuevo. Ruido, no señal.\n// - explicit-module-boundary-types: los contratos que importan ya están\n// gobernados (Result en await-requires-result, respuestas de controller en\n// nest-no-result-response); anotar todo lo demás es ceremonia (198\n// hallazgos) que la inferencia resuelve sin perder garantías.\n// - prefer-readonly-parameter-types: impracticable con cualquier parámetro\n// que venga de una librería externa.\nexport const typeDrivenRules = {\n // Silenciar la alarma no arregla el incendio: un error de tipos se\n // resuelve modelando mejor, no apagando el compilador. @ts-expect-error\n // queda permitido CON descripción: es la forma legítima de testear que un\n // estado inválido de verdad no compila.\n \"@typescript-eslint/ban-ts-comment\": [\n \"error\",\n {\n \"ts-expect-error\": \"allow-with-description\",\n \"ts-ignore\": true,\n \"ts-nocheck\": true,\n },\n ],\n // `type` en vez de `interface`: las uniones discriminadas son types, y la\n // homogeneidad evita el \"¿esto se puede extender por declaration merging?\"\n \"@typescript-eslint/consistent-type-definitions\": [\"error\", \"type\"],\n // `any` apaga el sistema de tipos: todo el esfuerzo de modelar estados\n // irrepresentables muere donde aparece uno.\n \"@typescript-eslint/no-explicit-any\": \"error\",\n // El hueco que await-requires-result no ve: una llamada async SIN await no\n // produce AwaitExpression — el rechazo muere sin pasar por trySafe. Esta\n // regla obliga a awaitear (y ahí entra el pipeline de Result). El operador\n // `void promesa` queda como única salida: fire-and-forget declarado y\n // greppeable, no interpretado.\n \"@typescript-eslint/no-floating-promises\": \"error\",\n // `!` es \"cállate, yo sé más que tú\" dicho al compilador. Si el valor no\n // puede ser nulo, que lo diga el tipo; si puede serlo, hay que modelarlo.\n \"@typescript-eslint/no-non-null-assertion\": \"error\",\n // La generalización type-aware de no-runtime-state-guard: si el tipo dice\n // que un estado es imposible, el guard defensivo sobra — y si el guard\n // hace falta, lo que está mal es el tipo. Requiere el tsconfig de\n // requires-strict-tsconfig para ser sólida: sin noUncheckedIndexedAccess,\n // `array[i]` miente y esta regla acusaría guards necesarios.\n \"@typescript-eslint/no-unnecessary-condition\": \"error\",\n};\n","// Config endurecida: ignora TODOS los comentarios `eslint-disable` (y demás\n// directivas inline) en los archivos que cubre. Así ni una persona ni un agente\n// pueden saltarse una regla con `// eslint-disable-next-line`.\nexport const strictConfig = {\n linterOptions: {\n noInlineConfig: true,\n },\n name: \"skapxd/strict\",\n};\n"],"mappings":";AAAA,OAAO,cAAc;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,SAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACXA,OAAOA,eAAc;;;ACAd,IAAM,YAAY;AAAA,EACvB,4CAA4C;AAAA,EAC5C,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,iCAAiC;AAAA,EACjC,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,oCAAoC;AAAA,EACpC,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,sCAAsC;AAAA,EACtC,yCAAyC;AAC3C;;;ACnBA,OAAOC,eAAc;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQA,UAAS;AAAA,EACnB;AACF;;;ACSO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,qCAAqC;AAAA,IACnC;AAAA,IACA;AAAA,MACE,mBAAmB;AAAA,MACnB,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,kDAAkD,CAAC,SAAS,MAAM;AAAA;AAAA;AAAA,EAGlE,sCAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,2CAA2C;AAAA;AAAA;AAAA,EAG3C,4CAA4C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5C,+CAA+C;AACjD;;;AH7CO,SAAS,oBAAoB,iBAA0B;AAC5D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,sBAAsBC,UAAS;AAAA,QAC/B,QAAQ;AAAA,MACV;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOH,gCAAgC;AAAA,MAClC;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,sBAAsBA,UAAS;AAAA,QAC/B,QAAQ;AAAA,MACV;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,gCAAgC;AAAA,QAChC,sCAAsC;AAAA;AAAA;AAAA;AAAA,QAItC,yCAAyC;AAAA,QACzC,0BAA0B;AAAA;AAAA;AAAA,QAG1B,8BAA8B;AAAA,QAC9B,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,UACtB;AAAA,UACA;AAAA,YACE,UAAU;AAAA,YACV,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,qCAAqC;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACF;;;AI5EO,IAAM,eAAe;AAAA,EAC1B,eAAe;AAAA,IACb,gBAAgB;AAAA,EAClB;AAAA,EACA,MAAM;AACR;","names":["tseslint","tseslint","tseslint"]}
1
+ {"version":3,"sources":["../src/shared/configs/create-typed-language-options.ts","../src/shared/configs/create-shared-configs.ts","../src/shared/configs/base-rules.ts","../src/shared/configs/create-base-language-options.ts","../src/shared/configs/type-driven-rules.ts","../src/shared/configs/strict-config.ts"],"sourcesContent":["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 tseslint from \"typescript-eslint\";\nimport { baseRules } from \"./base-rules\";\nimport { createBaseLanguageOptions } from \"./create-base-language-options\";\nimport { createTypedLanguageOptions } from \"./create-typed-language-options\";\nimport { typeDrivenRules } from \"./type-driven-rules\";\n\nexport function createSharedConfigs(pluginReference: unknown) {\n const baseLanguageOptions = createBaseLanguageOptions();\n const typedLanguageOptions = createTypedLanguageOptions();\n\n return {\n backend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/backend\",\n plugins: {\n \"@typescript-eslint\": tseslint.plugin,\n skapxd: pluginReference,\n },\n rules: {\n ...baseRules,\n ...typeDrivenRules,\n // La regla obligatoria del sistema de errores es await-requires-result\n // (todo await resuelve en Result). async-functions-return-result queda\n // apagada por defecto: exigir la firma por decreto choca con los bordes\n // del framework y bloquea la adopción incremental; la presión sobre los\n // awaits produce el mismo estado final. Ver \"¿Por qué está apagada por\n // defecto?\" en el README.\n \"skapxd/await-requires-result\": \"error\",\n },\n },\n base: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/base\",\n plugins: { skapxd: pluginReference },\n rules: baseRules,\n },\n frontend: {\n languageOptions: typedLanguageOptions,\n name: \"skapxd/shared/frontend\",\n plugins: {\n \"@typescript-eslint\": tseslint.plugin,\n skapxd: pluginReference,\n },\n rules: {\n ...baseRules,\n ...typeDrivenRules,\n // En el front no se obliga a retornar Result, pero todo await debe\n // resolver en uno: o la función llamada ya retorna Promise<Result>\n // (camino preferido: errores modelados en el dominio) o se envuelve\n // en trySafe en el sitio.\n \"skapxd/await-requires-result\": \"error\",\n \"skapxd/jsx-return-name-pascal-case\": \"error\",\n // Anti prop-drilling: ninguna prop viaja más de un nivel. Quien la\n // crea puede pasarla a UN hijo; quien la recibe no la reenvía —\n // estado y acciones a un store global o custom hook.\n \"skapxd/no-functions-inside-components\": \"error\",\n \"skapxd/no-tunnel-props\": \"error\",\n // Listeners en efectos: un AbortController por efecto, cleanup con\n // un solo abort() en vez de removeEventListener por listener.\n \"skapxd/prefer-abort-signal\": \"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 package: {\n languageOptions: baseLanguageOptions,\n name: \"skapxd/shared/package\",\n plugins: { skapxd: pluginReference },\n rules: {\n \"skapxd/one-root-function-per-file\": \"error\",\n },\n },\n };\n}\n","export const baseRules = {\n \"skapxd/class-properties-require-readonly\": \"error\",\n \"skapxd/max-public-methods\": \"error\",\n \"skapxd/no-accessors\": \"error\",\n \"skapxd/no-ad-hoc-ok-result\": \"error\",\n \"skapxd/no-deep-relative-imports\": \"error\",\n \"skapxd/no-default-export\": \"error\",\n \"skapxd/no-else\": \"error\",\n \"skapxd/no-emoji\": \"error\",\n \"skapxd/no-nested-if\": \"error\",\n \"skapxd/no-runtime-state-guard\": \"error\",\n \"skapxd/no-promise-chain\": \"error\",\n \"skapxd/no-try-catch\": \"error\",\n \"skapxd/one-root-function-per-file\": \"error\",\n \"skapxd/prefer-tagged-union-state\": \"error\",\n \"skapxd/requires-strict-tsconfig\": \"error\",\n \"skapxd/prefer-ts-pattern\": \"error\",\n \"skapxd/result-error-requires-cause\": \"error\",\n \"skapxd/result-error-requires-handling\": \"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","// Reglas de typescript-eslint que el diseño guiado por tipos exige y que no\n// tiene sentido reimplementar — typescript-eslint ya es peer dependency.\n//\n// Ausencias deliberadas (no son olvidos):\n// - switch-exhaustiveness-check: prefer-ts-pattern prohíbe el switch entero;\n// match().exhaustive() da la misma garantía sin él.\n// - prefer-readonly: superada por class-properties-require-readonly, que\n// exige readonly en la declaración (no solo en privados nunca reasignados).\n// - strict-boolean-expressions: castiga narrowing legítimo por cientos\n// (560 hallazgos en un backend real) sin hacer irrepresentable ningún\n// estado nuevo. Ruido, no señal.\n// - explicit-module-boundary-types: los contratos que importan ya están\n// gobernados (Result en await-requires-result, respuestas de controller en\n// nest-no-result-response); anotar todo lo demás es ceremonia (198\n// hallazgos) que la inferencia resuelve sin perder garantías.\n// - prefer-readonly-parameter-types: impracticable con cualquier parámetro\n// que venga de una librería externa.\nexport const typeDrivenRules = {\n // Silenciar la alarma no arregla el incendio: un error de tipos se\n // resuelve modelando mejor, no apagando el compilador. @ts-expect-error\n // queda permitido CON descripción: es la forma legítima de testear que un\n // estado inválido de verdad no compila.\n \"@typescript-eslint/ban-ts-comment\": [\n \"error\",\n {\n \"ts-expect-error\": \"allow-with-description\",\n \"ts-ignore\": true,\n \"ts-nocheck\": true,\n },\n ],\n // `type` en vez de `interface`: las uniones discriminadas son types, y la\n // homogeneidad evita el \"¿esto se puede extender por declaration merging?\"\n \"@typescript-eslint/consistent-type-definitions\": [\"error\", \"type\"],\n // `any` apaga el sistema de tipos: todo el esfuerzo de modelar estados\n // irrepresentables muere donde aparece uno.\n \"@typescript-eslint/no-explicit-any\": \"error\",\n // El hueco que await-requires-result no ve: una llamada async SIN await no\n // produce AwaitExpression — el rechazo muere sin pasar por trySafe. Esta\n // regla obliga a awaitear (y ahí entra el pipeline de Result). El operador\n // `void promesa` queda como única salida: fire-and-forget declarado y\n // greppeable, no interpretado.\n \"@typescript-eslint/no-floating-promises\": \"error\",\n // `!` es \"cállate, yo sé más que tú\" dicho al compilador. Si el valor no\n // puede ser nulo, que lo diga el tipo; si puede serlo, hay que modelarlo.\n \"@typescript-eslint/no-non-null-assertion\": \"error\",\n // La generalización type-aware de no-runtime-state-guard: si el tipo dice\n // que un estado es imposible, el guard defensivo sobra — y si el guard\n // hace falta, lo que está mal es el tipo. Requiere el tsconfig de\n // requires-strict-tsconfig para ser sólida: sin noUncheckedIndexedAccess,\n // `array[i]` miente y esta regla acusaría guards necesarios.\n // Es @typescript-eslint/no-unnecessary-condition re-registrada bajo un\n // nombre que dice lo que defiende, con mensajes que enseñan el fix.\n \"skapxd/no-impossible-branch\": \"error\",\n};\n","// Config endurecida: ignora TODOS los comentarios `eslint-disable` (y demás\n// directivas inline) en los archivos que cubre. Así ni una persona ni un agente\n// pueden saltarse una regla con `// eslint-disable-next-line`.\nexport const strictConfig = {\n linterOptions: {\n noInlineConfig: true,\n },\n name: \"skapxd/strict\",\n};\n"],"mappings":";AAAA,OAAO,cAAc;AAEd,SAAS,6BAA6B;AAC3C,SAAO;AAAA;AAAA;AAAA,IAGL,QAAQ,SAAS;AAAA,IACjB,eAAe;AAAA,MACb,gBAAgB;AAAA,IAClB;AAAA,EACF;AACF;;;ACXA,OAAOA,eAAc;;;ACAd,IAAM,YAAY;AAAA,EACvB,4CAA4C;AAAA,EAC5C,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,iCAAiC;AAAA,EACjC,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,oCAAoC;AAAA,EACpC,mCAAmC;AAAA,EACnC,4BAA4B;AAAA,EAC5B,sCAAsC;AAAA,EACtC,yCAAyC;AAC3C;;;ACnBA,OAAOC,eAAc;AAId,SAAS,4BAA4B;AAC1C,SAAO;AAAA,IACL,QAAQA,UAAS;AAAA,EACnB;AACF;;;ACSO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,qCAAqC;AAAA,IACnC;AAAA,IACA;AAAA,MACE,mBAAmB;AAAA,MACnB,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,kDAAkD,CAAC,SAAS,MAAM;AAAA;AAAA;AAAA,EAGlE,sCAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,2CAA2C;AAAA;AAAA;AAAA,EAG3C,4CAA4C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,+BAA+B;AACjC;;;AH/CO,SAAS,oBAAoB,iBAA0B;AAC5D,QAAM,sBAAsB,0BAA0B;AACtD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,sBAAsBC,UAAS;AAAA,QAC/B,QAAQ;AAAA,MACV;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOH,gCAAgC;AAAA,MAClC;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,IACT;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,sBAAsBA,UAAS;AAAA,QAC/B,QAAQ;AAAA,MACV;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,gCAAgC;AAAA,QAChC,sCAAsC;AAAA;AAAA;AAAA;AAAA,QAItC,yCAAyC;AAAA,QACzC,0BAA0B;AAAA;AAAA;AAAA,QAG1B,8BAA8B;AAAA,QAC9B,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,UACtB;AAAA,UACA;AAAA,YACE,UAAU;AAAA,YACV,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,EAAE,QAAQ,gBAAgB;AAAA,MACnC,OAAO;AAAA,QACL,qCAAqC;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACF;;;AI5EO,IAAM,eAAe;AAAA,EAC1B,eAAe;AAAA,IACb,gBAAgB;AAAA,EAClB;AAAA,EACA,MAAM;AACR;","names":["tseslint","tseslint","tseslint"]}
@@ -2759,6 +2759,33 @@ var noEmoji = {
2759
2759
  }
2760
2760
  };
2761
2761
 
2762
+ // src/rules/no-impossible-branch.ts
2763
+ import tseslint from "typescript-eslint";
2764
+ var original = tseslint.plugin.rules["no-unnecessary-condition"];
2765
+ var elTipoMiente = " Si la comprobacion hace falta en runtime, el tipo esta mintiendo: arregla el tipo, no borres el guard a ciegas (acceso por indice tipo `array[i]` u `obj[key]`? te falta `noUncheckedIndexedAccess` en el tsconfig \u2014 `skapxd/requires-strict-tsconfig` lo exige).";
2766
+ var noImpossibleBranch = {
2767
+ ...original,
2768
+ meta: {
2769
+ ...original.meta,
2770
+ docs: {
2771
+ ...original.meta.docs,
2772
+ description: "La rama imposible: condiciones que el type-checker demuestra constantes. Si el tipo dice que no puede pasar, la rama sobra; si la rama hace falta, lo que esta mal es el tipo."
2773
+ },
2774
+ messages: {
2775
+ ...original.meta.messages,
2776
+ alwaysFalsy: "Rama imposible: el tipo dice que este valor SIEMPRE es falsy \u2014 el camino positivo de esta condicion es codigo muerto." + elTipoMiente,
2777
+ alwaysNullish: "Rama imposible: el tipo dice que el lado izquierdo del `??` SIEMPRE es null/undefined \u2014 la expresion es solo su fallback." + elTipoMiente,
2778
+ alwaysTruthy: "Pregunta ya respondida: el tipo dice que este valor SIEMPRE es truthy \u2014 la condicion solo tiene un camino posible." + elTipoMiente,
2779
+ comparisonBetweenLiteralTypes: "Comparacion constante: `{{left}} {{operator}} {{right}}` siempre es {{trueOrFalse}} segun los tipos \u2014 esta decision no decide nada." + elTipoMiente,
2780
+ never: "Rama imposible: este valor es de tipo `never` \u2014 segun el modelo, este punto del codigo es inalcanzable." + elTipoMiente,
2781
+ neverNullish: "El `??` no aporta: el tipo dice que el lado izquierdo NUNCA es null/undefined \u2014 el fallback es codigo muerto." + elTipoMiente,
2782
+ neverOptionalChain: "El `?.` sobra: el tipo dice que este valor nunca es nullish \u2014 el encadenamiento opcional finge una duda que el modelo ya resolvio." + elTipoMiente,
2783
+ noOverlapBooleanExpression: "Comparacion imposible: los tipos de ambos lados no se solapan, asi que el resultado siempre es el mismo (ej. comparar con `null` un valor cuyo tipo nunca incluye `null`)." + elTipoMiente,
2784
+ noStrictNullCheck: "Esta regla necesita `strictNullChecks` (viene con `strict: true`): sin el, los tipos mienten sobre null/undefined y la regla acusaria guards necesarios. `skapxd/requires-strict-tsconfig` vigila esa premisa."
2785
+ }
2786
+ }
2787
+ };
2788
+
2762
2789
  // src/utils/get-no-tunnel-props-options.ts
2763
2790
  function getNoTunnelPropsOptions(options = {}) {
2764
2791
  return {
@@ -3323,7 +3350,7 @@ var preferTaggedUnionState = {
3323
3350
  };
3324
3351
 
3325
3352
  // src/rules/requires-strict-tsconfig.ts
3326
- import { dirname as dirname4, resolve as resolve2 } from "path";
3353
+ import { dirname as dirname5, resolve as resolve2 } from "path";
3327
3354
 
3328
3355
  // src/utils/get-strict-tsconfig-options.ts
3329
3356
  function getStrictTsconfigOptions(options = {}) {
@@ -3349,8 +3376,38 @@ function getStrictTsconfigOptions(options = {}) {
3349
3376
  };
3350
3377
  }
3351
3378
 
3352
- // src/utils/read-resolved-tsconfig.ts
3379
+ // src/utils/is-anchorless-check-redundant.ts
3353
3380
  import { dirname as dirname3 } from "path";
3381
+
3382
+ // src/utils/anchor-file-exists.ts
3383
+ import { existsSync as existsSync2 } from "fs";
3384
+ import { join as join2 } from "path";
3385
+ function anchorFileExists(rootDir, anchorFilePatterns) {
3386
+ return anchorFilePatterns.some((pattern) => {
3387
+ const literal = pattern.replace(/^\*\*\//, "");
3388
+ if (literal.includes("*") || literal.includes("{")) {
3389
+ return false;
3390
+ }
3391
+ return existsSync2(join2(rootDir, literal));
3392
+ });
3393
+ }
3394
+
3395
+ // src/utils/is-anchorless-check-redundant.ts
3396
+ var checkedRoots = /* @__PURE__ */ new Set();
3397
+ function isAnchorlessCheckRedundant(tsconfigPath, fallbackRootKey, anchorFilePatterns) {
3398
+ const rootKey = tsconfigPath ?? fallbackRootKey;
3399
+ if (checkedRoots.has(rootKey)) {
3400
+ return true;
3401
+ }
3402
+ checkedRoots.add(rootKey);
3403
+ if (!tsconfigPath) {
3404
+ return false;
3405
+ }
3406
+ return anchorFileExists(dirname3(tsconfigPath), anchorFilePatterns);
3407
+ }
3408
+
3409
+ // src/utils/read-resolved-tsconfig.ts
3410
+ import { dirname as dirname4 } from "path";
3354
3411
  import ts3 from "typescript";
3355
3412
  import { trySafe as trySafe3 } from "@skapxd/result";
3356
3413
  function readResolvedTsconfig(tsconfigPath) {
@@ -3362,7 +3419,7 @@ function readResolvedTsconfig(tsconfigPath) {
3362
3419
  return ts3.parseJsonConfigFileContent(
3363
3420
  raw.config,
3364
3421
  ts3.sys,
3365
- dirname3(tsconfigPath)
3422
+ dirname4(tsconfigPath)
3366
3423
  );
3367
3424
  });
3368
3425
  if (!parsed.ok || !parsed.value) {
@@ -3406,16 +3463,24 @@ var requiresStrictTsconfig = {
3406
3463
  create(context) {
3407
3464
  const options = getStrictTsconfigOptions(context.options[0]);
3408
3465
  const filename = context.filename ?? context.getFilename();
3409
- if (matchesAnyGlob(filename, options.allowFilePatterns) || !matchesAnyGlob(filename, options.anchorFilePatterns)) {
3466
+ if (matchesAnyGlob(filename, options.allowFilePatterns)) {
3410
3467
  return {};
3411
3468
  }
3469
+ const isAnchor = matchesAnyGlob(filename, options.anchorFilePatterns);
3412
3470
  return {
3413
3471
  Program(node) {
3414
3472
  const absoluteFilename = resolve2(context.cwd ?? process.cwd(), filename);
3415
3473
  const tsconfigPath = findProjectFile(
3416
- dirname4(absoluteFilename),
3474
+ dirname5(absoluteFilename),
3417
3475
  "tsconfig.json"
3418
3476
  );
3477
+ if (!isAnchor && isAnchorlessCheckRedundant(
3478
+ tsconfigPath,
3479
+ context.cwd ?? process.cwd(),
3480
+ options.anchorFilePatterns
3481
+ )) {
3482
+ return;
3483
+ }
3419
3484
  const compilerOptions = tsconfigPath ? readResolvedTsconfig(tsconfigPath) : null;
3420
3485
  if (!compilerOptions) {
3421
3486
  context.report({ messageId: "missingTsconfig", node });
@@ -3741,6 +3806,9 @@ var rules = {
3741
3806
  "no-default-export": noDefaultExport,
3742
3807
  "no-else": noElse,
3743
3808
  "no-emoji": noEmoji,
3809
+ // Re-registro de @typescript-eslint/no-unnecessary-condition con nombre
3810
+ // semántico y mensajes propios (ver src/rules/no-impossible-branch.ts).
3811
+ "no-impossible-branch": noImpossibleBranch,
3744
3812
  "no-tunnel-props": noTunnelProps,
3745
3813
  "no-functions-inside-components": noFunctionsInsideComponents,
3746
3814
  "no-try-catch": noTryCatch,
@@ -3757,4 +3825,4 @@ var rules = {
3757
3825
  export {
3758
3826
  rules
3759
3827
  };
3760
- //# sourceMappingURL=chunk-YUKVSZZ4.mjs.map
3828
+ //# sourceMappingURL=chunk-MLFXSEY7.mjs.map