@poveste/plugin-svelte 0.6.1 → 0.7.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.
package/client.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // https://github.com/microsoft/TypeScript/issues/33079
2
+ export * from './dist/client/index'
package/collect.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // https://github.com/microsoft/TypeScript/issues/33079
2
+ export * from './dist/collect/index'
@@ -1,5 +1,5 @@
1
- export { default as MountStory } from './mount';
2
- export { default as RenderStory } from './render';
1
+ export { default as MountStory } from './mount.js';
2
+ export { default as RenderStory } from './render.js';
3
3
  declare module '@poveste/shared' {
4
4
  interface StoryMeta {
5
5
  hasVariantChildComponents?: boolean;
package/package.json CHANGED
@@ -1,34 +1,66 @@
1
1
  {
2
2
  "name": "@poveste/plugin-svelte",
3
3
  "type": "module",
4
- "version": "0.6.1",
4
+ "version": "0.7.0",
5
5
  "description": "Poveste plugin for Svelte 5 and SvelteKit support",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan"
8
8
  },
9
9
  "license": "MIT",
10
+ "homepage": "https://poveste.dev",
10
11
  "repository": {
11
12
  "url": "https://github.com/poveste-dev/poveste.git",
12
13
  "type": "git",
13
14
  "directory": "packages/poveste-plugin-svelte"
14
15
  },
16
+ "bugs": "https://github.com/poveste-dev/poveste/issues",
17
+ "keywords": [
18
+ "poveste",
19
+ "histoire",
20
+ "storybook",
21
+ "component-playground",
22
+ "component-explorer",
23
+ "stories",
24
+ "styleguide",
25
+ "design-system",
26
+ "vite",
27
+ "svelte",
28
+ "svelte5",
29
+ "sveltekit",
30
+ "plugin"
31
+ ],
15
32
  "publishConfig": {
16
33
  "access": "public"
17
34
  },
18
35
  "exports": {
19
- "./client": "./dist/client/index.js",
20
- "./collect": "./dist/collect/index.js",
36
+ "./client": {
37
+ "types": "./dist/client/index.d.ts",
38
+ "default": "./dist/client/index.js"
39
+ },
40
+ "./collect": {
41
+ "types": "./dist/collect/index.d.ts",
42
+ "default": "./dist/collect/index.js"
43
+ },
21
44
  ".": "./dist/index.node.js",
22
45
  "./*": "./*"
23
46
  },
24
47
  "main": "./dist/index.node.js",
25
48
  "module": "./dist/index.node.js",
26
49
  "types": "./dist/index.node.d.ts",
50
+ "files": [
51
+ "assets",
52
+ "client.d.ts",
53
+ "collect.d.ts",
54
+ "dist"
55
+ ],
56
+ "engines": {
57
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
58
+ },
27
59
  "peerDependencies": {
28
60
  "@sveltejs/kit": "^2.53.0",
29
61
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
30
62
  "svelte": "^5.46.4",
31
- "poveste": "^0.6.1"
63
+ "poveste": "^0.7.0"
32
64
  },
33
65
  "peerDependenciesMeta": {
34
66
  "@sveltejs/kit": {
@@ -40,9 +72,9 @@
40
72
  "globby": "^14.0.2",
41
73
  "launch-editor": "^2.9.1",
42
74
  "pathe": "^1.1.2",
43
- "@poveste/controls": "^0.6.1",
44
- "@poveste/shared": "^0.6.1",
45
- "@poveste/vendors": "^0.6.1"
75
+ "@poveste/controls": "^0.7.0",
76
+ "@poveste/vendors": "^0.7.0",
77
+ "@poveste/shared": "^0.7.0"
46
78
  },
47
79
  "devDependencies": {
48
80
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
@@ -54,7 +86,7 @@
54
86
  "svelte-preprocess": "^6.0.3",
55
87
  "typescript": "5.6.3",
56
88
  "vite": "^8.0.0",
57
- "poveste": "0.6.1"
89
+ "poveste": "0.7.0"
58
90
  },
59
91
  "scripts": {
60
92
  "build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",
@@ -1,27 +0,0 @@
1
- <script>
2
- import { omitInheritStoryProps } from '@poveste/shared'
3
- import { getContext, setContext } from 'svelte'
4
- import MountVariant from './MountVariant.svelte'
5
-
6
- const story = getContext('__pvtStory')
7
- const index = { value: 0 }
8
- setContext('__pvtIndex', index)
9
- setContext('__pvtSlots', $$slots)
10
-
11
- // Not `$:`: Svelte invalidates a reactive statement when a variable it
12
- // references is reassigned, and `story` is a const read from context that
13
- // never is — so the statement ran exactly once whichever way it is written.
14
- const inheritedFromStory = Object.keys(story).filter(key => !omitInheritStoryProps.includes(key)).reduce((acc, key) => {
15
- acc[key] = story[key]
16
- return acc
17
- }, {})
18
- </script>
19
-
20
- {#if story.variants.length === 1 && story.variants[0].id === '_default'}
21
- <MountVariant {...inheritedFromStory} {...$$restProps} implicit>
22
- <slot />
23
- <slot name="controls" slot="controls" />
24
- </MountVariant>
25
- {:else}
26
- <slot />
27
- {/if}
@@ -1,74 +0,0 @@
1
- <script>
2
- import { afterUpdate, getContext, onDestroy } from 'svelte'
3
-
4
- export let source = null
5
- export let responsiveDisabled = false
6
- export let autoPropsDisabled = false
7
- export let setupApp = null
8
- export let implicit = false
9
-
10
- const story = getContext('__pvtStory')
11
- const index = getContext('__pvtIndex')
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
17
-
18
- const variant = story.variants[index.value]
19
- index.value++
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
-
26
- function updateVariant() {
27
- Object.assign(variant, {
28
- slots: () => ({
29
- default: true,
30
- controls: $$slots.controls ?? storySlots.controls,
31
- }),
32
- source,
33
- responsiveDisabled,
34
- autoPropsDisabled,
35
- setupApp,
36
- configReady: true,
37
- })
38
-
39
- if (!implicit && !story.meta?.hasVariantChildComponents) {
40
- story.meta = story.meta || {}
41
- Object.assign(story.meta, {
42
- hasVariantChildComponents: true,
43
- })
44
- }
45
- }
46
-
47
- function becomeTarget() {
48
- if (isTarget) return
49
- isTarget = true
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()
68
- })
69
- </script>
70
-
71
- {#if false}
72
- <slot />
73
- <slot name="controls" />
74
- {/if}
@@ -1,77 +0,0 @@
1
- <script>
2
- import { watch as _watch } from '@poveste/vendors/vue'
3
- import { getContext, onDestroy, setContext } from 'svelte'
4
-
5
- const story = getContext('__pvtStory')
6
- const currentVariant = getContext('__pvtVariant')
7
- const slotName = getContext('__pvtSlot')
8
-
9
- const index = { value: 0 }
10
- setContext('__pvtIndex', index)
11
-
12
- export let source = null
13
- export let initState = null
14
-
15
- // Poveste owns the state, not the story component.
16
- //
17
- // The story is mounted once per slot — the app renders it with
18
- // `slot-name="controls"`, the preview with the default — so a `let count` in
19
- // the story exists twice and cannot be shared. Svelte 4 bridged the two
20
- // instances with `$capture_state` / `$inject_state`; Svelte 5 provides neither,
21
- // and no shim does either. So state lives on the variant, which both mounts
22
- // read from the same context object and which the sandbox bridge already
23
- // serialises across the iframe boundary (#81).
24
- //
25
- // Whichever mount arrives first seeds it; the second must not, or it would
26
- // clobber whatever the user has already changed.
27
- if (initState && currentVariant && !currentVariant.__pvtStateSeeded) {
28
- currentVariant.__pvtStateSeeded = true
29
- currentVariant.state = { ...currentVariant.state, ...initState() }
30
- }
31
-
32
- // Only from the mount that renders the controls. The story is mounted once per
33
- // slot and, for an iframe layout, in a different realm — so the flag below cannot
34
- // dedupe across them and the warning would appear twice.
35
- if (!initState && slotName === 'controls' && $$slots.controls && currentVariant && !currentVariant.__pvtStateWarned) {
36
- currentVariant.__pvtStateWarned = true
37
- console.error([
38
- `[poveste] Story "${story?.title ?? story?.id}" has a controls slot but no \`initState\`.`,
39
- 'Controls will render and appear to work, but their edits cannot reach the story:',
40
- 'the story is mounted separately for each slot, so a component-local variable is not shared.',
41
- 'Pass `initState` and read state from the snippet —',
42
- 'https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate',
43
- ].join(' '))
44
- }
45
-
46
- // `variant.state` is a Vue-reactive object, so Svelte has no way to know when a
47
- // control writes into it. Mirroring the reference back into a local on every
48
- // Vue tick is what makes the template re-render: Svelte's invalidation treats
49
- // any object assignment as a change, so reassigning the same proxy is enough.
50
- let state = currentVariant?.state
51
-
52
- const stopWatchingState = _watch(() => currentVariant?.state, () => {
53
- state = currentVariant?.state
54
- }, { deep: true })
55
-
56
- onDestroy(stopWatchingState)
57
-
58
- // `source` may be a function of the state. Story props are evaluated in the
59
- // component's script, where the slot's `state` is not in scope, so a string
60
- // literal cannot reflect what the controls are doing — which is most of the
61
- // point of the source panel (#81).
62
- $: {
63
- const resolvedSource = typeof source === 'function' ? source(state) : source
64
- if (resolvedSource != null) {
65
- Object.assign(currentVariant, {
66
- source: resolvedSource,
67
- })
68
- }
69
- }
70
- </script>
71
-
72
- {#if slotName === 'controls'}
73
- <slot name="controls" {state} />
74
- {/if}
75
- {#if slotName === 'default' || story.meta?.hasVariantChildComponents}
76
- <slot {state} />
77
- {/if}
@@ -1,83 +0,0 @@
1
- <script>
2
- import { watch as _watch } from '@poveste/vendors/vue'
3
- import { getContext, onDestroy } from 'svelte'
4
-
5
- const story = getContext('__pvtStory')
6
- const currentVariant = getContext('__pvtVariant')
7
- const slotName = getContext('__pvtSlot')
8
- const index = getContext('__pvtIndex')
9
-
10
- const variant = story.variants[index.value]
11
- index.value++
12
-
13
- // Not `$:`: Svelte invalidates a reactive statement when a variable it
14
- // references is reassigned, and both of these are consts that never are —
15
- // so the statement ran exactly once whichever way it is written.
16
- const shouldRender = currentVariant.id === variant.id
17
-
18
- export let source = null
19
- export let initState = null
20
-
21
- // Same contract as `RenderStory`: poveste owns the state because the story is
22
- // mounted once per slot, so a component-local variable cannot be shared (#81).
23
- // `variant.state` is also the only bucket the sandbox bridge carries across the
24
- // iframe boundary, which is why state is per variant rather than per story.
25
- if (initState && shouldRender && !currentVariant.__pvtStateSeeded) {
26
- currentVariant.__pvtStateSeeded = true
27
- currentVariant.state = { ...currentVariant.state, ...initState() }
28
- }
29
-
30
- // Controls mount only — see the note in RenderStory: the two mounts do not share
31
- // a realm for iframe layouts, so this is what keeps it to one warning.
32
- if (!initState && shouldRender && slotName === 'controls' && $$slots.controls && !currentVariant.__pvtStateWarned) {
33
- currentVariant.__pvtStateWarned = true
34
- console.error([
35
- `[poveste] Variant "${variant?.title ?? variant?.id}" has a controls slot but no \`initState\`.`,
36
- 'Controls will render and appear to work, but their edits cannot reach the story:',
37
- 'the story is mounted separately for each slot, so a component-local variable is not shared.',
38
- 'Pass `initState` and read state from the snippet —',
39
- 'https://poveste.dev/guide/migration-from-histoire.html#svelte-story-state-moves-to-initstate',
40
- ].join(' '))
41
- }
42
-
43
- let state = currentVariant?.state
44
-
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
- }
55
-
56
- onDestroy(() => stopWatchingState())
57
-
58
- // `source` may be a function of the state. Story props are evaluated in the
59
- // component's script, where the slot's `state` is not in scope, so a string
60
- // literal cannot reflect what the controls are doing — which is most of the
61
- // point of the source panel (#81).
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) {
67
- const resolvedSource = typeof source === 'function' ? source(state) : source
68
- if (resolvedSource != null) {
69
- Object.assign(currentVariant, {
70
- source: resolvedSource,
71
- })
72
- }
73
- }
74
- </script>
75
-
76
- {#if shouldRender}
77
- {#if slotName === 'default'}
78
- <slot {state} />
79
- {/if}
80
- {#if slotName === 'controls'}
81
- <slot name="controls" {state} />
82
- {/if}
83
- {/if}
File without changes
@@ -1,68 +0,0 @@
1
- <script>
2
- import {
3
- createApp as _createApp,
4
- h as _h,
5
- reactive as _reactive,
6
- } from '@poveste/vendors/vue'
7
- import { createEventDispatcher, onMount } from 'svelte'
8
-
9
- export let controlComponent
10
- export let value
11
-
12
- const dispatch = createEventDispatcher()
13
-
14
- let el
15
-
16
- let app
17
-
18
- const state = _reactive({})
19
-
20
- function updateState(value, attrs) {
21
- Object.assign(state, {
22
- value,
23
- attrs,
24
- })
25
- }
26
-
27
- onMount(() => {
28
- updateState(value, $$restProps)
29
-
30
- app = _createApp({
31
- render() {
32
- const finalListeners = {}
33
- if (controlComponent.emits) {
34
- for (const k in controlComponent.emits) {
35
- const key = Array.isArray(controlComponent.emits) ? controlComponent.emits[k] : k
36
- const finalKey = key === 'input' ? 'update:modelValue' : key
37
- finalListeners[`on${finalKey.charAt(0).toUpperCase()}${finalKey.substring(1)}`] = (...args) => {
38
- if (key === 'update:modelValue') {
39
- value = args[0]
40
- }
41
- else {
42
- dispatch(key, ...args)
43
- }
44
- }
45
- }
46
- }
47
-
48
- // `state` is the Vue-reactive mirror kept in sync by the `$:` below.
49
- // Reading the plain `value` here would make the render depend on a
50
- // variable Vue cannot track — it only saw fresh values because the
51
- // `state` mutation was what triggered the re-render in the first place.
52
- return _h(controlComponent, {
53
- modelValue: state.value,
54
- ...state.attrs,
55
- ...finalListeners,
56
- key: 'component',
57
- })
58
- },
59
- })
60
- app.mount(el)
61
- })
62
-
63
- $: updateState(value, $$restProps)
64
- </script>
65
-
66
- <div bind:this={el}>
67
- {controlComponent.name}
68
- </div>
@@ -1,12 +0,0 @@
1
- export { default as MountStory } from './mount'
2
- export { default as RenderStory } from './render'
3
-
4
- declare module '@poveste/shared' {
5
- interface StoryMeta {
6
- hasVariantChildComponents?: boolean
7
- }
8
- }
9
-
10
- export function generateSourceCode() {
11
- // noop
12
- }
@@ -1,131 +0,0 @@
1
- import type { Story } from '@poveste/shared'
2
- import type {
3
- PropType as _PropType,
4
- } from '@poveste/vendors/vue'
5
- import type { SvelteStorySetupApi } from '../helpers.js'
6
- import { components } from '@poveste/controls'
7
- import {
8
- defineComponent as _defineComponent,
9
- h as _h,
10
- onMounted as _onMounted,
11
- onUnmounted as _onUnmounted,
12
- ref as _ref,
13
- watch as _watch,
14
- } from '@poveste/vendors/vue'
15
- import { writable } from 'svelte/store'
16
- // @ts-expect-error virtual module id
17
- import * as generatedSetup from 'virtual:$poveste-generated-global-setup'
18
- // @ts-expect-error virtual module id
19
- import * as setup from 'virtual:$poveste-setup'
20
- import {
21
- callSetupFunctions,
22
- mountSvelteComponent,
23
- } from '../util/svelte.js'
24
- import MountStorySvelte from './MountStory.svelte'
25
- import MountVariantSvelte from './MountVariant.svelte'
26
- import StubComponent from './Stub.svelte'
27
-
28
- export default _defineComponent({
29
- name: 'MountStory',
30
-
31
- props: {
32
- story: {
33
- type: Object as _PropType<Story>,
34
- required: true,
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
- },
44
- },
45
-
46
- setup(props) {
47
- const el = _ref<HTMLDivElement>()
48
- let app: any
49
- let target: HTMLDivElement
50
- let destroyApp: (() => void) | null = null
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
-
60
- async function mountStory() {
61
- target = document.createElement('div')
62
- el.value.appendChild(target)
63
-
64
- const mountedApp = await mountSvelteComponent(props.story.file.component, {
65
- target,
66
- props: {
67
- Hst: {
68
- Story: MountStorySvelte,
69
- Variant: MountVariantSvelte,
70
- ...getControls(),
71
- },
72
- },
73
- context: new Map(Object.entries({
74
- __pvtStory: props.story,
75
- __pvtTargetVariantId: targetVariantId,
76
- })),
77
- }, 'client')
78
- app = mountedApp.app
79
- destroyApp = mountedApp.destroy
80
-
81
- const setupApi: SvelteStorySetupApi = {
82
- app,
83
- story: props.story,
84
- variant: null,
85
- }
86
-
87
- await callSetupFunctions(generatedSetup, setup, setupApi)
88
- }
89
-
90
- function unmountStory() {
91
- destroyApp?.()
92
- destroyApp = null
93
- if (target) {
94
- target.parentNode?.removeChild(target)
95
- target = null
96
- }
97
- app = null
98
- }
99
-
100
- _watch(() => props.story.id, async () => {
101
- unmountStory()
102
- await mountStory()
103
- })
104
-
105
- _onMounted(async () => {
106
- await mountStory()
107
- })
108
-
109
- _onUnmounted(() => {
110
- unmountStory()
111
- })
112
-
113
- return {
114
- el,
115
- }
116
- },
117
-
118
- render() {
119
- return _h('div', {
120
- ref: 'el',
121
- })
122
- },
123
- })
124
-
125
- function getControls() {
126
- const result: Record<string, any> = {}
127
- for (const key in components) {
128
- result[key.substring(3)] = StubComponent
129
- }
130
- return result
131
- }