diff --git a/eslint-factory/src/rules/require-mkdirsync-try-catch.test.ts b/eslint-factory/src/rules/require-mkdirsync-try-catch.test.ts index 62b9daa2039..0277936a62d 100644 --- a/eslint-factory/src/rules/require-mkdirsync-try-catch.test.ts +++ b/eslint-factory/src/rules/require-mkdirsync-try-catch.test.ts @@ -36,7 +36,7 @@ describe("require-mkdirsync-try-catch", () => { }); }); - it("valid: non-fs objects with mkdirSync name are ignored", () => { + it("valid: non-fs receiver names with mkdirSync are ignored", () => { cjsRuleTester.run("require-mkdirsync-try-catch", requireMkdirSyncTryCatchRule, { valid: [`mockFs.mkdirSync(dir, { recursive: true });`, `storage.mkdirSync(dir);`, `myObj.mkdirSync(path);`, `const fs = require("mock-fs"); fs.mkdirSync(dir, { recursive: true });`], invalid: [], @@ -54,6 +54,21 @@ describe("require-mkdirsync-try-catch", () => { cjsRuleTester.run("require-mkdirsync-try-catch", requireMkdirSyncTryCatchRule, { valid: [], invalid: [ + { + code: `fs.mkdirSync(dir, { recursive: true });`, + errors: [ + { + messageId: "requireTryCatch", + data: { arg: "dir" }, + suggestions: [ + { + messageId: "wrapInTryCatch", + output: `try {\n fs.mkdirSync(dir, { recursive: true });\n} catch (err) {\n // TODO: handle filesystem failure for this fs.mkdirSync call.\n throw new Error(\n "fs.mkdirSync failed: " + (err instanceof Error ? err.message : String(err)),\n { cause: err },\n );\n}`, + }, + ], + }, + ], + }, { code: `const fs = require("fs"); fs.mkdirSync(dir, { recursive: true });`, errors: [ diff --git a/eslint-factory/src/rules/require-mkdirsync-try-catch.ts b/eslint-factory/src/rules/require-mkdirsync-try-catch.ts index 3c66ddda9c8..45fd28e7690 100644 --- a/eslint-factory/src/rules/require-mkdirsync-try-catch.ts +++ b/eslint-factory/src/rules/require-mkdirsync-try-catch.ts @@ -25,7 +25,7 @@ export const requireMkdirSyncTryCatchRule = createRule({ defaultOptions: [], create(context) { const sourceCode = context.sourceCode; - const resolveFsSyncMethod = createFsSyncMethodResolver(sourceCode, FS_SYNC_METHODS); + const resolveFsSyncMethod = createFsSyncMethodResolver(sourceCode, FS_SYNC_METHODS, { allowUnboundFsIdentifier: true }); return { CallExpression(node) { diff --git a/eslint-factory/src/rules/try-catch-rule-utils.ts b/eslint-factory/src/rules/try-catch-rule-utils.ts index 2ddcfd71779..089c8f2d9ba 100644 --- a/eslint-factory/src/rules/try-catch-rule-utils.ts +++ b/eslint-factory/src/rules/try-catch-rule-utils.ts @@ -168,6 +168,18 @@ export function createFsSyncMethodResolver(sourceCode: TSESLint.SourceCode, fsSy return false; } + function hasAnyBinding(identifierName: string, scopeNode: TSESTree.Node): boolean { + let scope: SourceCodeScope | null = sourceCode.getScope(scopeNode); + while (scope) { + const variable = scope.set.get(identifierName); + if (variable && variable.defs.length > 0) { + return true; + } + scope = scope.upper; + } + return false; + } + function resolveFsSyncMethodFromIdentifier(node: TSESTree.CallExpression): string | null { const callee = node.callee; if (callee.type !== AST_NODE_TYPES.Identifier) return null; @@ -219,7 +231,7 @@ export function createFsSyncMethodResolver(sourceCode: TSESLint.SourceCode, fsSy if (callee.type === AST_NODE_TYPES.MemberExpression) { if (callee.object.type !== AST_NODE_TYPES.Identifier) return null; - const canUseUnboundFsIdentifier = options.allowUnboundFsIdentifier === true && callee.object.name === "fs"; + const canUseUnboundFsIdentifier = options.allowUnboundFsIdentifier === true && callee.object.name === "fs" && !hasAnyBinding(callee.object.name, callee.object); if (!canUseUnboundFsIdentifier && !isIdentifierBoundToFsModule(callee.object.name, callee.object)) return null; return getFsSyncMethodFromProperty(callee); }