Open alexbjorlig opened 5 months ago
I'm not entirely sure what's going on, but I suspect superform is crashing, causing the whole story creation to fail, so the error we get is actually a result of the component crashing. In the console I see the following error first:
Error extracting stories TypeError: Invalid value used as weak map key TypeError: Invalid value used as weak map key
at WeakMap.set (<anonymous>)
at superForm (sveltekit-superforms_client.js?v=45b27c1d:2356:15)
at instance (Button.stories.svelte?t=1706737241955:162:15)
at init (chunk-PLRMHIPC.js?v=45b27c1d:2190:23)
at new Button_stories (Button.stories.svelte?t=1706737241955:190:3)
at createProxiedComponent (svelte-hooks.js?v=45b27c1d:341:9)
at new ProxyComponent (proxy.js?v=45b27c1d:242:7)
at new Proxy<Button.stories> (proxy.js?v=45b27c1d:349:11)
at construct_svelte_component_dev (chunk-PLRMHIPC.js?v=45b27c1d:2634:22)
at create_fragment (RegisterContext.svelte?v=45b27c1d:37:21)
That trace leads to this (compiled) source, (un-compiled source here):
const _currentPage = get_store_value(page2);
if (((_b = options.warnings) == null ? void 0 : _b.duplicateId) !== false) {
if (!formIds.has(_currentPage)) {
formIds.set(_currentPage, /* @__PURE__ */ new Set([_initialFormId]));
What's happening is that superform tries to do get(page)
, but the (mocked) page
store is undefined
, which then afterwards isn't a valid key for a WeakMap
, causing the crash.
This comment hints that a page mock is always needed, however when using this addon the first render is always with an undefined
page store, and then with a defined one afterwards. The following code in a X.stories.svelte
file:
import { get } from 'svelte/store';
import { page } from '$app/stores';
const pageValue = get(page);
console.log('LOG: from store', { pageValue })
Will lead to these logs in the console:
I'm assuming this happens because we set the store values in a decorator, and the first render of Svelte CSF stories happens before the story function runs, thus without the decorators.
@paoloricciuti I'm wondering if we can initiate the mock SvelteKit stores with a default value at creation time to more closely match a SvelteKit experience? Is that even possible? If so, we should probably try to match the SvelteKit Page
type, something like:
const emptyURL = new URL('');
const initialPageValue = {
url: emptyURL,
params: {},
route: {
id: null
},
status: 200,
error: null,
data: {},
state: {},
form: undefined
}
I'm not entirely sure what's going on, but I suspect superform is crashing, causing the whole story creation to fail, so the error we get is actually a result of the component crashing. In the console I see the following error first:
Error extracting stories TypeError: Invalid value used as weak map key TypeError: Invalid value used as weak map key at WeakMap.set (<anonymous>) at superForm (sveltekit-superforms_client.js?v=45b27c1d:2356:15) at instance (Button.stories.svelte?t=1706737241955:162:15) at init (chunk-PLRMHIPC.js?v=45b27c1d:2190:23) at new Button_stories (Button.stories.svelte?t=1706737241955:190:3) at createProxiedComponent (svelte-hooks.js?v=45b27c1d:341:9) at new ProxyComponent (proxy.js?v=45b27c1d:242:7) at new Proxy<Button.stories> (proxy.js?v=45b27c1d:349:11) at construct_svelte_component_dev (chunk-PLRMHIPC.js?v=45b27c1d:2634:22) at create_fragment (RegisterContext.svelte?v=45b27c1d:37:21)
That trace leads to this (compiled) source, (un-compiled source here):
const _currentPage = get_store_value(page2); if (((_b = options.warnings) == null ? void 0 : _b.duplicateId) !== false) { if (!formIds.has(_currentPage)) { formIds.set(_currentPage, /* @__PURE__ */ new Set([_initialFormId]));
What's happening is that superform tries to do
get(page)
, but the (mocked)page
store isundefined
, which then afterwards isn't a valid key for aWeakMap
, causing the crash.This comment hints that a page mock is always needed, however when using this addon the first render is always with an
undefined
page store, and then with a defined one afterwards. The following code in aX.stories.svelte
file:import { get } from 'svelte/store'; import { page } from '$app/stores'; const pageValue = get(page); console.log('LOG: from store', { pageValue })
Will lead to these logs in the console:
I'm assuming this happens because we set the store values in a decorator, and the first render of Svelte CSF stories happens before the story function runs, thus without the decorators.
@paoloricciuti I'm wondering if we can initiate the mock SvelteKit stores with a default value at creation time to more closely match a SvelteKit experience? Is that even possible? If so, we should probably try to match the SvelteKit
Page
type, something like:const emptyURL = new URL(''); const initialPageValue = { url: emptyURL, params: {}, route: { id: null }, status: 200, error: null, data: {}, state: {}, form: undefined }
I'll have to take a look but I guess the problem is more in the fact that this addon mounts the svelte component to collect the stories. And that is actually run before decorators run.
In theory we could return a default value from the store if the context is not defined to at least solve this kind of errors. In this specific case I'm pretty sure we could "reimplement" the decorator in the addon itself to provide the same values.
I'll try to explore those solutions a bit
... I guess the problem is more in the fact that this addon mounts the svelte component to collect the stories...
Yes I agree this isn't ideal, I hope that we change that behavior during The Great Migration™, but it might not be feasable.
In theory we could return a default value from the store if the context is not defined to at least solve this kind of errors. In this specific case I'm pretty sure we could "reimplement" the decorator in the addon itself to provide the same values.
I'm just thinking a default value that resembles a barebone SvelteKit page would be better DX overall, and not necessarily just to solve this pre-decorator problem, but it might cause gotchas I'm aware of.
... I guess the problem is more in the fact that this addon mounts the svelte component to collect the stories...
Yes I agree this isn't ideal, I hope that we change that behavior during The Great Migration™, but it might not be feasable.
In theory we could return a default value from the store if the context is not defined to at least solve this kind of errors. In this specific case I'm pretty sure we could "reimplement" the decorator in the addon itself to provide the same values.
I'm just thinking a default value that resembles a barebone SvelteKit page would be better DX overall, and not necessarily just to solve this pre-decorator problem, but it might cause gotchas I'm aware of.
I think the headless mount is pretty genius actually maybe it's fixable with a custom AST transform but I agree, having a default that is closer to the sveltekit default can be beneficial
Hi guys, just wanted to say that your commitment to improving the Svelte/Sveltekit/Storybook experience is 10/10 🚀 Hope you find a good solution for this, and if I can help test/debug something just let me know
Same here, it looks like the problem lies in the store mocking, but let me know if you need some Superforms debugging help.
@ciscoheat could we potentially "workaround" this issue for now, by adding another if storybook
case?
Sure, I'll add a fix for the next release.
Added now in 2.8.1, which will make the above test repo work when upgraded, except for the submit functionality, which can be fixed when https://github.com/storybookjs/storybook/pull/26338 is merged.
Describe the bug
I'm trying to write a story, and using sveltekit-superforms.
When calling the function
superForm
fromimport { superForm } from 'sveltekit-superforms/client';
I get the error:Steps to reproduce the behavior
Button.stories.svelte
in storybookExpected behavior
No error.
Screenshots and/or logs
Environment
Additional context
I'm unsure if we are strictly speaking about a bug in Addon-svelte-csf, or if this has something to do with Superforms.