@poveste/plugin-react 0.0.1 → 0.19.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Guillaume Chau (original "histoire" project)
4
+ Copyright (c) 2026 Sorin Gitlan (poveste fork)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
1
  # @poveste/plugin-react
2
2
 
3
- Placeholder that reserves the name for npm Trusted Publishing. The first real release is 0.19.0. See https://poveste.dev/guide/react/getting-started
3
+ [React](https://react.dev) support for [Poveste](https://poveste.dev): write stories as `.story.tsx` files and browse them in a Poveste book.
4
+
5
+ Requires Node `>=22.22.2`, `react@^19.0.0` and `react-dom@^19.0.0`, with a JSX transform such as `@vitejs/plugin-react` in your Vite config. See the [React guide](https://poveste.dev/guide/react/getting-started).
6
+
7
+ This first version renders stories and variants. The source panel shows the story file. The controls panel is not wired to React: a variant with `initState` gets the generic state editor, and one without says why there is nothing there.
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'
@@ -0,0 +1,33 @@
1
+ import type { Story } from '@poveste/shared';
2
+ import type { PropType as _PropType } from '@poveste/vendors/vue';
3
+ declare const _default: import("poveste-vue").DefineComponent<import("poveste-vue").ExtractPropTypes<{
4
+ story: {
5
+ type: _PropType<Story>;
6
+ required: true;
7
+ };
8
+ syncState: {
9
+ type: BooleanConstructor;
10
+ default: boolean;
11
+ };
12
+ targetVariantId: {
13
+ type: StringConstructor;
14
+ default: null;
15
+ };
16
+ }>, void, {}, {}, {}, import("poveste-vue").ComponentOptionsMixin, import("poveste-vue").ComponentOptionsMixin, {}, string, import("poveste-vue").PublicProps, Readonly<import("poveste-vue").ExtractPropTypes<{
17
+ story: {
18
+ type: _PropType<Story>;
19
+ required: true;
20
+ };
21
+ syncState: {
22
+ type: BooleanConstructor;
23
+ default: boolean;
24
+ };
25
+ targetVariantId: {
26
+ type: StringConstructor;
27
+ default: null;
28
+ };
29
+ }>> & Readonly<{}>, {
30
+ syncState: boolean;
31
+ targetVariantId: string;
32
+ }, {}, {}, {}, string, import("poveste-vue").ComponentProvideOptions, true, {}, any>;
33
+ export default _default;
@@ -0,0 +1,58 @@
1
+ import { applyState, clone, reportStoryError } from '@poveste/shared';
2
+ import { defineComponent as _defineComponent } from '@poveste/vendors/vue';
3
+ import { variantOptions } from '../types.js';
4
+ import { NO_CONTROLS } from './no-controls.js';
5
+ export default _defineComponent({
6
+ name: 'MountStory',
7
+ props: {
8
+ story: {
9
+ type: Object,
10
+ required: true,
11
+ },
12
+ // False in a sandbox realm, which does not own the state (#964): it receives
13
+ // the host's. Applying `initState` there as well would race that copy.
14
+ syncState: {
15
+ type: Boolean,
16
+ default: true,
17
+ },
18
+ // Set by a sandbox; accepted so it is not left as a stray attribute on a
19
+ // component that renders nothing (#197).
20
+ targetVariantId: {
21
+ type: String,
22
+ default: null,
23
+ },
24
+ },
25
+ setup(props) {
26
+ const options = props.story.file?.component;
27
+ const rawVariants = variantOptions(options);
28
+ void Promise.all(props.story.variants.map(async (variant, index) => {
29
+ const rawVariant = rawVariants[index];
30
+ const initState = rawVariant?.initState ?? options.initState;
31
+ if (initState && props.syncState) {
32
+ try {
33
+ applyState(variant.state, clone(await initState()));
34
+ }
35
+ catch (error) {
36
+ // Reported rather than left to reject: an unhandled rejection here
37
+ // never marks the variant ready, so the preview stays blank and says
38
+ // nothing. The variant still renders, from the state it has.
39
+ reportStoryError(error, { storyId: props.story.id, variantId: variant.id });
40
+ console.error(error);
41
+ }
42
+ }
43
+ Object.assign(variant, {
44
+ // A variant with state gets the panel's state editor; one without gets
45
+ // the explanation, which is a custom controls slot to the panel.
46
+ slots: () => ({ default: rawVariant?.render, controls: initState ? undefined : NO_CONTROLS }),
47
+ source: rawVariant?.source ?? options.source,
48
+ responsiveDisabled: rawVariant?.responsiveDisabled ?? options.responsiveDisabled,
49
+ autoPropsDisabled: true,
50
+ setupApp: rawVariant?.setupApp ?? options.setupApp,
51
+ configReady: true,
52
+ });
53
+ }));
54
+ },
55
+ render() {
56
+ return null;
57
+ },
58
+ });
@@ -0,0 +1,39 @@
1
+ import type { Story, Variant } from '@poveste/shared';
2
+ import type { PropType as _PropType } from '@poveste/vendors/vue';
3
+ declare const _default: import("poveste-vue").DefineComponent<import("poveste-vue").ExtractPropTypes<{
4
+ variant: {
5
+ type: _PropType<Variant>;
6
+ required: true;
7
+ };
8
+ story: {
9
+ type: _PropType<Story>;
10
+ required: true;
11
+ };
12
+ slotName: {
13
+ type: StringConstructor;
14
+ default: string;
15
+ };
16
+ }>, {
17
+ el: import("poveste-vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
18
+ explainsControls: () => boolean;
19
+ }, {}, {}, {}, import("poveste-vue").ComponentOptionsMixin, import("poveste-vue").ComponentOptionsMixin, {
20
+ ready: () => true;
21
+ }, string, import("poveste-vue").PublicProps, Readonly<import("poveste-vue").ExtractPropTypes<{
22
+ variant: {
23
+ type: _PropType<Variant>;
24
+ required: true;
25
+ };
26
+ story: {
27
+ type: _PropType<Story>;
28
+ required: true;
29
+ };
30
+ slotName: {
31
+ type: StringConstructor;
32
+ default: string;
33
+ };
34
+ }>> & Readonly<{
35
+ onReady?: () => any;
36
+ }>, {
37
+ slotName: string;
38
+ }, {}, {}, {}, string, import("poveste-vue").ComponentProvideOptions, true, {}, any>;
39
+ export default _default;
@@ -0,0 +1,175 @@
1
+ import { getSetupHook as _getSetupHook, clone, isEquivalent, reportStoryError } from '@poveste/shared';
2
+ import { defineComponent as _defineComponent, h as _h, onBeforeUnmount as _onBeforeUnmount, onMounted as _onMounted, ref as _ref, toRaw as _toRaw, watch as _watch, } from '@poveste/vendors/vue';
3
+ import { createElement, useSyncExternalStore } from 'react';
4
+ import { flushSync } from 'react-dom';
5
+ import { createRoot } from 'react-dom/client';
6
+ // @ts-expect-error virtual module id
7
+ import * as generatedSetup from 'virtual:$poveste-generated-global-setup';
8
+ // @ts-expect-error virtual module id
9
+ import * as setup from 'virtual:$poveste-setup';
10
+ import { REACT_SETUP_HOOK_NAMES } from '../index.js';
11
+ import { NO_CONTROLS, NO_CONTROLS_MESSAGE } from './no-controls.js';
12
+ export default _defineComponent({
13
+ name: 'RenderStory',
14
+ props: {
15
+ variant: {
16
+ type: Object,
17
+ required: true,
18
+ },
19
+ story: {
20
+ type: Object,
21
+ required: true,
22
+ },
23
+ slotName: {
24
+ type: String,
25
+ default: 'default',
26
+ },
27
+ },
28
+ emits: {
29
+ ready: () => true,
30
+ },
31
+ setup(props, { emit }) {
32
+ const el = _ref();
33
+ const slot = () => Reflect.get(props.variant.slots?.() ?? {}, props.slotName);
34
+ const explainsControls = () => slot() === NO_CONTROLS;
35
+ let root;
36
+ let stopSync;
37
+ let mounting = false;
38
+ async function mount() {
39
+ if (!props.variant.configReady) {
40
+ return;
41
+ }
42
+ // Checked before the element: the explanation renders no `el`, and the
43
+ // panel waits on this `ready` before it shows its toolbar.
44
+ if (explainsControls()) {
45
+ emit('ready');
46
+ return;
47
+ }
48
+ if (mounting || root || !el.value) {
49
+ return;
50
+ }
51
+ const renderVariant = slot();
52
+ if (!renderVariant) {
53
+ return;
54
+ }
55
+ mounting = true;
56
+ // Hooks run before mount, as in `@poveste/plugin-vue`, so a wrapper they
57
+ // add is in place for the first render rather than arriving after it.
58
+ const wrappers = [];
59
+ const setupApi = {
60
+ story: props.story,
61
+ variant: props.variant,
62
+ addWrapper: (wrapper) => {
63
+ wrappers.unshift(wrapper);
64
+ },
65
+ };
66
+ const occupant = { storyId: props.story.id, variantId: props.variant.id };
67
+ try {
68
+ await _getSetupHook(generatedSetup, REACT_SETUP_HOOK_NAMES)?.(setupApi);
69
+ await _getSetupHook(setup, REACT_SETUP_HOOK_NAMES)?.(setupApi);
70
+ await props.variant.setupApp?.(setupApi);
71
+ // A hook that awaited can outlive this component, whose element is gone
72
+ // by then; rendering now would leave a React root and a watcher that no
73
+ // unmount will ever reach.
74
+ if (!el.value) {
75
+ return;
76
+ }
77
+ // React reads state as a snapshot, so the variant's reactive state is
78
+ // copied out and replaced whole on each change: a new object is what
79
+ // tells `useSyncExternalStore` to render again.
80
+ const store = snapshotStore(clone(_toRaw(props.variant.state) ?? {}));
81
+ const setState = (next) => {
82
+ const current = store.get();
83
+ const merged = { ...current, ...(typeof next === 'function' ? next(current) : next) };
84
+ store.set(merged);
85
+ writeBack(props.variant.state, merged);
86
+ };
87
+ stopSync = _watch(() => props.variant.state, (value) => {
88
+ const incoming = clone(_toRaw(value));
89
+ if (!isEquivalent(incoming, store.get())) {
90
+ store.set(incoming);
91
+ }
92
+ }, { deep: true });
93
+ function Variant() {
94
+ const state = useSyncExternalStore(store.subscribe, store.get);
95
+ return renderVariant({ state, setState });
96
+ }
97
+ let node = createElement(Variant);
98
+ for (const wrapper of wrappers) {
99
+ node = createElement(wrapper, { story: props.story, variant: props.variant, children: node });
100
+ }
101
+ root = createRoot(el.value, {
102
+ // A render that throws is reported here rather than to the window,
103
+ // where nothing would tie it to the variant that threw.
104
+ onUncaughtError: (error) => {
105
+ reportStoryError(error, occupant);
106
+ console.error(error);
107
+ },
108
+ });
109
+ // Synchronously, so `ready` means the story is in the DOM rather than
110
+ // scheduled to be.
111
+ flushSync(() => root.render(node));
112
+ }
113
+ catch (error) {
114
+ // Whatever got as far as existing goes with the failure, so a later
115
+ // attempt starts clean rather than beside a watcher nothing can stop.
116
+ unmount();
117
+ reportStoryError(error, occupant);
118
+ console.error(error);
119
+ }
120
+ finally {
121
+ mounting = false;
122
+ }
123
+ emit('ready');
124
+ }
125
+ function unmount() {
126
+ stopSync?.();
127
+ stopSync = undefined;
128
+ root?.unmount();
129
+ root = undefined;
130
+ }
131
+ _onMounted(mount);
132
+ _watch(() => props.variant.configReady, mount);
133
+ _onBeforeUnmount(unmount);
134
+ return { el, explainsControls };
135
+ },
136
+ render() {
137
+ if (this.explainsControls()) {
138
+ return _h('p', { class: 'poveste-react-no-controls', style: 'padding: 12px; opacity: 0.7; font-size: 12px' }, NO_CONTROLS_MESSAGE);
139
+ }
140
+ return _h('div', { ref: 'el' });
141
+ },
142
+ });
143
+ function snapshotStore(initial) {
144
+ let snapshot = initial;
145
+ const listeners = new Set();
146
+ return {
147
+ get: () => snapshot,
148
+ set: (next) => {
149
+ snapshot = next;
150
+ for (const listener of listeners)
151
+ listener();
152
+ },
153
+ subscribe: (listener) => {
154
+ listeners.add(listener);
155
+ return () => listeners.delete(listener);
156
+ },
157
+ };
158
+ }
159
+ /**
160
+ * Carries the snapshot back to the variant's state one key at a time: only what
161
+ * changed is cloned, and a key the snapshot no longer has is removed rather than
162
+ * left behind. The `_h` keys are the app's own and are never the story's to drop.
163
+ */
164
+ function writeBack(target, snapshot) {
165
+ for (const key of Object.keys(target)) {
166
+ if (!key.startsWith('_h') && !Object.hasOwn(snapshot, key)) {
167
+ delete target[key];
168
+ }
169
+ }
170
+ for (const key of Object.keys(snapshot)) {
171
+ if (!Object.hasOwn(target, key) || !isEquivalent(target[key], snapshot[key])) {
172
+ target[key] = clone(snapshot[key]);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,6 @@
1
+ import type { ClientSupportPlugin } from '@poveste/shared';
2
+ import type { Component } from '@poveste/vendors/vue';
3
+ import type * as client from './index.js';
4
+ type Satisfies<T extends ClientSupportPlugin<Component>> = T;
5
+ export type ClientContract = Satisfies<typeof client>;
6
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export { default as MountStory } from './MountStory.js';
2
+ export { default as RenderStory } from './RenderStory.js';
3
+ /**
4
+ * Exported though it generates nothing: the source panel early-returns when a
5
+ * plugin has none, and would then skip its switch to the story file (#61, #1115).
6
+ * `plugin-vue`'s codegen turns a render tree back into source; React elements
7
+ * carry no source to recover, so the panel shows the story file instead.
8
+ */
9
+ export declare function generateSourceCode(): undefined;
@@ -0,0 +1,11 @@
1
+ export { default as MountStory } from './MountStory.js';
2
+ export { default as RenderStory } from './RenderStory.js';
3
+ /**
4
+ * Exported though it generates nothing: the source panel early-returns when a
5
+ * plugin has none, and would then skip its switch to the story file (#61, #1115).
6
+ * `plugin-vue`'s codegen turns a render tree back into source; React elements
7
+ * carry no source to recover, so the panel shows the story file instead.
8
+ */
9
+ export function generateSourceCode() {
10
+ // noop
11
+ }
@@ -0,0 +1,3 @@
1
+ /** Marks a variant's controls slot as the explanation, not a render function. */
2
+ export declare const NO_CONTROLS: unique symbol;
3
+ export declare const NO_CONTROLS_MESSAGE = "React stories have no controls yet. Give the variant an initState to edit its state here.";
@@ -0,0 +1,3 @@
1
+ /** Marks a variant's controls slot as the explanation, not a render function. */
2
+ export const NO_CONTROLS = Symbol('poveste:react-no-controls');
3
+ export const NO_CONTROLS_MESSAGE = 'React stories have no controls yet. Give the variant an initState to edit its state here.';
@@ -0,0 +1,2 @@
1
+ import type { ServerRunPayload } from '@poveste/shared';
2
+ export declare function run({ file, storyData }: ServerRunPayload): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { variantOptions } from '../types.js';
2
+ // Collection reads the story object and nothing else: no `render` is called, so
3
+ // no Solid component runs while the book is being indexed.
4
+ export async function run({ file, storyData }) {
5
+ const { default: options } = await import(/* @vite-ignore */ file.moduleId);
6
+ const story = {
7
+ id: options.id ?? file.id,
8
+ title: options.title ?? file.fileName,
9
+ group: options.group,
10
+ layout: options.layout ?? { type: 'single', iframe: true },
11
+ icon: options.icon,
12
+ iconColor: options.iconColor,
13
+ docsOnly: options.docsOnly ?? false,
14
+ variants: [],
15
+ };
16
+ story.variants = variantOptions(options).map((variant, index) => ({
17
+ id: variant.id ?? `${story.id}-${index}`,
18
+ title: variant.title ?? 'untitled',
19
+ icon: variant.icon ?? options.icon,
20
+ iconColor: variant.iconColor ?? options.iconColor,
21
+ }));
22
+ storyData.push(story);
23
+ }
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@poveste/shared';
2
+ export declare const REACT_SETUP_HOOK_NAMES: string[];
3
+ export declare function HstReact(): Plugin;
4
+ export * from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,71 @@
1
+ // No Node-only import here, on purpose: a story file imports `defineStory` from
2
+ // this entry, so it is bundled for the browser as well as loaded by the config.
3
+ export const REACT_SETUP_HOOK_NAMES = ['setupReact'];
4
+ export function HstReact() {
5
+ return {
6
+ name: '@poveste/plugin-react',
7
+ defaultConfig() {
8
+ return {
9
+ // Added to core's defaults rather than replacing them (#1124), so a book
10
+ // with React stories needs no `storyMatch` of its own.
11
+ storyMatch: ['**/*.story.tsx', '**/*.story.jsx'],
12
+ supportMatch: [
13
+ {
14
+ id: 'react',
15
+ patterns: ['**/*.story.tsx', '**/*.story.jsx'],
16
+ pluginIds: ['react'],
17
+ },
18
+ ],
19
+ vite: {
20
+ plugins: [
21
+ reloadOnStoryEdit(),
22
+ ],
23
+ // The story's JSX and this plugin's renderer must share one React: a
24
+ // hook called through a second copy throws "Invalid hook call", the
25
+ // same split as two Vue runtimes (#1060).
26
+ resolve: {
27
+ dedupe: ['react', 'react-dom'],
28
+ },
29
+ // React ships CommonJS, so Vite pre-bundles it, and the renderer's
30
+ // imports are found only once a story is opened. Found in two passes,
31
+ // the second re-chunks `react-dom` while the server still hands out the
32
+ // first pass's copy, and `react-dom/client` fails to import from it on
33
+ // every load until the cache is cleared. Named here, all of them are
34
+ // bundled in the first pass.
35
+ optimizeDeps: {
36
+ include: ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom', 'react-dom/client'],
37
+ },
38
+ },
39
+ };
40
+ },
41
+ supportPlugin: {
42
+ id: 'react',
43
+ moduleName: '@poveste/plugin-react',
44
+ setupFn: REACT_SETUP_HOOK_NAMES,
45
+ importStoryComponent: (file, index) => `import Comp${index} from ${JSON.stringify(file.moduleId)}`,
46
+ },
47
+ };
48
+ }
49
+ export * from './types.js';
50
+ /**
51
+ * Reloads the book when a React story file is edited in dev.
52
+ *
53
+ * A story file is data, so there is no component for Fast Refresh to patch in
54
+ * place, and the sandbox keeps the story it mapped on mount rather than taking
55
+ * the updated module: the edit was collected and then never shown. A reload
56
+ * shows it, at the cost of whatever was set in the panel. A component the story
57
+ * imports is not a story file and still refreshes in place.
58
+ */
59
+ function reloadOnStoryEdit() {
60
+ return {
61
+ name: 'poveste:react-story-reload',
62
+ apply: 'serve',
63
+ handleHotUpdate({ file, server }) {
64
+ if (!/\.story\.[jt]sx$/.test(file)) {
65
+ return undefined;
66
+ }
67
+ server.ws.send({ type: 'full-reload' });
68
+ return [];
69
+ },
70
+ };
71
+ }
@@ -0,0 +1,44 @@
1
+ import type { Awaitable, Story, StoryProps, Variant, VariantProps } from '@poveste/shared';
2
+ import type { ComponentType, ReactNode } from 'react';
3
+ export interface ReactRenderApi<S extends object> {
4
+ /** This render's snapshot. Never mutate it; call `setState`. */
5
+ state: S;
6
+ /**
7
+ * Merges into the state, as a class component's `setState` does: pass the
8
+ * keys that change, or a function of the current state. Changes also reach
9
+ * the controls panel and the host.
10
+ */
11
+ setState: (next: Partial<S> | ((state: S) => Partial<S>)) => void;
12
+ }
13
+ export interface ReactStorySetupApi {
14
+ story: Story;
15
+ variant: Variant;
16
+ /**
17
+ * Wraps every story this hook runs for. The first added is the outermost,
18
+ * as in `@poveste/plugin-vue`.
19
+ */
20
+ addWrapper: (wrapper: ReactWrapper) => void;
21
+ }
22
+ export type ReactWrapper = ComponentType<{
23
+ story: Story;
24
+ variant: Variant;
25
+ children: ReactNode;
26
+ }>;
27
+ export type ReactStorySetupHandler = (api: ReactStorySetupApi) => Awaitable<void>;
28
+ export interface VariantOptions<S extends object = Record<string, any>> extends Omit<VariantProps, 'setupApp'> {
29
+ /** A component body: called on every render, so hooks are allowed. */
30
+ render: (api: ReactRenderApi<S>) => ReactNode;
31
+ initState?: () => Awaitable<S>;
32
+ setupApp?: ReactStorySetupHandler;
33
+ }
34
+ export interface StoryOptions<S extends object = Record<string, any>> extends Omit<StoryProps, 'setupApp'> {
35
+ /** A story with no `variants` and a `render` of its own has one implicit variant. */
36
+ variants?: VariantOptions<S>[];
37
+ render?: (api: ReactRenderApi<S>) => ReactNode;
38
+ initState?: () => Awaitable<S>;
39
+ setupApp?: ReactStorySetupHandler;
40
+ }
41
+ export declare function defineStory<S extends object = Record<string, any>>(story: StoryOptions<S>): StoryOptions<S>;
42
+ export declare function defineSetupReact(handler: ReactStorySetupHandler): ReactStorySetupHandler;
43
+ /** The variants a story declares, with the implicit one made explicit. */
44
+ export declare function variantOptions(story: StoryOptions<any>): VariantOptions<any>[];
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ export function defineStory(story) {
2
+ return story;
3
+ }
4
+ export function defineSetupReact(handler) {
5
+ return handler;
6
+ }
7
+ /** The variants a story declares, with the implicit one made explicit. */
8
+ export function variantOptions(story) {
9
+ if (story.variants?.length) {
10
+ return story.variants;
11
+ }
12
+ return story.render ? [{ id: '_default', title: 'default', render: story.render }] : [];
13
+ }
package/package.json CHANGED
@@ -1,9 +1,92 @@
1
1
  {
2
2
  "name": "@poveste/plugin-react",
3
- "version": "0.0.1",
4
- "description": "Placeholder. Install @poveste/plugin-react@0.19.0 or later.",
3
+ "type": "module",
4
+ "version": "0.19.0",
5
+ "description": "Poveste plugin for React support, renderer only",
6
+ "author": {
7
+ "name": "Sorin Gitlan",
8
+ "url": "https://x.com/50rayn"
9
+ },
5
10
  "license": "MIT",
6
- "repository": { "type": "git", "url": "git+https://github.com/poveste-dev/poveste.git", "directory": "packages/poveste-plugin-react" },
7
- "engines": { "node": ">=22.22.2" },
8
- "publishConfig": { "access": "public" }
9
- }
11
+ "homepage": "https://poveste.dev",
12
+ "repository": {
13
+ "url": "https://github.com/poveste-dev/poveste.git",
14
+ "type": "git",
15
+ "directory": "packages/poveste-plugin-react"
16
+ },
17
+ "bugs": "https://github.com/poveste-dev/poveste/issues",
18
+ "keywords": [
19
+ "poveste",
20
+ "histoire",
21
+ "storybook",
22
+ "component-playground",
23
+ "component-explorer",
24
+ "stories",
25
+ "styleguide",
26
+ "design-system",
27
+ "vite",
28
+ "react",
29
+ "plugin"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "exports": {
35
+ "./client": {
36
+ "types": "./dist/client/index.d.ts",
37
+ "default": "./dist/client/index.js"
38
+ },
39
+ "./collect": {
40
+ "types": "./dist/collect/index.d.ts",
41
+ "default": "./dist/collect/index.js"
42
+ },
43
+ ".": {
44
+ "types": "./dist/index.d.ts",
45
+ "default": "./dist/index.js"
46
+ }
47
+ },
48
+ "main": "./dist/index.js",
49
+ "module": "./dist/index.js",
50
+ "types": "./dist/index.d.ts",
51
+ "files": [
52
+ "client.d.ts",
53
+ "collect.d.ts",
54
+ "dist"
55
+ ],
56
+ "engines": {
57
+ "node": ">=22.22.2"
58
+ },
59
+ "peerDependencies": {
60
+ "@vitejs/plugin-react": "^6.0.0",
61
+ "poveste": "^0.19.0",
62
+ "react": "^19.0.0",
63
+ "react-dom": "^19.0.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "@vitejs/plugin-react": {
67
+ "optional": true
68
+ }
69
+ },
70
+ "dependencies": {
71
+ "@poveste/shared": "^0.19.0",
72
+ "@poveste/vendors": "^0.19.0"
73
+ },
74
+ "devDependencies": {
75
+ "@types/node": "^26.6.3",
76
+ "@types/react": "^19.3.0",
77
+ "@types/react-dom": "^19.3.0",
78
+ "@vitejs/plugin-react": "^6.1.1",
79
+ "jsdom": "^30.1.1",
80
+ "poveste": "0.19.0",
81
+ "react": "^19.3.0",
82
+ "react-dom": "^19.3.0",
83
+ "typescript": "6.0.3",
84
+ "vite": "^8.3.1",
85
+ "vitest": "^5.0.2"
86
+ },
87
+ "scripts": {
88
+ "build": "rimraf dist && tsc -d",
89
+ "watch": "tsc -d -w --sourceMap",
90
+ "test": "vitest run"
91
+ }
92
+ }