@poveste/plugin-svelte 0.5.3 → 0.6.1

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.
@@ -1 +1 @@
1
- import{afterUpdate as e,getContext as t}from"svelte";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as n from"svelte/internal/client";n.from_html(`<!> <!>`,1);function r(r,i){let a=n.sanitize_slots(i);n.push(i,!1);let o=n.prop(i,`source`,8,null),s=n.prop(i,`responsiveDisabled`,8,!1),c=n.prop(i,`autoPropsDisabled`,8,!1),l=n.prop(i,`setupApp`,8,null),u=n.prop(i,`implicit`,8,!1),d=t(`__pvtStory`),f=t(`__pvtIndex`),p=t(`__pvtSlots`),m=d.variants[f.value];f.value++;function h(){Object.assign(m,{slots:()=>({default:!0,controls:a.controls??p.controls}),source:o(),responsiveDisabled:s(),autoPropsDisabled:c(),setupApp:l(),configReady:!0}),!u()&&!d.meta?.hasVariantChildComponents&&(d.meta=d.meta||{},Object.assign(d.meta,{hasVariantChildComponents:!0}))}h(),e(()=>{h()}),n.init();var g=n.comment(),_=n.first_child(g);n.if(_,e=>{}),n.append(r,g),n.pop()}export{r as default};
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,5 +1,5 @@
1
1
  <script>
2
- import { afterUpdate, getContext } from 'svelte'
2
+ import { afterUpdate, getContext, onDestroy } from 'svelte'
3
3
 
4
4
  export let source = null
5
5
  export let responsiveDisabled = false
@@ -10,10 +10,19 @@
10
10
  const story = getContext('__pvtStory')
11
11
  const index = getContext('__pvtIndex')
12
12
  const storySlots = getContext('__pvtSlots')
13
+ // A store: a sandbox sets it to the one variant this realm serves and changes
14
+ // it when the realm is retargeted (#240). Null in the app realm, which keeps
15
+ // every variant's bookkeeping.
16
+ const targetVariantId = getContext('__pvtTargetVariantId') ?? null
13
17
 
14
18
  const variant = story.variants[index.value]
15
19
  index.value++
16
20
 
