@poveste/plugin-svelte 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +4 -4
  2. package/dist/client/MountStory.js +1 -1
  3. package/dist/client/MountStory.svelte +8 -4
  4. package/dist/client/MountVariant.js +1 -1
  5. package/dist/client/MountVariant.svelte +41 -8
  6. package/dist/client/RenderStory.js +1 -1
  7. package/dist/client/RenderStory.svelte +23 -6
  8. package/dist/client/RenderVariant.js +1 -1
  9. package/dist/client/RenderVariant.svelte +19 -6
  10. package/dist/client/Wrap.js +1 -1
  11. package/dist/client/auto-props.js +1 -1
  12. package/dist/client/mount.d.ts +3 -3
  13. package/dist/client/mount.js +1 -1
  14. package/dist/client/render-story-components.d.ts +16 -0
  15. package/dist/client/render-story-components.js +1 -0
  16. package/dist/client/render.d.ts +1 -1
  17. package/dist/client/render.js +1 -1
  18. package/dist/client/util.d.ts +2 -2
  19. package/dist/collect/Story.js +1 -1
  20. package/dist/collect/Story.svelte +18 -8
  21. package/dist/collect/Variant.js +1 -1
  22. package/dist/collect/Variant.svelte +13 -8
  23. package/dist/collect/index.js +1 -1
  24. package/dist/commands/generate-story.client.js +4 -4
  25. package/dist/commands/generate-story.server.js +2 -2
  26. package/dist/context.d.ts +39 -0
  27. package/dist/context.js +35 -0
  28. package/dist/contexts.d.ts +17 -0
  29. package/dist/contexts.js +22 -0
  30. package/dist/helpers.d.ts +11 -2
  31. package/dist/index.node.js +3 -1
  32. package/dist/setup-hooks.d.ts +23 -0
  33. package/dist/setup-hooks.js +23 -0
  34. package/dist/util/auto-props-vite.d.ts +2 -2
  35. package/dist/util/auto-props-vite.js +1 -1
  36. package/dist/util/auto-props.js +13 -13
  37. package/dist/util/list-components.d.ts +1 -1
  38. package/dist/util/list-components.js +63 -6
  39. package/dist/util/svelte.js +13 -15
  40. package/package.json +20 -20
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Poveste + Svelte 5 and SvelteKit
2
2
 
3
- Requires `svelte@^5.46.4` and `@sveltejs/vite-plugin-svelte@^7.0.0`. SvelteKit is supported
3
+ Requires `svelte@^5.46.4` and `@sveltejs/vite-plugin-svelte@^7.3.0`. SvelteKit is supported
4
4
  by this same package — there is no separate plugin — through an optional
5
5
  `@sveltejs/kit@^2.53.0` peer.
6
6
 
@@ -53,8 +53,8 @@ and your content reads it from the snippet:
53
53
  > preview — Poveste owns the state instead. A story with controls but no `initState` logs an
54
54
  > error explaining this.
55
55
  >
56
- > [How, and why](https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate)
56
+ > [How, and why](https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate)
57
57
 
58
- [Svelte guide](https://poveste.dev/guide/svelte/getting-started.html) ·
59
- [SvelteKit](https://poveste.dev/guide/svelte/getting-started.html#sveltekit) ·
58
+ [Svelte guide](https://poveste.dev/guide/svelte/getting-started) ·
59
+ [SvelteKit](https://poveste.dev/guide/sveltekit/getting-started) ·
60
60
  [Documentation](https://poveste.dev)
@@ -1 +1 @@
1
- import e from"./MountVariant.svelte";import{omitInheritStoryProps as t}from"@poveste/shared";import{getContext as n,setContext as r}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";function a(a,o){let s=i.sanitize_slots(o),c=i.legacy_rest_props(o,[`children`,`$$slots`,`$$events`,`$$legacy`]),l=i.legacy_rest_props(c,[]);i.push(o,!1);let u=n(`__pvtStory`);r(`__pvtIndex`,{value:0}),r(`__pvtSlots`,s);let d=Object.keys(u).filter(e=>!t.includes(e)).reduce((e,t)=>(e[t]=u[t],e),{});i.init();var f=i.comment(),p=i.first_child(f),m=t=>{e(t,i.spread_props(()=>d,()=>l,{implicit:!0,children:(e,t)=>{var n=i.comment(),r=i.first_child(n);i.slot(r,o,`default`,{},null),i.append(e,n)},$$slots:{default:!0,controls:(e,t)=>{var n=i.comment(),r=i.first_child(n);i.slot(r,o,`controls`,{},null),i.append(e,n)}}}))},h=e=>{var t=i.comment(),n=i.first_child(t);i.slot(n,o,`default`,{},null),i.append(e,t)};i.if(p,e=>{i.untrack(()=>u.variants.length===1&&u.variants[0].id===`_default`)?e(m):e(h,-1)}),i.append(a,f),i.pop()}export{a as default};
1
+ import{slotsContext as e,storyContext as t,storyPropsContext as n,variantIndexContext as r}from"../contexts.js";import i from"./MountVariant.svelte";import{omitInheritStoryProps as a}from"@poveste/shared";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as o from"svelte/internal/client";function s(s,c){let l=o.sanitize_slots(c),u=o.legacy_rest_props(c,[`children`,`$$slots`,`$$events`,`$$legacy`]),d=o.legacy_rest_props(u,[]);o.push(c,!1);let f=t.get(`<Story>`);r.set({value:0}),e.set(l),n.set(d);let p=Object.keys(f).filter(e=>!a.includes(e)).reduce((e,t)=>(e[t]=f[t],e),{});o.init();var m=o.comment(),h=o.first_child(m),g=e=>{i(e,o.spread_props(()=>p,()=>d,{implicit:!0,children:(e,t)=>{var n=o.comment(),r=o.first_child(n);o.slot(r,c,`default`,{},null),o.append(e,n)},$$slots:{default:!0,controls:(e,t)=>{var n=o.comment(),r=o.first_child(n);o.slot(r,c,`controls`,{},null),o.append(e,n)}}}))},_=e=>{var t=o.comment(),n=o.first_child(t);o.slot(n,c,`default`,{},null),o.append(e,t)};o.if(h,e=>{o.untrack(()=>f.variants.length===1&&f.variants[0].id===`_default`)?e(g):e(_,-1)}),o.append(s,m),o.pop()}export{s as default};
@@ -1,12 +1,16 @@
1
1
  <script>
2
2
  import { omitInheritStoryProps } from '@poveste/shared'
3
- import { getContext, setContext } from 'svelte'
3
+ import { slotsContext, storyContext, storyPropsContext, variantIndexContext } from '../contexts.js'
4
4
  import MountVariant from './MountVariant.svelte'
5
5
 
6
- const story = getContext('__pvtStory')
6
+ const story = storyContext.get('<Story>')
7
7
  const index = { value: 0 }
8
- setContext('__pvtIndex', index)
9
- setContext('__pvtSlots', $$slots)
8
+ variantIndexContext.set(index)
9
+ slotsContext.set($$slots)
10
+ // Slots were given this treatment and props never were, so a story-level prop
11
+ // reached an implicit variant and was dropped the moment a story declared
12
+ // explicit ones — `$$restProps` is only spread in the branch below (#466).
13
+ storyPropsContext.set($$restProps)
10
14
 
11
15
  // Not `$:`: Svelte invalidates a reactive statement when a variable it
12
16
  // references is reassigned, and `story` is a const read from context that
@@ -1 +1 @@
1
- import{afterUpdate as e,getContext as t,onDestroy as n}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as r from"svelte/internal/client";r.from_html(`<!> <!>`,1);function i(i,a){let o=r.sanitize_slots(a);r.push(a,!1);let s=r.prop(a,`source`,8,null),c=r.prop(a,`responsiveDisabled`,8,!1),l=r.prop(a,`autoPropsDisabled`,8,!1),u=r.prop(a,`setupApp`,8,null),d=r.prop(a,`implicit`,8,!1),f=t(`__pvtStory`),p=t(`__pvtIndex`),m=t(`__pvtSlots`),h=t(`__pvtTargetVariantId`)??null,g=f.variants[p.value];p.value++;let _=!1;function v(){Object.assign(g,{slots:()=>({default:!0,controls:o.controls??m.controls}),source:s(),responsiveDisabled:c(),autoPropsDisabled:l(),setupApp:u(),configReady:!0}),!d()&&!f.meta?.hasVariantChildComponents&&(f.meta=f.meta||{},Object.assign(f.meta,{hasVariantChildComponents:!0}))}function y(){_||(_=!0,v())}if(h===null)y();else{let e=h.subscribe(e=>{(e===null||g?.id===e)&&y()});n(e)}e(()=>{_&&v()}),r.init();var b=r.comment(),x=r.first_child(b);r.if(x,e=>{}),r.append(i,b),r.pop()}export{i as default};
1
+ import{slotsContext as e,storyContext as t,storyPropsContext as n,targetVariantIdContext as r,variantIndexContext as i}from"../contexts.js";import{afterUpdate as a,onDestroy as o}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as s from"svelte/internal/client";s.from_html(`<!> <!>`,1);function c(c,l){let u=s.sanitize_slots(l);s.push(l,!1);let d=s.prop(l,`source`,8,null),f=s.prop(l,`responsiveDisabled`,8,null),p=s.prop(l,`autoPropsDisabled`,8,!1),m=s.prop(l,`setupApp`,8,null),h=s.prop(l,`implicit`,8,!1),g=s.prop(l,`initState`,8,null),_=t.get(`<Variant>`),v=i.get(`<Variant>`),y=e.getOptional(),b=n.getOptional()??{},x=r.getOptional(),S=_.variants[v.value];v.value++;let C=!1;function w(){Object.assign(S,{slots:()=>({default:!0,controls:u.controls??y.controls}),source:d(),responsiveDisabled:f()??b.responsiveDisabled??!1,autoPropsDisabled:p(),setupApp:m()??b.setupApp??null,configReady:!0}),!h()&&!_.meta?.hasVariantChildComponents&&(_.meta=_.meta||{},Object.assign(_.meta,{hasVariantChildComponents:!0}))}function T(){let e=g()??b.initState??null;e&&S&&!S.__pvtStateSeeded&&(S.__pvtStateSeeded=!0,S.state={...S.state,...e()})}function E(){C||(C=!0,T(),w())}if(x===null)E();else{let e=x.subscribe(e=>{(e===null||S?.id===e)&&E()});o(e)}a(()=>{C&&w()}),s.init();var D=s.comment(),O=s.first_child(D);s.if(O,e=>{}),s.append(c,D),s.pop()}export{c as default};
@@ -1,19 +1,26 @@
1
1
  <script>
2
- import { afterUpdate, getContext, onDestroy } from 'svelte'
2
+ import { afterUpdate, onDestroy } from 'svelte'
3
+ import { slotsContext, storyContext, storyPropsContext, targetVariantIdContext, variantIndexContext } from '../contexts.js'
3
4
 
4
5
  export let source = null
5
- export let responsiveDisabled = false
6
+ // `null` rather than `false`/a handler: these two inherit from the story, and
7
+ // a literal default is indistinguishable from a variant that set it — so a
8
+ // variant could not opt out of what the story declared. `??` falls through on
9
+ // null only, so an explicit `responsiveDisabled={false}` still wins (#466).
10
+ export let responsiveDisabled = null
6
11
  export let autoPropsDisabled = false
7
12
  export let setupApp = null
8
13
  export let implicit = false
14
+ export let initState = null
9
15
 
10
- const story = getContext('__pvtStory')
11
- const index = getContext('__pvtIndex')
12
- const storySlots = getContext('__pvtSlots')
16
+ const story = storyContext.get('<Variant>')
17
+ const index = variantIndexContext.get('<Variant>')
18
+ const storySlots = slotsContext.getOptional()
19
+ const storyProps = storyPropsContext.getOptional() ?? {}
13
20
  // A store: a sandbox sets it to the one variant this realm serves and changes
14
21
  // it when the realm is retargeted (#240). Null in the app realm, which keeps
15
22
  // every variant's bookkeeping.
16
- const targetVariantId = getContext('__pvtTargetVariantId') ?? null
23
+ const targetVariantId = targetVariantIdContext.getOptional()
17
24
 
18
25
  const variant = story.variants[index.value]
19
26
  index.value++
@@ -30,9 +37,9 @@
30
37
  controls: $$slots.controls ?? storySlots.controls,
31
38
  }),
32
39
  source,
33
- responsiveDisabled,
40
+ responsiveDisabled: responsiveDisabled ?? storyProps.responsiveDisabled ?? false,
34
41
  autoPropsDisabled,
35
- setupApp,
42
+ setupApp: setupApp ?? storyProps.setupApp ?? null,
36
43
  configReady: true,
37
44
  })
38
45
 
@@ -44,9 +51,35 @@
44
51
  }
