@poveste/plugin-svelte 0.6.1 → 0.8.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/src/helpers.ts DELETED
@@ -1,131 +0,0 @@
1
- import type { HstControlOption } from '@poveste/controls'
2
- import type { Story, StoryProps, Variant, VariantProps } from '@poveste/shared'
3
- import type { Component, Snippet } from 'svelte'
4
-
5
- export interface SvelteStorySetupApi {
6
- app: any
7
- story?: Story
8
- variant?: Variant
9
- }
10
-
11
- export type SvelteStorySetupHandler = (api: SvelteStorySetupApi) => Promise<void> | void
12
-
13
- export function defineSetupSvelte(handler: SvelteStorySetupHandler): SvelteStorySetupHandler {
14
- return handler
15
- }
16
-
17
- export const defineSetupSvelte3 = defineSetupSvelte
18
- export const defineSetupSvelte4 = defineSetupSvelte
19
- export const defineSetupSvelte5 = defineSetupSvelte
20
-
21
- export type StoryState = Record<string, any>
22
-
23
- /**
24
- * Props plus Svelte 5 children.
25
- *
26
- * `StoryProps` and `VariantProps` live in `@poveste/shared` and are shared with
27
- * the Vue plugin, so a Svelte-only `Snippet` cannot be added there — it is
28
- * intersected in here instead.
29
- *
30
- * This is what the move off `SvelteComponentTyped` needed. The class form carried
31
- * Svelte 4 slot typing, so nested content type-checked implicitly; `Component<P>`
32
- * has no slots, and Svelte 5 passes children as a prop (#99).
33
- */
34
- type WithChildren<P> = P & {
35
- children?: Snippet<[{ state: StoryState }]>
36
- controls?: Snippet<[{ state: StoryState }]>
37
- }
38
-
39
- /**
40
- * Story state is owned by poveste, not by the story component.
41
- *
42
- * The story is mounted once per slot, so a component-local variable exists twice
43
- * and cannot be shared. `initState` seeds `variant.state`, which both mounts read
44
- * and which the sandbox bridge carries across the iframe boundary (#81).
45
- */
46
- type WithState<P> = Omit<P, 'source'> & {
47
- initState?: () => StoryState
48
- /** A literal, or a function of the variant state so the source panel can track the controls. */
49
- source?: string | ((state: StoryState) => string)
50
- }
51
-
52
- export interface Hst {
53
- // Main built-ins
54
- Story: Component<WithState<WithChildren<StoryProps>>>
55
- Variant: Component<WithState<WithChildren<VariantProps>>>
56
- // Controls
57
- // Deliberately permissive. This was previously a bare `SvelteComponentTyped`,
58
- // i.e. props `any`; it wraps a Vue control whose default slot renders the label,
59
- // and the prop set `Wrap` forwards is not pinned down. Narrowing it is its own
60
- // change rather than a silent break.
61
- Button: Component<WithChildren<Record<string, any>>>
62
- ButtonGroup: Component<{
63
- value?: string
64
- options: (string | HstControlOption)[]
65
- title?: string
66
- }>
67
- Checkbox: Component<{
68
- value?: boolean
69
- title: string
70
- }>
71
- CheckboxList: Component<{
72
- value: string[]
73
- options: (string | HstControlOption)[]
74
- title?: string
75
- }>
76
- Text: Component<{
77
- value?: string
78
- title: string
79
- }>
80
- Number: Component<{
81
- value?: number
82
- title: string
83
- step?: number
84
- }>
85
- Slider: Component<{
86
- value?: number
87
- title: string
88
- min: number
89
- max: number
90
- step?: number
91
- }>
92
- Textarea: Component<{
93
- value?: string
94
- title: string
95
- }>
96
- Select: Component<{
97
- value?: string
98
- title: string
99
- options: Record<string, any> | string[] | HstControlOption[]
100
- }>
101
- Radio: Component<{
102
- value?: string
103
- options: HstControlOption[]
104
- title?: string
105
- }>
106
- Json: Component<{
107
- value: unknown
108
- title: string
109
- }>
110
- Shades: Component<{
111
- shades: Record<string, any>
112
- getName?: (key: string, color: string) => string
113
- search?: string
114
- }>
115
- TokenList: Component<{
116
- tokens: Record<string, string | number | any[] | Record<string, any>>
117
- getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
118
- }>
119
- TokenGrid: Component<{
120
- tokens: Record<string, string | number | any[] | Record<string, any>>
121
- getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
122
- colSize?: number
123
- }>
124
- CopyIcon: Component<{
125
- content: string
126
- }>
127
- ColorSelect: Component<{
128
- value?: string
129
- title: string
130
- }>
131
- }
package/src/index.node.ts DELETED
@@ -1,101 +0,0 @@
1
- import type { Plugin } from 'poveste'
2
- import { existsSync } from 'node:fs'
3
- import { createRequire } from 'node:module'
4
- import { dirname, join } from 'pathe'
5
- import { defaultColors } from 'poveste'
6
- import generateStoryCommand from './commands/generate-story.server.js'
7
- import { listComponentFiles } from './util/list-components.js'
8
- import { disableStoryComponentHmr } from './util/story-hmr.js'
9
-
10
- export function HstSvelte(): Plugin {
11
- return {
12
- name: '@poveste/plugin-svelte',
13
-
14
- defaultConfig() {
15
- const svelteClientAliases = getSvelteClientAliases()
16
-
17
- return {
18
- supportMatch: [
19
- {
20
- id: 'svelte',
21
- patterns: ['**/*.svelte'],
22
- pluginIds: ['svelte4'],
23
- },
24
- ],
25
- theme: {
26
- colors: {
27
- primary: defaultColors.orange,
28
- },
29
- logo: {
30
- square: '@poveste/plugin-svelte/assets/poveste-svelte.svg',
31
- light: '@poveste/plugin-svelte/assets/poveste-svelte-text.svg',
32
- dark: '@poveste/plugin-svelte/assets/poveste-svelte-text.svg',
33
- },
34
- },
35
- viteIgnorePlugins: [
36
- 'vite-plugin-sveltekit-compile',
37
- ],
38
- vite: svelteClientAliases.length
39
- ? {
40
- plugins: [
41
- disableStoryComponentHmr(),
42
- ],
43
- resolve: {
44
- alias: svelteClientAliases,
45
- },
46
- }
47
- : {
48
- plugins: [
49
- disableStoryComponentHmr(),
50
- ],
51
- },
52
- }
53
- },
54
-
55
- supportPlugin: {
56
- id: 'svelte4',
57
- moduleName: '@poveste/plugin-svelte',
58
- setupFn: ['setupSvelte3', 'setupSvelte4', 'setupSvelte5'],
59
- importStoryComponent: (file, index) => `import Comp${index} from ${JSON.stringify(file.moduleId)}`,
60
- },
61
-
62
- commands: [
63
- generateStoryCommand,
64
- ],
65
-
66
- async onDevEvent(api) {
67
- switch (api.event) {
68
- case 'listSvelteComponents': {
69
- return listComponentFiles(api.payload.search, api.getConfig().storyMatch)
70
- }
71
- }
72
- },
73
- }
74
- }
75
-
76
- export * from './helpers.js'
77
-
78
- function getSvelteClientAliases() {
79
- try {
80
- const require = createRequire(join(process.cwd(), 'package.json'))
81
- const sveltePackagePath = require.resolve('svelte/package.json')
82
- const svelteDir = dirname(sveltePackagePath)
83
-
84
- const aliasEntries = [
85
- [/^svelte$/, join(svelteDir, 'src/index-client.js')],
86
- [/^svelte\/legacy$/, join(svelteDir, 'src/legacy/legacy-client.js')],
87
- [/^svelte\/store$/, join(svelteDir, 'src/store/index-client.js')],
88
- [/^svelte\/reactivity$/, join(svelteDir, 'src/reactivity/index-client.js')],
89
- ] as const
90
-
91
- return aliasEntries
92
- .filter(([, replacement]) => existsSync(replacement))
93
- .map(([find, replacement]) => ({
94
- find,
95
- replacement,
96
- }))
97
- }
98
- catch {
99
- return []
100
- }
101
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './helpers.js'
@@ -1,16 +0,0 @@
1
- import { globby } from 'globby'
2
-
3
- export async function listComponentFiles(search = '', ignore: string[] = [], limit = 10) {
4
- let files = await globby('**/*.svelte', {
5
- gitignore: true,
6
- ignore: [
7
- 'node_modules',
8
- ...ignore,
9
- ],
10
- })
11
- if (search) {
12
- const searchText = search.toLowerCase()
13
- files = files.filter(file => file.toLowerCase().includes(searchText))
14
- }
15
- return files.slice(0, limit)
16
- }
@@ -1,52 +0,0 @@
1
- const storyFileRE = /\.story\.svelte$/
2
-
3
- type DynamicCompileOptions = NonNullable<SveltePluginApi['options']>['dynamicCompileOptions']
4
-
5
- interface SveltePluginApi {
6
- options?: {
7
- dynamicCompileOptions?: (data: {
8
- filename: string
9
- code: string
10
- compileOptions: Record<string, any>
11
- }) => Partial<Record<string, any>> | void | Promise<Partial<Record<string, any>> | void>
12
- }
13
- }
14
-
15
- interface SvelteConfigPlugin {
16
- name: string
17
- api?: SveltePluginApi
18
- __povesteStoryHmrPatched?: boolean
19
- }
20
-
21
- export function disableStoryComponentHmr() {
22
- return {
23
- name: 'poveste:svelte-story-hmr',
24
- apply: 'serve' as const,
25
- configResolved(config: { readonly plugins: readonly unknown[] }) {
26
- for (const plugin of config.plugins as SvelteConfigPlugin[]) {
27
- if (plugin.name !== 'vite-plugin-svelte:config' || plugin.__povesteStoryHmrPatched) {
28
- continue
29
- }
30
-
31
- const options = plugin.api?.options
32
- if (!options) {
33
- continue
34
- }
35
-
36
- const originalDynamicCompileOptions = options.dynamicCompileOptions as DynamicCompileOptions
37
- options.dynamicCompileOptions = async (data) => {
38
- const result = await originalDynamicCompileOptions?.(data)
39
- if (!storyFileRE.test(data.filename)) {
40
- return result
41
- }
42
-
43
- return {
44
- ...result,
45
- hmr: false,
46
- }
47
- }
48
- plugin.__povesteStoryHmrPatched = true
49
- }
50
- },
51
- }
52
- }
@@ -1,153 +0,0 @@
1
- import type { SvelteStorySetupApi, SvelteStorySetupHandler } from '../helpers.js'
2
- import * as svelte from 'svelte'
3
-
4
- type SetupModule = Record<string, unknown>
5
-
6
- const setupHookNames = [
7
- 'setupSvelte3',
8
- 'setupSvelte4',
9
- 'setupSvelte5',
10
- ] as const
11
-
12
- export interface MountedSvelteComponent {
13
- app: any
14
- destroy: () => void
15
- }
16
-
17
- export interface LegacyStateApi {
18
- captureState: () => Record<string, any> | null
19
- injectState: (state: Record<string, any>) => void
20
- }
21
-
22
- function loadSvelteModule(moduleId: string) {
23
- return import(/* @vite-ignore */ moduleId)
24
- }
25
-
26
- export async function mountSvelteComponent(
27
- component: any,
28
- options: Record<string, any>,
29
- mode: 'auto' | 'client' | 'server-compat' = 'auto',
30
- ): Promise<MountedSvelteComponent> {
31
- if (mode !== 'server-compat') {
32
- if (typeof (svelte as any)?.mount === 'function') {
33
- const app = (svelte as any).mount(component, options)
34
- return {
35
- app,
36
- destroy: () => {
37
- if (typeof (svelte as any).unmount === 'function') {
38
- ;(svelte as any).unmount(app)
39
- }
40
- else {
41
- app?.$destroy?.()
42
- }
43
- },
44
- }
45
- }
46
- }
47
-
48
- try {
49
- // eslint-disable-next-line new-cap
50
- const app = new component(options)
51
- return {
52
- app,
53
- destroy: () => {
54
- app?.$destroy?.()
55
- },
56
- }
57
- }
58
- catch (error) {
59
- const legacyModuleId = ['svelte', 'legacy'].join('/')
60
- const legacy = await loadSvelteModule(legacyModuleId).catch(() => null)
61
- if (typeof legacy?.createClassComponent === 'function') {
62
- const app = legacy.createClassComponent({
63
- component,
64
- ...options,
65
- })
66
- return {
67
- app,
68
- destroy: () => {
69
- app?.$destroy?.()
70
- },
71
- }
72
- }
73
-
74
- throw error
75
- }
76
- }
77
-
78
- export function getLegacyStateApi(app: any): LegacyStateApi | null {
79
- if (typeof app?.$capture_state !== 'function' || typeof app?.$inject_state !== 'function') {
80
- return null
81
- }
82
-
83
- return {
84
- captureState: () => app.$capture_state(),
85
- injectState: (state) => {
86
- app.$inject_state(state)
87
- },
88
- }
89
- }
90
-
91
- export async function callSetupFunctions(
92
- generatedSetup: SetupModule,
93
- setup: SetupModule,
94
- setupApi: SvelteStorySetupApi,
95
- variantSetupApp?: SvelteStorySetupHandler | null,
96
- ) {
97
- for (const hookName of setupHookNames) {
98
- const generatedHook = generatedSetup[hookName] as SvelteStorySetupHandler | undefined
99
- if (typeof generatedHook === 'function') {
100
- await generatedHook(setupApi)
101
- }
102
-
103
- const setupHook = setup[hookName] as SvelteStorySetupHandler | undefined
104
- if (typeof setupHook === 'function') {
105
- await setupHook(setupApi)
106
- }
107
- }
108
-
109
- if (typeof variantSetupApp === 'function') {
110
- await variantSetupApp(setupApi)
111
- }
112
- }
113
-
114
- /**
115
- * Adds `controlComponent` to a props object **without** spreading it.
116
- *
117
- * Svelte 5 passes a bound prop as an accessor pair — `bind:value` becomes a
118
- * `get value()` / `set value(v)` on the props object. Spreading invokes the
119
- * getter and writes a plain data property, so the setter is dropped and the
120
- * child's write-back silently goes nowhere: reads keep working, writes stop.
121
- * That was the whole of #81.
122
- *
123
- * Copying the descriptors keeps the setter intact.
124
- */
125
- function withControlComponent(props: any, controlComponent: any) {
126
- const merged = Object.defineProperties({}, Object.getOwnPropertyDescriptors(props ?? {}))
127
- Object.defineProperty(merged, 'controlComponent', {
128
- value: controlComponent,
129
- enumerable: true,
130
- writable: true,
131
- configurable: true,
132
- })
133
- return merged
134
- }
135
-
136
- export function createWrappedComponent(Wrap: any, controlComponent: any) {
137
- function ProxyWrap(anchorOrOptions: any, props?: any) {
138
- if (new.target) {
139
- return new Wrap({
140
- ...anchorOrOptions,
141
- props: withControlComponent(anchorOrOptions?.props, controlComponent),
142
- })
143
- }
144
-
145
- return Wrap(anchorOrOptions, withControlComponent(props, controlComponent))
146
- }
147
-
148
- if (Wrap?.element) {
149
- ProxyWrap.element = Wrap.element
150
- }
151
-
152
- return ProxyWrap
153
- }
package/svelte.config.js DELETED
@@ -1,5 +0,0 @@
1
- import sveltePreprocess from 'svelte-preprocess'
2
-
3
- export default {
4
- preprocess: sveltePreprocess(),
5
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "exclude": [
4
- "src/client",
5
- "src/collect"
6
- ]
7
- }
package/tsconfig.json DELETED
@@ -1,41 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- // Volar
5
- "jsx": "preserve",
6
- "lib": [
7
- "ESNext",
8
- "DOM"
9
- ],
10
- "rootDir": "src",
11
- "module": "ESNext",
12
- "moduleResolution": "bundler",
13
- "resolveJsonModule": true,
14
- "types": [
15
- "node"
16
- ],
17
- "strictBindCallApply": true,
18
- "strictFunctionTypes": true,
19
- "alwaysStrict": true,
20
- // Strict
21
- "noImplicitAny": false,
22
- "noImplicitThis": true,
23
- "outDir": "dist",
24
- "removeComments": false,
25
- "sourceMap": false,
26
- "allowSyntheticDefaultImports": true,
27
- "esModuleInterop": true,
28
- "verbatimModuleSyntax": true,
29
- "skipLibCheck": true,
30
- "preserveWatchOutput": true
31
- },
32
- "include": [
33
- "src"
34
- ],
35
- "exclude": [
36
- "node_modules",
37
- "generated/**/*",
38
- "dist/**/*",
39
- "src/**/*.spec.ts"
40
- ]
41
- }
package/vite.config.ts DELETED
@@ -1,103 +0,0 @@
1
- import { svelte } from '@sveltejs/vite-plugin-svelte'
2
- import fs from 'fs-extra'
3
- import { globbySync } from 'globby'
4
- import { defineConfig } from 'vite'
5
- import pkg from './package.json'
6
-
7
- export default defineConfig({
8
- plugins: [
9
- svelte(),
10
- {
11
- name: 'poveste:preserve:import.dynamic',
12
- enforce: 'pre',
13
- transform(code) {
14
- if (code.includes('import(')) {
15
- return {
16
- code: code.replace(/import\(/g, 'import__dyn('),
17
- }
18
- }
19
- },
20
- closeBundle() {
21
- try {
22
- const rawSvelteFiles = globbySync([
23
- 'src/client/**/*.svelte',
24
- 'src/collect/**/*.svelte',
25
- ])
26
- const rawSvelteBasenames = rawSvelteFiles.map(file => file.split('/').pop()?.replace(/\.svelte$/, '')).filter(Boolean)
27
-
28
- for (const file of rawSvelteFiles) {
29
- const target = file.replace(/^src\//, 'dist/')
30
- fs.ensureDirSync(target.replace(/\/[^/]+$/, ''))
31
- fs.copyFileSync(file, target)
32
- }
33
-
34
- const files = globbySync('./dist/**/*.js')
35
- for (const file of files) {
36
- let content = fs.readFileSync(file, 'utf-8')
37
- let updated = false
38
-
39
- if (content.includes('import__dyn')) {
40
- content = content.replace(/import__dyn\(/g, 'import(/* @vite-ignore */')
41
- updated = true
42
- }
43
-
44
- if (content.includes('.svelte.js')) {
45
- content = content.replace(/\.svelte\.js(["'])/g, '.svelte$1')
46
- updated = true
47
- }
48
-
49
- for (const basename of rawSvelteBasenames) {
50
- const compiledImport = `./${basename}.js`
51
- if (content.includes(compiledImport)) {
52
- content = content.replaceAll(compiledImport, `./${basename}.svelte`)
53
- updated = true
54
- }
55
- }
56
-
57
- if (updated) {
58
- fs.writeFileSync(file, content, 'utf-8')
59
- }
60
- }
61
- }
62
- catch (e) {
63
- console.error(e)
64
- }
65
- },
66
- },
67
- ],
68
- build: {
69
- emptyOutDir: false,
70
- outDir: 'dist',
71
- cssCodeSplit: false,
72
- rollupOptions: {
73
- external: [
74
- ...Object.keys(pkg.dependencies).map(dep => new RegExp(`^${dep}(\\/?)`)),
75
- ...Object.keys(pkg.peerDependencies).map(dep => new RegExp(`^${dep}(\\/?)`)),
76
- /^node:/,
77
- /^virtual:/,
78
- /^\$/, // Virtual modules
79
- ],
80
-
81
- input: [
82
- 'src/client/index.ts',
83
- 'src/collect/index.ts',
84
- ],
85
-
86
- output: {
87
- // manualChunks (id) {
88
- // if (id.includes('node_modules')) {
89
- // return 'vendor'
90
- // }
91
- // },
92
- entryFileNames: '[name].js',
93
- chunkFileNames: '[name].js',
94
- assetFileNames: '[name][extname]',
95
- // hoistTransitiveImports: false,
96
- preserveModules: true,
97
- preserveModulesRoot: 'src',
98
- },
99
- treeshake: false,
100
- preserveEntrySignatures: 'strict',
101
- },
102
- },
103
- })