@poveste/plugin-svelte 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.
Files changed (71) hide show
  1. package/LICENSE +22 -0
  2. package/assets/histoire-svelte-text.svg +89 -0
  3. package/assets/histoire-svelte.svg +52 -0
  4. package/dist/client/MountStory.js +1 -0
  5. package/dist/client/MountStory.svelte +24 -0
  6. package/dist/client/MountVariant.js +1 -0
  7. package/dist/client/MountVariant.svelte +47 -0
  8. package/dist/client/RenderStory.js +1 -0
  9. package/dist/client/RenderStory.svelte +27 -0
  10. package/dist/client/RenderVariant.js +1 -0
  11. package/dist/client/RenderVariant.svelte +32 -0
  12. package/dist/client/Stub.js +1 -0
  13. package/dist/client/Stub.svelte +0 -0
  14. package/dist/client/Wrap.js +1 -0
  15. package/dist/client/Wrap.svelte +63 -0
  16. package/dist/client/index.d.ts +8 -0
  17. package/dist/client/index.js +1 -0
  18. package/dist/client/mount.d.ts +16 -0
  19. package/dist/client/mount.js +1 -0
  20. package/dist/client/render.d.ts +34 -0
  21. package/dist/client/render.js +1 -0
  22. package/dist/client/util.d.ts +4 -0
  23. package/dist/client/util.js +1 -0
  24. package/dist/collect/Story.js +1 -0
  25. package/dist/collect/Story.svelte +34 -0
  26. package/dist/collect/Variant.js +1 -0
  27. package/dist/collect/Variant.svelte +24 -0
  28. package/dist/collect/index.d.ts +2 -0
  29. package/dist/collect/index.js +1 -0
  30. package/dist/commands/generate-story.client.d.ts +3 -0
  31. package/dist/commands/generate-story.client.js +27 -0
  32. package/dist/commands/generate-story.server.d.ts +3 -0
  33. package/dist/commands/generate-story.server.js +52 -0
  34. package/dist/helpers.d.ts +87 -0
  35. package/dist/helpers.js +6 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/index.node.d.ts +3 -0
  39. package/dist/index.node.js +90 -0
  40. package/dist/util/list-components.d.ts +1 -0
  41. package/dist/util/list-components.js +15 -0
  42. package/dist/util/story-hmr.d.ts +7 -0
  43. package/dist/util/story-hmr.js +30 -0
  44. package/dist/util/svelte.d.ts +18 -0
  45. package/dist/util/svelte.js +102 -0
  46. package/package.json +57 -0
  47. package/src/client/MountStory.svelte +24 -0
  48. package/src/client/MountVariant.svelte +47 -0
  49. package/src/client/RenderStory.svelte +27 -0
  50. package/src/client/RenderVariant.svelte +32 -0
  51. package/src/client/Stub.svelte +0 -0
  52. package/src/client/Wrap.svelte +63 -0
  53. package/src/client/index.ts +12 -0
  54. package/src/client/mount.ts +113 -0
  55. package/src/client/render.ts +191 -0
  56. package/src/client/util.ts +52 -0
  57. package/src/collect/Story.svelte +34 -0
  58. package/src/collect/Variant.svelte +24 -0
  59. package/src/collect/index.ts +53 -0
  60. package/src/commands/generate-story.client.ts +29 -0
  61. package/src/commands/generate-story.server.ts +62 -0
  62. package/src/helpers.ts +96 -0
  63. package/src/index.node.ts +101 -0
  64. package/src/index.ts +1 -0
  65. package/src/util/list-components.ts +16 -0
  66. package/src/util/story-hmr.ts +52 -0
  67. package/src/util/svelte.ts +137 -0
  68. package/svelte.config.js +5 -0
  69. package/tsconfig.build.json +7 -0
  70. package/tsconfig.json +41 -0
  71. package/vite.config.ts +103 -0
