@skapxd/eslint-opinionated 0.5.0 → 0.6.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
@@ -764,6 +764,23 @@ function Card() {
764
764
  helper en minúscula **sí** pueden tener funciones dentro — ahí es donde se mueve
765
765
  la lógica.
766
766
 
767
+ **Opciones** (desde v0.6.0) para permitir los dos patrones React idiomáticos
768
+ que la versión estricta bloqueaba — forzarlos a salir del componente producía
769
+ workarounds peores que el problema (`.bind(null, ...)`, adapters artificiales):
770
+
771
+ ```js
772
+ "skapxd/no-functions-inside-components": ["error", {
773
+ allowJsxCallbacks: true, // <button onClick={() => onSelect(id)} />
774
+ allowArrayMapCallbacks: true, // {entries.map((entry) => <Entry key={entry.id} />)}
775
+ }]
776
+ ```
777
+
778
+ Ambas exenciones aplican **solo a funciones anónimas inline en esa posición
779
+ exacta**: el valor directo de una prop JSX, o el primer argumento de `.map(...)`.
780
+ Un handler con nombre en el cuerpo (`const onClick = () => ...`), un callback de
781
+ `useEffect` o un `.forEach` siguen reportándose — la lógica con peso sigue
782
+ viviendo fuera del componente.
783
+
767
784
  ### `skapxd/no-try-catch`
768
785
 
769
786
  Prohíbe `try/catch`. La intención es que los errores se modelen como `Result` en
@@ -1337,6 +1337,37 @@ var noDeepRelativeImports = {
1337
1337
  }
1338
1338
  };
1339
1339
 
1340
+ // src/utils/get-no-functions-inside-components-options.ts
1341
+ function getNoFunctionsInsideComponentsOptions(options = {}) {
1342
+ return {
1343
+ allowArrayMapCallbacks: options.allowArrayMapCallbacks ?? false,
1344
+ allowJsxCallbacks: options.allowJsxCallbacks ?? false
1345
+ };
1346
+ }
1347
+
1348
+ // src/utils/is-anonymous-inline-function.ts
1349
+ function isAnonymousInlineFunction(node) {
1350
+ if (node.type === "ArrowFunctionExpression") {
1351
+ return true;
1352
+ }
1353
+ return node.type === "FunctionExpression" && !node.id;
1354
+ }
1355
+
1356
+ // src/utils/is-array-map-callback.ts
1357
+ function isArrayMapCallback(node) {
1358
+ const parent = node.parent;
1359
+ if (parent?.type !== "CallExpression" || parent.arguments[0] !== node) {
1360
+ return false;
1361
+ }
1362
+ const callee = parent.callee;
1363
+ return callee?.type === "MemberExpression" && !callee.computed && callee.property?.type === "Identifier" && callee.property.name === "map";
1364
+ }
1365
+
1366
+ // src/utils/is-jsx-attribute-callback.ts
1367
+ function isJsxAttributeCallback(node) {
1368
+ return node.parent?.type === "JSXExpressionContainer" && node.parent.parent?.type === "JSXAttribute";
1369
+ }
1370
+
1340
1371
  // src/rules/no-functions-inside-components.ts
1341
1372
  var noFunctionsInsideComponents = {
1342
1373
  meta: {
@@ -1347,17 +1378,39 @@ var noFunctionsInsideComponents = {
1347
1378
  messages: {
1348
1379
  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."
1349
1380
  },
1350
- schema: []
1381
+ schema: [
1382
+ {
1383
+ additionalProperties: false,
1384
+ properties: {
1385
+ allowArrayMapCallbacks: { type: "boolean" },
1386
+ allowJsxCallbacks: { type: "boolean" }
1387
+ },
1388
+ type: "object"
1389
+ }
1390
+ ]
1351
1391
  },
1352
1392
  create(context) {
1393
+ const options = getNoFunctionsInsideComponentsOptions(context.options[0]);
1353
1394
  function isComponentFunction(node) {
1354
1395
  return isFunctionNode(node) && isPascalCaseName(getFunctionName(node));
1355
1396
  }
1397
+ function isAllowedInlineCallback(node) {
1398
+ if (!isAnonymousInlineFunction(node)) {
1399
+ return false;
1400
+ }
1401
+ if (options.allowJsxCallbacks && isJsxAttributeCallback(node)) {
1402
+ return true;
1403
+ }
1404
+ return options.allowArrayMapCallbacks && isArrayMapCallback(node);
1405
+ }
1356
1406
  function reportIfInsideComponent(node) {
1357
1407
  const enclosingFunction = getContainingFunction(node);
1358
1408
  if (!enclosingFunction || !isComponentFunction(enclosingFunction)) {
1359
1409
  return;
1360
1410
  }
1411
+ if (isAllowedInlineCallback(node)) {
1412
+ return;
1413
+ }
1361
1414
  context.report({
1362
1415
  data: {
1363
1416
  component: getFunctionName(enclosingFunction)
@@ -1556,4 +1609,4 @@ var rules = {
1556
1609
  export {
1557
1610
  rules
1558
1611
  };
1559
- //# sourceMappingURL=chunk-QNSHGNVQ.mjs.map
1612
+ //# sourceMappingURL=chunk-5BA4KA37.mjs.map