Skip to content

Commit

Permalink
feat: properly handle arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbbreuer committed Nov 18, 2024
1 parent 429eee8 commit 56dd53a
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
14 changes: 9 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import { deepMerge } from './utils'
*
* @param {object} options - The configuration options.
* @param {string} options.name - The name of the configuration file.
* @param {string} [options.cwd] - The current working directory. If not provided, defaults to the current process's working directory.
* @param {string} [options.cwd] - The current working directory.
* @param {T} options.defaultConfig - The default configuration.
* @returns {Promise<T>} The merged configuration object.
* @returns {Promise<T>} The merged configuration.
* @example ```ts
* // imports from example.config.{ts,js} and merges with default config
* await loadConfig({ name: 'example', defaultConfig: { foo: 'bar' } })
* // Merges arrays if both configs are arrays, otherwise does object deep merge
* await loadConfig({
* name: 'example',
* defaultConfig: [{ foo: 'bar' }]
* })
* ```
*/
export async function loadConfig<T extends Record<string, unknown>>({ name, cwd, defaultConfig }: Config<T>): Promise<T> {
export async function loadConfig<T>({ name, cwd, defaultConfig }: Config<T>): Promise<T> {
const configPath = resolve(cwd || process.cwd(), `${name}.config`)

try {
Expand Down
28 changes: 20 additions & 8 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
/**
* Deep merge objects
* Deep merge objects or arrays
*
* @param target - The target object
* @param sources - The source objects
* @returns The merged object
* @example deepMerge({ foo: 'bar' }, { bar: 'baz' }) // { foo: 'bar', bar: 'baz' }
* @param target - The target object or array
* @param sources - The source objects or arrays
* @returns The merged result
*/
export function deepMerge<T extends object>(target: T, ...sources: Array<Partial<T>>): T {
export function deepMerge<T>(target: T, ...sources: Partial<T>[]): T {
if (!sources.length)
return target

const source = sources.shift()
if (!source)
return target

if (Array.isArray(target) && Array.isArray(source)) {
// If both are arrays, concatenate them
return [...target, ...source] as T
}

if (isObject(target) && isObject(source)) {
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const sourceValue = source[key]
if (isObject(sourceValue) && isObject(target[key])) {
target[key] = deepMerge(target[key] as any, sourceValue as any)
if (Array.isArray(sourceValue) && Array.isArray((target as any)[key])) {
// Merge arrays within objects
(target as any)[key] = [...(target as any)[key], ...sourceValue]
}
else if (isObject(sourceValue) && isObject((target as any)[key])) {
// Deep merge nested objects
(target as any)[key] = deepMerge((target as any)[key], sourceValue)
}
else {
// Replace primitive values and objects/arrays that don't match in type
(target as any)[key] = sourceValue
}
}
Expand Down

0 comments on commit 56dd53a

Please sign in to comment.