Skip to content

Commit

Permalink
style: format with prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Jan 24, 2023
1 parent 6b7a3f7 commit 26477ed
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 222 deletions.
7 changes: 3 additions & 4 deletions .eslintrc
@@ -1,8 +1,7 @@
{
"extends": [
"eslint-config-unjs"
],
"extends": ["eslint-config-unjs"],
"rules": {
"unicorn/prevent-abbreviations": 0
"unicorn/prevent-abbreviations": 0,
"unicorn/no-null": 0
}
}
1 change: 1 addition & 0 deletions .prettierrc
@@ -0,0 +1 @@
{}
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -20,7 +20,7 @@
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint --ext .ts src",
"lint": "eslint --ext .ts src && prettier -c src test",
"prepack": "pnpm build",
"release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
"test": "pnpm lint && pnpm vitest run",
Expand All @@ -32,6 +32,7 @@
"eslint": "^8.32.0",
"eslint-config-unjs": "^0.1.0",
"expect-type": "^0.15.0",
"prettier": "^2.8.3",
"standard-version": "^9.5.0",
"typescript": "^4.9.4",
"unbuild": "^1.1.1",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 23 additions & 9 deletions src/defu.ts
@@ -1,11 +1,16 @@
import type { Merger, DefuFn as DefuFunction, DefuInstance } from "./types";

