eslint-plugin-functype 2.104.2 → 2.104.4
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/dist/rules/prefer-try.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createImportFixer as e,hasFunctypeSymbol as t}from"../utils/import-fixer.js";const n={meta:{type:`suggestion`,hasSuggestions:!0,docs:{description:`Prefer Try for computations that may throw`,recommended:!0},schema:[{type:`object`,properties:{allowThrowInTests:{type:`boolean`,default:!0}},additionalProperties:!1}],messages:{preferTryOverTryCatch:`Prefer Try(() => ...) over try/catch block`,suggestTry:`Replace with Try(() => ...)`,suggestTryFromPromise:`Replace with Try.fromPromise(...)`,suggestAddImport:`Add {{symbol}} import from functype`}},create(n){let r=(n.options[0]||{}).allowThrowInTests!==!1;function i(){let e=n.filename;return/\.(test|spec)\.(ts|js|tsx|jsx)$/.test(e)||e.includes(`__tests__`)||e.includes(`/test/`)||e.includes(`/tests/`)}function a(e){return e.block?.body?.length===1}function o(e){if(!e.handler?.body)return!1;let t=e.handler.body.body;return t.length===0||t.length===1&&(t[0].type===`ReturnStatement`||t[0].type===`ExpressionStatement`)}function s(e){let t=e.block.body[0];return t.type===`ReturnStatement`&&t.argument?.type===`AwaitExpression`||t.type===`ExpressionStatement`&&t.expression?.type===`AwaitExpression`}return{TryStatement(c){if(r&&i()||c.handler&&c.handler.body&&c.handler.body.body.some(e=>e.type===`ThrowStatement`))return;let l=n.sourceCode,u=[];if(a(c)&&o(c)){let n=c.block.body[0];if(n.type===`ReturnStatement`){let r=n.argument,i=l.getText(r);if(s(c)){let e=r.type===`AwaitExpression`?r.argument:r,t=l.getText(e);u.push({messageId:`suggestTryFromPromise`,fix(e){return e.replaceText(c,`return Try.fromPromise(${t})`)}})}else u.push({messageId:`suggestTry`,fix(e){return e.replaceText(c,`return Try(() => ${i})`)}});t(l,`Try`)||u.push({messageId:`suggestAddImport`,data:{symbol:`Try`},fix:e(l,`Try`)})}}n.report({node:c,messageId:`preferTryOverTryCatch`,suggest:u})}}}};export{n as default};
|
|
1
|
+
import{createImportFixer as e,hasFunctypeSymbol as t}from"../utils/import-fixer.js";const n={meta:{type:`suggestion`,hasSuggestions:!0,docs:{description:`Prefer Try for computations that may throw`,recommended:!0},schema:[{type:`object`,properties:{allowThrowInTests:{type:`boolean`,default:!0}},additionalProperties:!1}],messages:{preferTryOverTryCatch:`Prefer Try(() => ...) over try/catch block`,suggestTry:`Replace with Try(() => ...)`,suggestTryFromPromise:`Replace with Try.fromPromise(...)`,suggestAddImport:`Add {{symbol}} import from functype`}},create(n){let r=(n.options[0]||{}).allowThrowInTests!==!1;function i(){let e=n.filename;return/\.(test|spec)\.(ts|js|tsx|jsx)$/.test(e)||e.includes(`__tests__`)||e.includes(`/test/`)||e.includes(`/tests/`)}function a(e){return e.block?.body?.length===1}function o(e){if(!e.handler?.body)return!1;let t=e.handler.body.body;return t.length===0||t.length===1&&(t[0].type===`ReturnStatement`||t[0].type===`ExpressionStatement`)}function s(e){let t=e.block.body[0];return t.type===`ReturnStatement`&&t.argument?.type===`AwaitExpression`||t.type===`ExpressionStatement`&&t.expression?.type===`AwaitExpression`}return{TryStatement(c){if(c.finalizer||r&&i()||c.handler&&c.handler.body&&c.handler.body.body.some(e=>e.type===`ThrowStatement`))return;let l=n.sourceCode,u=[];if(a(c)&&o(c)){let n=c.block.body[0];if(n.type===`ReturnStatement`){let r=n.argument,i=l.getText(r);if(s(c)){let e=r.type===`AwaitExpression`?r.argument:r,t=l.getText(e);u.push({messageId:`suggestTryFromPromise`,fix(e){return e.replaceText(c,`return Try.fromPromise(${t})`)}})}else u.push({messageId:`suggestTry`,fix(e){return e.replaceText(c,`return Try(() => ${i})`)}});t(l,`Try`)||u.push({messageId:`suggestAddImport`,data:{symbol:`Try`},fix:e(l,`Try`)})}}n.report({node:c,messageId:`preferTryOverTryCatch`,suggest:u})}}}};export{n as default};
|
|
2
2
|
//# sourceMappingURL=prefer-try.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-try.js","names":[],"sources":["../../src/rules/prefer-try.ts"],"sourcesContent":["import type { Rule } from \"eslint\"\n\nimport type { ASTNode } from \"../types/ast\"\nimport { createImportFixer, hasFunctypeSymbol } from \"../utils/import-fixer\"\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"suggestion\",\n hasSuggestions: true,\n docs: {\n description: \"Prefer Try for computations that may throw\",\n recommended: true,\n },\n schema: [\n {\n type: \"object\",\n properties: {\n allowThrowInTests: {\n type: \"boolean\",\n default: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n preferTryOverTryCatch: \"Prefer Try(() => ...) over try/catch block\",\n suggestTry: \"Replace with Try(() => ...)\",\n suggestTryFromPromise: \"Replace with Try.fromPromise(...)\",\n suggestAddImport: \"Add {{symbol}} import from functype\",\n },\n },\n\n create(context) {\n const options = context.options[0] || {}\n const allowThrowInTests = options.allowThrowInTests !== false\n\n function isInTestFile() {\n const filename = context.filename\n return (\n /\\.(test|spec)\\.(ts|js|tsx|jsx)$/.test(filename) ||\n filename.includes(\"__tests__\") ||\n filename.includes(\"/test/\") ||\n filename.includes(\"/tests/\")\n )\n }\n\n function isSimpleTryBody(node: ASTNode): boolean {\n return node.block?.body?.length === 1\n }\n\n function isSimpleCatch(node: ASTNode): boolean {\n if (!node.handler?.body) return false\n const catchBody = node.handler.body.body\n return (\n catchBody.length === 0 ||\n (catchBody.length === 1 &&\n (catchBody[0].type === \"ReturnStatement\" || catchBody[0].type === \"ExpressionStatement\"))\n )\n }\n\n function tryBodyHasAwait(node: ASTNode): boolean {\n const stmt = node.block.body[0]\n if (stmt.type === \"ReturnStatement\" && stmt.argument?.type === \"AwaitExpression\") return true\n if (stmt.type === \"ExpressionStatement\" && stmt.expression?.type === \"AwaitExpression\") return true\n return false\n }\n\n return {\n TryStatement(node: ASTNode) {\n // Allow try/catch in test files\n if (allowThrowInTests && isInTestFile()) return\n\n // Allow try/catch that re-throws in the catch block (even with logging)\n if (node.handler && node.handler.body) {\n const catchBody = node.handler.body.body\n const hasRethrow = catchBody.some((stmt: ASTNode) => stmt.type === \"ThrowStatement\")\n if (hasRethrow) return\n }\n\n const sourceCode = context.sourceCode\n const suggest: Rule.SuggestionReportDescriptor[] = []\n\n if (isSimpleTryBody(node) && isSimpleCatch(node)) {\n const tryStmt = node.block.body[0]\n const isReturn = tryStmt.type === \"ReturnStatement\"\n\n // Only suggest when the try body is a return statement — non-return expression\n // replacements would produce syntactically ambiguous code without knowing the context\n if (isReturn) {\n const expr = tryStmt.argument\n const exprText = sourceCode.getText(expr)\n\n if (tryBodyHasAwait(node)) {\n const awaitExpr = expr.type === \"AwaitExpression\" ? expr.argument : expr\n const innerText = sourceCode.getText(awaitExpr)\n suggest.push({\n messageId: \"suggestTryFromPromise\",\n fix(fixer) {\n return fixer.replaceText(node, `return Try.fromPromise(${innerText})`)\n },\n })\n } else {\n suggest.push({\n messageId: \"suggestTry\",\n fix(fixer) {\n return fixer.replaceText(node, `return Try(() => ${exprText})`)\n },\n })\n }\n\n if (!hasFunctypeSymbol(sourceCode, \"Try\")) {\n suggest.push({\n messageId: \"suggestAddImport\",\n data: { symbol: \"Try\" },\n fix: createImportFixer(sourceCode, \"Try\"),\n })\n }\n }\n }\n\n context.report({\n node,\n messageId: \"preferTryOverTryCatch\",\n suggest,\n })\n },\n }\n },\n}\n\nexport default rule\n"],"mappings":"oFAKA,MAAM,EAAwB,CAC5B,KAAM,CACJ,KAAM,aACN,eAAgB,GAChB,KAAM,CACJ,YAAa,6CACb,YAAa,EACf,EACA,OAAQ,CACN,CACE,KAAM,SACN,WAAY,CACV,kBAAmB,CACjB,KAAM,UACN,QAAS,EACX,CACF,EACA,qBAAsB,EACxB,CACF,EACA,SAAU,CACR,sBAAuB,6CACvB,WAAY,8BACZ,sBAAuB,oCACvB,iBAAkB,qCACpB,CACF,EAEA,OAAO,EAAS,CAEd,IAAM,GADU,EAAQ,QAAQ,IAAM,CAAC,EAAA,CACL,oBAAsB,GAExD,SAAS,GAAe,CACtB,IAAM,EAAW,EAAQ,SACzB,MACE,kCAAkC,KAAK,CAAQ,GAC/C,EAAS,SAAS,WAAW,GAC7B,EAAS,SAAS,QAAQ,GAC1B,EAAS,SAAS,SAAS,CAE/B,CAEA,SAAS,EAAgB,EAAwB,CAC/C,OAAO,EAAK,OAAO,MAAM,SAAW,CACtC,CAEA,SAAS,EAAc,EAAwB,CAC7C,GAAI,CAAC,EAAK,SAAS,KAAM,MAAO,GAChC,IAAM,EAAY,EAAK,QAAQ,KAAK,KACpC,OACE,EAAU,SAAW,GACpB,EAAU,SAAW,IACnB,EAAU,EAAE,CAAC,OAAS,mBAAqB,EAAU,EAAE,CAAC,OAAS,sBAExE,CAEA,SAAS,EAAgB,EAAwB,CAC/C,IAAM,EAAO,EAAK,MAAM,KAAK,GAG7B,OAFI,EAAK,OAAS,mBAAqB,EAAK,UAAU,OAAS,mBAC3D,EAAK,OAAS,uBAAyB,EAAK,YAAY,OAAS,iBAEvE,CAEA,MAAO,CACL,aAAa,EAAe,
|
|
1
|
+
{"version":3,"file":"prefer-try.js","names":[],"sources":["../../src/rules/prefer-try.ts"],"sourcesContent":["import type { Rule } from \"eslint\"\n\nimport type { ASTNode } from \"../types/ast\"\nimport { createImportFixer, hasFunctypeSymbol } from \"../utils/import-fixer\"\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"suggestion\",\n hasSuggestions: true,\n docs: {\n description: \"Prefer Try for computations that may throw\",\n recommended: true,\n },\n schema: [\n {\n type: \"object\",\n properties: {\n allowThrowInTests: {\n type: \"boolean\",\n default: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n preferTryOverTryCatch: \"Prefer Try(() => ...) over try/catch block\",\n suggestTry: \"Replace with Try(() => ...)\",\n suggestTryFromPromise: \"Replace with Try.fromPromise(...)\",\n suggestAddImport: \"Add {{symbol}} import from functype\",\n },\n },\n\n create(context) {\n const options = context.options[0] || {}\n const allowThrowInTests = options.allowThrowInTests !== false\n\n function isInTestFile() {\n const filename = context.filename\n return (\n /\\.(test|spec)\\.(ts|js|tsx|jsx)$/.test(filename) ||\n filename.includes(\"__tests__\") ||\n filename.includes(\"/test/\") ||\n filename.includes(\"/tests/\")\n )\n }\n\n function isSimpleTryBody(node: ASTNode): boolean {\n return node.block?.body?.length === 1\n }\n\n function isSimpleCatch(node: ASTNode): boolean {\n if (!node.handler?.body) return false\n const catchBody = node.handler.body.body\n return (\n catchBody.length === 0 ||\n (catchBody.length === 1 &&\n (catchBody[0].type === \"ReturnStatement\" || catchBody[0].type === \"ExpressionStatement\"))\n )\n }\n\n function tryBodyHasAwait(node: ASTNode): boolean {\n const stmt = node.block.body[0]\n if (stmt.type === \"ReturnStatement\" && stmt.argument?.type === \"AwaitExpression\") return true\n if (stmt.type === \"ExpressionStatement\" && stmt.expression?.type === \"AwaitExpression\") return true\n return false\n }\n\n return {\n TryStatement(node: ASTNode) {\n // Try cannot model `finally` — bail on any try statement with a finalizer.\n // Covers two bugs: (1) catch-less try/finally fires a misleading warning\n // with no useful autofix; (2) try/catch/finally autofix would lift the\n // catch into Try(() => …) and silently drop the finally clause. The FP\n // primitive for guaranteed cleanup is IO.bracket, on the lazy IO type.\n // See #206.\n if (node.finalizer) return\n\n // Allow try/catch in test files\n if (allowThrowInTests && isInTestFile()) return\n\n // Allow try/catch that re-throws in the catch block (even with logging)\n if (node.handler && node.handler.body) {\n const catchBody = node.handler.body.body\n const hasRethrow = catchBody.some((stmt: ASTNode) => stmt.type === \"ThrowStatement\")\n if (hasRethrow) return\n }\n\n const sourceCode = context.sourceCode\n const suggest: Rule.SuggestionReportDescriptor[] = []\n\n if (isSimpleTryBody(node) && isSimpleCatch(node)) {\n const tryStmt = node.block.body[0]\n const isReturn = tryStmt.type === \"ReturnStatement\"\n\n // Only suggest when the try body is a return statement — non-return expression\n // replacements would produce syntactically ambiguous code without knowing the context\n if (isReturn) {\n const expr = tryStmt.argument\n const exprText = sourceCode.getText(expr)\n\n if (tryBodyHasAwait(node)) {\n const awaitExpr = expr.type === \"AwaitExpression\" ? expr.argument : expr\n const innerText = sourceCode.getText(awaitExpr)\n suggest.push({\n messageId: \"suggestTryFromPromise\",\n fix(fixer) {\n return fixer.replaceText(node, `return Try.fromPromise(${innerText})`)\n },\n })\n } else {\n suggest.push({\n messageId: \"suggestTry\",\n fix(fixer) {\n return fixer.replaceText(node, `return Try(() => ${exprText})`)\n },\n })\n }\n\n if (!hasFunctypeSymbol(sourceCode, \"Try\")) {\n suggest.push({\n messageId: \"suggestAddImport\",\n data: { symbol: \"Try\" },\n fix: createImportFixer(sourceCode, \"Try\"),\n })\n }\n }\n }\n\n context.report({\n node,\n messageId: \"preferTryOverTryCatch\",\n suggest,\n })\n },\n }\n },\n}\n\nexport default rule\n"],"mappings":"oFAKA,MAAM,EAAwB,CAC5B,KAAM,CACJ,KAAM,aACN,eAAgB,GAChB,KAAM,CACJ,YAAa,6CACb,YAAa,EACf,EACA,OAAQ,CACN,CACE,KAAM,SACN,WAAY,CACV,kBAAmB,CACjB,KAAM,UACN,QAAS,EACX,CACF,EACA,qBAAsB,EACxB,CACF,EACA,SAAU,CACR,sBAAuB,6CACvB,WAAY,8BACZ,sBAAuB,oCACvB,iBAAkB,qCACpB,CACF,EAEA,OAAO,EAAS,CAEd,IAAM,GADU,EAAQ,QAAQ,IAAM,CAAC,EAAA,CACL,oBAAsB,GAExD,SAAS,GAAe,CACtB,IAAM,EAAW,EAAQ,SACzB,MACE,kCAAkC,KAAK,CAAQ,GAC/C,EAAS,SAAS,WAAW,GAC7B,EAAS,SAAS,QAAQ,GAC1B,EAAS,SAAS,SAAS,CAE/B,CAEA,SAAS,EAAgB,EAAwB,CAC/C,OAAO,EAAK,OAAO,MAAM,SAAW,CACtC,CAEA,SAAS,EAAc,EAAwB,CAC7C,GAAI,CAAC,EAAK,SAAS,KAAM,MAAO,GAChC,IAAM,EAAY,EAAK,QAAQ,KAAK,KACpC,OACE,EAAU,SAAW,GACpB,EAAU,SAAW,IACnB,EAAU,EAAE,CAAC,OAAS,mBAAqB,EAAU,EAAE,CAAC,OAAS,sBAExE,CAEA,SAAS,EAAgB,EAAwB,CAC/C,IAAM,EAAO,EAAK,MAAM,KAAK,GAG7B,OAFI,EAAK,OAAS,mBAAqB,EAAK,UAAU,OAAS,mBAC3D,EAAK,OAAS,uBAAyB,EAAK,YAAY,OAAS,iBAEvE,CAEA,MAAO,CACL,aAAa,EAAe,CAa1B,GANI,EAAK,WAGL,GAAqB,EAAa,GAGlC,EAAK,SAAW,EAAK,QAAQ,MACb,EAAK,QAAQ,KAAK,KACP,KAAM,GAAkB,EAAK,OAAS,gBACtD,EAAG,OAGlB,IAAM,EAAa,EAAQ,WACrB,EAA6C,CAAC,EAEpD,GAAI,EAAgB,CAAI,GAAK,EAAc,CAAI,EAAG,CAChD,IAAM,EAAU,EAAK,MAAM,KAAK,GAKhC,GAJiB,EAAQ,OAAS,kBAIpB,CACZ,IAAM,EAAO,EAAQ,SACf,EAAW,EAAW,QAAQ,CAAI,EAExC,GAAI,EAAgB,CAAI,EAAG,CACzB,IAAM,EAAY,EAAK,OAAS,kBAAoB,EAAK,SAAW,EAC9D,EAAY,EAAW,QAAQ,CAAS,EAC9C,EAAQ,KAAK,CACX,UAAW,wBACX,IAAI,EAAO,CACT,OAAO,EAAM,YAAY,EAAM,0BAA0B,EAAU,EAAE,CACvE,CACF,CAAC,CACH,MACE,EAAQ,KAAK,CACX,UAAW,aACX,IAAI,EAAO,CACT,OAAO,EAAM,YAAY,EAAM,oBAAoB,EAAS,EAAE,CAChE,CACF,CAAC,EAGE,EAAkB,EAAY,KAAK,GACtC,EAAQ,KAAK,CACX,UAAW,mBACX,KAAM,CAAE,OAAQ,KAAM,EACtB,IAAK,EAAkB,EAAY,KAAK,CAC1C,CAAC,CAEL,CACF,CAEA,EAAQ,OAAO,CACb,OACA,UAAW,wBACX,SACF,CAAC,CACH,CACF,CACF,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-functype",
|
|
3
|
-
"version": "2.104.
|
|
3
|
+
"version": "2.104.4",
|
|
4
4
|
"description": "Custom ESLint rules for functional TypeScript programming with functype library patterns including Do notation (ESLint 10+)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"@typescript-eslint/rule-tester": "^8.61.1",
|
|
44
44
|
"eslint-config-prettier": "^10.1.8",
|
|
45
45
|
"ts-builds": "^3.2.0",
|
|
46
|
-
"tsdown": "^0.22.
|
|
47
|
-
"functype": "^1.4.
|
|
46
|
+
"tsdown": "^0.22.3",
|
|
47
|
+
"functype": "^1.4.4"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public",
|