Skip to content

Commit b34bbf0

Browse files
committed
fix: use deep merge on user-provided rolldown options
1 parent dd0cb18 commit b34bbf0

3 files changed

Lines changed: 62 additions & 7 deletions

File tree

playground/build.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ async function main() {
77
path: "playground/index.ts",
88
// temporary file will be kept inside node_modules/.unrun
99
debug: true,
10+
inputOptions: {
11+
plugins: [],
12+
},
1013
});
1114
}
1215

src/utils/bundle.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
createSourceContextShimsPlugin,
1414
} from "../plugins";
1515
import type { ResolvedOptions } from "../options";
16+
import { deepMerge } from "./deep-merge";
1617

1718
export interface BundleOutput {
1819
chunk: OutputChunk;
@@ -55,17 +56,18 @@ export async function bundle(options: ResolvedOptions): Promise<BundleOutput> {
5556
},
5657
// Hide all logs by default
5758
logLevel: "silent",
58-
// Finally, apply user-provided overrides
59-
...options.inputOptions,
6059
};
6160

61+
// Apply user-provided overrides with deep merge
62+
const mergedInputOptions = deepMerge(inputOptions, options.inputOptions);
63+
6264
// Apply tsconfig if resolved
6365
if (tsconfig) {
64-
inputOptions.tsconfig = tsconfig;
66+
mergedInputOptions.tsconfig = tsconfig;
6567
}
6668

6769
// Setup bundle
68-
const bundle = await rolldown(inputOptions);
70+
const bundle = await rolldown(mergedInputOptions);
6971

7072
// Output options (https://rolldown.rs/reference/config-options#outputoptions)
7173
const outputOptions: OutputOptions = {
@@ -79,12 +81,13 @@ export async function bundle(options: ResolvedOptions): Promise<BundleOutput> {
7981
},
8082
}
8183
: {}),
82-
// Apply user-provided overrides last
83-
...options.outputOptions,
8484
};
8585

86+
// Apply user-provided overrides with deep merge
87+
const mergedOutputOptions = deepMerge(outputOptions, options.outputOptions);
88+
8689
// Generate bundle in memory
87-
const rolldownOutput = await bundle.generate(outputOptions);
90+
const rolldownOutput = await bundle.generate(mergedOutputOptions);
8891

8992
// Verify that the output is not empty
9093
if (!rolldownOutput.output[0]) {

src/utils/deep-merge.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Deeply merges two objects, with the second object taking precedence.
3+
* Arrays are concatenated (not replaced).
4+
* Undefined values in the source object are ignored (do not override target).
5+
* @param target The target object to merge into.
6+
* @param source The source object to merge from.
7+
* @returns A new object that is the result of deeply merging the source into the target.
8+
*/
9+
export function deepMerge<T extends Record<string, any>>(
10+
target: T,
11+
source: Partial<T> | undefined,
12+
): T {
13+
if (!source) {
14+
return target;
15+
}
16+
17+
const result = { ...target };
18+
19+
for (const key in source) {
20+
if (Object.prototype.hasOwnProperty.call(source, key)) {
21+
const targetValue = result[key];
22+
const sourceValue = source[key];
23+
24+
if (sourceValue === undefined) {
25+
continue;
26+
}
27+
28+
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
29+
// Concatenate arrays
30+
result[key] = [...targetValue, ...sourceValue] as any;
31+
} else if (
32+
typeof targetValue === "object" &&
33+
targetValue !== null &&
34+
typeof sourceValue === "object" &&
35+
sourceValue !== null &&
36+
!Array.isArray(targetValue) &&
37+
!Array.isArray(sourceValue)
38+
) {
39+
// Recursively merge objects
40+
result[key] = deepMerge({ ...targetValue }, sourceValue);
41+
} else {
42+
// Override with source value
43+
result[key] = sourceValue as any;
44+
}
45+
}
46+
}
47+
48+
return result;
49+
}

0 commit comments

Comments
 (0)