package/src/helpers.ts ADDED
@@ -0,0 +1,96 @@
1
+ import type { HstControlOption } from '@poveste/controls'
2
+ import type { Story, StoryProps, Variant, VariantProps } from '@poveste/shared'
3
+ import type { SvelteComponentTyped } 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 interface Hst {
22
+ // Main built-ins
23
+ Story: typeof SvelteComponentTyped<StoryProps>
24
+ Variant: typeof SvelteComponentTyped<VariantProps>
25
+ // Controls
26
+ Button: typeof SvelteComponentTyped
27
+ ButtonGroup: typeof SvelteComponentTyped<{
28
+ value?: string
29
+ options: (string | HstControlOption)[]
30
+ title?: string
31
+ }>
32
+ Checkbox: typeof SvelteComponentTyped<{
33
+ value?: boolean
34
+ title: string
35
+ }>
36
+ CheckboxList: typeof SvelteComponentTyped<{
37
+ value: string[]
38
+ options: (string | HstControlOption)[]
39
+ title?: string
40
+ }>
41
+ Text: typeof SvelteComponentTyped<{
42
+ value?: string
43
+ title: string
44
+ }>
45
+ Number: typeof SvelteComponentTyped<{
46
+ value?: number
47
+ title: string
48
+ step?: number
49
+ }>
50
+ Slider: typeof SvelteComponentTyped<{
51
+ value?: number
52
+ title: string
53
+ min: number
54
+ max: number
55
+ step?: number
56
+ }>
57
+ Textarea: typeof SvelteComponentTyped<{
58
+ value?: string
59
+ title: string
60
+ }>
61
+ Select: typeof SvelteComponentTyped<{
62
+ value?: string
63
+ title: string
64
+ options: Record<string, any> | string[] | HstControlOption[]
65
+ }>
66
+ Radio: typeof SvelteComponentTyped<{
67
+ value?: string
68
+ options: HstControlOption[]
69
+ title?: string
70
+ }>
71
+ Json: typeof SvelteComponentTyped<{
72
+ value: unknown
73
+ title: string
74
+ }>
75
+ Shades: typeof SvelteComponentTyped<{
76
+ shades: Record<string, any>
77
+ getName?: (key: string, color: string) => string
78
+ search?: string
79
+ }>
80
+ TokenList: typeof SvelteComponentTyped<{
81
+ tokens: Record<string, string | number | any[] | Record<string, any>>
82
+ getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
83
+ }>
84
+ TokenGrid: typeof SvelteComponentTyped<{
85
+ tokens: Record<string, string | number | any[] | Record<string, any>>
86
+ getName?: (key: string, value: string | number | any[] | Record<string, any>) => string
87
+ colSize?: number
88
+ }>
89
+ CopyIcon: typeof SvelteComponentTyped<{
90
+ content: string
91
+ }>
92
+ ColorSelect: typeof SvelteComponentTyped<{
93
+ value?: string
94
+ title: string
95
+ }>
96
+ }
@@ -0,0 +1,101 @@
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/histoire-svelte.svg',
31
+ light: '@poveste/plugin-svelte/assets/histoire-svelte-text.svg',
32
+ dark: '@poveste/plugin-svelte/assets/histoire-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 ADDED
@@ -0,0 +1 @@
1
+ export * from './helpers.js'
@@ -0,0 +1,16 @@
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
+ }
@@ -0,0 +1,52 @@
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
+ __histoireStoryHmrPatched?: boolean
19
+ }
20
+
21
+ export function disableStoryComponentHmr() {
22
+ return {
23
+ name: 'histoire: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.__histoireStoryHmrPatched) {
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.__histoireStoryHmrPatched = true
49
+ }
50
+ },
51
+ }
52
+ }
@@ -0,0 +1,137 @@
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
+ export function createWrappedComponent(Wrap: any, controlComponent: any) {
115
+ function ProxyWrap(anchorOrOptions: any, props?: any) {
116
+ if (new.target) {
117
+ return new Wrap({
118
+ ...anchorOrOptions,
119
+ props: {
120
+ ...anchorOrOptions?.props,
121
+ controlComponent,
122
+ },
123
+ })
124
+ }
125
+
126
+ return Wrap(anchorOrOptions, {
127
+ ...props,
128
+ controlComponent,
129
+ })
130
+ }
131
+
132
+ if (Wrap?.element) {
133
+ ProxyWrap.element = Wrap.element
134
+ }
135
+
136
+ return ProxyWrap
137
+ }
@@ -0,0 +1,5 @@
1
+ import sveltePreprocess from 'svelte-preprocess'
2
+
3
+ export default {
4
+ preprocess: sveltePreprocess(),
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": [
4
+ "src/client",
5
+ "src/collect"
6
+ ]
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,41 @@
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": "node",
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 ADDED
@@ -0,0 +1,103 @@
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: 'histoire: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
+ })