From 530593e0f1ddaef40f289c1118005c94a42cac33 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 25 Sep 2026 02:46:26 +0000 Subject: [PATCH 1/2] fix(test): flag recursive rm whose options object carries a non-null assertion --- .../tests/check-test-remove-tree.test.ts | 20 +++++++++++++++++++ scripts/check-test-remove-tree.mjs | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/agent-bundle/tests/check-test-remove-tree.test.ts b/packages/agent-bundle/tests/check-test-remove-tree.test.ts index e368533e0..04731943d 100644 --- a/packages/agent-bundle/tests/check-test-remove-tree.test.ts +++ b/packages/agent-bundle/tests/check-test-remove-tree.test.ts @@ -418,3 +418,23 @@ it('flags promises-namespace and asserted options without maxRetries', () => { `await rm(root, { ${recursiveTrue}, maxRetries: 5 });`, ]))).toEqual([]); }); + +it('unwraps non-null asserted options, alone and nested in other wrappers', () => { + expect(recursiveRmCalls(sample([ + "import { rm } from 'node:fs/promises';", + "import * as fs from 'node:fs/promises';", + "import { promises as fsp } from 'node:fs';", + `await rm(root, { ${recursiveTrue} }!);`, + `await fs.rm(root, { ${recursiveTrue} }!);`, + `await fsp.rm(root, { ${recursiveTrue} }!);`, + `await rm(root, ({ ${recursiveTrue} } as const)!);`, + `await rm(root, { ${recursiveTrue} }! satisfies Options);`, + `await rm(root, { ${recursiveTrue} }!);`, + ]))).toEqual([4, 5, 6, 7, 8, 9].map((line) => expect.objectContaining({ hasRetries: false, line }))); + + expect(bareRecursiveRmFailures('packages/agent-bundle/tests/example.test.ts', sample([ + "import { rm } from 'node:fs/promises';", + `await rm(root, { ${recursiveTrue}, maxRetries: 5 }!);`, + `await rm(root, ({ ${recursiveTrue}, maxRetries: 5 } as const)!);`, + ]))).toEqual([]); +}); diff --git a/scripts/check-test-remove-tree.mjs b/scripts/check-test-remove-tree.mjs index 925cf2ca8..5a8d48f89 100644 --- a/scripts/check-test-remove-tree.mjs +++ b/scripts/check-test-remove-tree.mjs @@ -9,7 +9,7 @@ * only real node:fs(/promises) ImportDeclaration bindings count, only Node-bound * call expressions are considered, and `recursive` / `maxRetries` are read from * the second argument's object-literal properties (including quoted keys, - * shorthand `maxRetries`, and Parenthesized / As / Satisfies wrappers). Nested + * shorthand `maxRetries`, and Parenthesized / As / Satisfies / `` / `!` wrappers). Nested * objects in the path argument, member calls, comments, strings, regexes, and * template substitutions are handled by the AST rather than text masking. * Named `promises` rebinds from `fs` / `node:fs` count as `.rm` carriers. @@ -125,6 +125,7 @@ const unwrapExpression = (node) => { || ts.isAsExpression(current) || ts.isSatisfiesExpression(current) || ts.isTypeAssertionExpression(current) + || ts.isNonNullExpression(current) ) ) { current = current.expression; From f27153c2dbba7ce1d4497c018cd24295ee222275 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 25 Sep 2026 02:49:00 +0000 Subject: [PATCH 2/2] fix(test): unwrap instantiation expressions around rm options --- packages/agent-bundle/tests/check-test-remove-tree.test.ts | 3 ++- scripts/check-test-remove-tree.mjs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/agent-bundle/tests/check-test-remove-tree.test.ts b/packages/agent-bundle/tests/check-test-remove-tree.test.ts index 04731943d..e4ae99fd6 100644 --- a/packages/agent-bundle/tests/check-test-remove-tree.test.ts +++ b/packages/agent-bundle/tests/check-test-remove-tree.test.ts @@ -430,7 +430,8 @@ it('unwraps non-null asserted options, alone and nested in other wrappers', () = `await rm(root, ({ ${recursiveTrue} } as const)!);`, `await rm(root, { ${recursiveTrue} }! satisfies Options);`, `await rm(root, { ${recursiveTrue} }!);`, - ]))).toEqual([4, 5, 6, 7, 8, 9].map((line) => expect.objectContaining({ hasRetries: false, line }))); + `await rm(root, ({ ${recursiveTrue} }));`, + ]))).toEqual([4, 5, 6, 7, 8, 9, 10].map((line) => expect.objectContaining({ hasRetries: false, line }))); expect(bareRecursiveRmFailures('packages/agent-bundle/tests/example.test.ts', sample([ "import { rm } from 'node:fs/promises';", diff --git a/scripts/check-test-remove-tree.mjs b/scripts/check-test-remove-tree.mjs index 5a8d48f89..ecfc4f2f5 100644 --- a/scripts/check-test-remove-tree.mjs +++ b/scripts/check-test-remove-tree.mjs @@ -9,7 +9,7 @@ * only real node:fs(/promises) ImportDeclaration bindings count, only Node-bound * call expressions are considered, and `recursive` / `maxRetries` are read from * the second argument's object-literal properties (including quoted keys, - * shorthand `maxRetries`, and Parenthesized / As / Satisfies / `` / `!` wrappers). Nested + * shorthand `maxRetries`, and Parenthesized / As / Satisfies / `` / `!` / instantiation wrappers). Nested * objects in the path argument, member calls, comments, strings, regexes, and * template substitutions are handled by the AST rather than text masking. * Named `promises` rebinds from `fs` / `node:fs` count as `.rm` carriers. @@ -126,6 +126,7 @@ const unwrapExpression = (node) => { || ts.isSatisfiesExpression(current) || ts.isTypeAssertionExpression(current) || ts.isNonNullExpression(current) + || ts.isExpressionWithTypeArguments(current) ) ) { current = current.expression;