Skip to content

Commit 457c43d

Browse files
committed
fix: prevent package name cross-contamination with word boundaries
- Fix critical regex bug where 'zip' would match 'unzip' causing wrong version updates - Add word boundaries (\b) to package regex in dependency file parser - Prevent 'unzip: ^6.0' from being overwritten with 'zip' version (3.0.0) - Add comprehensive tests for package name disambiguation - Covers edge cases like vue/vue-router and other similar package names - Resolves issue where constraint preservation failed due to substring matching Fixes: unzip: ^6.0 → ^3.0.0 (wrong) now correctly becomes unzip: ^6.0 → ^6.0.0
1 parent 4cad5c2 commit 457c43d

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

src/buddy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ export class Buddy {
328328
// Use ts-pkgx to resolve latest versions
329329
const resolved = await resolveDependencyFile(file.path)
330330

331-
for (const dep of resolved.allDependencies || []) {
331+
for (const dep of resolved.allDependencies || []) {
332332
// Compare constraint version with resolved version
333333
if (dep.constraint !== dep.version && dep.version) {
334334
// Extract version prefix (^, ~, >=, etc.) from constraint

src/utils/dependency-file-parser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,9 @@ export async function updateDependencyFile(filePath: string, content: string, up
141141

142142
// Create regex to find the package line and update its version
143143
// Handle various YAML formats: "package: version", "package:version", " package: ^version"
144+
// Use word boundaries to prevent partial matches (e.g., "zip" matching "unzip")
144145
const packageRegex = new RegExp(
145-
`(\\s*${cleanPackageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*:\\s*)([^\\n\\r]*)`,
146+
`(\\s*\\b${cleanPackageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b\\s*:\\s*)([^\\n\\r]*)`,
146147
'g',
147148
)
148149

test/dependency-file-parser.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,73 @@ devDependencies:
494494
expect(result[0].type).toBe('update')
495495
})
496496
})
497+
498+
describe('updateDependencyFile - package name disambiguation', () => {
499+
it('should correctly update similar package names without cross-contamination', async () => {
500+
const content = `dependencies:
501+
zip: ^3.0
502+
unzip: ^6.0
503+
node: ^22.12.0`
504+
505+
const updates: PackageUpdate[] = [
506+
{
507+
name: 'zip',
508+
currentVersion: '^3.0',
509+
newVersion: '3.0.0',
510+
updateType: 'patch',
511+
dependencyType: 'dependencies',
512+
file: 'deps.yaml',
513+
metadata: undefined,
514+
},
515+
{
516+
name: 'unzip',
517+
currentVersion: '^6.0',
518+
newVersion: '6.0.0',
519+
updateType: 'patch',
520+
dependencyType: 'dependencies',
521+
file: 'deps.yaml',
522+
metadata: undefined,
523+
},
524+
]
525+
526+
const result = await updateDependencyFile('deps.yaml', content, updates)
527+
528+
// Should preserve constraints and update to correct versions
529+
expect(result).toContain('zip: ^3.0.0')
530+
expect(result).toContain('unzip: ^6.0.0')
531+
532+
// Should NOT have cross-contamination (wrong packages having wrong versions)
533+
expect(result).not.toContain('unzip: ^3.0.0')
534+
expect(result).not.toMatch(/^\s*zip: \^6\.0\.0/m)
535+
536+
// Should preserve other packages unchanged
537+
expect(result).toContain('node: ^22.12.0')
538+
})
539+
540+
it('should handle packages with shared substrings correctly', async () => {
541+
const content = `dependencies:
542+
vue: ^3.0.0
543+
vue-router: ^4.0.0
544+
@vue/compiler-sfc: ^3.0.0`
545+
546+
const updates: PackageUpdate[] = [
547+
{
548+
name: 'vue',
549+
currentVersion: '^3.0.0',
550+
newVersion: '3.5.0',
551+
updateType: 'minor',
552+
dependencyType: 'dependencies',
553+
file: 'deps.yaml',
554+
metadata: undefined,
555+
},
556+
]
557+
558+
const result = await updateDependencyFile('deps.yaml', content, updates)
559+
560+
// Should only update 'vue', not 'vue-router' or '@vue/compiler-sfc'
561+
expect(result).toContain('vue: ^3.5.0')
562+
expect(result).toContain('vue-router: ^4.0.0')
563+
expect(result).toContain('@vue/compiler-sfc: ^3.0.0')
564+
})
565+
})
497566
})

0 commit comments

Comments
 (0)