Skip to content

Commit 786cd63

Browse files
committed
Recursively merge repo and package options (#1007)
1 parent f8663ba commit 786cd63

3 files changed

+55
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "major",
3+
"comment": "Recursively merge repo and package options. This has a very slight chance of causing a change in behavior if anyone had both a repo-level config, and some package-level package.json \"beachball\" config that set nested object options (which was never intentionally supported).",
4+
"packageName": "beachball",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

src/options/getOptions.ts

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { BeachballOptions } from '../types/BeachballOptions';
1+
import type { BeachballOptions, CliOptions, PackageOptions, RepoOptions } from '../types/BeachballOptions';
22
import { getCliOptions } from './getCliOptions';
33
import { getRepoOptions } from './getRepoOptions';
44
import { getDefaultOptions } from './getDefaultOptions';
@@ -8,6 +8,49 @@ import { getDefaultOptions } from './getDefaultOptions';
88
*/
99
export function getOptions(argv: string[]): BeachballOptions {
1010
const cliOptions = getCliOptions(argv);
11-
// TODO: proper recursive merging
12-
return { ...getDefaultOptions(), ...getRepoOptions(cliOptions), ...cliOptions };
11+
return mergeOptions({
12+
defaultOptions: getDefaultOptions(),
13+
repoOptions: getRepoOptions(cliOptions),
14+
cliOptions,
15+
});
16+
}
17+
18+
/**
19+
* Merge options. Arrays will overwrite.
20+
*/
21+
export function mergeOptions(params: {
22+
defaultOptions?: BeachballOptions;
23+
repoOptions?: RepoOptions;
24+
cliOptions?: CliOptions;
25+
packageOptions?: Partial<PackageOptions>;
26+
}): BeachballOptions {
27+
return mergeObjects(
28+
params.defaultOptions,
29+
params.repoOptions,
30+
params.packageOptions,
31+
params.cliOptions
32+
) as BeachballOptions;
33+
}
34+
35+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
36+
function mergeObjects(...objects: (Record<string, any> | undefined)[]) {
37+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38+
const acc: Record<string, any> = {};
39+
40+
for (const obj of objects) {
41+
if (!obj || typeof obj !== 'object') {
42+
continue;
43+
}
44+
45+
for (const [key, val] of Object.entries(obj)) {
46+
if (val && typeof val === 'object' && !Array.isArray(val)) {
47+
acc[key] = mergeObjects([acc[key] || {}, val]);
48+
} else {
49+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
50+
acc[key] = val;
51+
}
52+
}
53+
}
54+
55+
return acc;
1356
}

src/options/getPackageInfosWithOptions.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getRepoOptions } from './getRepoOptions';
55
import { getDefaultOptions } from './getDefaultOptions';
66
import { env } from '../env';
77
import type { PackageInfos } from '../types/PackageInfo';
8+
import { mergeOptions } from './getOptions';
89

910
/**
1011
* Fill in options to convert `workspace-tools` `PackageInfos` to the format used in this repo,
@@ -34,13 +35,7 @@ export function getPackageInfosWithOptions(wsPackageInfos: WSPackageInfo[]): Pac
3435
peerDependencies: packageJson.peerDependencies,
3536
optionalDependencies: packageJson.optionalDependencies,
3637
private: packageJson.private !== undefined ? packageJson.private : false,
37-
// TODO: proper recursive merging
38-
combinedOptions: {
39-
...defaultOptions,
40-
...repoOptions,
41-
...packageOptions,
42-
...cliOptions,
43-
},
38+
combinedOptions: mergeOptions({ defaultOptions, repoOptions, cliOptions, packageOptions }),
4439
packageOptions,
4540
};
4641
}

0 commit comments

Comments
 (0)