Skip to content

Commit 3cf5293

Browse files
committed
fix(from-assert): address PR review feedback
- Refuse to transform loose equality (equal/notEqual) in strict mode (behavior change is too risky; must use node:assert/strict) - Strip trailing message arguments (bupkis doesn't support them) - Fix CLI message to reference warnings instead of non-existent TODOs - Fix exclude glob handling to properly match directory segments - Include import modifications in the 'modified' check for file saving
1 parent ff2f7f2 commit 3cf5293

File tree

3 files changed

+41
-14
lines changed

3 files changed

+41
-14
lines changed

packages/from-assert/src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const printResult = (result: TransformResult): void => {
116116

117117
if (result.totalWarnings > 0) {
118118
console.log(
119-
`${ansi.yellow}Note: Search for "TODO: Manual migration needed" in your code for items requiring manual review.${ansi.reset}`,
119+
`${ansi.yellow}Note: Review the warnings above and the referenced files for assertions that may require manual migration.${ansi.reset}`,
120120
);
121121
}
122122
};

packages/from-assert/src/transform.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,20 @@ export const transform = async (
155155
for (const sourceFile of project.getSourceFiles()) {
156156
const filePath = sourceFile.getFilePath();
157157

158-
// Skip excluded files
159-
if (
160-
exclude.some((pattern) => filePath.includes(pattern.replace('**/', '')))
161-
) {
158+
// Skip excluded files using simple substring matching for common patterns
159+
// This handles patterns like '**/node_modules/**' by checking for '/node_modules/'
160+
const shouldExclude = exclude.some((pattern) => {
161+
// Extract the core directory/file name from glob patterns
162+
const corePath = pattern
163+
.replace(/^\*\*\//, '') // Remove leading **/
164+
.replace(/\/\*\*$/, '') // Remove trailing /**
165+
.replace(/\*/g, ''); // Remove remaining wildcards
166+
167+
// Check if the path contains the core pattern as a directory segment
168+
return corePath && filePath.includes(`/${corePath}/`);
169+
});
170+
171+
if (shouldExclude) {
162172
continue;
163173
}
164174

@@ -182,10 +192,13 @@ export const transform = async (
182192
});
183193
warnings.push(...importResult.warnings);
184194

195+
// File is modified if either assertions or imports were changed
196+
const fileModified = fileTransformCount > 0 || importResult.modified;
197+
185198
const fileResult: FileTransformResult = {
186199
errors,
187200
filePath,
188-
modified: fileTransformCount > 0,
201+
modified: fileModified,
189202
transformCount: fileTransformCount,
190203
warnings,
191204
};
@@ -195,7 +208,7 @@ export const transform = async (
195208
totalWarnings += warnings.length;
196209
totalErrors += errors.length;
197210

198-
if (fileTransformCount > 0) {
211+
if (fileModified) {
199212
modifiedFiles++;
200213
if (write) {
201214
await sourceFile.save();

packages/from-assert/src/transformers/assert-transformer.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,24 +184,38 @@ const transformSingleAssert = (
184184
}
185185

186186
// Determine subject and matcher args
187+
// Note: node:assert methods often have an optional trailing 'message' argument
188+
// that bupkis doesn't support, so we strip it for most assertions
187189
const subject = args[0] ?? '';
188-
const matcherArgs = args.slice(1);
190+
const allMatcherArgs = args.slice(1);
189191

190-
// Check for legacy loose assertions and add warning
192+
// For most assertions, only take the expected value, not the message
193+
// 'ok' assertions take only a value with no expected arg
194+
const hasExpectedArg = baseMatcher !== 'ok';
195+
const matcherArgs = hasExpectedArg ? allMatcherArgs.slice(0, 1) : [];
196+
197+
// Check for legacy loose assertions
191198
// Note: baseMatcher will be 'equal' for both 'equal' and 'notEqual' (via getBaseMethod)
192199
if (
193200
assertStyle === 'legacy' &&
194201
isLegacyMethod(baseMatcher) &&
195202
baseMatcher === 'equal'
196203
) {
197-
// In legacy mode with loose equality, transform but return a warning
204+
// In strict mode, refuse to transform loose equality (behavior change is too risky)
205+
if (mode === 'strict') {
206+
throw new Error(
207+
`Loose equality assertion '${method}' cannot be safely transformed - use node:assert/strict`,
208+
);
209+
}
210+
211+
// In best-effort mode, transform but return a warning
198212
// The warning alerts the user that behavior may differ (loose vs strict equality)
199213
const phrase = negated
200214
? `not ${transform.bupkisPhrase}`
201215
: transform.bupkisPhrase;
202216
const newCode =
203217
matcherArgs.length > 0
204-
? `expect(${subject}, '${phrase}', ${matcherArgs.join(', ')})`
218+
? `expect(${subject}, '${phrase}', ${matcherArgs[0]})`
205219
: `expect(${subject}, '${phrase}')`;
206220
call.replaceWithText(newCode);
207221

@@ -257,10 +271,10 @@ const transformSingleAssert = (
257271

258272
if (matcherArgs.length > 0) {
259273
// Handle special case for rejects with Error type
260-
if (baseMatcher === 'rejects' && matcherArgs.length > 0 && !negated) {
261-
newCode = `${expectFn}(${subject}, 'to reject with', ${matcherArgs.join(', ')})`;
274+
if (baseMatcher === 'rejects' && !negated) {
275+
newCode = `${expectFn}(${subject}, 'to reject with', ${matcherArgs[0]})`;
262276
} else {
263-
newCode = `${expectFn}(${subject}, '${phrase}', ${matcherArgs.join(', ')})`;
277+
newCode = `${expectFn}(${subject}, '${phrase}', ${matcherArgs[0]})`;
264278
}
265279
} else {
266280
newCode = `${expectFn}(${subject}, '${phrase}')`;

0 commit comments

Comments
 (0)