@teiler/svelte 0.0.30 → 0.1.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/README.md CHANGED
@@ -4,9 +4,16 @@
4
4
 
5
5
  **Teiler** is an open source library that simplifies the creation of stylish components for various frameworks.
6
6
 
7
- Currently in the **alpha phase**, the library is actively being developed and improved. It currently provides support solely for Svelte CSR (Client-Side Rendering).
7
+ Currently in the **alpha phase**, the library is actively being developed and improved. This package is the **Svelte 5** adapter and supports both CSR (Client-Side Rendering) and SSR (Server-Side Rendering).
8
8
 
9
- Join our community on our [Discord Server](https://discord.gg/J6Sv9sQ64t) to stay informed about the latest developments, exchange ideas, and connect with fellow developers. We are continuously working on expanding our support to include more frameworks, allowing developers to effortlessly create components across various environments.
9
+ Join our community on our [Discord Server](https://discord.gg/J6Sv9sQ64t) to stay informed about the latest developments, exchange ideas, and connect with fellow developers. We are continuously working on expanding our support to include more frameworks, allowing developers to effortlessly create components across various environments.
10
+
11
+ ## Requirements
12
+
13
+ - `svelte` `^5.16.0`
14
+ - A Svelte-aware bundler (Vite with `@sveltejs/vite-plugin-svelte`, SvelteKit, `rollup-plugin-svelte`, …)
15
+
16
+ The package ships uncompiled `.svelte` files through the `svelte` export condition, so your bundler compiles them for the right target (client or server) with the same Svelte version as your application.
10
17
 
11
18
  ### Example
12
19
 
@@ -33,6 +40,19 @@ const Button = component.button<{
33
40
  `
34
41
  ```
35
42
 
43
+ ```svelte
44
+ <Button _primary={primary} onclick={() => count++}>Clicked {count} times</Button>
45
+ ```
46
+
47
+ Props prefixed with `_` are used only for styles and are not forwarded to the DOM. Every other prop, including event handlers (`onclick`, `oninput`, …) and `class`, is forwarded to the rendered element.
48
+
49
+ ## Migrating from Svelte 4 (`@teiler/svelte` 0.0.x)
50
+
51
+ - Svelte 5 is required (`svelte` `^5.16.0`); Svelte 4 is no longer supported.
52
+ - Event handlers are props: `<Button onclick={handler}>` instead of `<Button on:click={handler}>`.
53
+ - If you provide the theme through context yourself, the `THEME` context holds a getter (`() => theme`) instead of a writable store. `ThemeProvider` usage is unchanged.
54
+ - UMD/CJS bundles are gone; the package is consumed as Svelte source by your bundler.
55
+
36
56
  ## Keyframes
37
57
 
38
58
  ```typescript
@@ -90,31 +110,65 @@ const ButtonLink = component.a(Button)`
90
110
  `
91
111
  ```
92
112
 
93
- ## Theme
113
+ ## `as` prop
94
114
 
95
- Example how to use themes.
115
+ Use `as` to render a component as a different element or another component without creating a new styled component. Styles stay the same, `as` is not passed to the rendered element:
96
116
 
97
- ```typescript
98
- // Main component inside application (`App.svelte`)
99
- <script context="module" lang="ts">
100
- export type CustomTheme = {
101
- fontColor: string,
102
- }
117
+ ```svelte
118
+ <script lang="ts">
119
+ import { Button } from './components'
120
+ import Link from './Link.svelte'
121
+ </script>
122
+
123
+ <Button as="a" href="/docs">Docs</Button>
124
+ <Button as={Link} to="/home">Home</Button>
125
+ ```
126
+
127
+ When `as` is a component, it receives `class`, `children` and every other forwarded prop. Svelte has no attribute fallthrough, so the component has to apply `class` to its root element:
128
+
129
+ ```svelte
130
+ <!-- Link.svelte -->
131
+ <script lang="ts">
132
+ import type { Snippet } from 'svelte'
133
+ import type { ClassValue } from 'svelte/elements'
134
+
135
+ const { to, class: className, children }: { to: string; class?: ClassValue; children?: Snippet } = $props()
103
136
  </script>
137
+
138
+ <a href={to} class={className}>{@render children?.()}</a>
139
+ ```
140
+
141
+ Props are typed from `as`: `as="a"` accepts anchor attributes, `as={Link}` accepts the props of `Link` and requires its required props. Without `as`, the component accepts the attributes of its own element. `svelte-check` reports an unknown tag in `as` and attributes the target does not accept:
142
+
143
+ ```svelte
144
+ <Button as="a" href="/docs">Docs</Button> <!-- ok -->
145
+ <Button href="/docs">Docs</Button> <!-- error: href is not a button attribute -->
146
+ <Button as={Link}>Home</Button> <!-- error: to is required -->
147
+ ```
148
+
149
+ > [!NOTE]
150
+ > `mount()` and `render()` called from TypeScript cannot infer `as`, so there props are typed from the element of the styled component.
151
+
152
+ ## Theme
153
+
154
+ ```svelte
155
+ <!-- App.svelte -->
104
156
  <script lang="ts">
157
+ import type { CustomTheme } from './theme'
158
+
105
159
  import { ThemeProvider } from '@teiler/svelte'
106
- import { Component } from './theme'
160
+ import { Component } from './components'
107
161
 
108
- export let theme: CustomTheme = {
109
- fontColor: 'red',
110
- }
162
+ let theme: CustomTheme = $state({ fontColor: 'red' })
111
163
  </script>
112
164
 
113
165
  <ThemeProvider {theme}>
114
166
  <Component>Some test text</Component>
115
167
  </ThemeProvider>
168
+ ```
116
169
 
117
- // Component with theme usage
170
+ ```typescript
171
+ // components.ts
118
172
  import { component } from '@teiler/svelte'
119
173
 
120
174
  const Component = component.div`
@@ -125,32 +179,76 @@ export { Component }
125
179
  ```
126
180
 
127
181
  To add typing for Typescript applications you need to add `extend` inside declaration file (`d.ts`)
182
+
128
183
  ```typescript
129
- import type { CustomTheme } from "./App.svelte";
184
+ import type { CustomTheme } from './theme'
130
185
 
131
186
  declare module '@teiler/core' {
132
187
  export interface DefaultTheme extends CustomTheme {}
133
188
  }
134
189
  ```
135
190
 
191
+ ## Server-Side Rendering
192
+
193
+ Styles are collected into a style sheet provided through the `STYLE_SHEET` context. Create one sheet per request, render, then put the extracted CSS into the document head.
194
+
195
+ ```typescript
196
+ import { createStyleSheet } from '@teiler/core'
197
+ import { render } from 'svelte/server'
198
+ import App from './App.svelte'
199
+
200
+ const sheet = createStyleSheet({})
201
+ const { body } = render(App, { context: new Map([['STYLE_SHEET', sheet]]) })
202
+ const { css, ids } = sheet.extract()
203
+
204
+ const styleTag = `<style data-teiler="${ids.join(' ')}">${css}</style>`
205
+ ```
206
+
207
+ Read `body` before calling `extract()`: styles are inserted while the component tree renders.
208
+
209
+ > [!WARNING]
210
+ > Always provide a style sheet through the `STYLE_SHEET` context on the server. Without it, every request shares one module-level style sheet, so styles accumulate across requests.
211
+
212
+ ## Hydration
213
+
214
+ On the client, pass the ids rendered by the server to `hydrate`, so styles that are already in the document are not inserted again. Provide the same style sheet to the app through context:
215
+
216
+ ```typescript
217
+ import { createStyleSheet } from '@teiler/core'
218
+ import { hydrate } from 'svelte'
219
+ import App from './App.svelte'
220
+
221
+ const sheet = createStyleSheet({})
222
+ const element = document.querySelector<HTMLStyleElement>('style[data-teiler]')
223
+
224
+ sheet.hydrate(element?.dataset.teiler?.split(' ') ?? [])
225
+
226
+ hydrate(App, { target: document.body, context: new Map([['STYLE_SHEET', sheet]]) })
227
+ ```
228
+
136
229
  ## Content Security Policy
137
230
 
138
- With a strict `style-src` policy, create the style sheet with a `nonce` and set it in the context of the main component. Without an explicit `nonce` no attribute is set, and the default style sheet never has one.
231
+ With a strict `style-src` policy, pass a `nonce` to the style sheet and provide it through the `STYLE_SHEET` context. Without an explicit `nonce` no attribute is set, and the default style sheet never has one.
139
232
 
140
- ```svelte
141
- <!-- Main component inside application (`App.svelte`) -->
142
- <script lang="ts">
143
- import { createStyleSheet } from '@teiler/core'
144
- import { setContext } from 'svelte'
233
+ On the server, `extract` returns the nonce, so it can be set on the rendered `<style>` tag:
145
234
 
146
- export let nonce: string
235
+ ```typescript
236
+ const sheet = createStyleSheet({ nonce })
237
+ const { body } = render(App, { context: new Map([['STYLE_SHEET', sheet]]) })
238
+ const { css, ids } = sheet.extract()
147
239
 
148
- setContext('STYLE_SHEET', createStyleSheet({ nonce }))
149
- </script>
240
+ const styleTag = `<style data-teiler="${ids.join(' ')}" nonce="${nonce}">${css}</style>`
241
+ ```
242
+
243
+ On the client, read the nonce from the server rendered tag before creating the style sheet:
244
+
245
+ ```typescript
246
+ const element = document.querySelector<HTMLStyleElement>('style[data-teiler]')
247
+ const sheet = createStyleSheet({ nonce: element?.nonce })
150
248
  ```
151
249
 
152
250
  > [!NOTE]
153
- > When reading the nonce from an existing element, use the `nonce` property, not `getAttribute('nonce')`. Browsers hide the attribute when the policy is sent in a header, so `getAttribute` returns an empty string.
251
+ > Use the `nonce` property, not `getAttribute('nonce')`. Browsers hide the attribute when the policy is sent in a header, so `getAttribute` returns an empty string.
154
252
 
155
253
  ## Sew a Pattern
156
254
 
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import type { DefaultTheme, HTMLElements, StyleDefinition } from '@teiler/core'
3
+ import type { Component, Snippet } from 'svelte'
4
+
5
+ import { clsx } from 'clsx'
6
+ import { insert } from '@teiler/core'
7
+ import { getStyleSheet } from './sheet.js'
8
+ import { getTheme } from './theme.js'
9
+
10
+ type Props = {
11
+ styleDefinition: StyleDefinition<HTMLElements, unknown>
12
+ as?: Exclude<HTMLElements, null> | Component<any>
13
+ class?: string
14
+ children?: Snippet
15
+ [key: string]: unknown
16
+ }
17
+
18
+ const { styleDefinition, as, class: className, children, ...props }: Props = $props()
19
+
20
+ const sheet = getStyleSheet()
21
+ const theme = getTheme()
22
+
23
+ const styleClassName = $derived(insert(sheet, styleDefinition, { ...props, theme: theme?.() ?? ({} as DefaultTheme) }))
24
+
25
+ const classes = $derived(clsx(styleClassName, styleDefinition.id, className))
26
+
27
+ const attributes = $derived(Object.fromEntries(Object.entries(props).filter(([key]) => key[0] !== '_')))
28
+ </script>
29
+
30
+ {#if styleClassName && styleDefinition.tag}
31
+ {@const Target = as ?? styleDefinition.tag}
32
+ {#if typeof Target === 'function'}
33
+ <Target class={classes} {...attributes} {children} />
34
+ {:else if children}
35
+ <svelte:element this={Target} class={classes} {...attributes}>{@render children()}</svelte:element>
36
+ {:else}
37
+ <svelte:element this={Target} class={classes} {...attributes} />
38
+ {/if}
39
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { HTMLElements, StyleDefinition } from '@teiler/core';
2
+ import type { Component, Snippet } from 'svelte';
3
+ type Props = {
4
+ styleDefinition: StyleDefinition<HTMLElements, unknown>;
5
+ as?: Exclude<HTMLElements, null> | Component<any>;
6
+ class?: string;
7
+ children?: Snippet;
8
+ [key: string]: unknown;
9
+ };
10
+ declare const Styled: Component<Props, {}, "">;
11
+ type Styled = ReturnType<typeof Styled>;
12
+ export default Styled;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import type { DefaultTheme } from '@teiler/core'
3
+ import type { Snippet } from 'svelte'
4
+
5
+ import { setTheme } from './theme.js'
6
+
7
+ const { theme, children }: { theme: DefaultTheme; children?: Snippet } = $props()
8
+
9
+ setTheme(() => theme)
10
+ </script>
11
+
12
+ {@render children?.()}
@@ -0,0 +1,9 @@
1
+ import type { DefaultTheme } from '@teiler/core';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ theme: DefaultTheme;
5
+ children?: Snippet;
6
+ };
7
+ declare const ThemeProvider: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ThemeProvider = ReturnType<typeof ThemeProvider>;
9
+ export default ThemeProvider;
@@ -0,0 +1,36 @@
1
+ import type { HTMLElements, Properties, StyleDefinition, TeilerComponent } from '@teiler/core';
2
+ import type { ComponentConstructorOptions, ComponentInternals, Snippet, Component as SvelteComponent, SvelteComponent as SvelteComponentInstance } from 'svelte';
3
+ import type { ClassValue, SvelteHTMLElements } from 'svelte/elements';
4
+ import { keyframes } from '@teiler/core';
5
+ type Tag = Exclude<HTMLElements, null>;
6
+ type ElementProps<Target> = Target extends keyof SvelteHTMLElements ? SvelteHTMLElements[Target] : {};
7
+ type AsTarget = Tag | SvelteComponent<never>;
8
+ type TargetProps<As> = As extends SvelteComponent<infer Props> ? Omit<Props, 'class' | 'children'> & {
9
+ class?: ClassValue;
10
+ children?: Snippet;
11
+ } : ElementProps<As>;
12
+ type PolymorphicProps<Target extends HTMLElements, Props, As> = Props & {
13
+ as?: As;
14
+ } & ([As] extends [never] ? ElementProps<Target> : [AsTarget] extends [As] ? ElementProps<Target> : TargetProps<As>);
15
+ interface SvelteTeilerComponent<Target extends HTMLElements, Props extends object> extends TeilerComponent<Target, Props> {
16
+ <As extends AsTarget = never>(internals: ComponentInternals, props: PolymorphicProps<Target, Props, As>): {};
17
+ new <As extends AsTarget = never>(options: ComponentConstructorOptions<PolymorphicProps<Target, Props, As>>): SvelteComponentInstance<PolymorphicProps<Target, Props, As>>;
18
+ }
19
+ type SvelteGlobalComponent<Props extends object> = TeilerComponent<null, Props> & SvelteComponent<Props>;
20
+ declare const createComponent: <Target extends HTMLElements, Props extends object = {}>(styleDefinition: StyleDefinition<Target, Props>) => SvelteTeilerComponent<Target, Props>;
21
+ type InferProps<Component, Props> = Component extends TeilerComponent<HTMLElements, infer P> ? P & Props : Props;
22
+ type InferComponent<Component, Props extends object, Extended> = Component extends TeilerComponent<infer E, infer P extends object> ? SvelteTeilerComponent<Extended extends HTMLElements ? Extended : E, Props & P> : SvelteTeilerComponent<HTMLElements, Props>;
23
+ type Component<Target extends HTMLElements, Extended extends HTMLElements | undefined = Target> = {
24
+ <Props extends object = {}>(string: TemplateStringsArray, ...properties: Properties<Props>[]): SvelteTeilerComponent<Target, Props>;
25
+ <Component>(binded: Component): <Props extends object = {}>(string: TemplateStringsArray, ...properties: Properties<InferProps<Component, Props>>[]) => InferComponent<Component, Props, Extended>;
26
+ };
27
+ type Global = {
28
+ <Props extends object = {}>(string: TemplateStringsArray, ...properties: Properties<Props>[]): SvelteGlobalComponent<Props>;
29
+ };
30
+ type ComponentWithTags = Component<'div', undefined> & {
31
+ [K in Exclude<HTMLElements, null>]: Component<K>;
32
+ };
33
+ declare const svelteComponent: ComponentWithTags;
34
+ declare const svelteGlobal: Global;
35
+ export type { SvelteGlobalComponent, SvelteTeilerComponent };
36
+ export { svelteComponent as component, svelteGlobal as global, keyframes, createComponent };
@@ -0,0 +1,25 @@
1
+ import { component, global, keyframes, styled, tags } from '@teiler/core';
2
+ import Styled from './Styled.svelte';
3
+ const withStyleDefinition = (props, styleDefinition) => {
4
+ return new Proxy(props, {
5
+ get: (target, key) => (key === 'styleDefinition' ? styleDefinition : Reflect.get(target, key)),
6
+ has: (target, key) => key === 'styleDefinition' || Reflect.has(target, key),
7
+ });
8
+ };
9
+ const createComponent = (styleDefinition) => {
10
+ const wrapped = (internals, props) => Styled(internals, withStyleDefinition(props, styleDefinition));
11
+ return Object.assign(wrapped, { styleDefinition });
12
+ };
13
+ const construct = (tag, compiler) => {
14
+ return (stringOrBinded, ...properties) => {
15
+ return styled(tag, compiler, createComponent, stringOrBinded, ...properties);
16
+ };
17
+ };
18
+ const svelteComponent = construct(undefined, component);
19
+ tags.forEach((tag) => {
20
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
21
+ // @ts-expect-error
22
+ svelteComponent[tag] = construct(tag, component);
23
+ });
24
+ const svelteGlobal = construct(null, global);
25
+ export { svelteComponent as component, svelteGlobal as global, keyframes, createComponent };
@@ -1,2 +1,2 @@
1
- export { component, global, keyframes, createComponent } from './component'
2
- export { default as ThemeProvider } from './ThemeProvider.svelte'
1
+ export { component, global, keyframes, createComponent } from './component.js';
2
+ export { default as ThemeProvider } from './ThemeProvider.svelte';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { component, global, keyframes, createComponent } from './component.js';
2
+ export { default as ThemeProvider } from './ThemeProvider.svelte';
@@ -0,0 +1,4 @@
1
+ import { type Sheet } from '@teiler/core';
2
+ export declare let styleSheet: Sheet | null;
3
+ export declare const StyleSheet = "STYLE_SHEET";
4
+ export declare function getStyleSheet(): Sheet;
package/dist/sheet.js ADDED
@@ -0,0 +1,14 @@
1
+ import { createStyleSheet } from '@teiler/core';
2
+ import { getContext } from 'svelte';
3
+ export let styleSheet = null;
4
+ export const StyleSheet = 'STYLE_SHEET';
5
+ export function getStyleSheet() {
6
+ const fromContext = getContext(StyleSheet);
7
+ if (fromContext) {
8
+ return fromContext;
9
+ }
10
+ if (styleSheet === null) {
11
+ styleSheet = createStyleSheet({});
12
+ }
13
+ return styleSheet;
14
+ }
@@ -0,0 +1,5 @@
1
+ import type { DefaultTheme } from '@teiler/core';
2
+ export declare const Theme = "THEME";
3
+ export type ThemeGetter = () => DefaultTheme;
4
+ export declare function setTheme(theme: ThemeGetter): void;
5
+ export declare function getTheme(): ThemeGetter | undefined;
package/dist/theme.js ADDED
@@ -0,0 +1,8 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ export const Theme = 'THEME';
3
+ export function setTheme(theme) {
4
+ setContext(Theme, theme);
5
+ }
6
+ export function getTheme() {
7
+ return getContext(Theme);
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teiler/svelte",
3
- "version": "0.0.30",
3
+ "version": "0.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/nerdslabs/teiler.git",
@@ -8,53 +8,54 @@
8
8
  },
9
9
  "homepage": "https://github.com/nerdslabs/teiler/tree/master/packages/svelte#readme",
10
10
  "type": "module",
11
- "main": "dist/teiler-svelte.cjs.js",
12
- "module": "./dist/teiler-svelte.esm.js",
13
- "types": "./dist/teiler-svelte.d.ts",
11
+ "svelte": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
14
13
  "exports": {
15
14
  ".": {
16
- "import": "./dist/teiler-svelte.esm.js",
17
- "require": "./dist/teiler-svelte.cjs.ts",
18
- "types": "./dist/teiler-svelte.d.ts"
15
+ "types": "./dist/index.d.ts",
16
+ "svelte": "./dist/index.js",
17
+ "default": "./dist/index.js"
19
18
  }
20
19
  },
20
+ "files": [
21
+ "dist",
22
+ "!dist/**/*.test.*",
23
+ "!dist/**/*.fixture.*"
24
+ ],
21
25
  "dependencies": {
22
- "@teiler/core": "0.1.0"
26
+ "@teiler/core": "0.1.0",
27
+ "clsx": "^2.1.1"
23
28
  },
24
29
  "peerDependencies": {
25
- "svelte": "^4.0.0"
30
+ "svelte": "^5.16.0"
26
31
  },
27
32
  "devDependencies": {
28
- "@chromatic-com/storybook": "^3.2.7",
29
- "@storybook/addon-essentials": "^8.6.18",
30
- "@storybook/addon-interactions": "^8.6.18",
31
- "@storybook/addon-links": "^8.6.18",
32
- "@storybook/blocks": "^8.6.18",
33
- "@storybook/svelte": "^8.6.18",
34
- "@storybook/svelte-vite": "^8.6.18",
35
- "@storybook/test": "^8.6.18",
36
- "@storybook/test-runner": "0.24.4",
37
- "@sveltejs/vite-plugin-svelte": "^3.1.2",
38
- "@swc/core": "^1.16.2",
39
- "@swc/jest": "^0.2.39",
40
- "@testing-library/svelte": "^4.2.3",
41
- "jest": "^29.7.0",
42
- "jest-environment-jsdom": "^29.7.0",
43
- "rolldown": "^1.2.11",
44
- "rolldown-plugin-dts": "^0.28.6",
45
- "storybook": "^8.6.18",
46
- "svelte": "^4.2.20",
47
- "svelte-jester": "^5.0.0",
48
- "svelte-loader": "^3.2.4",
49
- "svelte-preprocess": "^5.1.4",
50
- "typescript": "^5.8.3"
33
+ "@chromatic-com/storybook": "^5.3.1",
34
+ "@storybook/addon-links": "^10.6.0",
35
+ "@storybook/addon-vitest": "^10.6.0",
36
+ "@storybook/svelte": "^10.6.0",
37
+ "@storybook/svelte-vite": "^10.6.0",
38
+ "@sveltejs/package": "^2.5.8",
39
+ "@sveltejs/vite-plugin-svelte": "^7.3.1",
40
+ "@testing-library/svelte": "^5.4.2",
41
+ "@vitest/browser-playwright": "^4.1.11",
42
+ "@vitest/coverage-v8": "^4.1.11",
43
+ "jsdom": "^30.1.1",
44
+ "playwright": "^1.63.0",
45
+ "storybook": "^10.6.0",
46
+ "svelte": "^5.57.1",
47
+ "svelte-check": "^4.7.6",
48
+ "typescript": "^6.0.3",
49
+ "vite": "^8.3.1",
50
+ "vitest": "^4.1.11"
51
51
  },
52
52
  "scripts": {
53
- "build": "rolldown --config",
54
- "dev": "rolldown --config --watch",
55
- "test": "NODE_OPTIONS=--experimental-vm-modules jest",
53
+ "build": "svelte-package -i src -o dist",
54
+ "dev": "svelte-package -i src -o dist --watch",
55
+ "test": "vitest run --project client --project server",
56
+ "typecheck": "node scripts/typecheck.js",
56
57
  "storybook": "storybook dev -p 6006 --no-open",
57
- "storybook-test": "test-storybook --url http://localhost:6006",
58
+ "storybook-test": "vitest run --project storybook",
58
59
  "build-storybook": "storybook build"
59
60
  }
60
61
  }
package/.npmignore DELETED
@@ -1,5 +0,0 @@
1
- .turbo
2
- .storybook
3
- node_modules
4
- stories
5
- rollup.config.js