45
52
  }
46
53
 
54
+ /**
55
+ * Seeds `variant.state`, which is not only the render mounts' business.
56
+ *
57
+ * The app mounts the current story hidden and never renders a slot for it
58
+ * unless the story has one — so a story with `initState` and no controls
59
+ * snippet left the state empty, and the panel, which builds a control per key
60
+ * out of it, said there were no controls at all. `plugin-vue` seeds from its
61
+ * mount pass for exactly this reason.
62
+ *
63
+ * Guarded by the flag the render mounts already share, so whichever pass
64
+ * arrives first wins and a reader's edit is never seeded over.
65
+ *
66
+ * Read off the story props as well as this variant's own, because a
67
+ * story-level `initState` reaches an explicit variant only through the
68
+ * context — the spread in `MountStory` covers the implicit one alone.
69
+ */
70
+ function seedState() {
71
+ const seed = initState ?? storyProps.initState ?? null
72
+
73
+ if (!seed || !variant || variant.__pvtStateSeeded) return
74
+
75
+ variant.__pvtStateSeeded = true
76
+ variant.state = { ...variant.state, ...seed() }
77
+ }
78
+
47
79
  function becomeTarget() {
48
80
  if (isTarget) return
49
81
  isTarget = true
82
+ seedState()
50
83
  updateVariant()
51
84
  }
52
85
 
@@ -1 +1 @@
1
- import{watch as e}from"@poveste/vendors/vue";import{getContext as t,onDestroy as n,setContext as r}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";var a=i.from_html(`<!> <!>`,1);function o(o,s){let c=i.sanitize_slots(s);i.push(s,!1);let l=t(`__pvtStory`),u=i.mutable_source(t(`__pvtVariant`)),d=t(`__pvtSlot`);r(`__pvtIndex`,{value:0});let f=i.prop(s,`source`,8,null),p=i.prop(s,`initState`,8,null);p()&&i.get(u)&&!i.get(u).__pvtStateSeeded&&(i.mutate(u,i.get(u).__pvtStateSeeded=!0),i.mutate(u,i.get(u).state={...i.get(u).state,...p()()})),!p()&&d===`controls`&&c.controls&&i.get(u)&&!i.get(u).__pvtStateWarned&&(i.mutate(u,i.get(u).__pvtStateWarned=!0),console.error([`[poveste] Story "${l?.title??l?.id}" has a controls slot but no \`initState\`.`,`Controls will render and appear to work, but their edits cannot reach the story:`,`the story is mounted separately for each slot, so a component-local variable is not shared.`,"Pass `initState` and read state from the snippet —",`https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate`].join(` `)));let m=i.mutable_source(i.get(u)?.state),h=e(()=>i.get(u)?.state,()=>{i.set(m,i.get(u)?.state)},{deep:!0});n(h),i.legacy_pre_effect(()=>(i.deep_read_state(f()),i.get(m),i.get(u)),()=>{let e=typeof f()==`function`?f()(i.get(m)):f();e!=null&&Object.assign(i.get(u),{source:e})}),i.legacy_pre_effect_reset(),i.init();var g=a(),_=i.first_child(g),v=e=>{var t=i.comment(),n=i.first_child(t);i.slot(n,s,`controls`,{get state(){return i.get(m)}},null),i.append(e,t)};i.if(_,e=>{d===`controls`&&e(v)});var y=i.sibling(_,2),b=e=>{var t=i.comment(),n=i.first_child(t);i.slot(n,s,`default`,{get state(){return i.get(m)}},null),i.append(e,t)};i.if(y,e=>{i.untrack(()=>d==="default"||l.meta?.hasVariantChildComponents)&&e(b)}),i.append(o,g),i.pop()}export{o as default};
1
+ import{slotNameContext as e,storyContext as t,storyPropsContext as n,variantContext as r,variantIndexContext as i}from"../contexts.js";import{watch as a}from"@poveste/vendors/vue";import{onDestroy as o}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as s from"svelte/internal/client";var c=s.from_html(`<!> <!>`,1);function l(l,u){let d=s.sanitize_slots(u),f=s.legacy_rest_props(u,[`children`,`$$slots`,`$$events`,`$$legacy`]),p=s.legacy_rest_props(f,[`source`,`initState`]);s.push(u,!1);let m=t.get(`<Story>`),h=s.mutable_source(r.getOptional()),g=e.getOptional();i.set({value:0}),n.set(p),s.get(h)&&m.variants.length===1&&m.variants[0].id===`_default`&&Object.assign(s.get(h),{setupApp:p.setupApp??null});let _=s.prop(u,`source`,8,null),v=s.prop(u,`initState`,8,null);v()&&s.get(h)&&!s.get(h).__pvtStateSeeded&&(s.mutate(h,s.get(h).__pvtStateSeeded=!0),s.mutate(h,s.get(h).state={...s.get(h).state,...v()()})),!v()&&g===`controls`&&d.controls&&s.get(h)&&!s.get(h).__pvtStateWarned&&(s.mutate(h,s.get(h).__pvtStateWarned=!0),console.error([`[poveste] Story "${m?.title??m?.id}" has a controls slot but no \`initState\`.`,`Controls will render and appear to work, but their edits cannot reach the story:`,`the story is mounted separately for each slot, so a component-local variable is not shared.`,"Pass `initState` and read state from the snippet —",`https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate`].join(` `)));let y=s.mutable_source(s.get(h)?.state),b=a(()=>s.get(h)?.state,()=>{s.set(y,s.get(h)?.state)},{deep:!0});o(b),s.legacy_pre_effect(()=>(s.deep_read_state(_()),s.get(y),s.get(h)),()=>{let e=typeof _()==`function`?_()(s.get(y)):_();e!=null&&Object.assign(s.get(h),{source:e})}),s.legacy_pre_effect_reset(),s.init();var x=c(),S=s.first_child(x),C=e=>{var t=s.comment(),n=s.first_child(t);s.slot(n,u,`controls`,{get state(){return s.get(y)}},null),s.append(e,t)};s.if(S,e=>{g===`controls`&&e(C)});var w=s.sibling(S,2),T=e=>{var t=s.comment(),n=s.first_child(t);s.slot(n,u,`default`,{get state(){return s.get(y)}},null),s.append(e,t)};s.if(w,e=>{s.untrack(()=>g==="default"||m.meta?.hasVariantChildComponents)&&e(T)}),s.append(l,x),s.pop()}export{l as default};
@@ -1,13 +1,30 @@
1
1
  <script>
2
2
  import { watch as _watch } from '@poveste/vendors/vue'
3
- import { getContext, onDestroy, setContext } from 'svelte'
3
+ import { onDestroy } from 'svelte'
4
+ import { slotNameContext, storyContext, storyPropsContext, variantContext, variantIndexContext } from '../contexts.js'
4
5
 
5
- const story = getContext('__pvtStory')
6
- const currentVariant = getContext('__pvtVariant')
7
- const slotName = getContext('__pvtSlot')
6
+ const story = storyContext.get('<Story>')
7
+ const currentVariant = variantContext.getOptional()
8
+ const slotName = slotNameContext.getOptional()
8
9
 
9
10
  const index = { value: 0 }
10
- setContext('__pvtIndex', index)
11
+ variantIndexContext.set(index)
12
+ // The sandbox realm's half of story-prop inheritance. `MountStory` does this
13
+ // for the app realm, and the bridge carries only `variant.state` across the
14
+ // iframe — so without it a story-level prop reaches the sandbox by accident,
15
+ // when a grid cell happens to render in the same realm (#466).
16
+ storyPropsContext.set($$restProps)
17
+
18
+ // A story with no `<Hst.Variant>` children has no `RenderVariant` to write the
19
+ // handler, so the story is the only place it can come from — and without this
20
+ // it too reached `render.ts` only by accident, passing one run in three. At
21
+ // init for the same reason `RenderVariant` writes at init: the handler is read
22
+ // as soon as the component has mounted. The condition is `MountStory`'s own.
23
+ if (currentVariant && story.variants.length === 1 && story.variants[0].id === '_default') {
24
+ Object.assign(currentVariant, {
25
+ setupApp: $$restProps.setupApp ?? null,
26
+ })
27
+ }
11
28
 
