@skapxd/eslint-opinionated 0.1.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.
Files changed (42) hide show
  1. package/README.md +731 -0
  2. package/dist/astro/index.d.mts +33 -0
  3. package/dist/astro/index.d.ts +33 -0
  4. package/dist/astro/index.js +72 -0
  5. package/dist/astro/index.js.map +1 -0
  6. package/dist/astro/index.mjs +8 -0
  7. package/dist/astro/index.mjs.map +1 -0
  8. package/dist/chunk-3FB4H7N6.mjs +31 -0
  9. package/dist/chunk-3FB4H7N6.mjs.map +1 -0
  10. package/dist/chunk-BAHAXSWA.mjs +62 -0
  11. package/dist/chunk-BAHAXSWA.mjs.map +1 -0
  12. package/dist/chunk-CQKEQ32W.mjs +99 -0
  13. package/dist/chunk-CQKEQ32W.mjs.map +1 -0
  14. package/dist/chunk-RP7BOODV.mjs +1550 -0
  15. package/dist/chunk-RP7BOODV.mjs.map +1 -0
  16. package/dist/cli.d.mts +1 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/cli.js +3451 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/cli.mjs +3428 -0
  21. package/dist/cli.mjs.map +1 -0
  22. package/dist/index.d.mts +14 -0
  23. package/dist/index.d.ts +14 -0
  24. package/dist/index.js +1781 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/index.mjs +44 -0
  27. package/dist/index.mjs.map +1 -0
  28. package/dist/next/index.d.mts +65 -0
  29. package/dist/next/index.d.ts +65 -0
  30. package/dist/next/index.js +103 -0
  31. package/dist/next/index.js.map +1 -0
  32. package/dist/next/index.mjs +8 -0
  33. package/dist/next/index.mjs.map +1 -0
  34. package/dist/rules-qISQhAKV.d.mts +5 -0
  35. package/dist/rules-qISQhAKV.d.ts +5 -0
  36. package/dist/shared/index.d.mts +110 -0
  37. package/dist/shared/index.d.ts +110 -0
  38. package/dist/shared/index.js +1684 -0
  39. package/dist/shared/index.js.map +1 -0
  40. package/dist/shared/index.mjs +15 -0
  41. package/dist/shared/index.mjs.map +1 -0
  42. package/package.json +80 -0
