@poveste/plugin-vue 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 (81) hide show
  1. package/LICENSE +22 -0
  2. package/client.d.ts +2 -0
  3. package/collect.d.ts +2 -0
  4. package/components.d.ts +239 -0
  5. package/dist/bundled/client/app/MountStory.js +55 -0
  6. package/dist/bundled/client/app/RenderStory.js +74 -0
  7. package/dist/bundled/client/app/RouterLinkStub.js +40 -0
  8. package/dist/bundled/client/app/Story.js +112 -0
  9. package/dist/bundled/client/app/Variant.js +104 -0
  10. package/dist/bundled/client/app/auto-props.js +66 -0
  11. package/dist/bundled/client/app/global-components.js +81 -0
  12. package/dist/bundled/client/app/host.js +84 -0
  13. package/dist/bundled/client/app/index.js +6 -0
  14. package/dist/bundled/client/app/render-context.js +12 -0
  15. package/dist/bundled/client/app/util.js +70 -0
  16. package/dist/bundled/client/client.js +10 -0
  17. package/dist/bundled/client/codegen.js +196 -0
  18. package/dist/bundled/client/server/Story.js +79 -0
  19. package/dist/bundled/client/server/Variant.js +46 -0
  20. package/dist/bundled/client/server/run.js +51 -0
  21. package/dist/bundled/client/server/stub.js +8 -0
  22. package/dist/bundled/client/server.js +4 -0
  23. package/dist/bundled/node_modules/.pnpm/change-case@5.4.4/node_modules/change-case/dist/index.js +118 -0
  24. package/dist/client/app/MountStory.d.ts +16 -0
  25. package/dist/client/app/RenderStory.d.ts +38 -0
  26. package/dist/client/app/RouterLinkStub.d.ts +21 -0
  27. package/dist/client/app/Story.d.ts +33 -0
  28. package/dist/client/app/Variant.d.ts +78 -0
  29. package/dist/client/app/auto-props.d.ts +2 -0
  30. package/dist/client/app/global-components.d.ts +2 -0
  31. package/dist/client/app/host.d.ts +16 -0
  32. package/dist/client/app/index.d.ts +2 -0
  33. package/dist/client/app/render-context.d.ts +12 -0
  34. package/dist/client/app/util.d.ts +16 -0
  35. package/dist/client/client.d.ts +7 -0
  36. package/dist/client/codegen.d.ts +4 -0
  37. package/dist/client/server/Story.d.ts +81 -0
  38. package/dist/client/server/Variant.d.ts +52 -0
  39. package/dist/client/server/run.d.ts +2 -0
  40. package/dist/client/server/stub.d.ts +1 -0
  41. package/dist/client/server.d.ts +1 -0
  42. package/dist/commands/generate-story.client.d.ts +3 -0
  43. package/dist/commands/generate-story.client.js +27 -0
  44. package/dist/commands/generate-story.server.d.ts +3 -0
  45. package/dist/commands/generate-story.server.js +45 -0
  46. package/dist/helpers.d.ts +10 -0
  47. package/dist/helpers.js +3 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.js +1 -0
  50. package/dist/index.node.d.ts +3 -0
  51. package/dist/index.node.js +54 -0
  52. package/dist/util/list-components.d.ts +1 -0
  53. package/dist/util/list-components.js +15 -0
  54. package/package.json +71 -0
  55. package/src/client/app/MountStory.ts +83 -0
  56. package/src/client/app/RenderStory.ts +131 -0
  57. package/src/client/app/RouterLinkStub.ts +48 -0
  58. package/src/client/app/Story.ts +209 -0
  59. package/src/client/app/Variant.ts +196 -0
  60. package/src/client/app/auto-props.ts +135 -0
  61. package/src/client/app/global-components.ts +131 -0
  62. package/src/client/app/host.ts +139 -0
  63. package/src/client/app/index.ts +2 -0
  64. package/src/client/app/render-context.ts +23 -0
  65. package/src/client/app/util.ts +129 -0
  66. package/src/client/client.ts +8 -0
  67. package/src/client/codegen.ts +400 -0
  68. package/src/client/server/Story.ts +121 -0
  69. package/src/client/server/Variant.ts +56 -0
  70. package/src/client/server/run.ts +63 -0
  71. package/src/client/server/stub.ts +12 -0
  72. package/src/client/server.ts +1 -0
  73. package/src/commands/generate-story.client.ts +29 -0
  74. package/src/commands/generate-story.server.ts +55 -0
  75. package/src/helpers.ts +15 -0
  76. package/src/index.node.ts +63 -0
  77. package/src/index.ts +1 -0
  78. package/src/util/list-components.ts +16 -0
  79. package/tsconfig.build.json +6 -0
  80. package/tsconfig.json +40 -0
  81. package/vite.config.ts +70 -0