12
29
  export let source = null
13
30
  export let initState = null
@@ -39,7 +56,7 @@
39
56
  'Controls will render and appear to work, but their edits cannot reach the story:',
40
57
  'the story is mounted separately for each slot, so a component-local variable is not shared.',
41
58
  'Pass `initState` and read state from the snippet —',
42
- 'https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate',
59
+ 'https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate',
43
60
  ].join(' '))
44
61
  }
45
62
 
@@ -1 +1 @@
1
- import{shouldWarnAboutMissingInitState as e}from"../util/init-state-warning.js";import{watch as t}from"@poveste/vendors/vue";import{getContext as n,onDestroy as r}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";var a=i.from_html(`<!> <!>`,1);function o(o,s){let c=i.sanitize_slots(s);i.push(s,!1);let l=n(`__pvtStory`),u=i.mutable_source(n(`__pvtVariant`)),d=n(`__pvtSlot`),f=n(`__pvtIndex`),p=l.variants[f.value];f.value++;let m=i.get(u).id===p.id,h=i.prop(s,`source`,8,null),g=i.prop(s,`initState`,8,null);g()&&m&&!i.get(u).__pvtStateSeeded&&(i.mutate(u,i.get(u).__pvtStateSeeded=!0),i.mutate(u,i.get(u).state={...i.get(u).state,...g()()})),m&&e({hasInitState:!!g(),seeded:!!i.get(u).__pvtStateSeeded,slotName:d,hasControlsSlot:!!c.controls,alreadyWarned:!!i.get(u).__pvtStateWarned})&&(i.mutate(u,i.get(u).__pvtStateWarned=!0),console.error([`[poveste] Variant "${p?.title??p?.id}" has a controls slot but no \`initState\`.`,`Controls will render and appear to work, but their edits cannot reach the story:`,`the story is mounted separately for each slot, so a component-local variable is not shared.`,"Pass `initState` and read state from the snippet —",`https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate`].join(` `)));let _=i.mutable_source(i.get(u)?.state),v=()=>{};m&&(v=t(()=>i.get(u)?.state,()=>{i.set(_,i.get(u)?.state)},{deep:!0})),r(()=>v()),i.legacy_pre_effect(()=>(i.deep_read_state(h()),i.get(_),i.get(u)),()=>{if(m){let e=typeof h()==`function`?h()(i.get(_)):h();e!=null&&Object.assign(i.get(u),{source:e})}}),i.legacy_pre_effect_reset(),i.init();var y=i.comment(),b=i.first_child(y),x=e=>{var t=a(),n=i.first_child(t),r=e=>{var t=i.comment(),n=i.first_child(t);i.slot(n,s,`default`,{get state(){return i.get(_)}},null),i.append(e,t)};i.if(n,e=>{d==="default"&&e(r)});var o=i.sibling(n,2),c=e=>{var t=i.comment(),n=i.first_child(t);i.slot(n,s,`controls`,{get state(){return i.get(_)}},null),i.append(e,t)};i.if(o,e=>{d===`controls`&&e(c)}),i.append(e,t)};i.if(b,e=>{m&&e(x)}),i.append(o,y),i.pop()}export{o as default};
1
+ import{slotNameContext as e,storyContext as t,storyPropsContext as n,variantContext as r,variantIndexContext as i}from"../contexts.js";import{shouldWarnAboutMissingInitState as a}from"../util/init-state-warning.js";import{watch as o}from"@poveste/vendors/vue";import{onDestroy as s}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as c from"svelte/internal/client";var l=c.from_html(`<!> <!>`,1);function u(u,d){let f=c.sanitize_slots(d);c.push(d,!1);let p=t.get(`<Variant>`),m=c.mutable_source(r.getOptional()),h=e.getOptional(),g=i.get(`<Variant>`),_=n.getOptional()??{},v=p.variants[g.value];g.value++;let y=c.get(m).id===v.id,b=c.prop(d,`source`,8,null),x=c.prop(d,`initState`,8,null),S=c.prop(d,`setupApp`,8,null);y&&Object.assign(c.get(m),{setupApp:S()??_.setupApp??null}),x()&&y&&!c.get(m).__pvtStateSeeded&&(c.mutate(m,c.get(m).__pvtStateSeeded=!0),c.mutate(m,c.get(m).state={...c.get(m).state,...x()()})),y&&a({hasInitState:!!x(),seeded:!!c.get(m).__pvtStateSeeded,slotName:h,hasControlsSlot:!!f.controls,alreadyWarned:!!c.get(m).__pvtStateWarned})&&(c.mutate(m,c.get(m).__pvtStateWarned=!0),console.error([`[poveste] Variant "${v?.title??v?.id}" has a controls slot but no \`initState\`.`,`Controls will render and appear to work, but their edits cannot reach the story:`,`the story is mounted separately for each slot, so a component-local variable is not shared.`,"Pass `initState` and read state from the snippet —",`https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate`].join(` `)));let C=c.mutable_source(c.get(m)?.state),w=()=>{};y&&(w=o(()=>c.get(m)?.state,()=>{c.set(C,c.get(m)?.state)},{deep:!0})),s(()=>w()),c.legacy_pre_effect(()=>(c.deep_read_state(b()),c.get(C),c.get(m)),()=>{if(y){let e=typeof b()==`function`?b()(c.get(C)):b();e!=null&&Object.assign(c.get(m),{source:e})}}),c.legacy_pre_effect_reset(),c.init();var T=c.comment(),E=c.first_child(T),D=e=>{var t=l(),n=c.first_child(t),r=e=>{var t=c.comment(),n=c.first_child(t);c.slot(n,d,`default`,{get state(){return c.get(C)}},null),c.append(e,t)};c.if(n,e=>{h==="default"&&e(r)});var i=c.sibling(n,2),a=e=>{var t=c.comment(),n=c.first_child(t);c.slot(n,d,`controls`,{get state(){return c.get(C)}},null),c.append(e,t)};c.if(i,e=>{h===`controls`&&e(a)}),c.append(e,t)};c.if(E,e=>{y&&e(D)}),c.append(u,T),c.pop()}export{u as default};
@@ -1,12 +1,14 @@
1
1
  <script>
2
2
  import { watch as _watch } from '@poveste/vendors/vue'
3
- import { getContext, onDestroy } from 'svelte'
3
+ import { onDestroy } from 'svelte'
4
+ import { slotNameContext, storyContext, storyPropsContext, variantContext, variantIndexContext } from '../contexts.js'
4
5
  import { shouldWarnAboutMissingInitState } from '../util/init-state-warning.js'
5
6
 
6
- const story = getContext('__pvtStory')
7
- const currentVariant = getContext('__pvtVariant')
8
- const slotName = getContext('__pvtSlot')
9
- const index = getContext('__pvtIndex')
7
+ const story = storyContext.get('<Variant>')
8
+ const currentVariant = variantContext.getOptional()
9
+ const slotName = slotNameContext.getOptional()
10
+ const index = variantIndexContext.get('<Variant>')
11
+ const storyProps = storyPropsContext.getOptional() ?? {}
10
12
 
11
13
  const variant = story.variants[index.value]
12
14
  index.value++
@@ -18,6 +20,17 @@
18
20
 
19
21
  export let source = null
20
22
  export let initState = null
23
+ export let setupApp = null
24
+
25
+ // Written at init, not in a reactive block: `render.ts` reads
26
+ // `variant.setupApp` as soon as the story component has mounted, so anything
27
+ // deferred is missed for that render and never runs. Guarded like `source`
28
+ // below — this is *this* variant's handler, on the shared `currentVariant`.
29
+ if (shouldRender) {
30
+ Object.assign(currentVariant, {
31
+ setupApp: setupApp ?? storyProps.setupApp ?? null,
32
+ })
33
+ }
21
34
 
22
35
  // Same contract as `RenderStory`: poveste owns the state because the story is
23
36
  // mounted once per slot, so a component-local variable cannot be shared (#81).
@@ -41,7 +54,7 @@
41
54
  'Controls will render and appear to work, but their edits cannot reach the story:',
42
55
  'the story is mounted separately for each slot, so a component-local variable is not shared.',
43
56
  'Pass `initState` and read state from the snippet —',
44
- 'https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate',
57
+ 'https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate',
45
58
  ].join(' '))
46
59
  }
47
60
 