package/dist/index.js ADDED
@@ -0,0 +1,1781 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ configs: () => configs,
34
+ default: () => index_default,
35
+ rules: () => rules
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/shared/configs/base-rules.ts
40
+ var baseRules = {
41
+ "skapxd/no-ad-hoc-ok-result": "warn",
42
+ "skapxd/no-deep-relative-imports": "warn",
43
+ "skapxd/no-promise-chain": "error",
44
+ "skapxd/no-try-catch": "error",
45
+ "skapxd/one-root-function-per-file": "error",
46
+ "skapxd/prefer-ts-pattern": "error",
47
+ "skapxd/result-error-requires-cause": "error"
48
+ };
49
+
50
+ // src/shared/configs/create-typed-language-options.ts
51
+ function createTypedLanguageOptions() {
52
+ return {
53
+ parserOptions: {
54
+ projectService: true
55
+ }
56
+ };
57
+ }
58
+
59
+ // src/shared/configs/create-shared-configs.ts
60
+ function createSharedConfigs(pluginReference) {
61
+ const typedLanguageOptions = createTypedLanguageOptions();
62
+ return {
63
+ backend: {
64
+ languageOptions: typedLanguageOptions,
65
+ name: "skapxd/shared/backend",
66
+ plugins: { skapxd: pluginReference },
67
+ rules: {
68
+ ...baseRules,
69
+ "skapxd/async-functions-return-result": [
70
+ "error",
71
+ {
72
+ checkMissingReturnType: true,
73
+ resultTypeNames: ["Result", "ResultValue", "SafeResult"]
74
+ }
75
+ ]
76
+ }
77
+ },
78
+ base: {
79
+ name: "skapxd/shared/base",
80
+ plugins: { skapxd: pluginReference },
81
+ rules: baseRules
82
+ },
83
+ frontend: {
84
+ languageOptions: typedLanguageOptions,
85
+ name: "skapxd/shared/frontend",
86
+ plugins: { skapxd: pluginReference },
87
+ rules: {
88
+ ...baseRules,
89
+ "skapxd/jsx-return-name-pascal-case": "error",
90
+ "skapxd/no-functions-inside-components": "error",
91
+ "skapxd/no-jsx-ternary-null": "error",
92
+ "skapxd/max-hook-size": [
93
+ "warn",
94
+ {
95
+ maxLines: 120,
96
+ maxUseState: 1
97
+ }
98
+ ]
99
+ }
100
+ },
101
+ // Capa de servicios del front (peticiones, clientes de API): todo await
102
+ // debe ir envuelto en trySafe de @skapxd/result. El consumidor puede
103
+ // sobreescribir `files` si sus servicios viven en otra carpeta.
104
+ frontendServices: {
105
+ files: ["**/services/**/*.{ts,tsx}", "**/api/**/*.{ts,tsx}"],
106
+ languageOptions: typedLanguageOptions,
107
+ name: "skapxd/shared/frontend-services",
108
+ plugins: { skapxd: pluginReference },
109
+ rules: {
110
+ "skapxd/await-requires-try-safe": "error"
111
+ }
112
+ },
113
+ package: {
114
+ name: "skapxd/shared/package",
115
+ plugins: { skapxd: pluginReference },
116
+ rules: {
117
+ "skapxd/one-root-function-per-file": "error"
118
+ }
119
+ }
120
+ };
121
+ }
122
+
123
+ // src/shared/configs/strict-config.ts
124
+ var strictConfig = {
125
+ linterOptions: {
126
+ noInlineConfig: true
127
+ },
128
+ name: "skapxd/strict"
129
+ };
130
+
131
+ // src/astro/configs.ts
132
+ function createAstroConfigs(pluginReference) {
133
+ const typedLanguageOptions = createTypedLanguageOptions();
134
+ return [
135
+ {
136
+ files: ["src/**/*.{ts,tsx,astro}"],
137
+ name: "skapxd/astro/base",
138
+ plugins: { skapxd: pluginReference },
139
+ rules: baseRules
140
+ },
141
+ {
142
+ files: ["src/**/*.{ts,tsx}"],
143
+ languageOptions: typedLanguageOptions,
144
+ name: "skapxd/astro/typescript",
145
+ plugins: { skapxd: pluginReference },
146
+ rules: {
147
+ "skapxd/result-error-requires-cause": "error"
148
+ }
149
+ }
150
+ ];
151
+ }
152
+
153
+ // src/next/configs.ts
154
+ function createNextConfigs(pluginReference) {
155
+ const typedLanguageOptions = createTypedLanguageOptions();
156
+ return [
157
+ {
158
+ name: "skapxd/next/base",
159
+ plugins: { skapxd: pluginReference },
160
+ rules: baseRules
161
+ },
162
+ {
163
+ files: ["src/app/api/**/*.{ts,tsx}", "src/server/**/*.{ts,tsx}"],
164
+ languageOptions: typedLanguageOptions,
165
+ name: "skapxd/next/server",
166
+ plugins: { skapxd: pluginReference },
167
+ rules: {
168
+ ...baseRules,
169
+ "skapxd/async-functions-return-result": [
170
+ "error",
171
+ {
172
+ allowFilePatterns: [
173
+ "/(route|page|layout|template|loading|error|not-found)\\.tsx?$"
174
+ ],
175
+ allowNamePatterns: [
176
+ "^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$",
177
+ "^handle(Get|Post|Put|Patch|Delete|Head|Options)$",
178
+ "^start$"
179
+ ],
180
+ checkMissingReturnType: true,
181
+ resultTypeNames: ["Result", "ResultValue", "SafeResult"]
182
+ }
183
+ ]
184
+ }
185
+ },
186
+ {
187
+ files: ["**/*.tsx"],
188
+ name: "skapxd/next/react",
189
+ plugins: { skapxd: pluginReference },
190
+ rules: {
191
+ "skapxd/jsx-return-name-pascal-case": "error",
192
+ "skapxd/no-functions-inside-components": "error",
193
+ "skapxd/no-jsx-ternary-null": "error",
194
+ "skapxd/max-hook-size": [
195
+ "warn",
196
+ {
197
+ maxLines: 120,
198
+ maxUseState: 1
199
+ }
200
+ ]
201
+ }
202
+ }
203
+ ];
204
+ }
205
+
206
+ // src/utils/get-file-name.ts
207
+ function getFileName(filename) {
208
+ return filename.split(/[\\/]/).at(-1) ?? filename;
209
+ }
210
+
211
+ // src/utils/get-source-extension.ts
212
+ function getSourceExtension(fileName) {
213
+ return fileName.endsWith(".tsx") ? ".tsx" : ".ts";
214
+ }
215
+
216
+ // src/utils/get-directory-name.ts
217
+ function getDirectoryName(filename) {
218
+ return filename.split(/[\\/]/).slice(0, -1).join("/");
219
+ }
220
+
221
+ // src/utils/is-http-route-method.ts
222
+ function isHttpRouteMethod(functionName) {
223
+ return ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"].includes(
224
+ functionName
225
+ );
226
+ }
227
+
228
+ // src/utils/to-kebab-case.ts
229
+ function toKebabCase(value) {
230
+ return value.replace(/OEmbed/g, "Oembed").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLocaleLowerCase();
231
+ }
232
+
233
+ // src/utils/get-suggested-helper-file-name.ts
234
+ function getSuggestedHelperFileName({ extension, fileStem, functionName }) {
235
+ const helperFunctionName = fileStem === "route" && isHttpRouteMethod(functionName) ? `handle${functionName}` : functionName;
236
+ return `${toKebabCase(helperFunctionName)}${extension}`;
237
+ }
238
+
239
+ // src/utils/get-path-parts.ts
240
+ function getPathParts(filename) {
241
+ return filename.split(/[\\/]/).filter(Boolean);
242
+ }
243
+
244
+ // src/utils/is-in-source-root.ts
245
+ function isInSourceRoot(filename) {
246
+ const pathParts = getPathParts(filename);
247
+ return pathParts.at(-2) === "src";
248
+ }
249
+
250
+ // src/utils/is-inside-app-directory.ts
251
+ function isInsideAppDirectory(filename) {
252
+ return getPathParts(filename).includes("app");
253
+ }
254
+
255
+ // src/constants/next-app-metadata-file-stems.ts
256
+ var nextAppMetadataFileStems = [
257
+ "apple-icon",
258
+ "icon",
259
+ "manifest",
260
+ "opengraph-image",
261
+ "robots",
262
+ "sitemap",
263
+ "twitter-image"
264
+ ];
265
+
266
+ // src/constants/next-app-route-segment-file-stems.ts
267
+ var nextAppRouteSegmentFileStems = [
268
+ "default",
269
+ "error",
270
+ "forbidden",
271
+ "global-error",
272
+ "global-not-found",
273
+ "layout",
274
+ "loading",
275
+ "not-found",
276
+ "page",
277
+ "route",
278
+ "template",
279
+ "unauthorized"
280
+ ];
281
+
282
+ // src/constants/next-project-root-file-stems.ts
283
+ var nextProjectRootFileStems = [
284
+ "instrumentation",
285
+ "instrumentation-client",
286
+ "mdx-components",
287
+ "proxy"
288
+ ];
289
+
290
+ // src/utils/is-next-convention-file.ts
291
+ function isNextConventionFile({ fileStem, filename }) {
292
+ if ([
293
+ ...nextAppRouteSegmentFileStems,
294
+ ...nextAppMetadataFileStems
295
+ ].includes(fileStem)) {
296
+ return isInsideAppDirectory(filename);
297
+ }
298
+ if (nextProjectRootFileStems.includes(fileStem)) {
299
+ return isInSourceRoot(filename);
300
+ }
301
+ return false;
302
+ }
303
+
304
+ // src/utils/get-suggested-helper-path.ts
305
+ function getSuggestedHelperPath({ extension, fileStem, filename, functionName }) {
306
+ const helperFileName = getSuggestedHelperFileName({
307
+ extension,
308
+ fileStem,
309
+ functionName
310
+ });
311
+ if (isNextConventionFile({ fileStem, filename })) {
312
+ return `${getDirectoryName(filename)}/${helperFileName}`;
313
+ }
314
+ return `${getDirectoryName(filename)}/${toKebabCase(fileStem)}/${helperFileName}`;
315
+ }
316
+
317
+ // src/utils/get-move-suggestion.ts
318
+ function getMoveSuggestion({ filename, functionName }) {
319
+ const fileName = getFileName(filename);
320
+ const extension = getSourceExtension(fileName);
321
+ const fileStem = fileName.slice(0, -extension.length);
322
+ const suggestedPath = getSuggestedHelperPath({
323
+ extension,
324
+ fileStem,
325
+ filename,
326
+ functionName
327
+ });
328
+ if (fileStem === "route" && isHttpRouteMethod(functionName)) {
329
+ return `Mueve la implementacion de \`${functionName}\` a \`${suggestedPath}\` si solo se usa aqui y deja \`${functionName}\` en route.ts delegando a ese helper. No conviertas route.ts en route/index.ts porque Next no lo reconoce.`;
330
+ }
331
+ if (isNextConventionFile({ fileStem, filename })) {
332
+ return `Mueve \`${functionName}\` a \`${suggestedPath}\` si solo se usa aqui y deja \`${fileName}\` como entrypoint de Next. No conviertas \`${fileName}\` en \`${fileStem}/index${extension}\` porque Next exige el nombre exacto del archivo.`;
333
+ }
334
+ return `Mueve \`${functionName}\` a \`${suggestedPath}\` si solo se usa aqui.`;
335
+ }
336
+
337
+ // src/utils/get-function-node-name.ts
338
+ function getFunctionNodeName(node) {
339
+ return node.id?.name ?? "helper";
340
+ }
341
+
342
+ // src/utils/get-variable-declarator-name.ts
343
+ function getVariableDeclaratorName(variableDeclarator) {
344
+ return variableDeclarator.id.type === "Identifier" ? variableDeclarator.id.name : getFunctionNodeName(variableDeclarator.init);
345
+ }
346
+
347
+ // src/utils/is-function-node.ts
348
+ function isFunctionNode(node) {
349
+ return node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression";
350
+ }
351
+
352
+ // src/utils/get-root-function-entries.ts
353
+ function getRootFunctionEntries(statement) {
354
+ const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
355
+ if (!declaration) {
356
+ return [];
357
+ }
358
+ if (isFunctionNode(declaration)) {
359
+ return [
360
+ {
361
+ name: getFunctionNodeName(declaration),
362
+ node: declaration
363
+ }
364
+ ];
365
+ }
366
+ if (declaration.type !== "VariableDeclaration") {
367
+ return [];
368
+ }
369
+ return declaration.declarations.filter((variableDeclarator) => isFunctionNode(variableDeclarator.init)).map((variableDeclarator) => ({
370
+ name: getVariableDeclaratorName(variableDeclarator),
371
+ node: variableDeclarator.init
372
+ }));
373
+ }
374
+
375
+ // src/utils/get-suggested-helper-file-names.ts
376
+ function getSuggestedHelperFileNames({ extension, fileStem, functionNames }) {
377
+ return [
378
+ ...new Set(
379
+ functionNames.map(
380
+ (functionName) => getSuggestedHelperFileName({
381
+ extension,
382
+ fileStem,
383
+ functionName
384
+ })
385
+ )
386
+ )
387
+ ];
388
+ }
389
+
390
+ // src/utils/get-tree-child-lines.ts
391
+ function getTreeChildLines({ indent = "", names }) {
392
+ return names.map((name, index) => {
393
+ const branch = index === names.length - 1 ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
394
+ return `${indent}${branch} ${name}`;
395
+ });
396
+ }
397
+
398
+ // src/utils/get-structure-suggestion.ts
399
+ function getStructureSuggestion({ filename, functionNames }) {
400
+ const fileName = getFileName(filename);
401
+ const extension = getSourceExtension(fileName);
402
+ const fileStem = fileName.slice(0, -extension.length);
403
+ const directoryName = getDirectoryName(filename);
404
+ const helperFileNames = getSuggestedHelperFileNames({
405
+ extension,
406
+ fileStem,
407
+ functionNames
408
+ });
409
+ if (isNextConventionFile({ fileStem, filename })) {
410
+ return [
411
+ `${directoryName}/`,
412
+ ...getTreeChildLines({
413
+ names: [fileName, ...helperFileNames]
414
+ })
415
+ ].join("\n");
416
+ }
417
+ return [
418
+ `${directoryName}/${toKebabCase(fileStem)}/`,
419
+ ...getTreeChildLines({
420
+ names: [`index${extension}`, ...helperFileNames]
421
+ })
422
+ ].join("\n");
423
+ }
424
+
425
+ // src/rules/one-root-function-per-file.ts
426
+ var oneRootFunctionPerFile = {
427
+ meta: {
428
+ type: "suggestion",
429
+ docs: {
430
+ description: "Limita cada archivo a una sola funcion declarada en la raiz."
431
+ },
432
+ messages: {
433
+ tooManyRootFunctions: "Este archivo tiene {{count}} funciones en la raiz. Deja solo una funcion top-level por archivo. {{moveSuggestion}}\n\nEstructura sugerida:\n{{structureSuggestion}}\n\nSi se reutiliza, muevela a un modulo compartido con nombre de dominio."
434
+ },
435
+ schema: []
436
+ },
437
+ create(context) {
438
+ return {
439
+ Program(node) {
440
+ const rootFunctions = node.body.flatMap(
441
+ (statement) => getRootFunctionEntries(statement)
442
+ );
443
+ if (rootFunctions.length <= 1) {
444
+ return;
445
+ }
446
+ const firstHelper = rootFunctions[1];
447
+ const helperFunctionNames = rootFunctions.slice(1).map((rootFunction) => rootFunction.name);
448
+ context.report({
449
+ data: {
450
+ count: String(rootFunctions.length),
451
+ moveSuggestion: getMoveSuggestion({
452
+ filename: context.filename ?? context.getFilename(),
453
+ functionName: firstHelper.name
454
+ }),
455
+ structureSuggestion: getStructureSuggestion({
456
+ filename: context.filename ?? context.getFilename(),
457
+ functionNames: helperFunctionNames
458
+ })
459
+ },
460
+ messageId: "tooManyRootFunctions",
461
+ node: firstHelper.node
462
+ });
463
+ }
464
+ };
465
+ }
466
+ };
467
+
468
+ // src/utils/is-ast-node.ts
469
+ function isAstNode(value) {
470
+ return Boolean(value && typeof value === "object" && "type" in value);
471
+ }
472
+
473
+ // src/utils/get-node-children.ts
474
+ function getNodeChildren(node) {
475
+ return Object.entries(node).flatMap(([key, value]) => {
476
+ if (["parent", "loc", "range", "tokens", "comments"].includes(key)) {
477
+ return [];
478
+ }
479
+ if (Array.isArray(value)) {
480
+ return value.filter(isAstNode);
481
+ }
482
+ return isAstNode(value) ? [value] : [];
483
+ });
484
+ }
485
+
486
+ // src/utils/contains-jsx.ts
487
+ function containsJsx(node) {
488
+ if (!isAstNode(node)) {
489
+ return false;
490
+ }
491
+ if (node.type === "JSXElement" || node.type === "JSXFragment") {
492
+ return true;
493
+ }
494
+ return getNodeChildren(node).some((child) => containsJsx(child));
495
+ }
496
+
497
+ // src/utils/contains-own-jsx.ts
498
+ function containsOwnJsx(node) {
499
+ if (!isAstNode(node)) {
500
+ return false;
501
+ }
502
+ if (node.type === "JSXElement" || node.type === "JSXFragment") {
503
+ return true;
504
+ }
505
+ if (isFunctionNode(node)) {
506
+ return false;
507
+ }
508
+ return getNodeChildren(node).some((child) => containsOwnJsx(child));
509
+ }
510
+
511
+ // src/utils/function-returns-jsx.ts
512
+ function functionReturnsJsx(functionNode) {
513
+ if (functionNode.body?.type !== "BlockStatement") {
514
+ return containsJsx(functionNode.body);
515
+ }
516
+ return containsOwnJsx(functionNode.body);
517
+ }
518
+
519
+ // src/utils/is-pascal-case-name.ts
520
+ function isPascalCaseName(value) {
521
+ return /^[A-Z][A-Za-z0-9]*$/.test(value);
522
+ }
523
+
524
+ // src/utils/to-pascal-case.ts
525
+ function toPascalCase(value) {
526
+ return value.replace(/^[a-z]/, (letter) => letter.toLocaleUpperCase());
527
+ }
528
+
529
+ // src/rules/jsx-return-name-pascal-case.ts
530
+ var jsxReturnNamePascalCase = {
531
+ meta: {
532
+ type: "problem",
533
+ docs: {
534
+ description: "Exige nombres PascalCase para funciones que devuelven JSX."
535
+ },
536
+ messages: {
537
+ invalidName: "La funcion `{{name}}` devuelve JSX. Nombrala como componente, por ejemplo `{{suggestedName}}`, y usala con sintaxis JSX si aplica."
538
+ },
539
+ schema: []
540
+ },
541
+ create(context) {
542
+ function reportIfJsxReturningFunction(node, name, reportNode = node) {
543
+ if (!name || isPascalCaseName(name) || !functionReturnsJsx(node)) {
544
+ return;
545
+ }
546
+ context.report({
547
+ data: {
548
+ name,
549
+ suggestedName: toPascalCase(name)
550
+ },
551
+ messageId: "invalidName",
552
+ node: reportNode
553
+ });
554
+ }
555
+ return {
556
+ FunctionDeclaration(node) {
557
+ reportIfJsxReturningFunction(node, node.id?.name, node.id ?? node);
558
+ },
559
+ VariableDeclarator(node) {
560
+ if (!isFunctionNode(node.init) || node.id.type !== "Identifier") {
561
+ return;
562
+ }
563
+ reportIfJsxReturningFunction(node.init, node.id.name, node.id);
564
+ }
565
+ };
566
+ }
567
+ };
568
+
569
+ // src/utils/is-member-property-named.ts
570
+ function isMemberPropertyNamed(node, propertyName) {
571
+ if (node.computed) {
572
+ return node.property.type === "Literal" && node.property.value === propertyName;
573
+ }
574
+ return node.property.type === "Identifier" && node.property.name === propertyName;
575
+ }
576
+
577
+ // src/utils/is-callee-named.ts
578
+ function isCalleeNamed(node, names) {
579
+ if (node?.type === "Identifier") {
580
+ return names.includes(node.name);
581
+ }
582
+ if (node?.type === "MemberExpression") {
583
+ return names.some((name) => isMemberPropertyNamed(node, name));
584
+ }
585
+ return false;
586
+ }
587
+
588
+ // src/utils/contains-call-named.ts
589
+ function containsCallNamed(node, names) {
590
+ if (!isAstNode(node)) {
591
+ return false;
592
+ }
593
+ if (node.type === "CallExpression" && isCalleeNamed(node.callee, names)) {
594
+ return true;
595
+ }
596
+ return getNodeChildren(node).some((child) => containsCallNamed(child, names));
597
+ }
598
+
599
+ // src/utils/get-async-result-rule-options.ts
600
+ function getAsyncResultRuleOptions(options = {}) {
601
+ return {
602
+ allowFilePatterns: options.allowFilePatterns ?? [],
603
+ allowNamePatterns: options.allowNamePatterns ?? [],
604
+ checkMissingReturnType: options.checkMissingReturnType ?? true,
605
+ checkMissingReturnTypeWhenCallNames: options.checkMissingReturnTypeWhenCallNames ?? [],
606
+ promiseTypeNames: options.promiseTypeNames ?? ["Promise"],
607
+ requireCallNames: options.requireCallNames ?? [],
608
+ resultTypeNames: options.resultTypeNames ?? ["Result"]
609
+ };
610
+ }
611
+
612
+ // src/utils/get-property-name.ts
613
+ function getPropertyName(node) {
614
+ if (!node) {
615
+ return "anonymous";
616
+ }
617
+ if (node.type === "Identifier") {
618
+ return node.name;
619
+ }
620
+ if (node.type === "Literal") {
621
+ return String(node.value);
622
+ }
623
+ return "anonymous";
624
+ }
625
+
626
+ // src/utils/get-parent-function-name.ts
627
+ function getParentFunctionName(node) {
628
+ const parent = node.parent;
629
+ if (parent?.type === "VariableDeclarator") {
630
+ return getVariableDeclaratorName(parent);
631
+ }
632
+ if (parent?.type === "Property" || parent?.type === "MethodDefinition" || parent?.type === "PropertyDefinition") {
633
+ return getPropertyName(parent.key);
634
+ }
635
+ return getFunctionNodeName(node);
636
+ }
637
+
638
+ // src/utils/get-function-expression-name.ts
639
+ function getFunctionExpressionName(node) {
640
+ return node.id?.name ?? getParentFunctionName(node);
641
+ }
642
+
643
+ // src/utils/get-parent-function-report-node.ts
644
+ function getParentFunctionReportNode(node) {
645
+ const parent = node.parent;
646
+ if (parent?.type === "VariableDeclarator" && parent.id.type === "Identifier") {
647
+ return parent.id;
648
+ }
649
+ if (parent?.type === "Property" || parent?.type === "MethodDefinition" || parent?.type === "PropertyDefinition") {
650
+ return parent.key;
651
+ }
652
+ return node.id ?? node;
653
+ }
654
+
655
+ // src/utils/get-type-context.ts
656
+ function getTypeContext(context) {
657
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
658
+ const parserServices = sourceCode.parserServices;
659
+ if (!parserServices?.program) {
660
+ return null;
661
+ }
662
+ return {
663
+ checker: parserServices.program.getTypeChecker(),
664
+ services: parserServices
665
+ };
666
+ }
667
+
668
+ // src/utils/is-anonymous-generated-function-name.ts
669
+ function isAnonymousGeneratedFunctionName(name) {
670
+ return name === "anonymous" || name === "helper";
671
+ }
672
+
673
+ // src/utils/get-type-reference-parameters.ts
674
+ function getTypeReferenceParameters(node) {
675
+ return node.typeArguments?.params ?? node.typeParameters?.params ?? [];
676
+ }
677
+
678
+ // src/utils/is-type-reference-named.ts
679
+ function isTypeReferenceNamed(node, names) {
680
+ const typeName = node.typeName;
681
+ return typeName.type === "Identifier" && names.includes(typeName.name);
682
+ }
683
+
684
+ // src/utils/is-promise-of-result-type.ts
685
+ function isPromiseOfResultType(node, options) {
686
+ if (node.type !== "TSTypeReference") {
687
+ return false;
688
+ }
689
+ if (!isTypeReferenceNamed(node, options.promiseTypeNames)) {
690
+ return false;
691
+ }
692
+ const promiseValueType = getTypeReferenceParameters(node)[0];
693
+ return Boolean(
694
+ promiseValueType && promiseValueType.type === "TSTypeReference" && isTypeReferenceNamed(promiseValueType, options.resultTypeNames)
695
+ );
696
+ }
697
+
698
+ // src/utils/get-package-name.ts
699
+ var import_result = require("@skapxd/result");
700
+ var import_node_fs = __toESM(require("fs"));
701
+ var import_node_path = __toESM(require("path"));
702
+ var packageNameByDir = /* @__PURE__ */ new Map();
703
+ function getPackageName(fileName) {
704
+ const visited = [];
705
+ let dir = import_node_path.default.dirname(fileName);
706
+ while (true) {
707
+ if (packageNameByDir.has(dir)) {
708
+ const cached = packageNameByDir.get(dir);
709
+ for (const visitedDir of visited) packageNameByDir.set(visitedDir, cached);
710
+ return cached;
711
+ }
712
+ visited.push(dir);
713
+ const packageJsonPath = import_node_path.default.join(dir, "package.json");
714
+ if (import_node_fs.default.existsSync(packageJsonPath)) {
715
+ const parsed = (0, import_result.trySafe)(
716
+ () => JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"))
717
+ );
718
+ const name = parsed.ok ? parsed.value.name ?? null : null;
719
+ for (const visitedDir of visited) packageNameByDir.set(visitedDir, name);
720
+ return name;
721
+ }
722
+ const parent = import_node_path.default.dirname(dir);
723
+ if (parent === dir) {
724
+ for (const visitedDir of visited) packageNameByDir.set(visitedDir, null);
725
+ return null;
726
+ }
727
+ dir = parent;
728
+ }
729
+ }
730
+
731
+ // src/utils/is-skapxd-result-source-file.ts
732
+ function isSkapxdResultSourceFile(fileName) {
733
+ return getPackageName(fileName) === "@skapxd/result";
734
+ }
735
+
736
+ // src/utils/resolve-alias-symbol.ts
737
+ var import_typescript = __toESM(require("typescript"));
738
+ function resolveAliasSymbol(symbol, typeContext) {
739
+ return symbol.flags & import_typescript.default.SymbolFlags.Alias ? typeContext.checker.getAliasedSymbol(symbol) : symbol;
740
+ }
741
+
742
+ // src/utils/is-symbol-from-skapxd-result.ts
743
+ function isSymbolFromSkapxdResult(symbol, typeContext) {
744
+ const resolvedSymbol = resolveAliasSymbol(symbol, typeContext);
745
+ return (resolvedSymbol.getDeclarations() ?? []).some(
746
+ (declaration) => isSkapxdResultSourceFile(declaration.getSourceFile().fileName)
747
+ );
748
+ }
749
+
750
+ // src/utils/is-skapxd-named-type.ts
751
+ function isSkapxdNamedType(type, names, typeContext) {
752
+ return [
753
+ type.aliasSymbol,
754
+ type.symbol
755
+ ].some(
756
+ (symbol) => Boolean(
757
+ symbol && names.includes(symbol.getName()) && isSymbolFromSkapxdResult(symbol, typeContext)
758
+ )
759
+ );
760
+ }
761
+
762
+ // src/utils/is-skapxd-result-type.ts
763
+ function isSkapxdResultType(type, typeContext) {
764
+ if (isSkapxdNamedType(type, ["Result", "SafeResult"], typeContext)) {
765
+ return true;
766
+ }
767
+ if (!type.isUnion()) {
768
+ return false;
769
+ }
770
+ const hasOk = type.types.some(
771
+ (part) => isSkapxdNamedType(part, ["Ok"], typeContext)
772
+ );
773
+ const hasErr = type.types.some(
774
+ (part) => isSkapxdNamedType(part, ["Err"], typeContext)
775
+ );
776
+ return hasOk && hasErr;
777
+ }
778
+
779
+ // src/utils/is-skapxd-result-or-promise-result-type.ts
780
+ function isSkapxdResultOrPromiseResultType(type, typeContext) {
781
+ if (isSkapxdResultType(type, typeContext)) {
782
+ return true;
783
+ }
784
+ const promisedType = typeContext.checker.getPromisedTypeOfPromise(type);
785
+ return Boolean(promisedType && isSkapxdResultType(promisedType, typeContext));
786
+ }
787
+
788
+ // src/utils/matches-any-pattern.ts
789
+ function matchesAnyPattern(value, patterns) {
790
+ return patterns.some((pattern) => new RegExp(pattern).test(value));
791
+ }
792
+
793
+ // src/rules/async-functions-return-result.ts
794
+ var asyncFunctionsReturnResult = {
795
+ meta: {
796
+ type: "problem",
797
+ docs: {
798
+ description: "Exige Promise<Result<...>> en funciones async de dominio."
799
+ },
800
+ messages: {
801
+ missingReturnType: "La funcion async `{{name}}` debe declarar Promise<Result<...>> como tipo de retorno.",
802
+ invalidReturnType: "La funcion async `{{name}}` debe retornar Promise<Result<...>> para modelar errores de forma explicita."
803
+ },
804
+ schema: [
805
+ {
806
+ additionalProperties: false,
807
+ properties: {
808
+ allowFilePatterns: {
809
+ items: { type: "string" },
810
+ type: "array"
811
+ },
812
+ allowNamePatterns: {
813
+ items: { type: "string" },
814
+ type: "array"
815
+ },
816
+ checkMissingReturnType: { type: "boolean" },
817
+ checkMissingReturnTypeWhenCallNames: {
818
+ items: { type: "string" },
819
+ type: "array"
820
+ },
821
+ requireCallNames: {
822
+ items: { type: "string" },
823
+ type: "array"
824
+ },
825
+ promiseTypeNames: {
826
+ items: { type: "string" },
827
+ type: "array"
828
+ },
829
+ resultTypeNames: {
830
+ items: { type: "string" },
831
+ type: "array"
832
+ }
833
+ },
834
+ type: "object"
835
+ }
836
+ ]
837
+ },
838
+ create(context) {
839
+ const options = getAsyncResultRuleOptions(context.options[0]);
840
+ const filename = context.filename ?? context.getFilename();
841
+ const typeContext = getTypeContext(context);
842
+ function isSkapxdResultReturnType(annotation) {
843
+ if (typeContext) {
844
+ const type = typeContext.services.getTypeFromTypeNode(annotation);
845
+ return isSkapxdResultOrPromiseResultType(type, typeContext);
846
+ }
847
+ return isPromiseOfResultType(annotation, options);
848
+ }
849
+ function reportIfInvalidAsyncReturn(node, name, reportNode = node) {
850
+ if (!node.async || matchesAnyPattern(filename, options.allowFilePatterns)) {
851
+ return;
852
+ }
853
+ const functionName = name ?? "anonymous";
854
+ if (isAnonymousGeneratedFunctionName(functionName)) {
855
+ return;
856
+ }
857
+ if (matchesAnyPattern(functionName, options.allowNamePatterns)) {
858
+ return;
859
+ }
860
+ if (options.requireCallNames.length && !containsCallNamed(node.body, options.requireCallNames)) {
861
+ return;
862
+ }
863
+ const returnType = node.returnType?.typeAnnotation;
864
+ if (!returnType) {
865
+ if (options.checkMissingReturnType || containsCallNamed(node.body, options.checkMissingReturnTypeWhenCallNames)) {
866
+ context.report({
867
+ data: { name: functionName },
868
+ messageId: "missingReturnType",
869
+ node: reportNode
870
+ });
871
+ }
872
+ return;
873
+ }
874
+ if (isSkapxdResultReturnType(returnType)) {
875
+ return;
876
+ }
877
+ context.report({
878
+ data: { name: functionName },
879
+ messageId: "invalidReturnType",
880
+ node: reportNode
881
+ });
882
+ }
883
+ return {
884
+ ArrowFunctionExpression(node) {
885
+ reportIfInvalidAsyncReturn(
886
+ node,
887
+ getParentFunctionName(node),
888
+ getParentFunctionReportNode(node)
889
+ );
890
+ },
891
+ FunctionDeclaration(node) {
892
+ reportIfInvalidAsyncReturn(node, node.id?.name, node.id ?? node);
893
+ },
894
+ FunctionExpression(node) {
895
+ reportIfInvalidAsyncReturn(
896
+ node,
897
+ getFunctionExpressionName(node),
898
+ getParentFunctionReportNode(node)
899
+ );
900
+ }
901
+ };
902
+ }
903
+ };
904
+
905
+ // src/utils/get-containing-function.ts
906
+ function getContainingFunction(node) {
907
+ let currentNode = node.parent;
908
+ while (currentNode) {
909
+ if (isFunctionNode(currentNode)) {
910
+ return currentNode;
911
+ }
912
+ currentNode = currentNode.parent;
913
+ }
914
+ return null;
915
+ }
916
+
917
+ // src/utils/get-function-name.ts
918
+ function getFunctionName(node) {
919
+ if (node.type === "FunctionDeclaration") {
920
+ return getFunctionNodeName(node);
921
+ }
922
+ return getParentFunctionName(node);
923
+ }
924
+
925
+ // src/utils/get-returned-object-expression.ts
926
+ function getReturnedObjectExpression(node) {
927
+ if (!node) {
928
+ return null;
929
+ }
930
+ if (node.type === "ObjectExpression") {
931
+ return node;
932
+ }
933
+ if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression" || node.type === "ChainExpression") {
934
+ return getReturnedObjectExpression(node.expression);
935
+ }
936
+ return null;
937
+ }
938
+
939
+ // src/utils/unwrap-expression.ts
940
+ function unwrapExpression(node) {
941
+ if (node.type === "ChainExpression" || node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression") {
942
+ return unwrapExpression(node.expression);
943
+ }
944
+ return node;
945
+ }
946
+
947
+ // src/utils/get-boolean-literal-value.ts
948
+ function getBooleanLiteralValue(node) {
949
+ const unwrappedNode = unwrapExpression(node);
950
+ return unwrappedNode.type === "Literal" && typeof unwrappedNode.value === "boolean" ? unwrappedNode.value : null;
951
+ }
952
+
953
+ // src/utils/is-property-key-named.ts
954
+ function isPropertyKeyNamed(property, propertyName) {
955
+ if (property.key.type === "Identifier") {
956
+ return property.key.name === propertyName;
957
+ }
958
+ return property.key.type === "Literal" && property.key.value === propertyName;
959
+ }
960
+
961
+ // src/utils/has-boolean-ok-property.ts
962
+ function hasBooleanOkProperty(node) {
963
+ return node.properties.some(
964
+ (property) => property.type === "Property" && isPropertyKeyNamed(property, "ok") && getBooleanLiteralValue(property.value) !== null
965
+ );
966
+ }
967
+
968
+ // src/utils/is-exported-function.ts
969
+ function isExportedFunction(node) {
970
+ const parent = node.parent;
971
+ if (node.type === "FunctionDeclaration" && (parent?.type === "ExportNamedDeclaration" || parent?.type === "ExportDefaultDeclaration")) {
972
+ return true;
973
+ }
974
+ if ((node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && parent?.type === "VariableDeclarator" && parent.parent?.parent?.type === "ExportNamedDeclaration") {
975
+ return true;
976
+ }
977
+ return false;
978
+ }
979
+
980
+ // src/rules/no-ad-hoc-ok-result.ts
981
+ var noAdHocOkResult = {
982
+ meta: {
983
+ type: "problem",
984
+ docs: {
985
+ description: "Prohibe retornar contratos ad hoc con ok en funciones async exportadas."
986
+ },
987
+ messages: {
988
+ adHocOkResult: "No retornes objetos ad hoc con `ok` desde la funcion async `{{name}}`. Usa Result.ok(...) / Result.err(...) con un error discriminado `{ type: ... }`."
989
+ },
990
+ schema: []
991
+ },
992
+ create(context) {
993
+ return {
994
+ ReturnStatement(node) {
995
+ const returnedObject = getReturnedObjectExpression(node.argument);
996
+ if (!returnedObject || !hasBooleanOkProperty(returnedObject)) {
997
+ return;
998
+ }
999
+ const containingFunction = getContainingFunction(node);
1000
+ if (!containingFunction?.async || !isExportedFunction(containingFunction)) {
1001
+ return;
1002
+ }
1003
+ context.report({
1004
+ data: {
1005
+ name: getFunctionName(containingFunction)
1006
+ },
1007
+ messageId: "adHocOkResult",
1008
+ node
1009
+ });
1010
+ }
1011
+ };
1012
+ }
1013
+ };
1014
+
1015
+ // src/utils/get-await-requires-try-safe-options.ts
1016
+ function getAwaitRequiresTrySafeOptions(options = {}) {
1017
+ return {
1018
+ allowFilePatterns: options.allowFilePatterns ?? [],
1019
+ trySafeCallNames: options.trySafeCallNames ?? ["trySafe"]
1020
+ };
1021
+ }
1022
+
1023
+ // src/utils/get-await-scope-name.ts
1024
+ function getAwaitScopeName(node) {
1025
+ const containingFunction = getContainingFunction(node);
1026
+ return containingFunction ? getFunctionName(containingFunction) : "top-level";
1027
+ }
1028
+
1029
+ // src/utils/get-enclosing-try-safe-call.ts
1030
+ function getEnclosingTrySafeCall(node, trySafeCallNames) {
1031
+ let currentNode = node.parent;
1032
+ while (currentNode) {
1033
+ if (isFunctionNode(currentNode)) {
1034
+ const parent = currentNode.parent;
1035
+ if (parent?.type === "CallExpression" && parent.arguments.includes(currentNode) && isCalleeNamed(parent.callee, trySafeCallNames)) {
1036
+ return parent;
1037
+ }
1038
+ return null;
1039
+ }
1040
+ currentNode = currentNode.parent;
1041
+ }
1042
+ return null;
1043
+ }
1044
+
1045
+ // src/utils/contains-await-expression.ts
1046
+ function containsAwaitExpression(node) {
1047
+ if (!isAstNode(node)) {
1048
+ return false;
1049
+ }
1050
+ if (node.type === "AwaitExpression") {
1051
+ return true;
1052
+ }
1053
+ if (isFunctionNode(node)) {
1054
+ return false;
1055
+ }
1056
+ return getNodeChildren(node).some((child) => containsAwaitExpression(child));
1057
+ }
1058
+
1059
+ // src/utils/get-call-expression-example.ts
1060
+ function getCallExpressionExample(node, sourceCode) {
1061
+ const calleeText = sourceCode.getText(node.callee);
1062
+ if (node.arguments.length === 0) {
1063
+ return `${calleeText}()`;
1064
+ }
1065
+ if (node.arguments.length === 1 && unwrapExpression(node.arguments[0]).type === "ObjectExpression") {
1066
+ return `${calleeText}({...})`;
1067
+ }
1068
+ return `${calleeText}(...)`;
1069
+ }
1070
+
1071
+ // src/utils/get-awaited-operation-example.ts
1072
+ function getAwaitedOperationExample(node, sourceCode) {
1073
+ const unwrappedNode = unwrapExpression(node);
1074
+ if (unwrappedNode.type === "CallExpression") {
1075
+ return getCallExpressionExample(unwrappedNode, sourceCode);
1076
+ }
1077
+ const expressionText = sourceCode.getText(unwrappedNode).replace(/\s+/g, " ");
1078
+ return expressionText.length > 80 ? `${expressionText.slice(0, 77)}...` : expressionText;
1079
+ }
1080
+
1081
+ // src/utils/get-try-safe-await-suggestion.ts
1082
+ function getTrySafeAwaitSuggestion(node, sourceCode) {
1083
+ const callbackKeyword = containsAwaitExpression(node) ? "async " : "";
1084
+ return `await trySafe(${callbackKeyword}() => ${getAwaitedOperationExample(
1085
+ node,
1086
+ sourceCode
1087
+ )})`;
1088
+ }
1089
+
1090
+ // src/utils/is-skapxd-result-or-promise-result-expression.ts
1091
+ function isSkapxdResultOrPromiseResultExpression(node, typeContext) {
1092
+ return isSkapxdResultOrPromiseResultType(
1093
+ typeContext.services.getTypeAtLocation(node),
1094
+ typeContext
1095
+ );
1096
+ }
1097
+
1098
+ // src/utils/is-try-safe-call.ts
1099
+ function isTrySafeCall(node, trySafeCallNames) {
1100
+ return node?.type === "CallExpression" && isCalleeNamed(node.callee, trySafeCallNames);
1101
+ }
1102
+
1103
+ // src/rules/await-requires-try-safe.ts
1104
+ var awaitRequiresTrySafe = {
1105
+ meta: {
1106
+ type: "problem",
1107
+ docs: {
1108
+ description: "Exige que los await esten protegidos por trySafe."
1109
+ },
1110
+ messages: {
1111
+ unprotectedAwait: "El await dentro de `{{name}}` no esta protegido por trySafe. Envuelve la operacion asi: `{{suggestion}}`."
1112
+ },
1113
+ schema: [
1114
+ {
1115
+ additionalProperties: false,
1116
+ properties: {
1117
+ allowFilePatterns: {
1118
+ items: { type: "string" },
1119
+ type: "array"
1120
+ },
1121
+ trySafeCallNames: {
1122
+ items: { type: "string" },
1123
+ type: "array"
1124
+ }
1125
+ },
1126
+ type: "object"
1127
+ }
1128
+ ]
1129
+ },
1130
+ create(context) {
1131
+ const options = getAwaitRequiresTrySafeOptions(context.options[0]);
1132
+ const filename = context.filename ?? context.getFilename();
1133
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
1134
+ const typeContext = getTypeContext(context);
1135
+ function isSkapxdTrySafe(callNode) {
1136
+ if (!callNode) {
1137
+ return false;
1138
+ }
1139
+ if (!typeContext) {
1140
+ return true;
1141
+ }
1142
+ const symbol = typeContext.services.getSymbolAtLocation(callNode.callee);
1143
+ return Boolean(symbol && isSymbolFromSkapxdResult(symbol, typeContext));
1144
+ }
1145
+ return {
1146
+ AwaitExpression(node) {
1147
+ if (matchesAnyPattern(filename, options.allowFilePatterns)) {
1148
+ return;
1149
+ }
1150
+ if (typeContext && isSkapxdResultOrPromiseResultExpression(node.argument, typeContext)) {
1151
+ return;
1152
+ }
1153
+ const directCall = isTrySafeCall(node.argument, options.trySafeCallNames) ? node.argument : null;
1154
+ const enclosingCall = getEnclosingTrySafeCall(
1155
+ node,
1156
+ options.trySafeCallNames
1157
+ );
1158
+ if (isSkapxdTrySafe(directCall) || isSkapxdTrySafe(enclosingCall)) {
1159
+ return;
1160
+ }
1161
+ context.report({
1162
+ data: {
1163
+ name: getAwaitScopeName(node),
1164
+ suggestion: getTrySafeAwaitSuggestion(node.argument, sourceCode)
1165
+ },
1166
+ messageId: "unprotectedAwait",
1167
+ node
1168
+ });
1169
+ }
1170
+ };
1171
+ }
1172
+ };
1173
+
1174
+ // src/utils/get-ok-member-object.ts
1175
+ function getOkMemberObject(node) {
1176
+ const unwrappedNode = unwrapExpression(node);
1177
+ if (unwrappedNode.type !== "MemberExpression" || unwrappedNode.object.type !== "Identifier" || !isMemberPropertyNamed(unwrappedNode, "ok")) {
1178
+ return null;
1179
+ }
1180
+ return {
1181
+ name: unwrappedNode.object.name,
1182
+ node: unwrappedNode.object
1183
+ };
1184
+ }
1185
+
1186
+ // src/utils/is-failed-ok-comparison.ts
1187
+ function isFailedOkComparison(operator, comparedValue) {
1188
+ return operator === "===" && comparedValue === false || operator === "!==" && comparedValue === true;
1189
+ }
1190
+
1191
+ // src/utils/get-failed-result-binary-guard-name.ts
1192
+ function getFailedResultBinaryGuardName(node) {
1193
+ const leftResult = getOkMemberObject(node.left);
1194
+ const rightResult = getOkMemberObject(node.right);
1195
+ const leftBoolean = getBooleanLiteralValue(node.left);
1196
+ const rightBoolean = getBooleanLiteralValue(node.right);
1197
+ if (leftResult && rightBoolean !== null) {
1198
+ return isFailedOkComparison(node.operator, rightBoolean) ? leftResult : null;
1199
+ }
1200
+ if (rightResult && leftBoolean !== null) {
1201
+ return isFailedOkComparison(node.operator, leftBoolean) ? rightResult : null;
1202
+ }
1203
+ return null;
1204
+ }
1205
+
1206
+ // src/utils/get-result-check-argument.ts
1207
+ function getResultCheckArgument(node, methodName) {
1208
+ const unwrappedNode = unwrapExpression(node);
1209
+ if (unwrappedNode.type !== "CallExpression" || unwrappedNode.callee.type !== "MemberExpression" || !isMemberPropertyNamed(unwrappedNode.callee, methodName)) {
1210
+ return null;
1211
+ }
1212
+ const argument = unwrappedNode.arguments[0];
1213
+ if (argument?.type !== "Identifier") {
1214
+ return null;
1215
+ }
1216
+ return { name: argument.name, node: argument };
1217
+ }
1218
+
1219
+ // src/utils/get-failed-result-guard.ts
1220
+ function getFailedResultGuard(node) {
1221
+ const unwrappedNode = unwrapExpression(node);
1222
+ if (unwrappedNode.type === "UnaryExpression" && unwrappedNode.operator === "!") {
1223
+ return getOkMemberObject(unwrappedNode.argument) ?? getResultCheckArgument(unwrappedNode.argument, "isOk");
1224
+ }
1225
+ if (unwrappedNode.type === "CallExpression") {
1226
+ return getResultCheckArgument(unwrappedNode, "isErr");
1227
+ }
1228
+ if (unwrappedNode.type === "BinaryExpression" && ["===", "!=="].includes(unwrappedNode.operator)) {
1229
+ return getFailedResultBinaryGuardName(unwrappedNode);
1230
+ }
1231
+ return null;
1232
+ }
1233
+
1234
+ // src/utils/is-result-err-call.ts
1235
+ function isResultErrCall(node) {
1236
+ return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && isMemberPropertyNamed(node.callee, "err");
1237
+ }
1238
+
1239
+ // src/utils/get-own-result-err-calls.ts
1240
+ function getOwnResultErrCalls(node, isRoot = true) {
1241
+ if (!isAstNode(node)) {
1242
+ return [];
1243
+ }
1244
+ if (isFunctionNode(node) || !isRoot && node.type === "IfStatement") {
1245
+ return [];
1246
+ }
1247
+ const ownCalls = node.type === "CallExpression" && isResultErrCall(node) ? [node] : [];
1248
+ return [
1249
+ ...ownCalls,
1250
+ ...getNodeChildren(node).flatMap((child) => getOwnResultErrCalls(child, false))
1251
+ ];
1252
+ }
1253
+
1254
+ // src/utils/get-function-return-type.ts
1255
+ function getFunctionReturnType(node, typeContext) {
1256
+ if (node.returnType?.typeAnnotation) {
1257
+ return typeContext.services.getTypeFromTypeNode(node.returnType.typeAnnotation);
1258
+ }
1259
+ const functionType = typeContext.services.getTypeAtLocation(node);
1260
+ const signature = functionType.getCallSignatures()[0];
1261
+ return signature?.getReturnType() ?? null;
1262
+ }
1263
+
1264
+ // src/utils/function-returns-skapxd-result-type.ts
1265
+ function functionReturnsSkapxdResultType(node, typeContext) {
1266
+ const returnType = getFunctionReturnType(node, typeContext);
1267
+ return Boolean(returnType && isSkapxdResultOrPromiseResultType(returnType, typeContext));
1268
+ }
1269
+
1270
+ // src/utils/is-inside-skapxd-result-returning-function.ts
1271
+ function isInsideSkapxdResultReturningFunction(node, typeContext) {
1272
+ const containingFunction = getContainingFunction(node);
1273
+ return Boolean(
1274
+ containingFunction && functionReturnsSkapxdResultType(containingFunction, typeContext)
1275
+ );
1276
+ }
1277
+
1278
+ // src/utils/is-skapxd-result-err-call.ts
1279
+ function isSkapxdResultErrCall(node, typeContext) {
1280
+ if (!isResultErrCall(node)) {
1281
+ return false;
1282
+ }
1283
+ const symbol = typeContext.services.getSymbolAtLocation(node.callee.object);
1284
+ return Boolean(symbol && isSymbolFromSkapxdResult(symbol, typeContext));
1285
+ }
1286
+
1287
+ // src/utils/is-skapxd-result-expression.ts
1288
+ function isSkapxdResultExpression(node, typeContext) {
1289
+ return isSkapxdResultType(typeContext.services.getTypeAtLocation(node), typeContext);
1290
+ }
1291
+
1292
+ // src/utils/is-result-error-member.ts
1293
+ function isResultErrorMember(node, resultName) {
1294
+ const unwrappedNode = unwrapExpression(node);
1295
+ return unwrappedNode.type === "MemberExpression" && unwrappedNode.object.type === "Identifier" && unwrappedNode.object.name === resultName && isMemberPropertyNamed(unwrappedNode, "error");
1296
+ }
1297
+
1298
+ // src/utils/result-err-preserves-cause.ts
1299
+ function resultErrPreservesCause(node, resultName) {
1300
+ const unwrappedNode = unwrapExpression(node);
1301
+ if (isResultErrorMember(unwrappedNode, resultName)) {
1302
+ return true;
1303
+ }
1304
+ if (unwrappedNode.type !== "ObjectExpression") {
1305
+ return false;
1306
+ }
1307
+ return unwrappedNode.properties.some((property) => {
1308
+ if (property.type !== "Property" || !isPropertyKeyNamed(property, "cause")) {
1309
+ return false;
1310
+ }
1311
+ return isResultErrorMember(property.value, resultName);
1312
+ });
1313
+ }
1314
+
1315
+ // src/rules/result-error-requires-cause.ts
1316
+ var resultErrorRequiresCause = {
1317
+ meta: {
1318
+ type: "problem",
1319
+ docs: {
1320
+ description: "Exige preservar result.error como cause cuando una funcion que retorna Result transforma un Result fallido en Result.err."
1321
+ },
1322
+ messages: {
1323
+ missingCause: "El error de `{{name}}` ya existe como `{{name}}.error`. Preservalo en Result.err con `cause: {{name}}.error`."
1324
+ },
1325
+ schema: []
1326
+ },
1327
+ create(context) {
1328
+ const typeContext = getTypeContext(context);
1329
+ return {
1330
+ IfStatement(node) {
1331
+ const resultGuard = getFailedResultGuard(node.test);
1332
+ if (!typeContext || !resultGuard || !isSkapxdResultExpression(resultGuard.node, typeContext) || !isInsideSkapxdResultReturningFunction(node, typeContext)) {
1333
+ return;
1334
+ }
1335
+ for (const resultErrCall of getOwnResultErrCalls(node.consequent)) {
1336
+ if (!isSkapxdResultErrCall(resultErrCall, typeContext)) {
1337
+ continue;
1338
+ }
1339
+ if (resultErrCall.arguments.length === 0) {
1340
+ continue;
1341
+ }
1342
+ if (resultErrPreservesCause(resultErrCall.arguments[0], resultGuard.name)) {
1343
+ continue;
1344
+ }
1345
+ context.report({
1346
+ data: {
1347
+ name: resultGuard.name
1348
+ },
1349
+ messageId: "missingCause",
1350
+ node: resultErrCall
1351
+ });
1352
+ }
1353
+ }
1354
+ };
1355
+ }
1356
+ };
1357
+
1358
+ // src/utils/count-own-use-state-calls-in-node.ts
1359
+ function countOwnUseStateCallsInNode(node) {
1360
+ if (!isAstNode(node)) {
1361
+ return 0;
1362
+ }
1363
+ if (isFunctionNode(node)) {
1364
+ return 0;
1365
+ }
1366
+ const ownCount = node.type === "CallExpression" && isCalleeNamed(node.callee, ["useState"]) ? 1 : 0;
1367
+ return ownCount + getNodeChildren(node).reduce(
1368
+ (total, child) => total + countOwnUseStateCallsInNode(child),
1369
+ 0
1370
+ );
1371
+ }
1372
+
1373
+ // src/utils/count-own-use-state-calls.ts
1374
+ function countOwnUseStateCalls(node) {
1375
+ const body = node.body;
1376
+ return isAstNode(body) ? countOwnUseStateCallsInNode(body) : 0;
1377
+ }
1378
+
1379
+ // src/utils/get-function-line-count.ts
1380
+ function getFunctionLineCount(node) {
1381
+ return node.loc ? node.loc.end.line - node.loc.start.line + 1 : 0;
1382
+ }
1383
+
1384
+ // src/utils/get-max-hook-size-options.ts
1385
+ function getMaxHookSizeOptions(options = {}) {
1386
+ return {
1387
+ maxLines: options.maxLines ?? 120,
1388
+ maxUseState: options.maxUseState ?? 1
1389
+ };
1390
+ }
1391
+
1392
+ // src/utils/is-hook-name.ts
1393
+ function isHookName(name) {
1394
+ return /^use[A-Z0-9]/.test(name ?? "");
1395
+ }
1396
+
1397
+ // src/rules/max-hook-size.ts
1398
+ var maxHookSize = {
1399
+ meta: {
1400
+ type: "suggestion",
1401
+ docs: {
1402
+ description: "Limita el tama\xF1o y cantidad de estados propios en hooks React."
1403
+ },
1404
+ messages: {
1405
+ tooLargeHook: "El hook `{{name}}` es demasiado grande: tiene {{lines}} lineas. Maximo permitido: {{maxLines}} lineas. Extrae efectos, handlers o flujos a hooks/archivos semanticos.",
1406
+ tooManyUseState: "El hook `{{name}}` declara {{useStateCount}} useState. Maximo permitido: {{maxUseState}}. Usa useReducer con acciones semanticas cuando varios campos cambian juntos, o extrae estado a hooks especializados."
1407
+ },
1408
+ schema: [
1409
+ {
1410
+ additionalProperties: false,
1411
+ properties: {
1412
+ maxLines: { type: "number" },
1413
+ maxUseState: { type: "number" }
1414
+ },
1415
+ type: "object"
1416
+ }
1417
+ ]
1418
+ },
1419
+ create(context) {
1420
+ const options = getMaxHookSizeOptions(context.options[0]);
1421
+ function reportIfOversizedHook(node, name, reportNode = node) {
1422
+ if (!isHookName(name)) {
1423
+ return;
1424
+ }
1425
+ const lines = getFunctionLineCount(node);
1426
+ const useStateCount = countOwnUseStateCalls(node);
1427
+ if (lines > options.maxLines) {
1428
+ context.report({
1429
+ data: {
1430
+ lines: String(lines),
1431
+ maxLines: String(options.maxLines),
1432
+ name
1433
+ },
1434
+ messageId: "tooLargeHook",
1435
+ node: reportNode
1436
+ });
1437
+ }
1438
+ if (useStateCount > options.maxUseState) {
1439
+ context.report({
1440
+ data: {
1441
+ maxUseState: String(options.maxUseState),
1442
+ name,
1443
+ useStateCount: String(useStateCount)
1444
+ },
1445
+ messageId: "tooManyUseState",
1446
+ node: reportNode
1447
+ });
1448
+ }
1449
+ }
1450
+ return {
1451
+ ArrowFunctionExpression(node) {
1452
+ reportIfOversizedHook(
1453
+ node,
1454
+ getParentFunctionName(node),
1455
+ getParentFunctionReportNode(node)
1456
+ );
1457
+ },
1458
+ FunctionDeclaration(node) {
1459
+ reportIfOversizedHook(node, node.id?.name, node.id ?? node);
1460
+ },
1461
+ FunctionExpression(node) {
1462
+ reportIfOversizedHook(
1463
+ node,
1464
+ getFunctionExpressionName(node),
1465
+ getParentFunctionReportNode(node)
1466
+ );
1467
+ }
1468
+ };
1469
+ }
1470
+ };
1471
+
1472
+ // src/utils/count-parent-segments.ts
1473
+ function countParentSegments(source) {
1474
+ let count = 0;
1475
+ for (const part of source.split("/")) {
1476
+ if (part === "..") {
1477
+ count += 1;
1478
+ continue;
1479
+ }
1480
+ if (part === ".") {
1481
+ continue;
1482
+ }
1483
+ break;
1484
+ }
1485
+ return count;
1486
+ }
1487
+
1488
+ // src/rules/no-deep-relative-imports.ts
1489
+ var noDeepRelativeImports = {
1490
+ meta: {
1491
+ type: "suggestion",
1492
+ docs: {
1493
+ description: "Limita la profundidad de los imports relativos (`../`). Empuja hacia estructuras planas o alias de ruta."
1494
+ },
1495
+ messages: {
1496
+ deepRelativeImport: "El import `{{source}}` sube {{depth}} niveles con `../`. Maximo permitido: {{maxDepth}}. Usa un alias de ruta (ej. `@/...`) o acerca el modulo a quien lo usa."
1497
+ },
1498
+ schema: [
1499
+ {
1500
+ additionalProperties: false,
1501
+ properties: {
1502
+ maxDepth: { type: "number" }
1503
+ },
1504
+ type: "object"
1505
+ }
1506
+ ]
1507
+ },
1508
+ create(context) {
1509
+ const maxDepth = context.options[0]?.maxDepth ?? 0;
1510
+ function reportIfTooDeep(source) {
1511
+ if (!source || typeof source.value !== "string") {
1512
+ return;
1513
+ }
1514
+ const depth = countParentSegments(source.value);
1515
+ if (depth <= maxDepth) {
1516
+ return;
1517
+ }
1518
+ context.report({
1519
+ data: {
1520
+ depth: String(depth),
1521
+ maxDepth: String(maxDepth),
1522
+ source: source.value
1523
+ },
1524
+ messageId: "deepRelativeImport",
1525
+ node: source
1526
+ });
1527
+ }
1528
+ return {
1529
+ ExportAllDeclaration(node) {
1530
+ reportIfTooDeep(node.source);
1531
+ },
1532
+ ExportNamedDeclaration(node) {
1533
+ reportIfTooDeep(node.source);
1534
+ },
1535
+ ImportDeclaration(node) {
1536
+ reportIfTooDeep(node.source);
1537
+ },
1538
+ ImportExpression(node) {
1539
+ reportIfTooDeep(node.source);
1540
+ }
1541
+ };
1542
+ }
1543
+ };
1544
+
1545
+ // src/rules/no-functions-inside-components.ts
1546
+ var noFunctionsInsideComponents = {
1547
+ meta: {
1548
+ type: "suggestion",
1549
+ docs: {
1550
+ description: "Prohibe definir funciones dentro de componentes React; se recrean en cada render."
1551
+ },
1552
+ messages: {
1553
+ functionInsideComponent: "No definas funciones dentro del componente `{{component}}`: se recrean en cada render. Muevela a un hook (`useX`) o a un helper fuera del componente."
1554
+ },
1555
+ schema: []
1556
+ },
1557
+ create(context) {
1558
+ function isComponentFunction(node) {
1559
+ return isFunctionNode(node) && isPascalCaseName(getFunctionName(node));
1560
+ }
1561
+ function reportIfInsideComponent(node) {
1562
+ const enclosingFunction = getContainingFunction(node);
1563
+ if (!enclosingFunction || !isComponentFunction(enclosingFunction)) {
1564
+ return;
1565
+ }
1566
+ context.report({
1567
+ data: {
1568
+ component: getFunctionName(enclosingFunction)
1569
+ },
1570
+ messageId: "functionInsideComponent",
1571
+ node
1572
+ });
1573
+ }
1574
+ return {
1575
+ ArrowFunctionExpression: reportIfInsideComponent,
1576
+ FunctionDeclaration: reportIfInsideComponent,
1577
+ FunctionExpression: reportIfInsideComponent
1578
+ };
1579
+ }
1580
+ };
1581
+
1582
+ // src/rules/no-try-catch.ts
1583
+ var noTryCatch = {
1584
+ meta: {
1585
+ type: "suggestion",
1586
+ docs: {
1587
+ description: "Prohibe try/catch; usa trySafe de @skapxd/result para modelar el error como Result."
1588
+ },
1589
+ messages: {
1590
+ noTryCatch: "Usa `trySafe` de @skapxd/result en lugar de try/catch. El error queda modelado como Result en vez de saltar como excepcion."
1591
+ },
1592
+ schema: []
1593
+ },
1594
+ create(context) {
1595
+ return {
1596
+ TryStatement(node) {
1597
+ context.report({
1598
+ messageId: "noTryCatch",
1599
+ node
1600
+ });
1601
+ }
1602
+ };
1603
+ }
1604
+ };
1605
+
1606
+ // src/rules/prefer-ts-pattern.ts
1607
+ var preferTsPattern = {
1608
+ meta: {
1609
+ type: "suggestion",
1610
+ docs: {
1611
+ description: "Prefiere match() de ts-pattern sobre switch/case y ternarios anidados."
1612
+ },
1613
+ messages: {
1614
+ noSwitch: "Usa `match()` de ts-pattern en lugar de switch/case para un control de flujo exhaustivo y tipado.",
1615
+ noNestedTernary: "Usa `match()` de ts-pattern en lugar de ternarios anidados; mejora la legibilidad y la exhaustividad."
1616
+ },
1617
+ schema: []
1618
+ },
1619
+ create(context) {
1620
+ return {
1621
+ ConditionalExpression(node) {
1622
+ if (node.parent?.type === "ConditionalExpression") {
1623
+ context.report({
1624
+ messageId: "noNestedTernary",
1625
+ node
1626
+ });
1627
+ }
1628
+ },
1629
+ SwitchStatement(node) {
1630
+ context.report({
1631
+ messageId: "noSwitch",
1632
+ node
1633
+ });
1634
+ }
1635
+ };
1636
+ }
1637
+ };
1638
+
1639
+ // src/rules/no-jsx-ternary-null.ts
1640
+ var noJsxTernaryNull = {
1641
+ meta: {
1642
+ type: "suggestion",
1643
+ docs: {
1644
+ description: "Prefiere `condicion && <Elemento />` sobre un ternario con `null` al renderizar JSX."
1645
+ },
1646
+ messages: {
1647
+ preferLogicalAnd: "Usa `condicion && elemento` en lugar de un ternario con `null` para renderizar JSX condicional."
1648
+ },
1649
+ schema: []
1650
+ },
1651
+ create(context) {
1652
+ function isNullLiteral(node) {
1653
+ return node?.type === "Literal" && node.value === null;
1654
+ }
1655
+ return {
1656
+ ConditionalExpression(node) {
1657
+ const container = node.parent;
1658
+ if (container?.type !== "JSXExpressionContainer") {
1659
+ return;
1660
+ }
1661
+ const host = container.parent;
1662
+ if (host?.type !== "JSXElement" && host?.type !== "JSXFragment") {
1663
+ return;
1664
+ }
1665
+ if (!isNullLiteral(node.alternate) && !isNullLiteral(node.consequent)) {
1666
+ return;
1667
+ }
1668
+ context.report({
1669
+ messageId: "preferLogicalAnd",
1670
+ node
1671
+ });
1672
+ }
1673
+ };
1674
+ }
1675
+ };
1676
+
1677
+ // src/utils/is-promise-type.ts
1678
+ function isPromiseType(type, typeContext) {
1679
+ return typeContext.checker.getPromisedTypeOfPromise(type) !== void 0;
1680
+ }
1681
+
1682
+ // src/rules/no-promise-chain.ts
1683
+ var defaultMethods = ["catch", "finally", "then"];
1684
+ var noPromiseChain = {
1685
+ meta: {
1686
+ type: "suggestion",
1687
+ docs: {
1688
+ description: "Prohibe encadenar .then/.catch/.finally en promesas; usa await (envuelto en trySafe)."
1689
+ },
1690
+ messages: {
1691
+ noPromiseChain: "No encadenes `.{{method}}()` en una promesa. La unica forma de tratar funciones asincronas es `await` (envuelto en `trySafe`)."
1692
+ },
1693
+ schema: [
1694
+ {
1695
+ additionalProperties: false,
1696
+ properties: {
1697
+ methods: {
1698
+ items: { type: "string" },
1699
+ type: "array"
1700
+ }
1701
+ },
1702
+ type: "object"
1703
+ }
1704
+ ]
1705
+ },
1706
+ create(context) {
1707
+ const methods = context.options[0]?.methods ?? defaultMethods;
1708
+ const typeContext = getTypeContext(context);
1709
+ return {
1710
+ CallExpression(node) {
1711
+ const callee = node.callee;
1712
+ if (callee.type !== "MemberExpression") {
1713
+ return;
1714
+ }
1715
+ const method = methods.find((name) => isMemberPropertyNamed(callee, name));
1716
+ if (!method) {
1717
+ return;
1718
+ }
1719
+ if (typeContext) {
1720
+ const type = typeContext.services.getTypeAtLocation(callee.object);
1721
+ if (!isPromiseType(type, typeContext)) {
1722
+ return;
1723
+ }
1724
+ }
1725
+ context.report({
1726
+ data: { method },
1727
+ messageId: "noPromiseChain",
1728
+ node: callee.property
1729
+ });
1730
+ }
1731
+ };
1732
+ }
1733
+ };
1734
+
1735
+ // src/shared/rules.ts
1736
+ var rules = {
1737
+ "one-root-function-per-file": oneRootFunctionPerFile,
1738
+ "jsx-return-name-pascal-case": jsxReturnNamePascalCase,
1739
+ "async-functions-return-result": asyncFunctionsReturnResult,
1740
+ "no-ad-hoc-ok-result": noAdHocOkResult,
1741
+ "await-requires-try-safe": awaitRequiresTrySafe,
1742
+ "result-error-requires-cause": resultErrorRequiresCause,
1743
+ "max-hook-size": maxHookSize,
1744
+ "no-deep-relative-imports": noDeepRelativeImports,
1745
+ "no-functions-inside-components": noFunctionsInsideComponents,
1746
+ "no-try-catch": noTryCatch,
1747
+ "prefer-ts-pattern": preferTsPattern,
1748
+ "no-jsx-ternary-null": noJsxTernaryNull,
1749
+ "no-promise-chain": noPromiseChain
1750
+ };
1751
+
1752
+ // src/index.ts
1753
+ var plugin = {
1754
+ // Diccionario dinámico de configs (cada entrada es un flat config o un array
1755
+ // de ellos). Se tipa laxo, como es convención en los plugins de ESLint, para
1756
+ // que `skapxd.configs.<preset>` sea accesible desde el config del consumidor.
1757
+ configs: {},
1758
+ meta: {
1759
+ name: "@skapxd/eslint-opinionated",
1760
+ version: "0.1.0"
1761
+ },
1762
+ rules
1763
+ };
1764
+ var sharedConfigs = createSharedConfigs(plugin);
1765
+ var nextConfigs = createNextConfigs(plugin);
1766
+ var astroConfigs = createAstroConfigs(plugin);
1767
+ plugin.configs = {
1768
+ ...sharedConfigs,
1769
+ astro: astroConfigs,
1770
+ next: nextConfigs,
1771
+ shared: sharedConfigs,
1772
+ strict: strictConfig
1773
+ };
1774
+ var configs = plugin.configs;
1775
+ var index_default = plugin;
1776
+ // Annotate the CommonJS export names for ESM import in node:
1777
+ 0 && (module.exports = {
1778
+ configs,
1779
+ rules
1780
+ });
1781
+ //# sourceMappingURL=index.js.map