Skip to content

Commit 65180bd

Browse files
committed
fix(async-stack): ignore null timeout as 0 (currently resolve immediately)
1 parent 129d0ab commit 65180bd

8 files changed

Lines changed: 42 additions & 38 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@svelte-put/async-stack': patch
3+
---
4+
5+
make sure push configs are merged correctly and recursively but ignoring nullish values

packages/async-stack/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
"bugs": {
6363
"url": "https://github.com/vnphanquang/svelte-put/issues"
6464
},
65+
"dependencies": {
66+
"defu": "^6.1.4"
67+
},
6568
"devDependencies": {
6669
"@internals/tsconfig": "workspace:*"
6770
},

packages/async-stack/src/stack-item.svelte.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class StackItem<
1010
> {
1111
/** @default 'idle' */
1212
state: StackItemState;
13-
config: Required<StackItemInstanceConfig<string, UserComponent>>;
13+
config: StackItemInstanceConfig<string, UserComponent>;
1414
/**
1515
* a promise that resolves when the item is resolved,
1616
* by timing out or via the .resolve method

packages/async-stack/src/stack-item.svelte.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class StackItem {
88
/** @type {import('./types.package').StackItemState} */
99
// eslint-disable-next-line no-undef
1010
state = $state('idle');
11-
/** @type {Required<import('./types.package').StackItemInstanceConfig<string, UserComponent>>} */
11+
/** @type {import('./types.package').StackItemInstanceConfig<string, UserComponent>} */
1212
config;
1313

1414
#internals = {
@@ -26,7 +26,7 @@ export class StackItem {
2626
resolution;
2727

2828
/**
29-
* @param {Required<import('./types.package').StackItemInstanceConfig<string, UserComponent>>} config
29+
* @param {import('./types.package').StackItemInstanceConfig<string, UserComponent>} config
3030
*/
3131
constructor(config) {
3232
this.config = config;

packages/async-stack/src/stack.svelte.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class Stack<VariantMap extends Record<string, Component<any>> = {}> {
1515
/** stack items */
1616
items: StackItem<any>[];
1717
/** global config applied to the entire stack by default */
18-
config: Required<StackItemCommonConfig<string, Component<any>>>;
18+
config: StackItemCommonConfig<string, Component<any>>;
1919
actions: {
2020
/** register the element to render a stack item into */
2121
render: (node: HTMLElement, item: StackItem<any>) => StackItemRenderActionReturn;

packages/async-stack/src/stack.svelte.js

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { defu } from 'defu';
12
import { mount, tick, unmount } from 'svelte';
23

34
import { MissingComponentInCustomPush, NotFoundVariantConfig } from './errors.js';
@@ -59,7 +60,7 @@ export class Stack {
5960
*/
6061
constructor(variantConfigMap, init) {
6162
if (init?.id) this.config.id = init.id;
62-
if (init?.timeout) this.config.timeout = init.timeout;
63+
if (init?.timeout) this.config.timeout = init.timeout || 0;
6364
this.#variantConfigMap = variantConfigMap;
6465
}
6566

@@ -71,11 +72,13 @@ export class Stack {
7172
* @returns {StackItem<any>}
7273
*/
7374
push(variant, config) {
75+
/** @typedef {import('./types.package').StackItemInstanceConfig<string, import('svelte').Component<any>>} InstanceConfig */
76+
77+
/** @typedef {NonNullable<import('./types.package').StackItemCommonConfig<any, import('svelte').Component>['id']>} IdResolver */
78+
7479
// STEP 1: resolve instance config, merge with common config and variant config, if any
75-
/** @type {import('./types.package').StackItemInstanceConfig<string, import('svelte').Component<any>>} */
76-
let instanceConfig;
77-
/** @type {NonNullable<import('./types.package').StackItemCommonConfig<string, import('svelte').Component>['id']>} */
78-
let idResolver;
80+
/** @type {Omit<InstanceConfig,'id'> & { id: IdResolver }} */
81+
let mergedConfig;
7982

8083
if (variant === 'custom') {
8184
const rConfig =
@@ -85,48 +88,35 @@ export class Stack {
8588
if (!rConfig || !rConfig.component) {
8689
throw new MissingComponentInCustomPush();
8790
}
88-
instanceConfig = {
89-
...this.config,
90-
...rConfig,
91-
variant: 'custom',
92-
component: rConfig.component,
93-
props: rConfig.props ?? {},
94-
id: '',
95-
};
96-
idResolver = /** @type {any} */ (rConfig.id) ?? this.config.id;
91+
mergedConfig = defu({ variant: 'custom' }, rConfig, this.config);
9792
} else {
93+
const rConfig =
94+
/** @type {import('./types.package').StackItemByVariantPushConfig<string, import('svelte').Component<any>>} */ (
95+
config
96+
);
9897
const variantConfig = this.#variantConfigMap[variant];
9998
if (!variantConfig) {
10099
throw new NotFoundVariantConfig(variant, Object.keys(this.#variantConfigMap));
101100
}
102-
instanceConfig = {
103-
...this.config,
104-
...variantConfig,
105-
...config,
106-
props: {
107-
...variantConfig.props,
108-
...config?.props,
109-
},
110-
id: '',
111-
};
112-
idResolver = /** @type {any} */ (config?.id) ?? variantConfig.id ?? this.config.id;
101+
mergedConfig = /** @type {typeof mergedConfig} */ (defu(rConfig, variantConfig, this.config));
113102
}
114103

115104
// STEP 2: resolve id for the stack item
116-
if (idResolver === 'counter') {
117-
instanceConfig.id = (++this.#counter).toString();
118-
} else if (idResolver === 'uuid') {
119-
instanceConfig.id =
105+
let id = '';
106+
if (mergedConfig.id === 'counter') {
107+
id = (++this.#counter).toString();
108+
} else if (mergedConfig.id === 'uuid') {
109+
id =
120110
'crypto' in globalThis && crypto.randomUUID
121111
? crypto.randomUUID()
122112
: (++this.#counter).toString();
123113
} else {
124-
instanceConfig.id = idResolver(instanceConfig);
114+
id = mergedConfig.id(mergedConfig);
125115
}
126116

127117
// STEP 3: preparing the `StackItem` instance
128118
/** @type {StackItem<any>} */
129-
let pushed = new StackItem(instanceConfig);
119+
let pushed = new StackItem({ ...mergedConfig, id });
130120
pushed.resolution.then(() => {
131121
this.items = this.items.filter((n) => n.config.id !== pushed.config.id);
132122
});

packages/async-stack/src/types.package.d.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type StackItemCommonConfig<Variant extends string, UserComponent extends
2121
id?:
2222
| 'counter'
2323
| 'uuid'
24-
| ((config: Required<Omit<StackItemInstanceConfig<Variant, UserComponent>, 'id'>>) => string);
24+
| ((config: Omit<StackItemInstanceConfig<Variant, UserComponent>, 'id'>) => string);
2525
};
2626

2727
/** predefined variant config provided while building a {@link Stack} instance */
@@ -37,13 +37,16 @@ export type StackItemVariantConfig<
3737
props?: Omit<ComponentProps<UserComponent>, 'item'>;
3838
};
3939

40-
/** a resolved config for a {@link StackItemInstance} */
40+
/** a resolved config for a {@link StackItem} */
4141
export type StackItemInstanceConfig<
4242
Variant extends string,
4343
UserComponent extends Component<any>,
44-
> = Required<Omit<StackItemVariantConfig<Variant, UserComponent>, 'id'>> & {
44+
> = {
4545
id: string;
4646
timeout: number;
47+
variant: Variant;
48+
component: UserComponent;
49+
props?: Omit<ComponentProps<UserComponent>, 'item'>;
4750
};
4851

4952
/**

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)