@@ -1 +1 @@
1
- import{createApp as e,h as t,reactive as n}from"@poveste/vendors/vue";import{createEventDispatcher as r,onMount as i}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as a from"svelte/internal/client";var o=a.from_html(`<div> </div>`);function s(s,c){let l=a.legacy_rest_props(c,[`children`,`$$slots`,`$$events`,`$$legacy`]),u=a.legacy_rest_props(l,[`controlComponent`,`value`]);a.push(c,!1);let d=a.prop(c,`controlComponent`,8),f=a.prop(c,`value`,12),p=r(),m=a.mutable_source(),h,g=n({});function _(e,t){Object.assign(g,{value:e,attrs:t})}i(()=>{_(f(),u),h=e({render(){let e={};if(d().emits)for(let t in d().emits){let n=Array.isArray(d().emits)?d().emits[t]:t,r=n===`input`?`update:modelValue`:n;e[`on${r.charAt(0).toUpperCase()}${r.substring(1)}`]=(...e)=>{n===`update:modelValue`?f(e[0]):p(n,...e)}}return t(d(),{modelValue:g.value,...g.attrs,...e,key:`component`})}}),h.mount(a.get(m))}),a.legacy_pre_effect(()=>(a.deep_read_state(f()),a.deep_read_state(u)),()=>{_(f(),u)}),a.legacy_pre_effect_reset(),a.init();var v=o(),y=a.child(v,!0);a.reset(v),a.bind_this(v,e=>a.set(m,e),()=>a.get(m)),a.template_effect(()=>a.set_text(y,(a.deep_read_state(d()),a.untrack(()=>d().name)))),a.append(s,v),a.pop()}export{s as default};
1
+ import{createApp as e,h as t,reactive as n}from"@poveste/vendors/vue";import{createEventDispatcher as r,onMount as i}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as a from"svelte/internal/client";var o=a.from_html(`<div> </div>`);function s(s,c){let l=a.legacy_rest_props(c,[`children`,`$$slots`,`$$events`,`$$legacy`]),u=a.legacy_rest_props(l,[`controlComponent`,`value`]);a.push(c,!1);let d=a.prop(c,`controlComponent`,8),f=a.prop(c,`value`,12),p=r(),m=a.mutable_source(),h,g=n({});function _(e,t){Object.assign(g,{value:e,attrs:t})}i(()=>{_(f(),u),h=e({render(){let e={};if(d().emits)for(let t in d().emits){let n=Array.isArray(d().emits)?d().emits[t]:t,r=n===`input`?`update:modelValue`:n;e[`on${r.charAt(0).toUpperCase()}${r.substring(1)}`]=(...e)=>{n===`update:modelValue`?f(e[0]):p(n,...e)}}return t(d(),{modelValue:g.value,...g.attrs,...e,key:`component`})}}),h.mount(a.get(m))}),a.legacy_pre_effect(()=>(a.deep_read_state(f()),a.deep_read_state(u)),()=>{_(f(),u)}),a.legacy_pre_effect_reset(),a.init();var v=o(),y=a.only_child(v,!0);a.bind_this(v,e=>a.set(m,e),()=>a.get(m)),a.template_effect(()=>a.set_text(y,(a.deep_read_state(d()),a.untrack(()=>d().name)))),a.append(s,v),a.pop()}export{s as default};
@@ -1 +1 @@
1
- import{watch as e}from"@poveste/vendors/vue";import{readable as t}from"svelte/store";import{applyState as n}from"@poveste/shared";import{getContext as r}from"svelte";var i=Object.freeze({});function a(a){let l=r(`__pvtStory`),u=r(`__pvtVariant`),d=c(l,u),f=a.map(e=>Array.from({length:s(e)}).fill(i));if(d<0)return o(u),t(f);let p=a[d]??[],m=()=>{let e=f.slice();return e[d]=Array.from({length:s(p)},(e,t)=>u.state?._hPropState?.[t]??i),e},h=null;function g(){!u.state||h===u.state&&u.state._hPropDefs?.length||(h=u.state,n(u.state,{_hPropDefs:p}),u.state._hPropState||n(u.state,{_hPropState:{}}))}return t(m(),t=>{let n=()=>{g(),t(m())};n();let r=[e(()=>u.state,n),e(()=>u.state?._hPropState,n,{deep:!0}),e(()=>u.state?._hPropDefs?.length,n)];return()=>r.forEach(e=>e())})}function o(e){e?.state?._hPropDefs?.length&&n(e.state,{_hPropDefs:[]})}function s(e){return e.reduce((e,t)=>Math.max(e,t.index+1),0)}function c(e,t){return!e||!t||e.autoPropsDisabled||t.autoPropsDisabled?-1:e.variants?.findIndex(e=>e.id===t.id)??-1}export{a as autoProps,c as variantToDrive};
1
+ import{storyContext as e,variantContext as t}from"../contexts.js";import{watch as n}from"@poveste/vendors/vue";import{readable as r}from"svelte/store";import{applyState as i}from"@poveste/shared";var a=Object.freeze({});function o(o){let u=e.getOptional(),d=t.getOptional(),f=l(u,d),p=o.map(e=>Array.from({length:c(e)}).fill(a));if(f<0||!d)return s(d),r(p);let m=o[f]??[],h=()=>{let e=p.slice();return e[f]=Array.from({length:c(m)},(e,t)=>d.state?._hPropState?.[t]??a),e},g=null,_=()=>{!d.state||g===d.state&&d.state._hPropDefs?.length||(g=d.state,i(d.state,{_hPropDefs:m}),d.state._hPropState||i(d.state,{_hPropState:{}}))};return r(h(),e=>{let t=()=>{_(),e(h())};t();let r=[n(()=>d.state,t),n(()=>d.state?._hPropState,t,{deep:!0}),n(()=>d.state?._hPropDefs?.length,t)];return()=>r.forEach(e=>e())})}function s(e){e?.state?._hPropDefs?.length&&i(e.state,{_hPropDefs:[]})}function c(e){return e.reduce((e,t)=>Math.max(e,t.index+1),0)}function l(e,t){return!e||!t||e.autoPropsDisabled||t.autoPropsDisabled?-1:e.variants?.findIndex(e=>e.id===t.id)??-1}export{o as autoProps,l as variantToDrive};
@@ -7,10 +7,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
7
7
  };
8
8
  targetVariantId: {
9
9
  type: StringConstructor;
10
- default: any;
10
+ default: null;
11
11
  };
12
12
  }>, {
13
- el: import("vue").Ref<HTMLDivElement, HTMLDivElement>;
13
+ el: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
14
14
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
15
15
  story: {
16
16
  type: _PropType<Story>;
@@ -18,7 +18,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
18
18
  };
19
19
  targetVariantId: {
20
20
  type: StringConstructor;
21
- default: any;
21
+ default: null;
22
22
  };
23
23
  }>> & Readonly<{}>, {
24
24
  targetVariantId: string;
@@ -1 +1 @@
1
- import{callSetupFunctions as e,mountSvelteComponent as t}from"../util/svelte.js";import n from"./MountVariant.svelte";import r from"./MountStory.svelte";import i from"./Stub.svelte";import{components as a}from"@poveste/controls";import{defineComponent as o,h as s,onMounted as c,onUnmounted as l,ref as u,watch as d}from"@poveste/vendors/vue";import{writable as f}from"svelte/store";import*as p from"virtual:$poveste-generated-global-setup";import*as m from"virtual:$poveste-setup";var h=o({name:`MountStory`,props:{story:{type:Object,required:!0},targetVariantId:{type:String,default:null}},setup(i){let a=u(),o,s,h=null,_=f(i.targetVariantId);d(()=>i.targetVariantId,e=>{_.set(e)});async function v(){s=document.createElement(`div`),a.value.appendChild(s);let c=await t(i.story.file.component,{target:s,props:{Hst:{Story:r,Variant:n,...g()}},context:new Map(Object.entries({__pvtStory:i.story,__pvtTargetVariantId:_}))},`client`,{storyId:i.story.id,variantId:i.targetVariantId??void 0});o=c.app,h=c.destroy;let l={app:o,story:i.story,variant:null};await e(p,m,l)}function y(){h?.(),h=null,s&&=(s.parentNode?.removeChild(s),null),o=null}return d(()=>i.story.id,async()=>{y(),await v()}),c(async()=>{await v()}),l(()=>{y()}),{el:a}},render(){return s(`div`,{ref:`el`})}});function g(){let e={};for(let t in a)e[t.substring(3)]=i;return e}export{h as default};
1
+ import{storyContext as e,targetVariantIdContext as t}from"../contexts.js";import{callSetupFunctions as n,mountSvelteComponent as r}from"../util/svelte.js";import i from"./MountVariant.svelte";import a from"./MountStory.svelte";import o from"./Stub.svelte";import{components as s}from"@poveste/controls";import{defineComponent as c,h as l,onMounted as u,onUnmounted as d,ref as f,watch as p}from"@poveste/vendors/vue";import{writable as m}from"svelte/store";import*as h from"virtual:$poveste-generated-global-setup";import*as g from"virtual:$poveste-setup";var _=c({name:`MountStory`,props:{story:{type:Object,required:!0},targetVariantId:{type:String,default:null}},setup(o){let s=f(),c,l=null,_=null,y=m(o.targetVariantId);p(()=>o.targetVariantId,e=>{y.set(e)});async function b(){if(!s.value||!o.story.file)return;l=document.createElement(`div`),s.value.appendChild(l);let u=await r(o.story.file.component,{target:l,props:{Hst:{Story:a,Variant:i,...v()}},context:new Map([[e.key,o.story],[t.key,y]])},`client`,{storyId:o.story.id,variantId:o.targetVariantId??void 0});c=u.app,_=u.destroy;let d={app:c,story:o.story,variant:void 0};await n(h,g,d)}function x(){_?.(),_=null,l&&=(l.parentNode?.removeChild(l),null),c=null}return p(()=>o.story.id,async()=>{x(),await b()}),u(async()=>{await b()}),d(()=>{x()}),{el:s}},render(){return l(`div`,{ref:`el`})}});function v(){let e={};for(let t in s)e[t.substring(3)]=o;return e}export{_ as default};
@@ -0,0 +1,16 @@
1
+ import type { Story, Variant } from '@poveste/shared';
2
+ type SvelteComponentFunction = (anchor: any, props: any) => any;
3
+ /**
4
+ * `Hst.Story` and `Hst.Variant` for a render of `story` that shows `variant`.
5
+ *
6
+ * The story's `{#each}` calls `Hst.Variant` once per variant, and a sandbox mounts
7
+ * the story again on every retarget. A `RenderVariant` for another variant renders
8
+ * nothing and changes nothing, yet creating them was most of a retarget: 3ms of 7ms
9
+ * at 1000 variants (#888). So only the shown one is created, and the rest are
10
+ * counted, which is all they did.
11
+ */
12
+ export declare function renderStoryComponents(story: Story, variant: Variant): {
13
+ Story: SvelteComponentFunction;
14
+ Variant: SvelteComponentFunction;
15
+ };
16
+ export {};
@@ -0,0 +1 @@
1
+ import{variantIndexContext as e}from"../contexts.js";import t from"./RenderStory.svelte";import n from"./RenderVariant.svelte";import{toRaw as r}from"@poveste/vendors/vue";function i(i,a){let o=r(i).variants.findIndex(({id:e})=>e===a.id),s=0;return{Story:(e,n)=>(s=0,t(e,n)),Variant:(t,r)=>{if(s++===o)return e.get(`<Story>`).value=o,n(t,r)}}}export{i as renderStoryComponents};
@@ -14,7 +14,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
14
14
  default: string;
15
15
  };
