@skapxd/eslint-opinionated 0.10.0 → 0.11.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
@@ -548,6 +548,7 @@ de cada regla):
548
548
  | `no-default-export` | `allowFilePatterns` (globs, aditivos a los integrados) |
549
549
  | `no-emoji` | `allowFilePatterns` (globs) |
550
550
  | `no-functions-inside-components` | `allowJsxCallbacks`, `allowArrayMapCallbacks` (ambas `true` por defecto) |
551
+ | `no-nested-if` | `allowFilePatterns` (globs) |
551
552
  | `no-promise-chain` | `methods` |
552
553
  | `no-tunnel-props` | `allowFilePatterns` (globs), `allowPropPatterns` (regex) |
553
554
  | `prefer-abort-signal` | `allowFilePatterns` (globs), `effectNames` (default `["useEffect", "useLayoutEffect"]`) |
@@ -571,6 +572,7 @@ matchea en cualquier carpeta). Las 7 reglas restantes no tienen opciones: su
571
572
  | `skapxd/no-deep-relative-imports` | Limita la profundidad de los imports relativos (`../`). |
572
573
  | `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. |
573
574
  | `skapxd/no-emoji` | Prohíbe emojis en strings y JSX; cada sistema los renderiza distinto. Usa un icono SVG. |
575
+ | `skapxd/no-nested-if` | Prohíbe `if` anidados: retorno anticipado o `match()`. Menos carga cognitiva y sin puntos ciegos para las demás reglas. |
574
576
  | `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. |
575
577
  | `skapxd/prefer-abort-signal` | Listeners en efectos se limpian con `AbortController` (`{ signal }` + `abort()`), no con `removeEventListener`. |
576
578
  | `skapxd/no-functions-inside-components` | Prohíbe definir funciones dentro de componentes React. |
@@ -689,10 +691,19 @@ if (!result.ok) {
689
691
  }
