diff --git a/depsynky/src/__tests__/bump.test.ts b/depsynky/src/__tests__/bump.test.ts index 1ccb9e6e..621eb67f 100644 --- a/depsynky/src/__tests__/bump.test.ts +++ b/depsynky/src/__tests__/bump.test.ts @@ -160,4 +160,53 @@ describe('bumpVersion', () => { // dependency should NOT be updated because sync is false expect(pkgB.dependencies?.['pkg-a']).toBe('^1.0.0') }) + + test('does not bump dependent when the link is devDependency', async () => { + const { app, pkg } = buildApp( + { + packages: [ + { name: 'pkg-a', version: '1.0.0' }, + { name: 'pkg-b', version: '1.0.0', devDependencies: { 'pkg-a': 'workspace:*' } }, + { name: 'pkg-c', version: '1.0.0', devDependencies: { 'pkg-a': '^1.0.0' } } + ] + }, + async () => 'patch' + ) + + await app.bumpVersion({ pkgName: 'pkg-a', sync: true }) + + const pkgA = await pkg.read('pkg-a') + const pkgB = await pkg.read('pkg-b') + const pkgC = await pkg.read('pkg-c') + + expect(pkgA.version).toBe('1.0.1') + expect(pkgB.version).toBe('1.0.0') + expect(pkgC.version).toBe('1.0.0') + + expect(pkgB.devDependencies?.['pkg-a']).toBe('workspace:*') + expect(pkgC.devDependencies?.['pkg-a']).toBe('^1.0.1') // synced but not bumped + }) + + test('does not bump recursive dependents when "none" is picked', async () => { + const { app, pkg } = buildApp( + { + packages: [ + { name: 'pkg-a', version: '1.0.0' }, + { name: 'pkg-b', version: '1.0.0', dependencies: { 'pkg-a': '1.0.0' } }, + { name: 'pkg-c', version: '1.0.0', dependencies: { 'pkg-b': '1.0.0' } } + ] + }, + async ({ pkgName }) => (pkgName === 'pkg-b' ? 'none' : 'patch') + ) + + await app.bumpVersion({ pkgName: 'pkg-a', sync: true }) + + const pkgA = await pkg.read('pkg-a') + const pkgB = await pkg.read('pkg-b') + const pkgC = await pkg.read('pkg-c') + + expect(pkgA.version).toBe('1.0.1') + expect(pkgB.version).toBe('1.0.0') + expect(pkgC.version).toBe('1.0.0') // not bumped because pkg-b was not bumped + }) }) diff --git a/depsynky/src/application/application.ts b/depsynky/src/application/application.ts index 137d9b5a..8c091ca9 100644 --- a/depsynky/src/application/application.ts +++ b/depsynky/src/application/application.ts @@ -32,15 +32,21 @@ export class DepSynkyApplication { ) {} public async bumpVersion(args: BumpVersionArgs) { - let pkgName = args.pkgName + const { dependency } = await this._pnpm.findDirectReferences(args.pkgName) - const { dependency, dependents } = await this._pnpm.findRecursiveReferences(pkgName) - const targetPackages = [dependency, ...dependents] + const allPackages = await this._pnpm.searchWorkspaces() + const targetVersions = this._pnpmVersions(allPackages) + + const visited = new Set() + const queue: types.PnpmWorkspace[] = [dependency] - const currentVersions = this._pnpmVersions(targetPackages) - const targetVersions = { ...currentVersions } + while (queue.length > 0) { + const { path: pkgPath, content } = queue.shift()! + if (visited.has(content.name)) { + continue + } + visited.add(content.name) - for (const { path: pkgPath, content } of targetPackages) { if (content.private) { continue // no need to bump the version of private packages } @@ -61,12 +67,20 @@ export class DepSynkyApplication { targetVersions[content.name] = next await this._pkgJson.update(pkgPath, { version: next }) + + // only follow dependents of packages that were actually bumped + const { dependents } = await this._pnpm.findDirectReferences(content.name) + for (const dep of dependents) { + if (!visited.has(dep.content.name)) { + queue.push(dep) + } + } } await this.listVersions() if (args.sync) { logger.info('Syncing versions...') - this.syncVersions({ ...args, targetVersions }) + await this.syncVersions({ ...args, targetVersions }) } } diff --git a/depsynky/src/application/pnpm-service.ts b/depsynky/src/application/pnpm-service.ts index f0457fa2..18f4aedc 100644 --- a/depsynky/src/application/pnpm-service.ts +++ b/depsynky/src/application/pnpm-service.ts @@ -76,12 +76,8 @@ export class PnpmWorkspaceService implements types.PnpmService { public isLocalVersion = (version: string) => version.startsWith(LOCAL_VERSION_PREFIX) private _findDirectDependents = (workspaces: types.PnpmWorkspace[], pkgName: string): types.PnpmWorkspace[] => { - return workspaces.filter( - (w) => - w.content.dependencies?.[pkgName] || - w.content.devDependencies?.[pkgName] || - w.content.peerDependencies?.[pkgName] - ) + // devDependencies are'nt considered as real dependencies for the purpose of bumping versions, so we ignore them here + return workspaces.filter((w) => w.content.dependencies?.[pkgName] || w.content.peerDependencies?.[pkgName]) } public listPublicPackages = async (): Promise => {