21
+ // A sandbox mounts the story to serve one variant. The others still get an
22
+ // instance — that is the story's `{#each}` — but their slot/config sync and
23
+ // the afterUpdate re-run are work for a variant this realm never renders (#197).
24
+ let isTarget = false
25
+
17
26
  function updateVariant() {
18
27
  Object.assign(variant, {
19
28
  slots: () => ({
@@ -34,10 +43,28 @@
34
43
  })
35
44
  }
36
45
  }
37
- updateVariant()
38
46
 
39
- afterUpdate(() => {
47
+ function becomeTarget() {
48
+ if (isTarget) return
49
+ isTarget = true
40
50
  updateVariant()
51
+ }
52
+
53
+ if (targetVariantId === null) {
54
+ becomeTarget()
55
+ }
56
+ else {
57
+ // Subscribed rather than `$`-read: the subscriber runs as the store is
58
+ // set, so the variant has registered before the render pass that follows
59
+ // the retarget mounts it.
60
+ const unsubscribe = targetVariantId.subscribe((id) => {
61
+ if (id === null || variant?.id === id) becomeTarget()
62
+ })
63
+ onDestroy(unsubscribe)
64
+ }
65
+
66
+ afterUpdate(() => {
67
+ if (isTarget) updateVariant()
41
68
  })
42
69
  </script>
43
70
 
@@ -1 +1 @@
1
- import{watch as e}from"@poveste/vendors/vue";import{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";var i=r.from_html(`<!> <!>`,1);function a(a,o){let s=r.sanitize_slots(o);r.push(o,!1);let c=t(`__pvtStory`),l=r.mutable_source(t(`__pvtVariant`)),u=t(`__pvtSlot`),d=t(`__pvtIndex`),f=c.variants[d.value];d.value++;let p=r.get(l).id===f.id,m=r.prop(o,`source`,8,null),h=r.prop(o,`initState`,8,null);h()&&p&&!r.get(l).__pvtStateSeeded&&(r.mutate(l,r.get(l).__pvtStateSeeded=!0),r.mutate(l,r.get(l).state={...r.get(l).state,...h()()})),!h()&&p&&u===`controls`&&s.controls&&!r.get(l).__pvtStateWarned&&(r.mutate(l,r.get(l).__pvtStateWarned=!0),console.error([`[poveste] Variant "${f?.title??f?.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 g=r.mutable_source(r.get(l)?.state),_=e(()=>r.get(l)?.state,()=>{r.set(g,r.get(l)?.state)},{deep:!0});n(_),r.legacy_pre_effect(()=>(r.deep_read_state(m()),r.get(g),r.get(l)),()=>{let e=typeof m()==`function`?m()(r.get(g)):m();e!=null&&Object.assign(r.get(l),{source:e})}),r.legacy_pre_effect_reset(),r.init();var v=r.comment(),y=r.first_child(v),b=e=>{var t=i(),n=r.first_child(t),a=e=>{var t=r.comment(),n=r.first_child(t);r.slot(n,o,`default`,{get state(){return r.get(g)}},null),r.append(e,t)};r.if(n,e=>{u==="default"&&e(a)});var s=r.sibling(n,2),c=e=>{var t=r.comment(),n=r.first_child(t);r.slot(n,o,`controls`,{get state(){return r.get(g)}},null),r.append(e,t)};r.if(s,e=>{u===`controls`&&e(c)}),r.append(e,t)};r.if(y,e=>{p&&e(b)}),r.append(a,v),r.pop()}export{a as default};
1
+ import{watch as e}from"@poveste/vendors/vue";import{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";var i=r.from_html(`<!> <!>`,1);function a(a,o){let s=r.sanitize_slots(o);r.push(o,!1);let c=t(`__pvtStory`),l=r.mutable_source(t(`__pvtVariant`)),u=t(`__pvtSlot`),d=t(`__pvtIndex`),f=c.variants[d.value];d.value++;let p=r.get(l).id===f.id,m=r.prop(o,`source`,8,null),h=r.prop(o,`initState`,8,null);h()&&p&&!r.get(l).__pvtStateSeeded&&(r.mutate(l,r.get(l).__pvtStateSeeded=!0),r.mutate(l,r.get(l).state={...r.get(l).state,...h()()})),!h()&&p&&u===`controls`&&s.controls&&!r.get(l).__pvtStateWarned&&(r.mutate(l,r.get(l).__pvtStateWarned=!0),console.error([`[poveste] Variant "${f?.title??f?.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 g=r.mutable_source(r.get(l)?.state),_=()=>{};p&&(_=e(()=>r.get(l)?.state,()=>{r.set(g,r.get(l)?.state)},{deep:!0})),n(()=>_()),r.legacy_pre_effect(()=>(r.deep_read_state(m()),r.get(g),r.get(l)),()=>{if(p){let e=typeof m()==`function`?m()(r.get(g)):m();e!=null&&Object.assign(r.get(l),{source:e})}}),r.legacy_pre_effect_reset(),r.init();var v=r.comment(),y=r.first_child(v),b=e=>{var t=i(),n=r.first_child(t),a=e=>{var t=r.comment(),n=r.first_child(t);r.slot(n,o,`default`,{get state(){return r.get(g)}},null),r.append(e,t)};r.if(n,e=>{u==="default"&&e(a)});var s=r.sibling(n,2),c=e=>{var t=r.comment(),n=r.first_child(t);r.slot(n,o,`controls`,{get state(){return r.get(g)}},null),r.append(e,t)};r.if(s,e=>{u===`controls`&&e(c)}),r.append(e,t)};r.if(y,e=>{p&&e(b)}),r.append(a,v),r.pop()}export{a as default};
@@ -42,17 +42,28 @@
42
42
 
43
43
  let state = currentVariant?.state
44
44
 
45
- const stopWatchingState = _watch(() => currentVariant?.state, () => {
46
- state = currentVariant?.state
47
- }, { deep: true })
45
+ // Only the variant that renders watches. The story's `{#each}` instantiates
46
+ // one of these per variant in every realm, and a deep Vue watcher per
47
+ // instance on the same state object was a thousand watchers to render one
48
+ // button (#197). The others render nothing, so they have nothing to update.
49
+ let stopWatchingState = () => {}
50
+ if (shouldRender) {
51
+ stopWatchingState = _watch(() => currentVariant?.state, () => {
52
+ state = currentVariant?.state
53
+ }, { deep: true })
54
+ }
48
55
 
49
- onDestroy(stopWatchingState)
56
+ onDestroy(() => stopWatchingState())
50
57
 
51
58
  // `source` may be a function of the state. Story props are evaluated in the
52
59
  // component's script, where the slot's `state` is not in scope, so a string
53
60
  // literal cannot reflect what the controls are doing — which is most of the
54
61
  // point of the source panel (#81).
55
- $: {
62
+ //
63
+ // Guarded by `shouldRender`: this is *this* variant's `source`, and it goes
64
+ // onto `currentVariant` — so an unguarded instance for another variant would
65
+ // overwrite the rendered variant's source with its own.
66
+ $: if (shouldRender) {
56
67
  const resolvedSource = typeof source === 'function' ? source(state) : source
57
68
  if (resolvedSource != null) {
58
69
  Object.assign(currentVariant, {
@@ -5,6 +5,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
5
5
  type: _PropType<Story>;
6
6
  required: true;
7
7
  };
8
+ targetVariantId: {
9
+ type: StringConstructor;
10
+ default: any;
11
+ };
8
12
  }>, {
9
13
  el: import("vue").Ref<HTMLDivElement, HTMLDivElement>;
10
14
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
@@ -12,5 +16,11 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
12
16
  type: _PropType<Story>;
13
17
  required: true;
14
18
  };
15
- }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
19
+ targetVariantId: {
20
+ type: StringConstructor;
21
+ default: any;
22
+ };
23
+ }>> & Readonly<{}>, {
24
+ targetVariantId: string;
25
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
16
26
  export default _default;
@@ -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*as f from"virtual:$poveste-generated-global-setup";import*as p from"virtual:$poveste-setup";var m=o({name:`MountStory`,props:{story:{type:Object,required:!0}},setup(i){let a=u(),o,s,m=null;async function g(){s=document.createElement(`div`),a.value.appendChild(s);let c=await t(i.story.file.component,{target:s,props:{Hst:{Story:r,Variant:n,...h()}},context:new Map(Object.entries({__pvtStory:i.story}))},`client`);o=c.app,m=c.destroy;let l={app:o,story:i.story,variant:null};await e(f,p,l)}function _(){m?.(),m=null,s&&=(s.parentNode?.removeChild(s),null),o=null}return d(()=>i.story.id,async()=>{_(),await g()}),c(async()=>{await g()}),l(()=>{_()}),{el:a}},render(){return s(`div`,{ref:`el`})}});function h(){let e={};for(let t in a)e[t.substring(3)]=i;return e}export{m as default};
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`);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 +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};await e(i,a,f),await o(),l[0]?.variants.length||l[0].variants.push({id:`_default`,title:`default`}),d.destroy()}export{s as run};
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};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/plugin-svelte",
3
3
  "type": "module",
4
- "version": "0.5.3",
4
+ "version": "0.6.1",
5
5
  "description": "Poveste plugin for Svelte 5 and SvelteKit support",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan"
@@ -28,7 +28,7 @@
28
28
  "@sveltejs/kit": "^2.53.0",
29
29
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
30
30
  "svelte": "^5.46.4",
31
- "poveste": "^0.5.3"
31
+ "poveste": "^0.6.1"
32
32
  },
33
33
  "peerDependenciesMeta": {
34
34
  "@sveltejs/kit": {
@@ -40,9 +40,9 @@
40
40
  "globby": "^14.0.2",
41
41
  "launch-editor": "^2.9.1",
42
42
  "pathe": "^1.1.2",
43
- "@poveste/controls": "^0.5.3",
44
- "@poveste/shared": "^0.5.3",
45
- "@poveste/vendors": "^0.5.3"
43
+ "@poveste/controls": "^0.6.1",
44
+ "@poveste/shared": "^0.6.1",
45
+ "@poveste/vendors": "^0.6.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
@@ -54,7 +54,7 @@
54
54
  "svelte-preprocess": "^6.0.3",
55
55
  "typescript": "5.6.3",
56
56
  "vite": "^8.0.0",
57
- "poveste": "0.5.3"
57
+ "poveste": "0.6.1"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { afterUpdate, getContext } from 'svelte'
2
+ import { afterUpdate, getContext, onDestroy } from 'svelte'
3
3
 
4
4
  export let source = null
5
5
  export let responsiveDisabled = false
@@ -10,10 +10,19 @@
10
10
  const story = getContext('__pvtStory')
11
11
  const index = getContext('__pvtIndex')
12
12
  const storySlots = getContext('__pvtSlots')
13
+ // A store: a sandbox sets it to the one variant this realm serves and changes
14
+ // it when the realm is retargeted (#240). Null in the app realm, which keeps
15
+ // every variant's bookkeeping.
16
+ const targetVariantId = getContext('__pvtTargetVariantId') ?? null
13
17
 
14
18
  const variant = story.variants[index.value]
15
19
  index.value++
16
20
 
21
+ // A sandbox mounts the story to serve one variant. The others still get an
22
+ // instance — that is the story's `{#each}` — but their slot/config sync and
23
+ // the afterUpdate re-run are work for a variant this realm never renders (#197).
24
+ let isTarget = false
25
+
17
26
  function updateVariant() {
18
27
  Object.assign(variant, {
19
28
  slots: () => ({
@@ -34,10 +43,28 @@
34
43
  })
35
44
  }
36
45
  }
37
- updateVariant()
38
46
 
39
- afterUpdate(() => {
47
+ function becomeTarget() {
48
+ if (isTarget) return
49
+ isTarget = true
40
50
  updateVariant()
51
+ }
52
+
53
+ if (targetVariantId === null) {
54
+ becomeTarget()
55
+ }
56
+ else {
57
+ // Subscribed rather than `$`-read: the subscriber runs as the store is
58
+ // set, so the variant has registered before the render pass that follows
59
+ // the retarget mounts it.
60
+ const unsubscribe = targetVariantId.subscribe((id) => {
61
+ if (id === null || variant?.id === id) becomeTarget()
62
+ })
63
+ onDestroy(unsubscribe)
64
+ }
65
+
66
+ afterUpdate(() => {
67
+ if (isTarget) updateVariant()
41
68
  })
42
69
  </script>
43
70
 
@@ -42,17 +42,28 @@
42
42
 
43
43
  let state = currentVariant?.state
44
44
 
45
- const stopWatchingState = _watch(() => currentVariant?.state, () => {
46
- state = currentVariant?.state
47
- }, { deep: true })
45
+ // Only the variant that renders watches. The story's `{#each}` instantiates
46
+ // one of these per variant in every realm, and a deep Vue watcher per
47
+ // instance on the same state object was a thousand watchers to render one
48
+ // button (#197). The others render nothing, so they have nothing to update.
49
+ let stopWatchingState = () => {}
50
+ if (shouldRender) {
51
+ stopWatchingState = _watch(() => currentVariant?.state, () => {
52
+ state = currentVariant?.state
53
+ }, { deep: true })
54
+ }
48
55
 
49
- onDestroy(stopWatchingState)
56
+ onDestroy(() => stopWatchingState())
50
57
 
51
58
  // `source` may be a function of the state. Story props are evaluated in the
52
59
  // component's script, where the slot's `state` is not in scope, so a string
53
60
  // literal cannot reflect what the controls are doing — which is most of the
54
61
  // point of the source panel (#81).
55
- $: {
62
+ //
63
+ // Guarded by `shouldRender`: this is *this* variant's `source`, and it goes
64
+ // onto `currentVariant` — so an unguarded instance for another variant would
65
+ // overwrite the rendered variant's source with its own.
66
+ $: if (shouldRender) {
56
67
  const resolvedSource = typeof source === 'function' ? source(state) : source
57
68
  if (resolvedSource != null) {
58
69
  Object.assign(currentVariant, {
@@ -12,6 +12,7 @@ import {
12
12
  ref as _ref,
13
13
  watch as _watch,
14
14
  } from '@poveste/vendors/vue'
15
+ import { writable } from 'svelte/store'
15
16
  // @ts-expect-error virtual module id
16
17
  import * as generatedSetup from 'virtual:$poveste-generated-global-setup'
17
18
  // @ts-expect-error virtual module id
@@ -32,6 +33,14 @@ export default _defineComponent({
32
33
  type: Object as _PropType<Story>,
33
34
  required: true,
34
35
  },
36
+
37
+ // Set by a sandbox: the one variant this realm serves. The story's `{#each}`
38
+ // still instantiates a MountVariant per variant — that is the user's
39
+ // template — but the others can skip their bookkeeping (#197).
40
+ targetVariantId: {
41
+ type: String,
42
+ default: null,
43
+ },
35
44
  },
36
45
 
37
46
  setup(props) {
@@ -40,6 +49,14 @@ export default _defineComponent({
40
49
  let target: HTMLDivElement
41
50
  let destroyApp: (() => void) | null = null
42
51
 
52
+ // A store rather than a value, so a retargeted realm (#240) can tell the
53
+ // variant it now serves to register without remounting the story — the
54
+ // context is fixed at mount, a store subscription is not.
55
+ const targetVariantId = writable<string | null>(props.targetVariantId)
56
+ _watch(() => props.targetVariantId, (id) => {
57
+ targetVariantId.set(id)
58
+ })
59
+
43
60
  async function mountStory() {
44
61
  target = document.createElement('div')
45
62
  el.value.appendChild(target)
@@ -55,6 +72,7 @@ export default _defineComponent({
55
72
  },
56
73
  context: new Map(Object.entries({
57
74
  __pvtStory: props.story,
75
+ __pvtTargetVariantId: targetVariantId,
58
76
  })),
59
77
  }, 'client')
60
78
  app = mountedApp.app
@@ -42,7 +42,19 @@ export async function run({ file, el, storyData }: ServerRunPayload) {
42
42
 
43
43
  await tick()
44
44
 
45
- if (!storyData[0]?.variants.length) {
45
+ // A file with no `Hst.Story` in it collects nothing, which is a thing people
46
+ // write — a scratch file, a component that lost its story tag in a refactor.
47
+ // The optional chain below used to guard only the read: with no story the
48
+ // condition came out true and the body then dereferenced the same `undefined`,
49
+ // so the collector crashed the whole build instead of skipping one file. The
50
+ // Vue collector warns and carries on, and the shared warning downstream
51
+ // (`No story found for …`) is already written for exactly this.
52
+ if (!storyData[0]) {
53
+ mountedApp.destroy()
54
+ return
55
+ }
56
+
57
+ if (!storyData[0].variants.length) {
46
58
  storyData[0].variants.push({
47
59
  id: '_default',
48
60
  title: 'default',