Skip to content

Commit ee3875f

Browse files
duranbdandelany
authored andcommitted
refactor(workspaces): replace regex with string matching for file extensions
Replace regex-based pattern matching with simple string comparison in `doesFilenameMatchExtension` and `replaceFileExtension` functions to avoid unintended behavior with special characters. Changes: - Use `endsWith()` instead of regex for extension matching - Add support for compound extensions (e.g., 'seqn.txt') - Use string slicing instead of regex replacement - Add validation check before replacing extension - Remove tests for regex special character handling - Add tests for compound extension scenarios This prevents issues where special regex characters in extensions (like `*`, `+`, `.`) would be interpreted as patterns rather than literal strings, making extension matching more predictable and correct.
1 parent 7f8e80b commit ee3875f

2 files changed

Lines changed: 21 additions & 13 deletions

File tree

src/utilities/workspaces.test.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,6 +1390,12 @@ describe('Workspace utility function tests', () => {
13901390
expect(doesFilenameMatchExtension('txt', 'file.txt')).toBe(true);
13911391
expect(doesFilenameMatchExtension('seq', 'test.seq')).toBe(true);
13921392
expect(doesFilenameMatchExtension('json', 'data.json')).toBe(true);
1393+
expect(doesFilenameMatchExtension('seqn.txt', 'sequence.seqn.txt')).toBe(true);
1394+
expect(doesFilenameMatchExtension('seqn.txt', 'sequence.thing.seqn.txt')).toBe(true);
1395+
expect(doesFilenameMatchExtension('seqn.txt', 'sequence.seqn.txt.thing')).toBe(false);
1396+
expect(doesFilenameMatchExtension('seqn.txt', 'sequence.seqn')).toBe(false);
1397+
expect(doesFilenameMatchExtension('seqn.txt', 'sequence.txt')).toBe(false);
1398+
expect(doesFilenameMatchExtension('seqn.txt', 'seqn.txt')).toBe(false);
13931399
});
13941400

13951401
test('Should match extension with leading dot', () => {
@@ -1459,14 +1465,6 @@ describe('Workspace utility function tests', () => {
14591465
expect(doesFilenameMatchExtension('seq', 'sequential.js')).toBe(false);
14601466
});
14611467

1462-
test('Should handle special regex characters in extension', () => {
1463-
// Note: The function does NOT escape regex special characters
1464-
// So these are treated as regex patterns, not literal strings
1465-
expect(doesFilenameMatchExtension('t*t', 'file.tt')).toBe(true); // t*t means "zero or more t" + "t"
1466-
expect(doesFilenameMatchExtension('t+t', 'file.ttt')).toBe(true); // t+t means "one or more t" + "t"
1467-
expect(doesFilenameMatchExtension('t*t', 'file.t')).toBe(true); // matches .t (zero t's + t)
1468-
});
1469-
14701468
test('Should match exact extension only', () => {
14711469
expect(doesFilenameMatchExtension('js', 'file.js')).toBe(true);
14721470
expect(doesFilenameMatchExtension('js', 'file.json')).toBe(false);

src/utilities/workspaces.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,14 +367,24 @@ export function isFileConflictResponse(response: BulkOperationResponse) {
367367
}
368368

369369
export function doesFilenameMatchExtension(extension: string, filename: string) {
370-
return new RegExp(`\\.${extension.replace(/^\./, '')}$`, 'i').test(filename);
370+
// Normalize extension to include leading dot
371+
const normalizedExtension = extension.startsWith('.') ? extension : `.${extension}`;
372+
// Case-insensitive check if filename ends with the extension
373+
return filename.toLowerCase().endsWith(normalizedExtension.toLowerCase());
371374
}
372375

373376
export function replaceFileExtension(filename: string, fromExtension: string, toExtension: string) {
374-
return filename.replace(
375-
new RegExp(`\\.${fromExtension.replace(/^\./, '')}$`, 'i'),
376-
`.${toExtension.replace(/^\./, '')}`,
377-
);
377+
// Normalize extensions to include leading dots
378+
const normalizedFromExtension = fromExtension.startsWith('.') ? fromExtension : `.${fromExtension}`;
379+
const normalizedToExtension = toExtension.startsWith('.') ? toExtension : `.${toExtension}`;
380+
381+
// Check if filename ends with the fromExtension (case-insensitive)
382+
if (!doesFilenameMatchExtension(normalizedFromExtension, filename)) {
383+
return filename;
384+
}
385+
386+
// Replace only the extension at the end, preserving the original case of the filename
387+
return `${filename.slice(0, -normalizedFromExtension.length)}${normalizedToExtension}`;
378388
}
379389

380390
export const WorkspaceApi = {

0 commit comments

Comments
 (0)