function isObject (value: any) {
function isObject(value: any) {
return value !== null && typeof value === "object";
}

// Base function to apply defaults
function _defu<T> (baseObject: T, defaults: any, namespace: string = ".", merger?: Merger): T {
function _defu<T>(
baseObject: T,
defaults: any,
namespace = ".",
merger?: Merger
): T {
if (!isObject(defaults)) {
return _defu(baseObject, {}, namespace, merger);
}
Expand All @@ -30,7 +35,12 @@ function _defu<T> (baseObject: T, defaults: any, namespace: string = ".", merger
if (Array.isArray(value) && Array.isArray(object[key])) {
object[key] = [...value, ...object[key]];
} else if (isObject(value) && isObject(object[key])) {
object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger);
object[key] = _defu(
value,
object[key],
(namespace ? `${namespace}.` : "") + key.toString(),
merger
);
} else {
object[key] = value;
}
Expand All @@ -40,25 +50,29 @@ function _defu<T> (baseObject: T, defaults: any, namespace: string = ".", merger
}

// Create defu wrapper with optional merger and multi arg support
export function createDefu (merger?: Merger): DefuFunction {
// eslint-disable-next-line unicorn/no-array-reduce
return (...arguments_) => arguments_.reduce((p, c) => _defu(p, c, "", merger), {} as any);
export function createDefu(merger?: Merger): DefuFunction {
return (...arguments_) =>
// eslint-disable-next-line unicorn/no-array-reduce
arguments_.reduce((p, c) => _defu(p, c, "", merger), {} as any);
}

// Standard version
export const defu = createDefu() as DefuInstance;
export default defu;

// Custom version with function merge support
export const defuFn = createDefu((object, key, currentValue, _namespace) => {
if (typeof object[key] !== "undefined" && typeof currentValue === "function") {
export const defuFn = createDefu((object, key, currentValue) => {
if (
typeof object[key] !== "undefined" &&
typeof currentValue === "function"
) {
object[key] = currentValue(object[key]);
return true;
}
});

// Custom version with function merge support only for defined arrays
export const defuArrayFn = createDefu((object, key, currentValue, _namespace) => {
export const defuArrayFn = createDefu((object, key, currentValue) => {
if (Array.isArray(object[key]) && typeof currentValue === "function") {
object[key] = currentValue(object[key]);
return true;
Expand Down
149 changes: 83 additions & 66 deletions src/types.ts
@@ -1,94 +1,111 @@
export type Input = Record<string | number | symbol, any>
export type IgnoredInput = boolean | number | null | any[] | Record<never, any> | undefined
export type Input = Record<string | number | symbol, any>;
export type IgnoredInput =
| boolean
| number
| null
| any[]
| Record<never, any>
| undefined;

export type Merger = <T extends Input, K extends keyof T>(
object: T,
key: keyof T,
value: T[K],
namespace: string
) => any
) => any;

type nullish = null | undefined | void
type nullish = null | undefined | void;

export type MergeObjects<
Destination extends Input,
Defaults extends Input
> = Destination extends Defaults ? Destination : Omit<Destination, keyof Destination & keyof Defaults> & Omit<Defaults, keyof Destination & keyof Defaults> &
{
-readonly [Key in keyof Destination & keyof Defaults]:
Destination[Key] extends nullish
? Defaults[Key] extends nullish
? nullish
: Defaults[Key]
: Defaults[Key] extends nullish
> = Destination extends Defaults
? Destination
: Omit<Destination, keyof Destination & keyof Defaults> &
Omit<Defaults, keyof Destination & keyof Defaults> & {
-readonly [Key in keyof Destination &
keyof Defaults]: Destination[Key] extends nullish
? Defaults[Key] extends nullish
? nullish
: Defaults[Key]
: Defaults[Key] extends nullish
? Destination[Key]
: Merge<Destination[Key], Defaults[Key]> // eslint-disable-line no-use-before-define
}
: Merge<Destination[Key], Defaults[Key]>; // eslint-disable-line no-use-before-define
};

export type Defu<S extends Input, D extends Array<Input | IgnoredInput>> =
D extends [infer F, ...infer Rest]
? F extends Input
? Rest extends Array<Input | IgnoredInput>
? Defu<MergeObjects<S, F>, Rest>
: MergeObjects<S, F>
: F extends IgnoredInput
? Rest extends Array<Input | IgnoredInput>
? Defu<S, Rest>
: S
: S
export type Defu<
S extends Input,
D extends Array<Input | IgnoredInput>
> = D extends [infer F, ...infer Rest]
? F extends Input
? Rest extends Array<Input | IgnoredInput>
? Defu<MergeObjects<S, F>, Rest>
: MergeObjects<S, F>
: F extends IgnoredInput
? Rest extends Array<Input | IgnoredInput>
? Defu<S, Rest>
: S
: S
: S;

export type DefuFn = <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
export type DefuFn = <
Source extends Input,
Defaults extends Array<Input | IgnoredInput>
>(
source: Source,
...defaults: Defaults
) => Defu<Source, Defaults>
) => Defu<Source, Defaults>;

export interface DefuInstance {
<Source extends Input, Defaults extends Array<Input | IgnoredInput>>(source: Source | IgnoredInput, ...defaults: Defaults): Defu<Source, Defaults>
fn: DefuFn
arrayFn: DefuFn
extend(merger?: Merger): DefuFn
<Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
source: Source | IgnoredInput,
...defaults: Defaults
): Defu<Source, Defaults>;
fn: DefuFn;
arrayFn: DefuFn;
extend(merger?: Merger): DefuFn;
}

export type MergeArrays<Destination, Source> = Destination extends Array<infer DestinationType>
export type MergeArrays<Destination, Source> = Destination extends Array<
infer DestinationType
>
? Source extends Array<infer SourceType>
? Array<DestinationType | SourceType>
: Source | Array<DestinationType>
: Source | Destination
: Source | Destination;

export type Merge<
Destination extends Input,
Defaults extends Input
> =
export type Merge<Destination extends Input, Defaults extends Input> =
// Remove explicitly null types
Destination extends nullish
? Defaults extends nullish
? nullish
: Defaults
: Defaults extends nullish
? Defaults extends nullish
? nullish
: Defaults
: Defaults extends nullish
? Destination
// Handle arrays
: Destination extends Array<any>
? Defaults extends Array<any>
? MergeArrays<Destination, Defaults>
: Destination | Defaults
// Don't attempt to merge Functions, RegExps, Promises
: Destination extends Function
? Destination | Defaults
: Destination extends RegExp
? Destination | Defaults
: Destination extends Promise<any>
? Destination | Defaults
// Don't attempt to merge Functions, RegExps, Promises
: Defaults extends Function
? Destination | Defaults
: Defaults extends RegExp
? Destination | Defaults
: Defaults extends Promise<any>
? Destination | Defaults
// Ensure we only merge Records
: Destination extends Input
? Defaults extends Input
? MergeObjects<Destination, Defaults>
: Destination | Defaults
: Destination | Defaults
: // Handle arrays
Destination extends Array<any>
? Defaults extends Array<any>
? MergeArrays<Destination, Defaults>
: Destination | Defaults
: // Don't attempt to merge Functions, RegExps, Promises
// eslint-disable-next-line @typescript-eslint/ban-types
Destination extends Function
? Destination | Defaults
: Destination extends RegExp
? Destination | Defaults
: Destination extends Promise<any>
? Destination | Defaults
: // Don't attempt to merge Functions, RegExps, Promises
// eslint-disable-next-line @typescript-eslint/ban-types
Defaults extends Function
? Destination | Defaults
: Defaults extends RegExp
? Destination | Defaults
: Defaults extends Promise<any>
? Destination | Defaults
: // Ensure we only merge Records
Destination extends Input
? Defaults extends Input
? MergeObjects<Destination, Defaults>
: Destination | Defaults
: Destination | Defaults;

0 comments on commit 26477ed

Please sign in to comment.