16
16
  }>, {
17
- el: import("vue").Ref<HTMLDivElement, HTMLDivElement>;
17
+ el: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
18
18
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
19
19
  variant: {
20
20
  type: _PropType<Variant>;
@@ -1 +1 @@
1
- import{callSetupFunctions as e,createWrappedComponent as t,getLegacyStateApi as n,mountSvelteComponent as r}from"../util/svelte.js";import i from"./RenderStory.svelte";import a from"./RenderVariant.svelte";import{syncState as o}from"./util.js";import s from"./Wrap.svelte";import{components as c}from"@poveste/controls";import{defineComponent as l,h as u,onMounted as d,onUnmounted as f,ref as p,watch as m}from"@poveste/vendors/vue";import*as h from"virtual:$poveste-generated-global-setup";import*as g from"virtual:$poveste-setup";var _=l({name:`RenderStory`,props:{variant:{type:Object,required:!0},story:{type:Object,required:!0},slotName:{type:String,default:`default`}},setup(t,{emit:s}){let c=p(),l,u,_=[];function y(e,t){document.addEventListener(e,t);let n=()=>document.removeEventListener(e,t);return _.push(n),{off:n}}async function b(){u=document.createElement(`div`),c.value.appendChild(u);let d=[],{off:f}=y(`SvelteRegisterComponent`,e=>{let{component:t}=e.detail;d.push(t)}),p=await r(t.story.file.component,{target:u,props:{Hst:{Story:i,Variant:a,...v()}},context:new Map(Object.entries({__pvtStory:t.story,__pvtVariant:t.variant,__pvtSlot:t.slotName}))},`client`,{storyId:t.story.id,variantId:t.variant?.id});l=p.app,_.push(()=>{p.destroy()});let m=d.find(e=>e.$$&&l?.$$&&e.$$===l.$$)??l;f(),d=[];function b(){let e=m?.$replace;e&&(m.$replace=(...t)=>{let n=e.apply(m,t);return m=n??m,n})}b();let x=n(m);if(x){let{apply:e,stop:n}=o(t.variant.state,e=>{x.injectState(e)});_.push(n);let r,i=()=>{e(x.captureState()),r=requestAnimationFrame(i)};r=requestAnimationFrame(i),_.push(()=>{cancelAnimationFrame(r)}),e(x.captureState())}let S={app:l,story:t.story,variant:t.variant};await e(h,g,S,t.variant.setupApp),s(`ready`)}function x(){_.forEach(e=>e()),_=[],u&&=(u.parentNode?.removeChild(u),null),l=null}return m(()=>t.story.id,async()=>{x(),await b()}),d(async()=>{await b()}),f(()=>{x()}),{el:c}},render(){return u(`div`,{ref:`el`})}});function v(){let e={};for(let t in c)e[t.substring(3)]=y(c[t]);return e}function y(e){return t(s,e)}export{_ as default};
1
+ import{slotNameContext as e,storyContext as t,variantContext as n}from"../contexts.js";import{callSetupFunctions as r,createWrappedComponent as i,getLegacyStateApi as a,mountSvelteComponent as o}from"../util/svelte.js";import{renderStoryComponents as s}from"./render-story-components.js";import{syncState as c}from"./util.js";import l from"./Wrap.svelte";import{components as u}from"@poveste/controls";import{defineComponent as d,h as f,onMounted as p,onUnmounted as m,ref as h,watch as g}from"@poveste/vendors/vue";import*as _ from"virtual:$poveste-generated-global-setup";import*as v from"virtual:$poveste-setup";var y=d({name:`RenderStory`,props:{variant:{type:Object,required:!0},story:{type:Object,required:!0},slotName:{type:String,default:`default`}},setup(i,{emit:l}){let u=h(),d,f=null,y=[];function x(e,t){document.addEventListener(e,t);let n=()=>document.removeEventListener(e,t);return y.push(n),{off:n}}async function S(){if(!u.value||!i.story.file)return;f=document.createElement(`div`),u.value.appendChild(f);let p=[],{off:m}=x(`SvelteRegisterComponent`,e=>{let{component:t}=e.detail;p.push(t)}),h=await o(i.story.file.component,{target:f,props:{Hst:{...s(i.story,i.variant),...b()}},context:new Map([[t.key,i.story],[n.key,i.variant],[e.key,i.slotName]])},`client`,{storyId:i.story.id,variantId:i.variant?.id});d=h.app,y.push(()=>{h.destroy()});let g=p.find(e=>e.$$&&d?.$$&&e.$$===d.$$)??d;m(),p=[];function S(){let e=g?.$replace;e&&(g.$replace=(...t)=>{let n=e.apply(g,t);return g=n??g,n})}S();let C=a(g);if(C){let{apply:e,stop:t}=c(i.variant.state,e=>{C.injectState(e)});y.push(t);let n,r=()=>{e(C.captureState()),n=requestAnimationFrame(r)};n=requestAnimationFrame(r),y.push(()=>{cancelAnimationFrame(n)}),e(C.captureState())}let w={app:d,story:i.story,variant:i.variant};await r(_,v,w,i.variant.setupApp),l(`ready`)}function C(){y.forEach(e=>e()),y=[],f&&=(f.parentNode?.removeChild(f),null),d=null}return g(()=>i.story.id,async()=>{C(),await S()}),p(async()=>{await S()}),m(()=>{C()}),{el:u}},render(){return f(`div`,{ref:`el`})}});function b(){let e={};for(let[t,n]of Object.entries(u))e[t.substring(3)]=x(n);return e}function x(e){return i(l,e)}export{y as default};
@@ -1,4 +1,4 @@
1
- export declare function syncState(variantState: any, onChange: (state: any) => unknown): {
2
- apply: (value: any) => void;
1
+ export declare function syncState(variantState: Record<string, any>, onChange: (state: Record<string, any>) => unknown): {
2
+ apply: (value: Record<string, any> | null | undefined) => void;
3
3
  stop(): void;
4
4
  };
@@ -1 +1 @@
1
- import{getContext as e,setContext as t}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as n from"svelte/internal/client";function r(r,i){n.push(i,!1);let a=n.prop(i,`title`,8,null),o=n.prop(i,`id`,8,null),s=n.prop(i,`group`,8,null),c=n.prop(i,`layout`,8,null),l=n.prop(i,`icon`,8,null),u=n.prop(i,`iconColor`,8,null),d=n.prop(i,`docsOnly`,8,!1),f=n.prop(i,`autoPropsDisabled`,8,null),p=n.prop(i,`initState`,8,null),m=e(`__pvtAddStory`),h=e(`__pvtStoryFile`),g={id:o()??h.id,title:a()??h.fileName,group:s(),layout:c(),icon:l(),iconColor:u(),docsOnly:d(),autoPropsDisabled:f(),variants:[]};m(g);let _=p()?p()():{};t(`__pvtStory`,g),t(`__pvtAddVariant`,e=>{g.variants.push(e)}),n.init();var v=n.comment(),y=n.first_child(v);n.slot(y,i,`default`,{get state(){return _}},null),n.append(r,v),n.pop()}export{r as default};
1
+ import{addStoryContext as e,addVariantContext as t,collectStoryContext as n,storyFileContext as r}from"../contexts.js";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";function a(a,o){i.push(o,!1);let s=i.prop(o,`title`,8,null),c=i.prop(o,`id`,8,null),l=i.prop(o,`group`,8,null),u=i.prop(o,`layout`,8,null),d=i.prop(o,`icon`,8,null),f=i.prop(o,`iconColor`,8,null),p=i.prop(o,`docsOnly`,8,!1),m=i.prop(o,`autoPropsDisabled`,8,null),h=i.prop(o,`initState`,8,null),g=e.getOptional(),_=r.getOptional(),v=c()??_?.id,y=s()??_?.fileName;if(!v||y===void 0)throw Error("[poveste] a <Story> collected outside a story file needs an `id` and a `title`");let b={id:v,title:y,group:l(),layout:u(),icon:d(),iconColor:f(),docsOnly:p(),autoPropsDisabled:m(),variants:[]};g?.(b);let x=h()?h()():{};n.set(b),t.set(e=>{b.variants.push(e)}),i.init();var S=i.comment(),C=i.first_child(S);i.slot(C,o,`default`,{get state(){return x}},null),i.append(a,S),i.pop()}export{a as default};
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { getContext, setContext } from 'svelte'
2
+ import { addStoryContext, addVariantContext, collectStoryContext, storyFileContext } from '../contexts.js'
3
3
 
4
4
  export let title = null
5
5
  export let id = null
@@ -11,12 +11,22 @@
11
11
  export let autoPropsDisabled = null
12
12
  export let initState = null
13
13
 
14
- const addStory = getContext('__pvtAddStory')
15
- const file = getContext('__pvtStoryFile')
14
+ const addStory = addStoryContext.getOptional()
15
+ const file = storyFileContext.getOptional()
16
+
17
+ // Guarded the way `plugin-vue` guards it: collected outside a run there is no
18
+ // file to fall back on, and reading `file.id` off `undefined` said nothing
19
+ // about stories (#981).
20
+ const storyId = id ?? file?.id
21
+ const storyTitle = title ?? file?.fileName
22
+
23
+ if (!storyId || storyTitle === undefined) {
24
+ throw new Error('[poveste] a <Story> collected outside a story file needs an `id` and a `title`')
25
+ }
16
26
 
17
27
  const story = {
18
- id: id ?? file.id,
19
- title: title ?? file.fileName,
28
+ id: storyId,
29
+ title: storyTitle,
20
30
  group,
21
31
  layout,
22
32
  icon,
@@ -29,7 +39,7 @@
29
39
  variants: [],
30
40
  }
31
41
 
32
- addStory(story)
42
+ addStory?.(story)
33
43
 
34
44
  // Collection renders the story's markup purely to discover its variants, but
35
45
  // that markup now reads `state`. Without a value here every expression
@@ -37,8 +47,8 @@
37
47
  // `Cannot read properties of undefined` (#81).
38
48
  const state = initState ? initState() : {}
39
49
 
40
- setContext('__pvtStory', story)
41
- setContext('__pvtAddVariant', (variant) => {
50
+ collectStoryContext.set(story)
51
+ addVariantContext.set((variant) => {
42
52
  story.variants.push(variant)
43
53
  })
44
54
  </script>
@@ -1 +1 @@
1
- import{getContext as e}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as t from"svelte/internal/client";function n(n,r){t.push(r,!1);let i=t.prop(r,`title`,8,`untitled`),a=t.prop(r,`id`,8,null),o=t.prop(r,`icon`,8,null),s=t.prop(r,`iconColor`,8,null);t.prop(r,`initState`,8,null);let c=e(`__pvtStory`),l=e(`__pvtAddVariant`);function u(){return`${c.id}-${c.variants.length}`}l({id:a()??u(),title:i(),icon:o(),iconColor:s()}),t.init(),t.pop()}export{n as default};
1
+ import{addVariantContext as e,collectStoryContext as t}from"../contexts.js";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as n from"svelte/internal/client";function r(r,i){n.push(i,!1);let a=n.prop(i,`title`,8,`untitled`),o=n.prop(i,`id`,8,null),s=n.prop(i,`icon`,8,null),c=n.prop(i,`iconColor`,8,null),l=n.prop(i,`autoPropsDisabled`,8,!1);n.prop(i,`initState`,8,null);let u=t.get(`<Variant>`);e.get(`<Variant>`)({id:o()??`${u.id}-${u.variants.length}`,title:a(),icon:s(),iconColor:c(),autoPropsDisabled:l()}),n.init(),n.pop()}export{r as default};
@@ -1,27 +1,32 @@
1
1
  <script>
2
- import { getContext } from 'svelte'
2
+ import { addVariantContext, collectStoryContext } from '../contexts.js'
3
3
 
4
4
  export let title = 'untitled'
5
5
  export let id = null
6
6
  export let icon = null
7
7
  export let iconColor = null
8
+ // Collected rather than left to `MountVariant`, which sets it when the mount
9
+ // realm runs — and the realm that reads it renders in parallel, so whether a
10
+ // variant's auto-props were off came down to which mount finished first (#890).
11
+ export let autoPropsDisabled = false
8
12
  // Absorbs the prop; collection never renders variant children. `export const`
9
13
  // (the compiler's suggestion) would make passing one an error.
10
14
  // svelte-ignore export_let_unused
11
15
  export let initState = null
12
16
 
13
- const story = getContext('__pvtStory')
14
- const addVariant = getContext('__pvtAddVariant')
15
-
16
- function generateId() {
17
- return `${story.id}-${story.variants.length}`
18
- }
17
+ // Asked for before anything reads them, so a missing `<Story>` is reported
18
+ // once and the same way whether or not the variant carries an `id`. Reading
19
+ // `story.id` inside `generateId` meant the no-id path died on `undefined`
20
+ // and the id path died on `addVariant` not being a function (#981).
21
+ const story = collectStoryContext.get('<Variant>')
22
+ const addVariant = addVariantContext.get('<Variant>')
19
23
 
20
24
  const variant = {
21
- id: id ?? generateId(),
25
+ id: id ?? `${story.id}-${story.variants.length}`,
22
26
  title,
23
27
  icon,
24
28
  iconColor,
29
+ autoPropsDisabled,
25
30
  }
26
31
 
27
32
  addVariant(variant)
@@ -1 +1 @@
1
- import{callSetupFunctions as e,mountSvelteComponent as t}from"../util/svelte.js";import n from"./Story.svelte";import r from"./Variant.svelte";import*as i from"virtual:$poveste-generated-global-setup";import*as a from"virtual:$poveste-setup";import{tick as o}from"svelte";async function s({file:s,el:c,storyData:l}){let{default:u}=await import(/* @vite-ignore */s.moduleId),d=await t(u,{target:c,props:{Hst:{Story:n,Variant:r}},context:new Map(Object.entries({__pvtAddStory(e){l.push(e)},__pvtStoryFile:s}))},`client`),f={app:d.app,story:null,variant:null};if(await e(i,a,f),await o(),!l[0]){d.destroy();return}l[0].variants.length||l[0].variants.push({id:`_default`,title:`default`}),d.destroy()}export{s as run};
1
+ import{addStoryContext as e,storyFileContext as t}from"../contexts.js";import{callSetupFunctions as n,mountSvelteComponent as r}from"../util/svelte.js";import i from"./Story.svelte";import a from"./Variant.svelte";import*as o from"virtual:$poveste-generated-global-setup";import*as s from"virtual:$poveste-setup";import{tick as c}from"svelte";async function l({file:l,el:u,storyData:d}){let{default:f}=await import(/* @vite-ignore */l.moduleId),p=await r(f,{target:u,props:{Hst:{Story:i,Variant:a}},context:new Map([[e.key,e=>{d.push(e)}],[t.key,l]])},`client`),m={app:p.app,story:void 0,variant:void 0};if(await n(o,s,m),await c(),!d[0]){p.destroy();return}d[0].variants.length||d[0].variants.push({id:`_default`,title:`default`}),p.destroy()}export{l as run};
@@ -14,13 +14,13 @@ export default {
14
14
  label: 'File name',
15
15
  type: 'text',
16
16
  required: true,
17
- defaultValue: answers => answers.component?.replace(/[^/]+\/([^/]+)\.svelte$/, '$1.story.svelte'),
17
+ defaultValue: answers => answers['component']?.replace(/[^/]+\/([^/]+)\.svelte$/, '$1.story.svelte'),
18
18
  },
19
19
  ],
20
20
  clientAction: (params) => {
21
- const index = params.component.lastIndexOf('/');
22
- const dirname = params.component.substring(0, index + 1);
23
- const file = `${dirname}${params.fileName}`;
21
+ const index = params['component'].lastIndexOf('/');
22
+ const dirname = params['component'].substring(0, index + 1);
23
+ const file = `${dirname}${params['fileName']}`;
24
24
  const storyId = kebabCase(file.toLowerCase());
25
25
  openStory(storyId);
26
26
  },
@@ -7,11 +7,11 @@ export default {
7
7
  icon: 'https://svelte.dev/favicon.png',
8
8
  searchText: 'generate create',
9
9
  async serverAction(params) {
10
- const targetFile = path.join(path.dirname(params.component), params.fileName);
10
+ const targetFile = path.join(path.dirname(params['component']), params['fileName']);
11
11
  if (fs.existsSync(targetFile)) {
12
12
  throw new Error(`File ${targetFile} already exists`);
13
13
  }
14
- const { component, componentName, isTs } = await getComponentInfo(params.component);
14
+ const { component, componentName, isTs } = await getComponentInfo(params['component']);
15
15
  const content = `<script${isTs ? ' lang="ts"' : ''}>
16
16
  ${isTs
17
17
  ? `import type { Hst } from '@poveste/plugin-svelte'\n
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A context keyed by a symbol, with its set and its get in one place.
3
+ *
4
+ * Ten of these were string keys carrying `any` (#981). Svelte's context map
5
+ * takes any key, so a symbol costs nothing and the type travels with it.
6
+ *
7
+ * `get` and `getOptional` are separate functions rather than one call with a
8
+ * default, because "nobody set this" and "this is allowed to be absent" are
9
+ * different questions that arrived as the same `undefined` — and the first is
10
+ * what an author hits when they nest a component wrongly.
11
+ *
12
+ * The same helper, the same split and the same sentence as `plugin-vue`'s, so
13
+ * the two plugins report a nesting mistake identically. Only the underlying
14
+ * primitive differs: `setContext`/`getContext` rather than `provide`/`inject`.
15
+ */
16
+ export interface Context<T> {
17
+ key: symbol;
18
+ set: (value: T) => void;
19
+ /** Throws, naming the provider, when nothing set it. */
20
+ get: (consumer: string) => T;
21
+ /** `null` when nothing set it, which the caller is expected to handle. */
22
+ getOptional: () => T | null;
23
+ }
24
+ /**
25
+ * `description` is what the symbol shows in a stack; `provider` is the
26
+ * component a reader has to go and add, and is the only part of the message
27
+ * they can act on.
28
+ *
29
+ * `key` is exposed because the collect runner seeds its contexts through a
30
+ * `Map` handed to `mount`, which is not a component and so cannot call `set`.
31
+ *
32
+ * The key comes from the global registry rather than being minted here, because
33
+ * this module is evaluated more than once. A collection worker's module graph is
34
+ * invalidated while a story is mid-run, so `set` and `get` can each hold their
35
+ * own instance of this file — a plain `Symbol()` then differs between them and
36
+ * the lookup misses. Every description here is unique, so the registry cannot
37
+ * put two contexts on one key.
38
+ */
39
+ export declare function createContext<T>(description: string, provider: string): Context<T>;
@@ -0,0 +1,35 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ /**
3
+ * `description` is what the symbol shows in a stack; `provider` is the
4
+ * component a reader has to go and add, and is the only part of the message
5
+ * they can act on.
6
+ *
7
+ * `key` is exposed because the collect runner seeds its contexts through a
8
+ * `Map` handed to `mount`, which is not a component and so cannot call `set`.
9
+ *
10
+ * The key comes from the global registry rather than being minted here, because
11
+ * this module is evaluated more than once. A collection worker's module graph is
12
+ * invalidated while a story is mid-run, so `set` and `get` can each hold their
13
+ * own instance of this file — a plain `Symbol()` then differs between them and
14
+ * the lookup misses. Every description here is unique, so the registry cannot
15
+ * put two contexts on one key.
16
+ */
17
+ export function createContext(description, provider) {
18
+ const key = Symbol.for(description);
19
+ return {
20
+ key,
21
+ set(value) {
22
+ setContext(key, value);
23
+ },
24
+ get(consumer) {
25
+ const value = getContext(key);
26
+ if (value === undefined || value === null) {
27
+ throw new Error(`[poveste] a ${consumer} has to be inside a ${provider}`);
28
+ }
29
+ return value;
30
+ },
31
+ getOptional() {
32
+ return getContext(key) ?? null;
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,17 @@
1
+ import type { ServerStory, ServerStoryFile, ServerVariant, Story, Variant } from '@poveste/shared';
2
+ /** Collect mode: the file being collected, seeded by `collect/index.ts`. */
3
+ export declare const storyFileContext: import("./context.js").Context<ServerStoryFile>;
4
+ /** Collect mode, optional: a `<Story>` collected outside a run has nowhere to report to. */
5
+ export declare const addStoryContext: import("./context.js").Context<(story: ServerStory) => void>;
6
+ /** Collect mode: what `<Variant>` reports itself to. */
7
+ export declare const collectStoryContext: import("./context.js").Context<ServerStory>;
8
+ export declare const addVariantContext: import("./context.js").Context<(variant: ServerVariant) => void>;
9
+ export declare const storyContext: import("./context.js").Context<Story>;
10
+ export declare const variantContext: import("./context.js").Context<Variant>;
11
+ export declare const slotNameContext: import("./context.js").Context<string>;
12
+ export declare const slotsContext: import("./context.js").Context<Record<string, any>>;
13
+ export declare const storyPropsContext: import("./context.js").Context<Record<string, any>>;
14
+ export declare const variantIndexContext: import("./context.js").Context<{
15
+ value: number;
16
+ }>;
17
+ export declare const targetVariantIdContext: import("./context.js").Context<string | null>;
@@ -0,0 +1,22 @@
1
+ import { createContext } from './context.js';
2
+ /*
3
+ * Every context this plugin passes, in one place (#981). Collect mode and the
4
+ * render path are separate keys even where they carry a story each, because
5
+ * they carry different shapes — the same mistake `plugin-vue` made with one
6
+ * string used in two realms.
7
+ */
8
+ /** Collect mode: the file being collected, seeded by `collect/index.ts`. */
9
+ export const storyFileContext = createContext('poveste-svelte-story-file', 'story file');
10
+ /** Collect mode, optional: a `<Story>` collected outside a run has nowhere to report to. */
11
+ export const addStoryContext = createContext('poveste-svelte-add-story', 'story file');
12
+ /** Collect mode: what `<Variant>` reports itself to. */
13
+ export const collectStoryContext = createContext('poveste-svelte-collect-story', '<Story>');
14
+ export const addVariantContext = createContext('poveste-svelte-add-variant', '<Story>');
15
+ /* The render path. */
16
+ export const storyContext = createContext('poveste-svelte-story', '<Story>');
17
+ export const variantContext = createContext('poveste-svelte-variant', '<Variant>');
18
+ export const slotNameContext = createContext('poveste-svelte-slot', '<Story>');
19
+ export const slotsContext = createContext('poveste-svelte-slots', '<Story>');
20
+ export const storyPropsContext = createContext('poveste-svelte-story-props', '<Story>');
21
+ export const variantIndexContext = createContext('poveste-svelte-variant-index', '<Story>');
22
+ export const targetVariantIdContext = createContext('poveste-svelte-target-variant-id', '<Story>');
package/dist/helpers.d.ts CHANGED
@@ -3,8 +3,8 @@ import type { Story, StoryProps, Variant, VariantProps } from '@poveste/shared';
3
3
  import type { Component, Snippet } from 'svelte';
4
4
  export interface SvelteStorySetupApi {
5
5
  app: any;
6
- story?: Story;
7
- variant?: Variant;
6
+ story?: Story | undefined;
7
+ variant?: Variant | undefined;
8
8
  }
9
9
  export type SvelteStorySetupHandler = (api: SvelteStorySetupApi) => Promise<void> | void;
10
10
  export declare function defineSetupSvelte(handler: SvelteStorySetupHandler): SvelteStorySetupHandler;
@@ -91,6 +91,15 @@ export interface Hst {
91
91
  options: HstControlOption[];
92
92
  title?: string;
93
93
  }>;
94
+ Color: Component<{
95
+ value?: string;
96
+ title?: string;
97
+ }>;
98
+ Date: Component<{
99
+ value?: string;
100
+ title?: string;
101
+ time?: boolean;
102
+ }>;
94
103
  Json: Component<{
95
104
  value: unknown;
96
105
  title: string;
@@ -3,6 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { dirname, join } from 'pathe';
4
4
  import { defaultColors } from 'poveste';
5
5
  import generateStoryCommand from './commands/generate-story.server.js';
6
+ import { SVELTE_SETUP_HOOK_NAMES } from './setup-hooks.js';
6
7
  import { svelteAutoProps } from './util/auto-props-vite.js';
7
8
  import { svelteKitAssetsDir } from './util/kit-assets.js';
8
9
  import { listComponentFiles } from './util/list-components.js';
@@ -47,7 +48,7 @@ export function HstSvelte() {
47
48
  supportPlugin: {
48
49
  id: 'svelte4',
49
50
  moduleName: '@poveste/plugin-svelte',
50
- setupFn: ['setupSvelte3', 'setupSvelte4', 'setupSvelte5'],
51
+ setupFn: SVELTE_SETUP_HOOK_NAMES,
51
52
  importStoryComponent: (file, index) => `import Comp${index} from ${JSON.stringify(file.moduleId)}`,
52
53
  },
53
54
  commands: [
@@ -59,6 +60,7 @@ export function HstSvelte() {
59
60
  return listComponentFiles(api.payload.search, api.getConfig().storyMatch);
60
61
  }
61
62
  }
63
+ return undefined;
62
64
  },
63
65
  };
64
66
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The names a Svelte setup file may export, most established first.
3
+ *
4
+ * The numbers are historical. `@poveste/plugin-svelte` supports Svelte 5 only,
5
+ * so `setupSvelte3` and `setupSvelte4` name majors it cannot run — they are
6
+ * accepted because histoire users wrote them, not because they select anything.
7
+ * `setupSvelte` is the unnumbered name, added as an alias rather than a rename,
8
+ * which is the only way to introduce it without breaking every existing setup
9
+ * file (#157).
10
+ *
11
+ * Order is load-bearing: `getSetupHook` runs the first one present, so a file
12
+ * that already exports `setupSvelte5` behaves exactly as before and the new name
13
+ * is the one that yields.
14
+ */
15
+ export declare const SVELTE_SETUP_HOOK_NAMES: string[];
16
+ /**
17
+ * Names this plugin has stopped reading.
18
+ *
19
+ * Empty, and meant to stay empty until 1.0 retires the numbered names. It is
20
+ * here so that doing so is a string moving from the list above to this one,
21
+ * with the warning already written and already tested.
22
+ */
23
+ export declare const SVELTE_RETIRED_SETUP_HOOK_NAMES: string[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The names a Svelte setup file may export, most established first.
3
+ *
4
+ * The numbers are historical. `@poveste/plugin-svelte` supports Svelte 5 only,
5
+ * so `setupSvelte3` and `setupSvelte4` name majors it cannot run — they are
6
+ * accepted because histoire users wrote them, not because they select anything.
7
+ * `setupSvelte` is the unnumbered name, added as an alias rather than a rename,
8
+ * which is the only way to introduce it without breaking every existing setup
9
+ * file (#157).
10
+ *
11
+ * Order is load-bearing: `getSetupHook` runs the first one present, so a file
12
+ * that already exports `setupSvelte5` behaves exactly as before and the new name
13
+ * is the one that yields.
14
+ */
15
+ export const SVELTE_SETUP_HOOK_NAMES = ['setupSvelte3', 'setupSvelte4', 'setupSvelte5', 'setupSvelte'];
16
+ /**
17
+ * Names this plugin has stopped reading.
18
+ *
19
+ * Empty, and meant to stay empty until 1.0 retires the numbered names. It is
20
+ * here so that doing so is a string moving from the list above to this one,
21
+ * with the warning already written and already tested.
22
+ */
23
+ export const SVELTE_RETIRED_SETUP_HOOK_NAMES = [];
@@ -4,6 +4,6 @@ export declare function svelteAutoProps(): {
4
4
  enforce: "pre";
5
5
  transform(this: any, code: string, id: string): Promise<{
6
6
  code: string;
7
- map: any;
8
- }>;
7
+ map: null;
8
+ } | undefined>;
9
9
  };
@@ -11,7 +11,7 @@ export function svelteAutoProps() {
11
11
  name: 'poveste:svelte-auto-props',
12
12
  enforce: 'pre',
13
13
  async transform(code, id) {
14
- if (!id.split('?')[0].endsWith('.svelte') || !code.includes(RENDERS_A_STORY) || !IMPORTS_A_COMPONENT.test(code)) {
14
+ if (!id.replace(/\?.*$/s, '').endsWith('.svelte') || !code.includes(RENDERS_A_STORY) || !IMPORTS_A_COMPONENT.test(code)) {
15
15
  return undefined;
16
16
  }
17
17
  const result = await transformStoryAutoProps(code, async (specifier) => {
@@ -57,12 +57,12 @@ export async function transformStoryAutoProps(code, propsOf) {
57
57
  function importedComponents(body) {
58
58
  const found = new Map();
59
59
  for (const node of body) {
60
- if (node.type !== 'ImportDeclaration' || !/\.svelte$/.test(node.source?.value ?? '')) {
60
+ if (node.type !== 'ImportDeclaration' || !/\.svelte$/.test(node['source']?.value ?? '')) {
61
61
  continue;
62
62
  }
63
- for (const specifier of node.specifiers ?? []) {
63
+ for (const specifier of node['specifiers'] ?? []) {
64
64
  if (specifier.type === 'ImportDefaultSpecifier') {
65
- found.set(specifier.local.name, node.source.value);
65
+ found.set(specifier.local.name, node['source'].value);
66
66
  }
67
67
  }
68
68
  }
@@ -75,13 +75,13 @@ function importedComponents(body) {
75
75
  */
76
76
  function collectTargets(fragment, imported) {
77
77
  const story = findStory(fragment);
78
- if (!story || blockWrapsAVariant(story.fragment)) {
78
+ if (!story || blockWrapsAVariant(story['fragment'])) {
79
79
  return undefined;
80
80
  }
81
81
  // At any depth and in document order, matching how `collect/Variant.svelte`
82
82
  // registers them. A story with none renders its children as variant zero.
83
83
  const variants = [];
84
- walk(story.fragment, (node) => {
84
+ walk(story['fragment'], (node) => {
85
85
  if (isHst(node, 'Variant')) {
86
86
  variants.push(node);
87
87
  }
@@ -90,7 +90,7 @@ function collectTargets(fragment, imported) {
90
90
  const targets = [];
91
91
  scopes.forEach((scope, variant) => {
92
92
  let index = 0;
93
- walk(scope.fragment, (node, ancestors) => {
93
+ walk(scope['fragment'], (node, ancestors) => {
94
94
  // The controls snippet builds the panel, not the preview. A component in
95
95
  // a block is skipped on its own.
96
96
  if (isControls(node)) {
@@ -99,7 +99,7 @@ function collectTargets(fragment, imported) {
99
99
  if (node.type !== 'Component' || insideBlock(ancestors)) {
100
100
  return undefined;
101
101
  }
102
- const specifier = imported.get(node.name);
102
+ const specifier = imported.get(node['name']);
103
103
  if (specifier) {
104
104
  targets.push({ node, variant, index: index++, specifier });
105
105
  }
@@ -131,12 +131,12 @@ function insideBlock(ancestors) {
131
131
  }
132
132
  function isControls(node) {
133
133
  if (node.type === 'SnippetBlock') {
134
- return node.expression?.name === 'controls';
134
+ return node['expression']?.name === 'controls';
135
135
  }
136
- return (node.attributes ?? []).some((attribute) => attribute.name === 'slot' && attribute.value?.[0]?.data === 'controls');
136
+ return (node['attributes'] ?? []).some((attribute) => attribute['name'] === 'slot' && attribute['value']?.[0]?.data === 'controls');
137
137
  }
138
138
  function isHst(node, member) {
139
- return node.type === 'Component' && (member ? node.name === `${HST}.${member}` : node.name?.startsWith(`${HST}.`));
139
+ return node.type === 'Component' && (member ? node['name'] === `${HST}.${member}` : node['name']?.startsWith(`${HST}.`));
140
140
  }
141
141
  // `then`/`catch`/`pending` and `fallback` are branches a variant can hide in.
142
142
  const CHILD_KEYS = ['fragment', 'nodes', 'children', 'body', 'consequent', 'alternate', 'pending', 'then', 'catch', 'fallback'];
@@ -172,7 +172,7 @@ async function describe(targets, propsOf) {
172
172
  for (const target of targets) {
173
173
  const props = await sources.get(target.specifier);
174
174
  if (props?.length) {
175
- defs[target.variant].push({ name: target.node.name, index: target.index, props });
175
+ defs[target.variant]?.push({ name: target.node['name'], index: target.index, props });
176
176
  }
177
177
  }
178
178
  return defs;
@@ -222,9 +222,9 @@ function literal(value) {
222
222
  .replace(/\u2029/g, '\\u2029');
223
223
  }
224
224
  function attributesEnd(code, node) {
225
- const attributes = node.attributes ?? [];
225
+ const attributes = node['attributes'] ?? [];
226
226
  if (attributes.length > 0) {
227
227
  return Math.max(...attributes.map((attribute) => attribute.end));
228
228
  }
229
- return code.indexOf(node.name, node.start) + node.name.length;
229
+ return code.indexOf(node['name'], node.start) + node['name'].length;
230
230
  }
@@ -1 +1 @@
1
- export declare function listComponentFiles(search?: string, ignore?: string[], limit?: number): Promise<string[]>;
1
+ export declare function listComponentFiles(search?: string, ignoredPatterns?: string[], limit?: number, cwd?: string): Promise<string[]>;
@@ -1,12 +1,69 @@
1
- import { globby } from 'globby';
2
- export async function listComponentFiles(search = '', ignore = [], limit = 10) {
3
- let files = await globby('**/*.svelte', {
4
- gitignore: true,
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import ignore from 'ignore';
4
+ import { glob } from 'tinyglobby';
5
+ function toPosix(path) {
6
+ return path.split(sep).join('/');
7
+ }
8
+ /**
9
+ * Whether a file under `cwd` is git-ignored, the way globby's `gitignore: true`
10
+ * decided it: every `.gitignore` from the repository root down, each pattern
11
+ * relative to its own file. tinyglobby reads none of them (#839).
12
+ */
13
+ async function gitIgnoredFilter(cwd) {
14
+ let root = cwd;
15
+ while (!existsSync(join(root, '.git'))) {
16
+ const parent = dirname(root);
17
+ if (parent === root) {
18
+ root = cwd;
19
+ break;
20
+ }
21
+ root = parent;
22
+ }
23
+ const ancestors = [];
24
+ for (let dir = cwd; dir !== root;) {
25
+ dir = dirname(dir);
26
+ ancestors.unshift(join(dir, '.gitignore'));
27
+ }
28
+ // Shallow files first, so a nested `.gitignore` can override its parent.
29
+ const nested = (await glob('**/.gitignore', { cwd, absolute: true, dot: true, ignore: ['**/node_modules/**', '**/.git/**'] }))
30
+ .sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b));
31
+ const rules = ignore();
32
+ for (const file of [...ancestors, ...nested]) {
33
+ if (!existsSync(file)) {
34
+ continue;
35
+ }
36
+ const base = toPosix(relative(root, dirname(file)));
37
+ for (const raw of readFileSync(file, 'utf8').split(/\r?\n/)) {
38
+ const line = raw.trimEnd();
39
+ if (!line || line.startsWith('#')) {
40
+ continue;
41
+ }
42
+ const negated = line.startsWith('!');
43
+ let pattern = negated ? line.slice(1) : line;
44
+ if (base) {
45
+ // A pattern with a slash before its end is anchored to its own directory;
46
+ // one without matches at any depth below it.
47
+ const anchored = pattern.replace(/\/$/, '').includes('/');
48
+ pattern = anchored ? `${base}/${pattern.replace(/^\//, '')}` : `${base}/**/${pattern}`;
49
+ }
50
+ rules.add(negated ? `!${pattern}` : pattern);
51
+ }
52
+ }
53
+ return (file) => {
54
+ const path = toPosix(relative(root, resolve(cwd, file)));
55
+ return !path.startsWith('..') && rules.ignores(path);
56
+ };
57
+ }
58
+ export async function listComponentFiles(search = '', ignoredPatterns = [], limit = 10, cwd = process.cwd()) {
59
+ const isIgnored = await gitIgnoredFilter(cwd);
60
+ let files = (await glob('**/*.svelte', {
61
+ cwd,
5
62
  ignore: [
6
63
  'node_modules',
7
- ...ignore,
64
+ ...ignoredPatterns,
8
65
  ],
9
- });
66
+ })).filter(file => !isIgnored(file));
10
67
  if (search) {
11
68
  const searchText = search.toLowerCase();
12
69
  files = files.filter(file => file.toLowerCase().includes(searchText));
@@ -1,10 +1,6 @@
1
- import { reportStoryError } from '@poveste/shared';
1
+ import { getSetupHook, reportStoryError } from '@poveste/shared';
2
2
  import * as svelte from 'svelte';
3
- const setupHookNames = [
4
- 'setupSvelte3',
5
- 'setupSvelte4',
6
- 'setupSvelte5',
7
- ];
3
+ import { SVELTE_RETIRED_SETUP_HOOK_NAMES, SVELTE_SETUP_HOOK_NAMES } from '../setup-hooks.js';
8
4
  function loadSvelteModule(moduleId) {
9
5
  return import(/* @vite-ignore */ moduleId);
10
6
  }
@@ -81,15 +77,17 @@ export function getLegacyStateApi(app) {
81
77
  };
82
78
  }
83
79
  export async function callSetupFunctions(generatedSetup, setup, setupApi, variantSetupApp) {
84
- for (const hookName of setupHookNames) {
85
- const generatedHook = generatedSetup[hookName];
86
- if (typeof generatedHook === 'function') {
87
- await generatedHook(setupApi);
88
- }
89
- const setupHook = setup[hookName];
90
- if (typeof setupHook === 'function') {
91
- await setupHook(setupApi);
92
- }
80
+ // One hook per module, not every name present. The loop this replaces ran all
81
+ // of them, so a file part-way through the migration — exporting `setupSvelte5`
82
+ // and `setupSvelte` at once — had its setup applied twice, silently. That is
83
+ // the failure the Vue pair has always been shaped to prevent (#157).
84
+ const generatedHook = getSetupHook(generatedSetup, SVELTE_SETUP_HOOK_NAMES, SVELTE_RETIRED_SETUP_HOOK_NAMES);
85
+ if (generatedHook) {
86
+ await generatedHook(setupApi);
87
+ }
88
+ const setupHook = getSetupHook(setup, SVELTE_SETUP_HOOK_NAMES, SVELTE_RETIRED_SETUP_HOOK_NAMES);
89
+ if (setupHook) {
90
+ await setupHook(setupApi);
93
91
  }
94
92
  if (typeof variantSetupApp === 'function') {
95
93
  await variantSetupApp(setupApi);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/plugin-svelte",
3
3
  "type": "module",
4
- "version": "0.14.0",
4
+ "version": "0.16.0",
5
5
  "description": "Poveste plugin for Svelte 5 and SvelteKit support",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan",
@@ -60,12 +60,12 @@
60
60
  "dist"
61
61
  ],
62
62
  "engines": {
63
- "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
63
+ "node": ">=22.22.2"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@sveltejs/kit": "^2.53.0",
67
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
68
- "poveste": "^0.14.0",
67
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
68
+ "poveste": "^0.16.0",
69
69
  "svelte": "^5.46.4"
70
70
  },
71
71
  "peerDependenciesMeta": {
@@ -74,26 +74,26 @@
74
74
  }
75
75
  },
76
76
  "dependencies": {
77
- "@poveste/controls": "^0.14.0",
78
- "@poveste/shared": "^0.14.0",
79
- "@poveste/vendors": "^0.14.0",
77
+ "@poveste/controls": "^0.16.0",
78
+ "@poveste/shared": "^0.16.0",
79
+ "@poveste/vendors": "^0.16.0",
80
80
  "change-case": "^5.4.4",
81
- "globby": "^14.0.2",
81
+ "ignore": "^7.0.10",
82
82
  "launch-editor": "^2.9.1",
83
- "pathe": "^1.1.2"
83
+ "pathe": "^2.0.3",
84
+ "tinyglobby": "^0.2.17"
84
85
  },
85
86
  "devDependencies": {
86
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
87
- "@types/node": "^22.10.1",
88
- "concurrently": "^9.1.0",
89
- "fs-extra": "^11.2.0",
90
- "globby": "^14.0.2",
91
- "poveste": "0.14.0",
92
- "svelte": "5.55.0",
93
- "svelte-preprocess": "^6.0.3",
94
- "typescript": "5.6.3",
95
- "vite": "^8.0.0",
96
- "vitest": "^5.0.0"
87
+ "@sveltejs/vite-plugin-svelte": "^7.3.1",
88
+ "@types/node": "^26.6.3",
89
+ "concurrently": "^10.0.5",
90
+ "fs-extra": "^11.4.1",
91
+ "poveste": "0.16.0",
92
+ "svelte": "5.57.1",
93
+ "svelte-preprocess": "^6.0.5",
94
+ "typescript": "6.0.3",
95
+ "vite": "^8.3.1",
96
+ "vitest": "^5.0.2"
97
97
  },
98
98
  "scripts": {
99
99
  "build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",