( changedFiles: string[], packages: Map<string, WorkspacePackage>, rootDir: string, config: BumpyConfig, )
| 239 | |
| 240 | /** Map changed files to the packages they belong to */ |
| 241 | export async function findChangedPackages( |
| 242 | changedFiles: string[], |
| 243 | packages: Map<string, WorkspacePackage>, |
| 244 | rootDir: string, |
| 245 | config: BumpyConfig, |
| 246 | ): Promise<string[]> { |
| 247 | const changed = new Set<string>(); |
| 248 | |
| 249 | // Build per-package matchers (per-package patterns override root patterns) |
| 250 | const matchers = new Map<string, picomatch.Matcher>(); |
| 251 | for (const [name, pkg] of packages) { |
| 252 | const pkgConfig = await loadPackageConfig(pkg.dir, config, name); |
| 253 | const patterns = pkgConfig.changedFilePatterns ?? config.changedFilePatterns; |
| 254 | matchers.set(name, picomatch(patterns)); |
| 255 | } |
| 256 | |
| 257 | const ignoredFields = config.ignoredPackageJsonFields ?? ['devDependencies']; |
| 258 | let baseRef: string | null = null; // resolved lazily, only when a package.json needs diffing |
| 259 | |
| 260 | for (const [name, pkg] of packages) { |
| 261 | const pkgRelDir = relative(rootDir, pkg.dir); |
| 262 | // Root package (single-package repo) has an empty relative dir — every changed |
| 263 | // file belongs to it and is matched directly. Other packages only own files |
| 264 | // under their own directory. |
| 265 | const isRoot = pkgRelDir === ''; |
| 266 | const prefix = `${pkgRelDir}/`; |
| 267 | const matcher = matchers.get(name)!; |
| 268 | |
| 269 | let pkgJsonOnlyTrigger = false; |
| 270 | for (const file of changedFiles) { |
| 271 | let relToPackage: string; |
| 272 | if (isRoot) { |
| 273 | relToPackage = file; |
| 274 | } else { |
| 275 | if (!file.startsWith(prefix)) continue; |
| 276 | relToPackage = file.slice(prefix.length); |
| 277 | } |
| 278 | if (!matcher(relToPackage)) continue; |
| 279 | if (relToPackage === 'package.json') { |
| 280 | pkgJsonOnlyTrigger = true; // defer — refine by inspecting which fields changed |
| 281 | } else { |
| 282 | changed.add(name); // any other matched file is a definite change |
| 283 | break; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | // package.json was the only thing that matched — only flag if a publish-affecting |
| 288 | // field actually changed (a dev-only dependency bump shouldn't require a release). |
| 289 | if (!changed.has(name) && pkgJsonOnlyTrigger) { |
| 290 | baseRef ??= getBaseCompareRef(rootDir, config.baseBranch); |
| 291 | if ( |
| 292 | await packageJsonAffectsRelease(rootDir, baseRef, pkgRelDir, ignoredFields, pkg.bumpy?.releaseTriggeringDevDeps) |
| 293 | ) { |
| 294 | changed.add(name); |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 |
searching dependent graphs…