@@ -0,0 +1,63 @@
1
+ import type { ServerRunPayload } from '@poveste/shared'
2
+ import type { Vue3StorySetupApi, Vue3StorySetupHandler } from '../../helpers.js'
3
+ // @ts-expect-error virtual module id
4
+ import * as generatedSetup from 'virtual:$histoire-generated-global-setup'
5
+ // @ts-expect-error virtual module id
6
+ import * as setup from 'virtual:$histoire-setup'
7
+ import { createApp, h } from 'vue'
8
+ import Story from './Story'
9
+ import Variant from './Variant'
10
+
11
+ export async function run({ file, storyData, el }: ServerRunPayload) {
12
+ const { default: Comp } = await import(/* @vite-ignore */ file.moduleId)
13
+
14
+ const app = createApp({
15
+ provide: {
16
+ addStory(data) {
17
+ storyData.push(data)
18
+ },
19
+ },
20
+ render() {
21
+ return h(Comp, {
22
+ ref: 'comp',
23
+ data: file,
24
+ })
25
+ },
26
+ })
27
+
28
+ app.component('Story', Story)
29
+
30
+ app.component('Variant', Variant)
31
+
32
+ // Call app setups to resolve global assets such as components
33
+
34
+ const setupApi: Vue3StorySetupApi = {
35
+ app,
36
+ story: null,
37
+ variant: null,
38
+ addWrapper: () => { /* noop */ },
39
+ }
40
+
41
+ if (typeof generatedSetup?.setupVue3 === 'function') {
42
+ const setupFn = generatedSetup.setupVue3 as Vue3StorySetupHandler
43
+ await setupFn(setupApi)
44
+ }
45
+
46
+ if (typeof setup?.setupVue3 === 'function') {
47
+ const setupFn = setup.setupVue3 as Vue3StorySetupHandler
48
+ await setupFn(setupApi)
49
+ }
50
+
51
+ app.mount(el)
52
+
53
+ if (Comp.doc) {
54
+ const el = document.createElement('div')
55
+ el.innerHTML = Comp.doc
56
+ const text = el.textContent
57
+ storyData.forEach((s) => {
58
+ s.docsText = text
59
+ })
60
+ }
61
+
62
+ app.unmount()
63
+ }
@@ -0,0 +1,12 @@
1
+ const stub = { name: 'StubbedComponent', render: () => null }
2
+
3
+ export function autoStubComponents(vnodes: any[]) {
4
+ for (const vnode of vnodes) {
5
+ if (typeof vnode.type === 'object' && (vnode.type as any).name !== 'PovesteVariant') {
6
+ vnode.type = stub
7
+ }
8
+ if (Array.isArray(vnode.children)) {
9
+ autoStubComponents(vnode.children)
10
+ }
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ export * from './server/run.js'
@@ -0,0 +1,29 @@
1
+ import type { ClientCommandOptions } from 'poveste'
2
+ import { kebabCase } from 'change-case'
3
+ import { openStory, sendEvent } from 'poveste/plugin'
4
+
5
+ export default {
6
+ prompts: [
7
+ {
8
+ field: 'component',
9
+ label: 'Choose a component',
10
+ type: 'select',
11
+ options: async search => sendEvent('listVueComponents', { search }),
12
+ required: true,
13
+ },
14
+ {
15
+ field: 'fileName',
16
+ label: 'File name',
17
+ type: 'text',
18
+ required: true,
19
+ defaultValue: answers => answers.component?.replace(/[^/]+\/([^/]+)\.vue$/, '$1.story.vue'),
20
+ },
21
+ ],
22
+ clientAction: (params) => {
23
+ const index = params.component.lastIndexOf('/')
24
+ const dirname = params.component.substring(0, index + 1)
25
+ const file = `${dirname}${params.fileName}`
26
+ const storyId = kebabCase(file.toLowerCase())
27
+ openStory(storyId)
28
+ },
29
+ } as ClientCommandOptions
@@ -0,0 +1,55 @@
1
+ import type { PluginCommand } from 'poveste'
2
+ import fs from 'node:fs'
3
+ import launchEditor from 'launch-editor'
4
+ import path from 'pathe'
5
+
6
+ export default {
7
+ id: 'histoire:plugin-vue:generate-story',
8
+ label: 'Generate Vue 3 story from component',
9
+ icon: 'https://vuejs.org/logo.svg',
10
+ searchText: 'generate create',
11
+ async serverAction(params) {
12
+ const targetFile = path.join(path.dirname(params.component), params.fileName)
13
+
14
+ if (fs.existsSync(targetFile)) {
15
+ throw new Error(`File ${targetFile} already exists`)
16
+ }
17
+
18
+ const { component, componentName, isTs } = await getComponentInfo(params.component)
19
+
20
+ const content = `<script setup${isTs ? ' lang="ts"' : ''}>
21
+ import ${componentName} from './${component}'
22
+ </script>
23
+
24
+ <template>
25
+ <Story>
26
+ <Variant title="Default">
27
+ <${componentName} />
28
+ </Variant>
29
+ </Story>
30
+ </template>
31
+ `
32
+
33
+ await fs.promises.writeFile(targetFile, content, 'utf-8')
34
+
35
+ launchEditor(targetFile)
36
+ },
37
+ clientSetupFile: '@poveste/plugin-vue/dist/commands/generate-story.client.js',
38
+ } as PluginCommand
39
+
40
+ async function isComponentTs(component: string) {
41
+ const componentContent = await fs.promises.readFile(component, 'utf-8')
42
+ return componentContent.includes('lang="ts"')
43
+ }
44
+
45
+ async function getComponentInfo(file: string) {
46
+ const component = path.basename(file)
47
+ const componentName = component.replace(path.extname(component), '')
48
+ const isTs = await isComponentTs(file)
49
+
50
+ return {
51
+ component,
52
+ componentName,
53
+ isTs,
54
+ }
55
+ }
package/src/helpers.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { Story, Variant } from '@poveste/shared'
2
+ import type { App, Component } from 'vue'
3
+
4
+ export interface Vue3StorySetupApi {
5
+ app: App
6
+ story?: Story
7
+ variant?: Variant
8
+ addWrapper: (wrapper: Component) => void
9
+ }
10
+
11
+ export type Vue3StorySetupHandler = (api: Vue3StorySetupApi) => Promise<void> | void
12
+
13
+ export function defineSetupVue3(handler: Vue3StorySetupHandler): Vue3StorySetupHandler {
14
+ return handler
15
+ }
@@ -0,0 +1,63 @@
1
+ import type { Plugin } from 'poveste'
2
+
3
+ import generateStoryCommand from './commands/generate-story.server.js'
4
+ import { listComponentFiles } from './util/list-components.js'
5
+
6
+ export function HstVue(): Plugin {
7
+ return {
8
+ name: '@poveste/plugin-vue',
9
+
10
+ defaultConfig() {
11
+ return {
12
+ supportMatch: [
13
+ {
14
+ id: 'vue',
15
+ patterns: ['**/*.vue'],
16
+ pluginIds: ['vue3'],
17
+ },
18
+ ],
19
+ }
20
+ },
21
+
22
+ config() {
23
+ return {
24
+ vite: {
25
+ plugins: [
26
+ {
27
+ name: 'histoire-plugin-vue',
28
+ enforce: 'post',
29
+ transform(code, id) {
30
+ // Remove vue warnings about unknown components
31
+ if ((this.meta as any).histoire?.isCollecting && id.endsWith('.vue')) {
32
+ return `const _stubComponent = (name) => ['Story','Variant'].some(validName => name.toLowerCase() === validName.toLowerCase()) ? _resolveComponent(name) : ({ render: () => null });${code?.replaceAll('_resolveComponent(', '_stubComponent(') ?? ''}`
33
+ }
34
+ },
35
+ },
36
+ ],
37
+ },
38
+ }
39
+ },
40
+
41
+ supportPlugin: {
42
+ id: 'vue3',
43
+ moduleName: '@poveste/plugin-vue',
44
+ setupFn: 'setupVue3',
45
+ importStoriesPrepend: `import { defineAsyncComponent as defineAsyncComponentVue3 } from 'vue'`,
46
+ importStoryComponent: (file, index) => `const Comp${index} = defineAsyncComponentVue3(() => import(${JSON.stringify(file.moduleId)}))`,
47
+ },
48
+
49
+ commands: [
50
+ generateStoryCommand,
51
+ ],
52
+
53
+ async onDevEvent(api) {
54
+ switch (api.event) {
55
+ case 'listVueComponents': {
56
+ return listComponentFiles(api.payload.search, api.getConfig().storyMatch)
57
+ }
58
+ }
59
+ },
60
+ }
61
+ }
62
+
63
+ export * from './helpers.js'
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('**/*.vue', {
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,6 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": [
4
+ "src/client"
5
+ ]
6
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
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
+ "skipLibCheck": true,
29
+ "preserveWatchOutput": true
30
+ },
31
+ "include": [
32
+ "src"
33
+ ],
34
+ "exclude": [
35
+ "node_modules",
36
+ "generated/**/*",
37
+ "dist/**/*",
38
+ "src/**/*.spec.ts"
39
+ ]
40
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,70 @@
1
+ import fs from 'node:fs'
2
+ import { globbySync } from 'globby'
3
+ import { defineConfig } from 'vite'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ {
8
+ name: 'histoire:preserve:import.dynamic',
9
+ enforce: 'pre',
10
+ transform(code) {
11
+ if (code.includes('import(')) {
12
+ return {
13
+ code: code.replace(/import\(/g, 'import__dyn('),
14
+ }
15
+ }
16
+ },
17
+ closeBundle() {
18
+ try {
19
+ const files = globbySync('./dist/**/*.js')
20
+ for (const file of files) {
21
+ const content = fs.readFileSync(file, 'utf-8')
22
+ if (content.includes('import__dyn')) {
23
+ fs.writeFileSync(file, content.replace(/import__dyn\(/g, 'import(/* @vite-ignore */'), 'utf-8')
24
+ }
25
+ }
26
+ }
27
+ catch (e) {
28
+ console.error(e)
29
+ }
30
+ },
31
+ },
32
+ ],
33
+
34
+ build: {
35
+ emptyOutDir: false,
36
+ outDir: 'dist/bundled',
37
+ lib: {
38
+ entry: '',
39
+ formats: ['es'],
40
+ },
41
+ rollupOptions: {
42
+ external: [
43
+ /\$histoire/,
44
+ /@poveste/,
45
+ 'vue',
46
+ ],
47
+
48
+ input: [
49
+ 'src/client/client.ts',
50
+ 'src/client/server.ts',
51
+ ],
52
+
53
+ output: {
54
+ // manualChunks (id) {
55
+ // if (id.includes('node_modules')) {
56
+ // return 'vendor'
57
+ // }
58
+ // },
59
+ entryFileNames: '[name].js',
60
+ chunkFileNames: '[name].js',
61
+ assetFileNames: '[name][extname]',
62
+ // hoistTransitiveImports: false,
63
+ preserveModules: true,
64
+ preserveModulesRoot: 'src',
65
+ },
66
+ treeshake: false,
67
+ preserveEntrySignatures: 'strict',
68
+ },
69
+ },
70
+ })