690
692
  ```
691
693
 
694
+ Reconoce todas las formas del guard de Result fallido — `!result.ok`,
695
+ `result.ok === false`, `result.ok !== true`, `Result.isErr(result)` y
696
+ `if (result.error)` — y dentro del guard exige el `cause` en todo
697
+ `Result.err(...)`. Un `Result.err()` **sin argumentos** también se reporta:
698
+ descartar el error por completo es el peor caso, no una exención. Y un `cause`
699
+ con otro valor (`cause: new Error(...)`) no cuenta: tiene que ser literalmente
700
+ el `result.error` del guard.
701
+
692
702
  Esta regla es type-aware. Usa TypeScript parser services para confirmar que el
693
703
  valor del guard y `Result.err` vienen de `@skapxd/result`. Por eso funciona con
694
704
  aliases, re-exports y tipos inferidos, sin depender solo del nombre importado en
695
- el archivo.
705
+ el archivo. Su punto ciego histórico —el `Result.err` escondido en un `if`
706
+ anidado— lo elimina `skapxd/no-nested-if` de raíz.
696
707
 
697
708
  ### `skapxd/await-requires-result`
698
709
 
@@ -848,6 +859,35 @@ Revisa imports estáticos (`import`), re-exports (`export ... from`) e imports
848
859
  dinámicos (`import(...)`). El remedio habitual es un alias de ruta (`@/...`) o
849
860
  acercar el módulo a quien lo usa.
850
861
 
862
+ ### `skapxd/no-nested-if`
863
+
864
+ Prohíbe un `if` dentro de otro `if` (en la misma función). Cada nivel de
865
+ anidación suma carga cognitiva para quien lee — y además crea puntos ciegos
866
+ para las demás reglas: un `Result.err` dentro de un if anidado quedaba fuera
867
+ del alcance de `result-error-requires-cause`. Esta regla elimina la categoría
868
+ completa de evasión en vez de parchear cada caso.
869
+
870
+ ```ts
871
+ // ❌ anidado: el lector mantiene dos condiciones en la cabeza
872
+ if (!response.ok) {
873
+ if (shouldReport) {
874
+ return Result.err({ cause: response.error, message: "...", type: "X" });
875
+ }
876
+ }
877
+
878
+ // ✅ retorno anticipado: una condición a la vez, camino feliz sin sangría
879
+ if (!response.ok && shouldReport) {
880
+ return Result.err({ cause: response.error, message: "...", type: "X" });
881
+ }
882
+
883
+ // ✅ o match() si son variantes de un mismo valor
884
+ ```
885
+
886
+ No cuenta como anidación: la cadena `else if` (es secuencia, no anidación), y
887
+ una función definida dentro del `if` (unidad cognitiva aparte). El propio
888
+ código de este plugin se aplanó con retorno anticipado al activar la regla —
889
+ cinco casos, todos quedaron más legibles.
890
+
851
891
  ### `skapxd/no-default-export`
852
892
 
853
893
  Prohíbe `export default` (incluida la forma `export { x as default }`). Con
@@ -14,6 +14,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
14
14
  "skapxd/no-deep-relative-imports": string;
15
15
  "skapxd/no-default-export": string;
16
16
  "skapxd/no-emoji": string;
17
+ "skapxd/no-nested-if": string;
17
18
  "skapxd/no-promise-chain": string;
18
19
  "skapxd/no-try-catch": string;
19
20
  "skapxd/one-root-function-per-file": string;
@@ -31,6 +32,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
31
32
  "skapxd/no-deep-relative-imports": string;
32
33
  "skapxd/no-default-export": string;
33
34
  "skapxd/no-emoji": string;
35
+ "skapxd/no-nested-if": string;
34
36
  "skapxd/no-promise-chain": string;
35
37
  "skapxd/no-try-catch": string;
36
38
  "skapxd/one-root-function-per-file": string;
@@ -14,6 +14,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
14
14
  "skapxd/no-deep-relative-imports": string;
15
15
  "skapxd/no-default-export": string;
16
16
  "skapxd/no-emoji": string;
17
+ "skapxd/no-nested-if": string;
17
18
  "skapxd/no-promise-chain": string;
18
19
  "skapxd/no-try-catch": string;
19
20
  "skapxd/one-root-function-per-file": string;
@@ -31,6 +32,7 @@ declare function createAstroConfigs(pluginReference: unknown): ({
31
32
  "skapxd/no-deep-relative-imports": string;
32
33
  "skapxd/no-default-export": string;
33
34
  "skapxd/no-emoji": string;
35
+ "skapxd/no-nested-if": string;
34
36
  "skapxd/no-promise-chain": string;
35
37
  "skapxd/no-try-catch": string;
36
38
  "skapxd/one-root-function-per-file": string;
@@ -40,6 +40,7 @@ var baseRules = {
40
40
  "skapxd/no-deep-relative-imports": "error",
41
41
  "skapxd/no-default-export": "error",
42
42
  "skapxd/no-emoji": "error",
43
+ "skapxd/no-nested-if": "error",
43
44
  "skapxd/no-promise-chain": "error",
44
45
  "skapxd/no-try-catch": "error",
45
46
  "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-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
+ {"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-nested-if\": \"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,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,sCAAsC;AACxC;;;ACXA,+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-7VZBQ6FD.mjs";
4
- import "../chunk-GEVX3BTI.mjs";
3
+ } from "../chunk-3NM7FEIN.mjs";
4
+ import "../chunk-J472YWOD.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-GEVX3BTI.mjs";
5
+ } from "./chunk-J472YWOD.mjs";
6
6
 
7
7
  // src/astro/configs.ts
8
8
  function createAstroConfigs(pluginReference) {
@@ -40,4 +40,4 @@ function createAstroConfigs(pluginReference) {
40
40
  export {
41
41
  createAstroConfigs
42
42
  };
43
- //# sourceMappingURL=chunk-7VZBQ6FD.mjs.map
43
+ //# sourceMappingURL=chunk-3NM7FEIN.mjs.map
@@ -644,14 +644,16 @@ var asyncFunctionsReturnResult = {
644
644
  return;
645
645
  }
646
646
  const returnType = node.returnType?.typeAnnotation;
647
+ const missingReturnTypeIsReportable = options.checkMissingReturnType || containsCallNamed(node.body, options.checkMissingReturnTypeWhenCallNames);
648
+ if (!returnType && missingReturnTypeIsReportable) {
649
+ context.report({
650
+ data: { name: functionName },
651
+ messageId: "missingReturnType",
652
+ node: reportNode
653
+ });
654
+ return;
655
+ }
647
656
  if (!returnType) {
648
- if (options.checkMissingReturnType || containsCallNamed(node.body, options.checkMissingReturnTypeWhenCallNames)) {
649
- context.report({
650
- data: { name: functionName },
651
- messageId: "missingReturnType",
652
- node: reportNode
653
- });
654
- }
655
657
  return;
656
658
  }
657
659
  if (isSkapxdResultReturnType(returnType)) {
@@ -813,14 +815,13 @@ function getAwaitScopeName(node) {
813
815
  function getEnclosingTrySafeCall(node, trySafeCallNames) {
814
816
  let currentNode = node.parent;
815
817
  while (currentNode) {
816
- if (isFunctionNode(currentNode)) {
817
- const parent = currentNode.parent;
818
- if (parent?.type === "CallExpression" && parent.arguments.includes(currentNode) && isCalleeNamed(parent.callee, trySafeCallNames)) {
819
- return parent;
820
- }
821
- return null;
818
+ if (!isFunctionNode(currentNode)) {
819
+ currentNode = currentNode.parent;
820
+ continue;
822
821
  }
823
- currentNode = currentNode.parent;
822
+ const parent = currentNode.parent;
823
+ const isTrySafeArgument = parent?.type === "CallExpression" && parent.arguments.includes(currentNode) && isCalleeNamed(parent.callee, trySafeCallNames);
824
+ return isTrySafeArgument ? parent : null;
824
825
  }
825
826
  return null;
826
827
  }
@@ -954,6 +955,18 @@ var awaitRequiresResult = {
954
955
  }
955
956
  };
956
957
 
958
+ // src/utils/get-error-member-object.ts
959
+ function getErrorMemberObject(node) {
960
+ const unwrappedNode = unwrapExpression(node);
961
+ if (unwrappedNode.type !== "MemberExpression" || unwrappedNode.object.type !== "Identifier" || !isMemberPropertyNamed(unwrappedNode, "error")) {
962
+ return null;
963
+ }
964
+ return {
965
+ name: unwrappedNode.object.name,
966
+ node: unwrappedNode.object
967
+ };
968
+ }
969
+
957
970
  // src/utils/get-ok-member-object.ts
958
971
  function getOkMemberObject(node) {
959
972
  const unwrappedNode = unwrapExpression(node);
@@ -1002,6 +1015,9 @@ function getResultCheckArgument(node, methodName) {
1002
1015
  // src/utils/get-failed-result-guard.ts
1003
1016
  function getFailedResultGuard(node) {
1004
1017
  const unwrappedNode = unwrapExpression(node);
1018
+ if (unwrappedNode.type === "MemberExpression") {
1019
+ return getErrorMemberObject(unwrappedNode);
1020
+ }
1005
1021
  if (unwrappedNode.type === "UnaryExpression" && unwrappedNode.operator === "!") {
1006
1022
  return getOkMemberObject(unwrappedNode.argument) ?? getResultCheckArgument(unwrappedNode.argument, "isOk");
1007
1023
  }
@@ -1119,10 +1135,7 @@ var resultErrorRequiresCause = {
1119
1135
  if (!isSkapxdResultErrCall(resultErrCall, typeContext)) {
1120
1136
  continue;
1121
1137
  }
1122
- if (resultErrCall.arguments.length === 0) {
1123
- continue;
1124
- }
1125
- if (resultErrPreservesCause(resultErrCall.arguments[0], resultGuard.name)) {
1138
+ if (resultErrCall.arguments.length > 0 && resultErrPreservesCause(resultErrCall.arguments[0], resultGuard.name)) {
1126
1139
  continue;
1127
1140
  }
1128
1141
  context.report({
@@ -1740,14 +1753,9 @@ function hasAbortSignalOption(callExpression, sourceCode, typeContext) {
1740
1753
  if (options.type === "ObjectExpression") {
1741
1754
  return objectExpressionHasSignal(options);
1742
1755
  }
1743
- if (options.type === "Identifier") {
1744
- const initializer = getVariableInitializer(
1745
- options,
1746
- sourceCode.getScope(options)
1747
- );
1748
- if (initializer?.type === "ObjectExpression") {
1749
- return objectExpressionHasSignal(initializer);
1750
- }
1756
+ const initializer = options.type === "Identifier" ? getVariableInitializer(options, sourceCode.getScope(options)) : null;
1757
+ if (initializer?.type === "ObjectExpression") {
1758
+ return objectExpressionHasSignal(initializer);
1751
1759
  }
1752
1760
  if (typeContext) {
1753
1761
  const type = typeContext.services.getTypeAtLocation(options);
@@ -1760,11 +1768,9 @@ function hasAbortSignalOption(callExpression, sourceCode, typeContext) {
1760
1768
  function isInsideEffectCallback(node, effectNames) {
1761
1769
  let current = node.parent;
1762
1770
  while (current) {
1763
- if (isFunctionNode(current)) {
1764
- const call = current.parent;
1765
- if (call?.type === "CallExpression" && call.arguments[0] === current && isCalleeNamed(call.callee, effectNames)) {
1766
- return true;
1767
- }
1771
+ const call = isFunctionNode(current) ? current.parent : null;
1772
+ if (call?.type === "CallExpression" && call.arguments[0] === current && isCalleeNamed(call.callee, effectNames)) {
1773
+ return true;
1768
1774
  }
1769
1775
  current = current.parent;
1770
1776
  }
@@ -1903,6 +1909,67 @@ var noJsxTernaryNull = {
1903
1909
  }
1904
1910
  };
1905
1911
 
1912
+ // src/utils/get-no-nested-if-options.ts
1913
+ function getNoNestedIfOptions(options = {}) {
1914
+ return {
1915
+ allowFilePatterns: options.allowFilePatterns ?? []
1916
+ };
1917
+ }
1918
+
1919
+ // src/utils/is-nested-if-statement.ts
1920
+ function isNestedIfStatement(node) {
1921
+ if (node.parent?.type === "IfStatement" && node.parent.alternate === node) {
1922
+ return false;
1923
+ }
1924
+ let current = node.parent;
1925
+ while (current && !isFunctionNode(current)) {
1926
+ if (current.type === "IfStatement") {
1927
+ return true;
1928
+ }
1929
+ current = current.parent;
1930
+ }
1931
+ return false;
1932
+ }
1933
+
1934
+ // src/rules/no-nested-if.ts
1935
+ var noNestedIf = {
1936
+ meta: {
1937
+ type: "suggestion",
1938
+ docs: {
1939
+ description: "Prohibe if anidados; usa retorno anticipado (guard clauses) o match() de ts-pattern."
1940
+ },
1941
+ messages: {
1942
+ noNestedIf: "No anides un if dentro de otro: cada nivel suma carga cognitiva y crea puntos ciegos para las demas reglas. Aplana con retorno anticipado (`if (!x) return ...;` y sigue el camino feliz) o decide con `match()` de ts-pattern si son variantes de un mismo valor."
1943
+ },
1944
+ schema: [
1945
+ {
1946
+ additionalProperties: false,
1947
+ properties: {
1948
+ allowFilePatterns: {
1949
+ items: { type: "string" },
1950
+ type: "array"
1951
+ }
1952
+ },
1953
+ type: "object"
1954
+ }
1955
+ ]
1956
+ },
1957
+ create(context) {
1958
+ const options = getNoNestedIfOptions(context.options[0]);
1959
+ const filename = context.filename ?? context.getFilename();
1960
+ if (matchesAnyGlob(filename, options.allowFilePatterns)) {
1961
+ return {};
1962
+ }
1963
+ return {
1964
+ IfStatement(node) {
1965
+ if (isNestedIfStatement(node)) {
1966
+ context.report({ messageId: "noNestedIf", node });
1967
+ }
1968
+ }
1969
+ };
1970
+ }
1971
+ };
1972
+
1906
1973
  // src/utils/is-promise-type.ts
1907
1974
  function isPromiseType(type, typeContext) {
1908
1975
  return typeContext.checker.getPromisedTypeOfPromise(type) !== void 0;
@@ -1945,11 +2012,11 @@ var noPromiseChain = {
1945
2012
  if (!method) {
1946
2013
  return;
1947
2014
  }
1948
- if (typeContext) {
1949
- const type = typeContext.services.getTypeAtLocation(callee.object);
1950
- if (!isPromiseType(type, typeContext)) {
1951
- return;
1952
- }
2015
+ if (typeContext && !isPromiseType(
2016
+ typeContext.services.getTypeAtLocation(callee.object),
2017
+ typeContext
2018
+ )) {
2019
+ return;
1953
2020
  }
1954
2021
  context.report({
1955
2022
  data: { method },
@@ -1988,10 +2055,11 @@ var rules = {
1988
2055
  "prefer-abort-signal": preferAbortSignal,
1989
2056
  "prefer-ts-pattern": preferTsPattern,
1990
2057
  "no-jsx-ternary-null": noJsxTernaryNull,
2058
+ "no-nested-if": noNestedIf,
1991
2059
  "no-promise-chain": noPromiseChain
1992
2060
  };
1993
2061
 
1994
2062
  export {
1995
2063
  rules
1996
2064
  };
1997
- //# sourceMappingURL=chunk-WO7EUISC.mjs.map
2065
+ //# sourceMappingURL=chunk-EXMF54EM.mjs.map