├── .eslintignore ├── packages ├── storybook-nuxt-cli │ ├── .gitignore │ ├── playground │ │ ├── .npmrc │ │ ├── server │ │ │ └── tsconfig.json │ │ ├── app.vue │ │ ├── tsconfig.json │ │ ├── public │ │ │ └── favicon.ico │ │ ├── nuxt.config.ts │ │ ├── .gitignore │ │ ├── package.json │ │ └── README.md │ ├── index.mjs │ ├── build.config.ts │ ├── .storybook │ │ ├── rendererAssets │ │ │ ├── common │ │ │ │ ├── assets │ │ │ │ │ ├── assets.jpg │ │ │ │ │ ├── context.jpg │ │ │ │ │ ├── styling.jpg │ │ │ │ │ ├── youtube.svg │ │ │ │ │ ├── checkmark.svg │ │ │ │ │ ├── document.svg │ │ │ │ │ ├── figma.svg │ │ │ │ │ ├── accessibility.svg │ │ │ │ │ ├── discord.svg │ │ │ │ │ ├── github.svg │ │ │ │ │ ├── chromatic.svg │ │ │ │ │ ├── tutorials.svg │ │ │ │ │ └── typography.svg │ │ │ │ ├── header.css │ │ │ │ ├── button.css │ │ │ │ ├── page.css │ │ │ │ └── Configure.mdx │ │ │ └── .eslintrc.json │ │ ├── preview-js │ │ ├── preview-ts │ │ ├── main-js │ │ └── main-ts │ ├── .eslintrc │ ├── src │ │ ├── index.ts │ │ ├── build.ts │ │ ├── add-module.ts │ │ └── init.ts │ ├── package.json │ └── CHANGELOG.md └── storybook-nuxt │ ├── preview.js │ ├── .npmrc │ ├── preset.js │ ├── playground │ ├── .npmrc │ ├── components │ │ ├── MyWelcome.vue │ │ ├── MyNuxtLink.vue │ │ ├── Pre.vue │ │ ├── MyComposable.vue │ │ ├── MyNuxtImage.vue │ │ ├── PinButton.vue │ │ ├── MyButton.vue │ │ ├── MyNuxtPage.vue │ │ ├── vuetify │ │ │ └── index.vue │ │ ├── pinia │ │ │ └── index.vue │ │ └── PiniaLogo.vue │ ├── server │ │ └── tsconfig.json │ ├── pages │ │ ├── parent │ │ │ ├── b.vue │ │ │ ├── index.vue │ │ │ ├── reload-[id].vue │ │ │ └── static-[id].vue │ │ ├── parent.vue │ │ ├── catchall │ │ │ └── [...id].vue │ │ ├── index.vue │ │ └── about.vue │ ├── app.vue │ ├── tsconfig.json │ ├── public │ │ ├── favicon.ico │ │ └── images │ │ │ └── sb-nuxt-logo.jpg │ ├── stories │ │ ├── types.ts │ │ ├── pages │ │ │ ├── Home.stories.ts │ │ │ ├── About.stories.ts │ │ │ └── CustomNav.stories.ts │ │ ├── NuxtImage.stories.ts │ │ ├── Vuetify.stories.ts │ │ ├── MyNuxtWelcome.stories.ts │ │ ├── RuntimeComposable.stories.ts │ │ ├── Pinia.stories.ts │ │ └── NuxtButton.stories.ts │ ├── composables │ │ └── useMyComposable.ts │ ├── .gitignore │ ├── .storybook │ │ ├── preview.ts │ │ └── main.ts │ ├── i18n.config.ts │ ├── nuxt.config.ts │ ├── README.md │ ├── package.json │ ├── stores │ │ └── counter.ts │ └── assets │ │ └── button.css │ ├── src │ ├── index.ts │ ├── runtime │ │ ├── composables │ │ │ └── router.ts │ │ ├── plugins │ │ │ └── storybook.ts │ │ └── components │ │ │ └── nuxt-link.ts │ ├── dirs.ts │ ├── preview.ts │ ├── types.d.ts │ └── preset.ts │ ├── template │ └── cli │ │ ├── js │ │ ├── MyWelcome.vue │ │ └── MyNuxtWelcome.stories.js │ │ ├── ts-3-8 │ │ ├── MyWelcome.vue │ │ └── MyNuxtWelcome.stories.ts │ │ ├── ts-4-9 │ │ ├── MyNuxtWelcome.stories.ts │ │ └── MyWelcome.vue │ │ └── ts │ │ ├── MyNuxtWelcome.stories.ts │ │ └── MyWelcome.vue │ ├── tsconfig.json │ ├── .storybook │ └── tsconfig.json │ ├── build.config.ts │ ├── CHANGELOG.md │ ├── README.md │ └── package.json ├── pnpm-workspace.yaml ├── playgrounds └── my-nuxt-app │ ├── .npmrc │ ├── server │ └── tsconfig.json │ ├── app.vue │ ├── public │ └── favicon.ico │ ├── tsconfig.json │ ├── nuxt.config.ts │ ├── .gitignore │ ├── package.json │ └── README.md ├── .npmrc ├── pnpm-publish-summary.json ├── .eslintrc ├── tsconfig.json ├── .storybook └── tsconfig.json ├── .github └── workflows │ ├── ci.yml │ ├── release.yml │ └── release-next.yml ├── package.json ├── .gitignore └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | clones -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /packages/storybook-nuxt/preview.js: -------------------------------------------------------------------------------- 1 | export * from './dist/preview' 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - docs/** 3 | - packages/** 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | git-checks=false 2 | publish-branch=main 3 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/preset.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/preset.cjs'); 2 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/index.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import ('./dist/index.mjs') // 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | git-checks=false 2 | publish-branch=main 3 | shamefully-hoist=true 4 | strict-peer-dependencies=false -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/app.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/app.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/parent/b.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/index.ts: -------------------------------------------------------------------------------- 1 | // Path: src/types.d.ts'; 2 | export * from '@storybook/vue3' 3 | export * from './types.d' 4 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/parent/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/parent.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/playgrounds/my-nuxt-app/public/favicon.ico -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/app.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/catchall/[...id].vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/js/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts-3-8/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyNuxtLink.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt/playground/public/favicon.ico -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt-cli/playground/public/favicon.ico -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | }) 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | 'src/index', 6 | ], 7 | }) 8 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | }) 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/public/images/sb-nuxt-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt/playground/public/images/sb-nuxt-logo.jpg -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/assets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/assets.jpg -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/context.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/context.jpg -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/styling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt/HEAD/packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/styling.jpg -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/Pre.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /pnpm-publish-summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "publishedPackages": [ 3 | { 4 | "name": "@storybook-vue/nuxt", 5 | "version": "0.1.0-rc.10" 6 | }, 7 | { 8 | "name": "storybook-nuxt", 9 | "version": "0.1.0-rc.10" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/runtime/composables/router.ts: -------------------------------------------------------------------------------- 1 | import { useNuxtApp } from 'nuxt/app' 2 | import { useRouter as useVueRouter } from 'vue-router' 3 | 4 | export function useRouter() { 5 | const router = useNuxtApp()?.$router ?? useVueRouter() 6 | return router 7 | } 8 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/types.ts: -------------------------------------------------------------------------------- 1 | // types.ts 2 | export interface Props { 3 | /** 4 | * description for prop "a" type definiton 5 | * in external file . 6 | * @file ./types.ts 7 | * @default "Hello World" 8 | * */ 9 | 10 | a: string 11 | } 12 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyComposable.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "rules": { 6 | "vue/no-v-text-v-html-on-component": "off", 7 | "n/prefer-global/process": "off", 8 | "strict": "off", 9 | "indent": "off" 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "@antfu" 9 | ], 10 | "rules": { 11 | "vue/no-v-text-v-html-on-component": "off", 12 | "n/prefer-global/process": "off" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/about.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/preview-js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const preview = { 4 | parameters: { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | controls: { 7 | matchers: { 8 | color: /(background|color)$/i, 9 | date: /Date$/, 10 | }, 11 | }, 12 | }, 13 | }; 14 | c 15 | export default preview; 16 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/composables/useMyComposable.ts: -------------------------------------------------------------------------------- 1 | export function useMyComposable() { 2 | // Because your composable is called in the right place in the lifecycle, 3 | // useRuntimeConfig will also work 4 | const config = useRuntimeConfig() 5 | // console.log('useMyComposable config', config) 6 | return { config } 7 | } 8 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/parent/reload-[id].vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nuxt-storybook 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/preview-ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/vue3"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | controls: { 7 | matchers: { 8 | color: /(background|color)$/i, 9 | date: /Date$/, 10 | }, 11 | }, 12 | }, 13 | }; 14 | 15 | export default preview; 16 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/pages/parent/static-[id].vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "scripts": { 5 | "build": "nuxt build", 6 | "dev": "nuxt dev", 7 | "generate": "nuxt generate", 8 | "preview": "nuxt preview", 9 | "postinstall": "nuxt prepare" 10 | }, 11 | "devDependencies": { 12 | "@nuxt/devtools": "latest", 13 | "@types/node": "^18.19.26", 14 | "nuxt": "3.11.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import { type Preview } from "@storybook/vue3"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | controls: { 7 | matchers: { 8 | color: /(background|color)$/i, 9 | date: /Date$/, 10 | }, 11 | }, 12 | }, 13 | 14 | }; 15 | console.log('.storybook Preview =============') 16 | export default preview; 17 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/main-js: -------------------------------------------------------------------------------- 1 | const config = { 2 | stories: [ 3 | "$storiesPath/**/*.mdx", 4 | "$storiesGlob", 5 | ], 6 | addons: [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials", 9 | "@storybook/addon-interactions", 10 | ], 11 | framework: { 12 | name: "@storybook-vue/nuxt", 13 | options: {}, 14 | }, 15 | docs: { 16 | autodocs: "tag", 17 | }, 18 | }; 19 | export default config; 20 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/i18n.config.ts: -------------------------------------------------------------------------------- 1 | export default defineI18nConfig(() => ({ 2 | legacy: false, 3 | locale: 'en', 4 | defaultLocale: 'en', 5 | messages: { 6 | en: { 7 | welcome: 'Welcome to Storybook ❤️ {name} ', 8 | }, 9 | fr: { 10 | welcome: 'Bienvenue a Storybook ❤️ {name} ', 11 | }, 12 | ar: { 13 | welcome: ' ناكست ❤️ {name} ❤️ مرحبا بكم في ستوري بوك ', 14 | }, 15 | }, 16 | })) 17 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/main-ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook-vue/nuxt"; 2 | 3 | const config: StorybookConfig = { 4 | stories: [ 5 | "$storiesPath/**/*.mdx", 6 | "$storiesGlob", 7 | ], 8 | addons: [ 9 | "@storybook/addon-links", 10 | "@storybook/addon-essentials", 11 | "@storybook/addon-interactions", 12 | ], 13 | framework: { 14 | name: "@storybook-vue/nuxt", 15 | options: {}, 16 | }, 17 | docs: { 18 | autodocs: "tag", 19 | }, 20 | }; 21 | export default config; 22 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "type": "module", 4 | "private": true, 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare", 11 | "storybook": "storybook dev --port 6006", 12 | "build-storybook": "storybook build" 13 | }, 14 | "dependencies": { 15 | "nuxt": "^3.11.0", 16 | "vue": "^3.4.21", 17 | "vue-router": "^4.3.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyNuxtImage.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.storybook/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "types": ["node"], 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "strict": true, 11 | "paths": { 12 | "#app": [ 13 | "./node_modules/nuxt/dist/app" 14 | ], 15 | "#app/entry": [ 16 | "./node_modules/nuxt/dist/app/entry" 17 | ] 18 | } 19 | }, 20 | "include": ["src/**/*"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-undef": "off", 4 | "@typescript-eslint/ban-types": "off", 5 | "react/prop-types": "off", 6 | "react/react-in-jsx-scope": "off", 7 | "import/extensions": "off", 8 | "import/no-unresolved": "off", 9 | "import/no-extraneous-dependencies": "off" 10 | }, 11 | "overrides": [ 12 | { 13 | "files": ["nextjs/**/*.@(jsx|tsx)"], 14 | "rules": { 15 | "react/no-unknown-property": "off" 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | modules: [ 4 | '@nuxt/image', 5 | '@pinia/nuxt', 6 | 'vuetify-nuxt-module', 7 | ], 8 | runtimeConfig: { 9 | app: { 10 | name: 'Nuxt', 11 | version: '1.0.0', 12 | baseURL: '/', 13 | host: 'localhost', 14 | port: 3000, 15 | }, 16 | }, 17 | pinia: { 18 | autoImports: ['defineStore', 'acceptHMRUpdate'], 19 | }, 20 | imports: { 21 | dirs: ['./stores'], 22 | }, 23 | 24 | }) 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/dirs.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { dirname, resolve } from 'pathe' 3 | 4 | export const distDir = dirname(fileURLToPath(import.meta.url)) 5 | 6 | export const runtimeDir = resolve(distDir, 'runtime') 7 | export const pluginsDir = resolve(runtimeDir, 'plugins') 8 | export const componentsDir = resolve(runtimeDir, 'components') 9 | export const composablesDir = resolve(runtimeDir, 'composables') 10 | 11 | export const packageDir = resolve(distDir, '..') 12 | 13 | export const dirs = [distDir, packageDir, pluginsDir, componentsDir, composablesDir] 14 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook-vue/nuxt"; 2 | 3 | 4 | const config: StorybookConfig = { 5 | stories: [ 6 | "../stories/**/*.mdx", 7 | "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)", 8 | ], 9 | addons: [ 10 | "@storybook/addon-links", 11 | "@storybook/addon-essentials", 12 | "@storybook/addon-interactions", 13 | ], 14 | framework: { 15 | name: "@storybook-vue/nuxt", 16 | options: { 17 | docgen :"vue-component-meta" 18 | }, 19 | }, 20 | docs: { 21 | autodocs: "tag", 22 | }, 23 | }; 24 | export default config; 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | Youtube logo 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts-4-9/MyNuxtWelcome.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyNuxtWelcome from './MyWelcome.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Example/NuxtWelcome', 9 | component: MyNuxtWelcome, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | 18 | export const NuxtWelcomeStory: Story = { 19 | args: {}, 20 | } 21 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts/MyNuxtWelcome.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyNuxtWelcome from './MyWelcome.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Example/NuxtWelcome', 9 | component: MyNuxtWelcome, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | 18 | export const NuxtWelcomeStory: Story = { 19 | args: {}, 20 | } 21 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | Checkmark 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/js/MyNuxtWelcome.stories.js: -------------------------------------------------------------------------------- 1 | import MyNuxtWelcome from './MyWelcome.vue' 2 | 3 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 4 | const meta = { 5 | title: 'Example/NuxtWelcome', 6 | component: MyNuxtWelcome, 7 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 8 | tags: ['autodocs'], 9 | 10 | } 11 | 12 | export default meta 13 | 14 | /* 15 | *👇 Render functions are a framework specific feature 16 | to allow you control on how the component renders. 17 | * See https://storybook.js.org/docs/vue/api/csf 18 | * to learn how to use render functions. 19 | */ 20 | 21 | export const NuxtWelcomeStory = { 22 | args: { }, 23 | } 24 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/header.css: -------------------------------------------------------------------------------- 1 | .storybook-header { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | .storybook-header svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | .storybook-header h1 { 16 | font-weight: 700; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | .storybook-header button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .storybook-header .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/document.svg: -------------------------------------------------------------------------------- 1 | 2 | Document 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | .storybook-button--primary { 11 | color: white; 12 | background-color: #1ea7fd; 13 | } 14 | .storybook-button--secondary { 15 | color: #333; 16 | background-color: transparent; 17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 18 | } 19 | .storybook-button--small { 20 | font-size: 12px; 21 | padding: 10px 16px; 22 | } 23 | .storybook-button--medium { 24 | font-size: 14px; 25 | padding: 11px 20px; 26 | } 27 | .storybook-button--large { 28 | font-size: 16px; 29 | padding: 12px 24px; 30 | } 31 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts-3-8/MyNuxtWelcome.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyNuxtWelcome from './MyWelcome.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Example/NuxtWelcome', 9 | component: MyNuxtWelcome, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /** 18 | * See https://storybook.js.org/docs/vue/api/csf 19 | * to learn how to use render functions. 20 | */ 21 | 22 | export const NuxtWelcomeStory: Story = { 23 | args: {}, 24 | } 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/figma.svg: -------------------------------------------------------------------------------- 1 | 2 | Figma logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/preview.ts: -------------------------------------------------------------------------------- 1 | const vueAppRootContainer = document.createElement('div') 2 | vueAppRootContainer.id = '__nuxt' 3 | vueAppRootContainer.setAttribute('hidden', 'true') 4 | document.body.appendChild(vueAppRootContainer) 5 | 6 | // entry() 7 | const logger = console 8 | async function nuxtAppEntry() { 9 | const nuxtApp = () => import('#app/entry').then(m => m.default).catch(() => {}) 10 | // i 11 | const vueAppPromise = nuxtApp().catch((_error) => { 12 | // consola.error('Error while mounting app:', error) 13 | }) 14 | return vueAppPromise 15 | } 16 | 17 | nuxtAppEntry().then((app: any) => { 18 | logger.log('nuxtAppEntry done', app) 19 | app().then(() => { 20 | logger.log('nuxtAppEntry app done') 21 | }).catch(() => { logger.log('nuxtAppEntry app error') }) 22 | // app() 23 | }) 24 | 25 | export default nuxtAppEntry 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "baseUrl": ".", 5 | "strict": true, 6 | "module": "esnext", 7 | "lib": ["esnext", "dom"], 8 | "target": "esnext", 9 | "moduleResolution": "node", 10 | "noUnusedLocals": true, 11 | "allowSyntheticDefaultImports": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "paths": { 15 | "@storybook-vue/nuxt": ["./packages/storybook-nuxt/src/index.ts"] 16 | 17 | } 18 | }, 19 | "exclude": [ 20 | "**/node_modules", 21 | "**/dist", 22 | "**/playground", 23 | "**/storybook-static", 24 | "playgrounds/**/*", 25 | "**/clones", 26 | "**/docs", 27 | "./packages/**/playground", 28 | "./packages/storybook-nuxt-cli/**/*", 29 | "./packages/devtools/client", 30 | "./playgrounds/my-nuxt-ap" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/pages/Home.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyComponent from '~/pages/index.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Pages/Home ', 9 | component: MyComponent, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const NewsPage: Story = { 24 | args: { }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/NuxtImage.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyComponent from '~/components/MyNuxtImage.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Plugins/NuxtImage', 9 | component: MyComponent, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const NuxtImage: Story = { 24 | args: { }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/Vuetify.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import Vuetify from '~/components/vuetify/index.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Plugins/Vuetify ', 9 | component: Vuetify, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const Primary: Story = { 24 | args: { message: 'Hello World' }, 25 | } 26 | -------------------------------------------------------------------------------- /.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "incremental": false, 5 | "noImplicitAny": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "moduleResolution": "Node", 9 | "target": "ES2020", 10 | "module": "CommonJS", 11 | "skipLibCheck": false, 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | "isolatedModules": true, 15 | "strictBindCallApply": true, 16 | "lib": [ 17 | "dom", 18 | "dom.iterable", 19 | "esnext" 20 | ], 21 | "noUnusedLocals": true, 22 | 23 | "strict": true 24 | }, 25 | "exclude": [ 26 | "dist", 27 | "**/dist", 28 | "node_modules", 29 | "**/node_modules" 30 | ], 31 | "ts-node": { 32 | "transpileOnly": true, 33 | "files": true, 34 | "compilerOptions": { 35 | "types": [ 36 | "node" 37 | ] 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/MyNuxtWelcome.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyNuxtWelcome from '~/components/MyWelcome.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Components/NuxtWelcome ', 9 | component: MyNuxtWelcome, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const Welcome: Story = { 24 | args: {}, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/RuntimeComposable.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyComponent from '~/components/MyComposable.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Composables/Use Config ', 9 | component: MyComponent, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const UseConfigComposable: Story = { 24 | args: {}, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/pages/About.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyComponent from '~/pages/about.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Pages/About ', 9 | component: MyComponent, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const AboutPage: Story = { 24 | args: { msg: 'Storybook ❤️‍🔥 Nuxt ❤️‍🔥 Router' }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/Pinia.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyComponent from '~/components/pinia/index.vue' 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 6 | 7 | const meta = { 8 | title: 'Plugins/Pinia ', 9 | component: MyComponent, 10 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 11 | tags: ['autodocs'], 12 | 13 | } satisfies Meta 14 | 15 | export default meta 16 | type Story = StoryObj 17 | /* 18 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 19 | * See https://storybook.js.org/docs/vue/api/csf 20 | * to learn how to use render functions. 21 | */ 22 | 23 | export const Pinia: Story = { 24 | args: { msg: 'Storybook ❤️‍🔥 Nuxt ❤️‍🔥 Pinia' }, 25 | } 26 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "incremental": false, 5 | "noImplicitAny": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "moduleResolution": "Node", 9 | "target": "ES2020", 10 | "module": "CommonJS", 11 | "skipLibCheck": false, 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | "isolatedModules": true, 15 | "strictBindCallApply": true, 16 | "lib": [ 17 | "dom", 18 | "dom.iterable", 19 | "esnext" 20 | ], 21 | "noUnusedLocals": true, 22 | 23 | "strict": true 24 | }, 25 | "exclude": [ 26 | "dist", 27 | "**/dist", 28 | "node_modules", 29 | "**/node_modules" 30 | ], 31 | "ts-node": { 32 | "transpileOnly": true, 33 | "files": true, 34 | "compilerOptions": { 35 | "types": [ 36 | "node" 37 | ] 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import { initNuxt, initStorybook } from './init' 4 | 5 | // ... Your other code ... 6 | 7 | // Define the init command 8 | program 9 | .command('init') 10 | .description('Initialize the Storybook configuration') 11 | .option('-s, --start', 'Start Storybook after initialization') 12 | .option('-m, --enable-module ', 'enable module in nuxt.config') 13 | .option('-p, --port ', 'Port to run Storybook on', '6006') 14 | .option('-c, --ci', 'Run in CI mode') // avoid interactive prompts and browser opening 15 | .action(async (options) => { 16 | // if current directory is empty, create a new project 17 | 18 | const nuxt = await initNuxt().catch(() => null) 19 | if (nuxt) 20 | initStorybook(Boolean(options.start), options.port, options.ci, Boolean(options.enableModule)) 21 | }) 22 | 23 | // Parse command-line arguments 24 | program.parse(process.argv) 25 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | declaration: true, 5 | entries: [ 6 | { input: 'src/index' }, 7 | { input: 'src/preview' }, 8 | { input: 'src/preset', outDir: 'dist/', format: 'cjs', ext: 'js' }, 9 | { 10 | input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm', ext: 'js', 11 | }, 12 | 13 | ], 14 | 15 | rollup: { 16 | emitCJS: true, 17 | inlineDependencies: true, 18 | }, 19 | dependencies: [ 20 | 'vue-router', 21 | '@storybook/vue3', 22 | '@storybook/builder-vite', 23 | '@storybook/vue3-vite', 24 | ], 25 | externals: [ 26 | 'nuxt', 27 | 'nuxt/schema', 28 | 'nuxt/app', 29 | 'vue', 30 | '@storybook/types', 31 | '@storybook/vue3', 32 | '@vue/shared', 33 | '@unhead/vue', 34 | '@nuxt/devtools-kit', 35 | '#app/composables/state', 36 | '#app/entry', 37 | '#build/plugins', 38 | ], 39 | failOnWarn: false, 40 | }) 41 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | jobs: 11 | ci: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - uses: pnpm/action-setup@v2 19 | name: Install pnpm 20 | with: 21 | version: 7 22 | run_install: false 23 | - name: Get pnpm store directory 24 | shell: bash 25 | run: | 26 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 27 | 28 | - uses: actions/cache@v3 29 | name: Setup pnpm cache 30 | with: 31 | path: ${{ env.STORE_PATH }} 32 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 33 | restore-keys: | 34 | ${{ runner.os }}-pnpm-store- 35 | 36 | - name: Install dependencies 37 | run: pnpm install --no-frozen-lockfile 38 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/template/cli/ts-4-9/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 31 | -------------------------------------------------------------------------------- /playgrounds/my-nuxt-app/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt 3 Minimal Starter 2 | 3 | Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | ``` 19 | 20 | ## Development Server 21 | 22 | Start the development server on `http://localhost:3000`: 23 | 24 | ```bash 25 | # npm 26 | npm run dev 27 | 28 | # pnpm 29 | pnpm run dev 30 | 31 | # yarn 32 | yarn dev 33 | ``` 34 | 35 | ## Production 36 | 37 | Build the application for production: 38 | 39 | ```bash 40 | # npm 41 | npm run build 42 | 43 | # pnpm 44 | pnpm run build 45 | 46 | # yarn 47 | yarn build 48 | ``` 49 | 50 | Locally preview production build: 51 | 52 | ```bash 53 | # npm 54 | npm run preview 55 | 56 | # pnpm 57 | pnpm run preview 58 | 59 | # yarn 60 | yarn preview 61 | ``` 62 | 63 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 64 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt 3 Minimal Starter 2 | 3 | Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | ``` 19 | 20 | ## Development Server 21 | 22 | Start the development server on `http://localhost:3000`: 23 | 24 | ```bash 25 | # npm 26 | npm run dev 27 | 28 | # pnpm 29 | pnpm run dev 30 | 31 | # yarn 32 | yarn dev 33 | ``` 34 | 35 | ## Production 36 | 37 | Build the application for production: 38 | 39 | ```bash 40 | # npm 41 | npm run build 42 | 43 | # pnpm 44 | pnpm run build 45 | 46 | # yarn 47 | yarn build 48 | ``` 49 | 50 | Locally preview production build: 51 | 52 | ```bash 53 | # npm 54 | npm run preview 55 | 56 | # pnpm 57 | pnpm run preview 58 | 59 | # yarn 60 | yarn preview 61 | ``` 62 | 63 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 64 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "scripts": { 5 | "build": "nuxt build", 6 | "dev": "nuxt dev", 7 | "generate": "nuxt generate", 8 | "preview": "nuxt preview", 9 | "postinstall": "nuxt prepare", 10 | "storybook": "storybook dev -p 6006", 11 | "build-storybook": "storybook build" 12 | }, 13 | "devDependencies": { 14 | "@nuxt/devtools": "latest", 15 | "@nuxt/image": "rc", 16 | "@nuxt/ui-templates": "^1.3.1", 17 | "@nuxtjs/color-mode": "^3.3.3", 18 | "@pinia/nuxt": "^0.5.1", 19 | "@storybook-vue/nuxt": "workspace:*", 20 | "@storybook/addon-essentials": "^8.0.3", 21 | "@storybook/addon-interactions": "^8.0.3", 22 | "@storybook/addon-links": "^8.0.3", 23 | "@storybook/blocks": "^8.0.3", 24 | "@storybook/testing-library": "^0.2.2", 25 | "@types/node": "^20.11.30", 26 | "nuxt": "^3.11.1", 27 | "storybook": "^8.0.3", 28 | "typescript": "^5.4.3", 29 | "vue": "^3.4.21", 30 | "vue-router": "^4.3.0", 31 | "vuetify-nuxt-module": "^0.13.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/playground/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt 3 Minimal Starter 2 | 3 | Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm run dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm run build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm run preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/pages/CustomNav.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | import { useRouter } from 'vue-router' 3 | 4 | import MyNuxtPage from '~/components/MyNuxtPage.vue' 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 7 | 8 | const meta = { 9 | title: 'Pages/App ', 10 | component: MyNuxtPage, 11 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 12 | tags: ['autodocs'], 13 | 14 | } satisfies Meta 15 | 16 | export default meta 17 | type Story = StoryObj 18 | /* 19 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 20 | * See https://storybook.js.org/docs/vue/api/csf 21 | * to learn how to use render functions. 22 | */ 23 | 24 | export const Home: Story = { 25 | args: { }, 26 | render(args: any) { 27 | return ({ 28 | 29 | components: { MyNuxtPage }, 30 | setup() { 31 | useRouter().push('/') 32 | return { args } 33 | }, 34 | template: '
', 35 | }) 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/PinButton.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 50 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stores/counter.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line promise/param-names 2 | const delay = (t: number) => new Promise(r => setTimeout(r, t)) 3 | 4 | export const useCounter = defineStore('counter', { 5 | state: () => ({ 6 | n: 2, 7 | incrementedTimes: 0, 8 | decrementedTimes: 0, 9 | numbers: [] as number[], 10 | }), 11 | 12 | getters: { 13 | double: state => state.n * 2, 14 | }, 15 | 16 | actions: { 17 | increment(amount = 1) { 18 | this.incrementedTimes++ 19 | this.n += amount 20 | }, 21 | 22 | changeMe() { 23 | // console.log('change me to test HMR') 24 | }, 25 | 26 | async fail() { 27 | const n = this.n 28 | await delay(1000) 29 | this.numbers.push(n) 30 | await delay(1000) 31 | if (this.n !== n) 32 | throw new Error('Someone changed n!') 33 | 34 | return n 35 | }, 36 | 37 | async decrementToZero(interval = 300) { 38 | if (this.n <= 0) 39 | return 40 | 41 | while (this.n > 0) { 42 | this.$patch((state) => { 43 | state.n-- 44 | state.decrementedTimes++ 45 | }) 46 | await delay(interval) 47 | } 48 | }, 49 | }, 50 | }) 51 | 52 | if (import.meta.hot) 53 | import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot)) 54 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { BuilderOptions, StorybookConfig as StorybookConfigBase } from '@storybook/types'; 2 | import type { 3 | Preview, StoryFn, StoryObj, VueRenderer, Meta, DecoratorFunction, 4 | } from '@storybook/vue3'; 5 | 6 | 7 | declare let STORYBOOK_VUE_GLOBAL_PLUGINS: string[]; 8 | declare let STORYBOOK_VUE_GLOBAL_MIXINS: string[]; 9 | 10 | type FrameworkName = '@storybook-vue/nuxt'; 11 | type BuilderName = '@storybook/builder-vite'; 12 | 13 | export type FrameworkOptions = NuxtOptions & { 14 | builder?: BuilderOptions; 15 | }; 16 | 17 | type StorybookConfigFramework = { 18 | framework: FrameworkName | { name: FrameworkName; options: FrameworkOptions } 19 | core?: StorybookConfigBase['core'] & { builder?: BuilderName } 20 | typescript?: StorybookConfigBase['typescript']; 21 | previewAnnotations?: StorybookConfigBase['previewAnnotations']; 22 | stories?: StorybookConfigBase['stories']; 23 | addons?: StorybookConfigBase['addons']; 24 | docs?: StorybookConfigBase['docs']; 25 | }; 26 | /** 27 | * The interface for Storybook configuration in `main.ts` files. 28 | */ 29 | export type StorybookConfig = { viteFinal?:Record } & StorybookConfigFramework; 30 | export interface NuxtOptions { 31 | } 32 | export { 33 | Meta, StoryFn, StoryObj, Preview, VueRenderer, DecoratorFunction, 34 | }; 35 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/stories/NuxtButton.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3' 2 | 3 | import MyPre from '~/components/Pre.vue' 4 | import MyButton from '~/components/MyButton.vue' 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 7 | 8 | const meta = { 9 | title: 'Components/NuxtLink ', 10 | component: MyButton, 11 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 12 | tags: ['autodocs'], 13 | argTypes: { 14 | backgroundColor: { control: 'color' }, 15 | onClick: { action: 'clicked' }, 16 | }, 17 | args: { primary: false }, // default value 18 | } satisfies Meta 19 | 20 | export default meta 21 | type Story = StoryObj 22 | /* 23 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 24 | * See https://storybook.js.org/docs/vue/api/csf 25 | * to learn how to use render functions. 26 | */ 27 | 28 | export const Primary: Story = { 29 | args: { primary: true, label: 'Primary' }, 30 | } 31 | 32 | export const PreLink: Story = { 33 | args: { primary: true, label: 'My Story' }, 34 | render: () => ({ 35 | components: { MyPre }, 36 | template: ' Hello', 37 | }), 38 | } 39 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyButton.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/MyNuxtPage.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 43 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storybook-nuxt", 3 | "version": "0.2.7", 4 | "description": "Storybook init CLI for Nuxt ", 5 | "author": "Chakas@", 6 | "license": "ISC", 7 | "keywords": [ 8 | "storybook", 9 | "cli", 10 | "nuxt" 11 | ], 12 | "bin": { 13 | "storybook-nuxt": "./index.mjs" 14 | }, 15 | "files": [ 16 | ".storybook/**/*", 17 | "dist", 18 | "*.d.ts", 19 | "*.cjs", 20 | "*.mjs" 21 | ], 22 | "scripts": { 23 | "build": "unbuild", 24 | "stub": "unbuild --stub", 25 | "dev:prepare": "stub && build && test", 26 | "prepack": "unbuild", 27 | "dev": "cd playground && node ../index.mjs init --enable-module ", 28 | "test": "cd playground && node ../index.mjs init ", 29 | "release": "pnpm changelogen --release --push && pnpm publish" 30 | }, 31 | "dependencies": { 32 | "child_process": "^1.0.2", 33 | "commander": "^11.0.0", 34 | "consola": "^3.2.3", 35 | "diff": "^5.2.0", 36 | "execa": "^7.2.0", 37 | "global-dirs": "^3.0.1", 38 | "magicast": "^0.3.3", 39 | "pathe": "^1.1.2", 40 | "picocolors": "^1.0.0", 41 | "pkg-types": "^1.0.3", 42 | "prompts": "^2.4.2", 43 | "rc9": "^2.1.1", 44 | "semver": "^7.6.0" 45 | }, 46 | "devDependencies": { 47 | "@types/diff": "^5.0.9", 48 | "@types/prompts": "^2.4.9", 49 | "unbuild": "^2.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.2.6 5 | 6 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.2.6-alpha.0...v0.2.6) 7 | 8 | ## v0.2.1 9 | 10 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.2.1-alpha.1...v0.2.1) 11 | 12 | ## v0.2.0 13 | 14 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.2.1-alpha.0...v0.2.0) 15 | 16 | ## v0.2.0 17 | 18 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.2.0-alpha.4...v0.2.0) 19 | 20 | ## v0.1.9 21 | 22 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.9-alpha.0...v0.1.9) 23 | 24 | ## v0.1.8 25 | 26 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.8-alpha.0...v0.1.8) 27 | 28 | ### 📦 Build 29 | 30 | - Storybook-static,still have nuxt inst issue ([ac15e16](https://github.com/storybook-vue/storybook-nuxt/commit/ac15e16)) 31 | 32 | ### ❤️ Contributors 33 | 34 | - ChakAs3 ([@chakAs3](http://github.com/chakAs3)) 35 | 36 | ## v0.1.7 37 | 38 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.7-alpha.0...v0.1.7) 39 | 40 | ## v0.1.4 41 | 42 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.3...v0.1.4) 43 | 44 | ## v0.1.3 45 | 46 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.3-alpha.0...v0.1.3) 47 | 48 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/page.css: -------------------------------------------------------------------------------- 1 | .storybook-page { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | .storybook-page h2 { 12 | font-weight: 700; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | .storybook-page p { 21 | margin: 1em 0; 22 | } 23 | 24 | .storybook-page a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | .storybook-page ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | .storybook-page li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | .storybook-page .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | .storybook-page .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | .storybook-page .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | .storybook-page .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/vuetify/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 57 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/accessibility.svg: -------------------------------------------------------------------------------- 1 | 2 | Accessibility 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/src/build.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'node:child_process' 2 | 3 | import path from 'node:path' 4 | import { existsSync } from 'node:fs' 5 | 6 | // Unicode icons for better display 7 | const CHECKMARK = '\u2714' // ✔ 8 | const CROSSMARK = '\u274C' // ❌ 9 | const STARTMARK = '\u25B6' // ▶ 10 | 11 | const logger = console 12 | 13 | async function buildStorybook() { 14 | logger.log(`${STARTMARK} Building Storybook ...`) 15 | logger.log() 16 | // Path to the project root 17 | const projectRoot = process.cwd() 18 | 19 | logger.info('🔌 projectRoot :', projectRoot) 20 | 21 | const buildNuxtProcess = spawn('npx', ['storybook', 'build', '--output-dir', '.output/pub'], { 22 | cwd: projectRoot, 23 | stdio: 'inherit', 24 | }) 25 | buildNuxtProcess.on('close', async (code) => { 26 | if (code !== 0) 27 | logger.error(`${CROSSMARK} nuxt build failed with code ${code}`) 28 | 29 | else 30 | logger.log(`${CHECKMARK} nuxt build successfully!`) 31 | }) 32 | 33 | // Install required packages using pnpm 34 | } 35 | 36 | // Function to detect the package manager 37 | export function detectPackageManager() { 38 | if (existsSync(path.join(process.cwd(), 'package-lock.json'))) 39 | return 'npm' 40 | 41 | else if (existsSync(path.join(process.cwd(), 'yarn.lock'))) 42 | return 'yarn' 43 | 44 | else if (existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'))) 45 | return 'pnpm' 46 | 47 | else if (existsSync(path.join(process.cwd(), 'bun.lock'))) 48 | return 'bun' 49 | 50 | return undefined 51 | } 52 | 53 | export { buildStorybook } 54 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | Discord logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.1.10 5 | 6 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.9...v0.1.10) 7 | 8 | ### 🏡 Chore 9 | 10 | - **release:** V0.1.9 ([0262273](https://github.com/storybook-vue/storybook-nuxt/commit/0262273)) 11 | 12 | ### ❤️ Contributors 13 | 14 | - ChakAs3 ([@chakAs3](http://github.com/chakAs3)) 15 | 16 | ## v0.1.9 17 | 18 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.9...v0.1.9) 19 | 20 | ## v0.1.6 21 | 22 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.4...v0.1.6) 23 | 24 | ### 🩹 Fixes 25 | 26 | - Fix error "No builder configured in core." ([888ce4c](https://github.com/storybook-vue/storybook-nuxt/commit/888ce4c)) 27 | 28 | ### 🏡 Chore 29 | 30 | - **release:** V0.1.4 ([5d92c87](https://github.com/storybook-vue/storybook-nuxt/commit/5d92c87)) 31 | - **release:** V0.1.5 ([ebd4509](https://github.com/storybook-vue/storybook-nuxt/commit/ebd4509)) 32 | 33 | ### ❤️ Contributors 34 | 35 | - ChakAs3 ([@chakAs3](http://github.com/chakAs3)) 36 | - Tobias Diez 37 | 38 | ## v0.1.5 39 | 40 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.4...v0.1.5) 41 | 42 | ### 🩹 Fixes 43 | 44 | - Fix error "No builder configured in core." ([888ce4c](https://github.com/storybook-vue/storybook-nuxt/commit/888ce4c)) 45 | 46 | ### 🏡 Chore 47 | 48 | - **release:** V0.1.4 ([5d92c87](https://github.com/storybook-vue/storybook-nuxt/commit/5d92c87)) 49 | 50 | ### ❤️ Contributors 51 | 52 | - Tobias Diez 53 | - ChakAs3 ([@chakAs3](http://github.com/chakAs3)) 54 | 55 | ## v0.1.4 56 | 57 | [compare changes](https://github.com/storybook-vue/storybook-nuxt/compare/v0.1.4...v0.1.4) 58 | 59 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/assets/button.css: -------------------------------------------------------------------------------- 1 | .storybook { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 1.5; 5 | color: #333; 6 | background-color: white; 7 | } 8 | 9 | .storybook-button { 10 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 11 | font-weight: 700; 12 | border: 0; 13 | border-radius: 3em; 14 | cursor: pointer; 15 | display: inline-block; 16 | line-height: 1; 17 | } 18 | .storybook-button--primary { 19 | color: white; 20 | background-color: #1ea7fd; 21 | } 22 | .storybook-button--secondary { 23 | color: #333; 24 | background-color: transparent; 25 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 26 | } 27 | .storybook-button--small { 28 | font-size: 12px; 29 | padding: 10px 16px; 30 | } 31 | .storybook-button--medium { 32 | font-size: 14px; 33 | padding: 11px 20px; 34 | } 35 | .storybook-button--large { 36 | font-size: 16px; 37 | padding: 12px 24px; 38 | } 39 | .with-border { 40 | border: 1px solid #d8adad; 41 | border-radius: 6px; 42 | padding: 10px; 43 | } 44 | .sb-row { 45 | display: flex; 46 | gap: 20px; 47 | align-items: center; 48 | border: 1px solid #d8adad; 49 | border-radius: 6px; 50 | padding: 10px; 51 | } 52 | 53 | .sb-column { 54 | display: flex; 55 | flex-direction: column; 56 | align-items: flex-start; 57 | justify-content: center; 58 | } 59 | .lang-selector { 60 | display: flex; 61 | align-items: left; 62 | justify-content: left; 63 | gap: 10px; 64 | padding: 20px 0px; 65 | } 66 | .welcome { 67 | display: flex; 68 | align-items: left; 69 | flex-direction: column; 70 | justify-content: left; 71 | gap: 10px; 72 | font-size: large; 73 | width: 100%; 74 | padding: 20px 0px; 75 | } 76 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/github.svg: -------------------------------------------------------------------------------- 1 | 2 | Github logo 3 | 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook-nuxt/monorepo", 3 | "version": "0.0.1", 4 | "packageManager": "pnpm@8.6.12", 5 | "description": "Storybook for Nuxt and Vite: Develop Vue3 components in isolation with Hot Reloading.", 6 | "keywords": [ 7 | "storybook", 8 | "nuxt", 9 | "vite", 10 | "vue3" 11 | ], 12 | "scripts": { 13 | "build": "pnpm -r --filter=\"./packages/**/*\" run build", 14 | "stub": "pnpm -r run stub", 15 | "cleanup": "rimraf 'packages/**/node_modules' 'node_modules'", 16 | "dev": "pnpm run stub && pnpm -C packages/storybook-nuxt dev", 17 | "dev:cli": "pnpm run stub && pnpm -C packages/storybook-nuxt-cli dev", 18 | "test:cli": "pnpm -C packages/storybook-nuxt-cli test", 19 | "lint": "eslint .", 20 | "release": "pnpm -r publish", 21 | "test": "pnpm lint", 22 | "docs": "nuxi dev docs", 23 | "typecheck": "vue-tsc --noEmit", 24 | "postinstall": "npx simple-git-hooks", 25 | "prepare": "pnpm -r --filter=\"./packages/*\" run build" 26 | }, 27 | "devDependencies": { 28 | "@antfu/eslint-config": "^0.43.1", 29 | "@storybook-vue/nuxt": "workspace:*", 30 | "@types/node": "^20.11.30", 31 | "@typescript-eslint/eslint-plugin": "^5.62.0", 32 | "@typescript-eslint/parser": "^5.62.0", 33 | "eslint": "^8.57.0", 34 | "eslint-config-airbnb-typescript": "^18.0.0", 35 | "lint-staged": "^15.2.2", 36 | "rimraf": "^5.0.5", 37 | "simple-git-hooks": "^2.11.1", 38 | "typescript": "^5.1.6", 39 | "ufo": "^1.5.3", 40 | "unbuild": "^2.0.0", 41 | "vite": "^5.0.0", 42 | "vite-hot-client": "^0.2.3", 43 | "vue-tsc": "^2.0.7" 44 | }, 45 | "simple-git-hooks": { 46 | "pre-commit": "pnpm lint-staged" 47 | }, 48 | "lint-staged": { 49 | "*": "eslint --fix" 50 | }, 51 | "publishConfig": { 52 | "access": "public" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | dist 7 | 8 | # Editor files 9 | .vscode 10 | 11 | # Node dependencies 12 | node_modules 13 | 14 | # Logs 15 | logs 16 | *.log 17 | 18 | # Misc 19 | .DS_Store 20 | .fleet 21 | .idea 22 | 23 | # Local env files 24 | .env 25 | .env.* 26 | !.env.example 27 | # If you need to ignore a file that is not in .gitignore 28 | packages/cli 29 | packages/storybook-nuxt-cli/test-pnpm/package.json 30 | packages/storybook-nuxt-cli/test-pnpm/.storybook/main.ts 31 | packages/storybook-nuxt-cli/test-pnpm/.storybook/preview.ts 32 | packages/storybook-nuxt-cli/test-pnpm/stories/button.css 33 | packages/storybook-nuxt-cli/test-pnpm/stories/Configure.mdx 34 | packages/storybook-nuxt-cli/test-pnpm/stories/header.css 35 | packages/storybook-nuxt-cli/test-pnpm/stories/MyNuxtWelcome.stories.ts 36 | packages/storybook-nuxt-cli/test-pnpm/stories/MyWelcome.vue 37 | packages/storybook-nuxt-cli/test-pnpm/stories/page.css 38 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/accessibility.svg 39 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/assets.jpg 40 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/checkmark.svg 41 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/chromatic.svg 42 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/context.jpg 43 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/discord.svg 44 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/document.svg 45 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/figma.svg 46 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/github.svg 47 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/styling.jpg 48 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/tutorials.svg 49 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/typography.svg 50 | packages/storybook-nuxt-cli/test-pnpm/stories/assets/youtube.svg 51 | packages/storybook-nuxt-cli/test-pnpm/package.json 52 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/chromatic.svg: -------------------------------------------------------------------------------- 1 | 2 | Chromatic logo 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/runtime/plugins/storybook.ts: -------------------------------------------------------------------------------- 1 | import { createNuxtApp, defineNuxtPlugin } from 'nuxt/app' 2 | import { getContext } from 'unctx' 3 | 4 | // import logger from 'consola' 5 | 6 | import type { App } from 'vue' 7 | import { createRouter, createWebHistory } from 'vue-router' 8 | 9 | // @ts-expect-error virtual file 10 | import plugins from '#build/plugins' 11 | 12 | import '#build/css' 13 | 14 | const globalWindow = window as any 15 | const logger = console 16 | 17 | export default defineNuxtPlugin({ 18 | name: 'storybook-nuxt-plugin', 19 | enforce: 'pre', // or 'post' 20 | 21 | setup(nuxtApp: any) { 22 | logger.log('🔌 🔌 🔌 [storybook-nuxt-plugin] setup ', { nuxtApp }) 23 | const nuxtMainApp = getContext('nuxt-app') 24 | if (nuxtMainApp) 25 | logger.info('🔌 [storybook-nuxt-plugin] setup already done ', nuxtMainApp) 26 | 27 | if (nuxtApp.globalName !== 'nuxt') 28 | return 29 | const applyNuxtPlugins = async (vueApp: App, storyContext: any) => { 30 | const nuxt = createNuxtApp({ vueApp, globalName: `nuxt-${storyContext.id}` }) 31 | getContext('nuxt-app').set(nuxt, true) 32 | 33 | const router = nuxtApp.$router ?? createRouter({ history: createWebHistory(), routes: [] }) 34 | nuxt.$router = router 35 | 36 | getContext(nuxt.globalName).set(nuxt, true) 37 | 38 | nuxt.hooks.callHook('app:created', vueApp) 39 | for (const plugin of plugins) { 40 | try { 41 | if (typeof plugin === 'function' && !plugin.toString().includes('definePayloadReviver')) 42 | await vueApp.runWithContext(() => plugin(nuxt)) 43 | } 44 | catch (e) { 45 | logger.error('Error in plugin ', plugin) 46 | } 47 | } 48 | 49 | return nuxt 50 | } 51 | 52 | globalWindow.PLUGINS_SETUP_FUNCTIONS ||= new Set() 53 | globalWindow.PLUGINS_SETUP_FUNCTIONS.add(applyNuxtPlugins) 54 | }, 55 | 56 | hooks: { 57 | 'app:created': function () { 58 | }, 59 | }, 60 | }) 61 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: release 5 | 6 | on: 7 | release: 8 | types: [created] 9 | push: 10 | tags: 11 | - 'release*' 12 | - 'v*' 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 16 22 | - uses: pnpm/action-setup@v2 23 | name: Install pnpm 24 | with: 25 | version: 7 26 | run_install: false 27 | - name: Get pnpm store directory 28 | shell: bash 29 | run: | 30 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 31 | 32 | - uses: actions/cache@v3 33 | name: Setup pnpm cache 34 | with: 35 | path: ${{ env.STORE_PATH }} 36 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 37 | restore-keys: | 38 | ${{ runner.os }}-pnpm-store- 39 | 40 | - name: Install dependencies 41 | run: pnpm install --no-frozen-lockfile 42 | 43 | publish-npm: 44 | runs-on: ubuntu-latest 45 | needs: build 46 | environment: development 47 | steps: 48 | - name: Checkout 🛎️ 49 | uses: actions/checkout@v3 50 | 51 | - uses: pnpm/action-setup@v2 52 | 53 | - name: Use Node LTS ✨ 54 | uses: actions/setup-node@v3 55 | with: 56 | node-version: lts/* 57 | registry-url: https://registry.npmjs.org 58 | cache: pnpm 59 | 60 | - name: Install dependencies 📦️ 61 | run: pnpm install --no-frozen-lockfile 62 | 63 | - name: Build 🔨 64 | run: pnpm build 65 | 66 | - name: Publish 🚀️ 67 | run: pnpm publish --recursive --access public --report-summary 68 | env: 69 | NODE_AUTH_TOKEN: ${{secrets.NPMJS_TOKEN}} 70 | -------------------------------------------------------------------------------- /.github/workflows/release-next.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: release 5 | 6 | on: 7 | release: 8 | types: [created] 9 | push: 10 | branches: 11 | - next 12 | tags: 13 | - 'dev*' 14 | - 'test*' 15 | - 'beta*' 16 | - 'alpha*' 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: actions/setup-node@v3 24 | with: 25 | node-version: 16 26 | - uses: pnpm/action-setup@v2 27 | name: Install pnpm 28 | with: 29 | version: 7 30 | run_install: false 31 | - name: Get pnpm store directory 32 | shell: bash 33 | run: | 34 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 35 | 36 | - uses: actions/cache@v3 37 | name: Setup pnpm cache 38 | with: 39 | path: ${{ env.STORE_PATH }} 40 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 41 | restore-keys: | 42 | ${{ runner.os }}-pnpm-store- 43 | 44 | - name: Install dependencies 45 | run: pnpm install --no-frozen-lockfile 46 | 47 | publish-npm: 48 | runs-on: ubuntu-latest 49 | needs: build 50 | environment: development 51 | steps: 52 | - name: Checkout 🛎️ 53 | uses: actions/checkout@v3 54 | 55 | - uses: pnpm/action-setup@v2 56 | 57 | - name: Use Node LTS ✨ 58 | uses: actions/setup-node@v3 59 | with: 60 | node-version: lts/* 61 | registry-url: https://registry.npmjs.org 62 | cache: pnpm 63 | 64 | - name: Install dependencies 📦️ 65 | run: pnpm install --no-frozen-lockfile 66 | 67 | - name: Build 🔨 68 | run: pnpm build 69 | 70 | - name: Publish 🚀️ 71 | run: pnpm publish packages/storybook-nuxt --tag next --access public --report-summary 72 | env: 73 | NODE_AUTH_TOKEN: ${{secrets.NPMJS_TOKEN}} 74 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/tutorials.svg: -------------------------------------------------------------------------------- 1 | 2 | Tutorials 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/pinia/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 89 | 90 | 101 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/README.md: -------------------------------------------------------------------------------- 1 | # Storybook for Nuxt framework 2 | 3 | 4 | ![sb-nuxt (2)](https://github.com/storybook-vue/nuxt/assets/711292/6cd06c77-1b6e-4a45-9666-a97d091a27c0) 5 | 6 | 7 | Storybook package for [**Nuxt framework**](https://nuxt.com/) with zero configs. seamless integration supporting all Nuxt fancy features 8 | 9 | 10 | 11 | https://github.com/storybook-vue/nuxt/assets/711292/e66a1899-ab7c-42dd-b358-59e22ff0f609 12 | 13 | 14 | 15 | ## Supported Features 16 | 17 | 👉 [Nuxt Modules](#nuxts-image-component) 18 | 19 | 👉 [Nuxt Plugins](#nuxt-font-optimization) 20 | 21 | 👉 [All in-built Nuxt Components](#nuxt-components) 22 | 23 | 👉 [Sass/Scss](#sassscss) 24 | 25 | 👉 [Css/Sass/Scss Modules](#csssassscss-modules) 26 | 27 | 👉 [ JSX ](#styled-jsx) 28 | 29 | 👉 [Postcss](#postcss) 30 | 31 | 👉 [Auto Imports](#auto-imports) 32 | 33 | 👉 [Runtime Config](#runtime-config) 34 | 35 | 👉 [Composables](#composables) 36 | 37 | 👉 [Typescript](#typescript) (already supported out of the box by Storybook) 38 | 39 | 👉 [Nuxt DevTools](https://devtools.nuxtjs.org/) : finally as Bonus, Nuxt DevTools works amazingly with your Storybook, full features 40 | 41 | 42 | https://github.com/storybook-vue/nuxt/assets/711292/63cc1fb3-ec6b-4df2-ad61-d87e5692f385 43 | 44 | 45 | 46 | ## Requirements 47 | 48 | - [Nuxt](https://nuxt.com/) >= 3.x 49 | - [Storybook](https://storybook.js.org/) >= 7.x 50 | 51 | ## Demo 52 | 53 | Checkout the demo repo [storybook7-nuxt3-demo](https://github.com/storybook-vue/storybook-nuxt-demo) 54 | or try it on [Stackblitz](https://stackblitz.com/~/github.com/storybook-vue/storybook-nuxt-demo) 55 | 56 | ## Getting Started 57 | 58 | ### In a project without Storybook 59 | 60 | Follow the prompts after running this command in your Nuxt project's root directory: 61 | 62 | ```bash 63 | npx storybook-nuxt init 64 | ``` 65 | 66 | [More on getting started with Storybook](https://storybook.js.org/docs/vue3/get-started/install) 67 | 68 | #### Automatic migration 69 | 70 | When running the `upgrade` command above, you should get a prompt asking you to migrate to `@storybook-vue/nuxt`, which should handle everything for you. In case auto-migration does not work for your project, refer to the manual migration below. 71 | 72 | 73 | 74 | Update your `main.js` to change the framework property: 75 | 76 | ```js 77 | // .storybook/main.js 78 | export default { 79 | // ... 80 | framework: { 81 | name: '@storybook-vue/nuxt', // Add this 82 | options: {}, 83 | }, 84 | } 85 | ``` 86 | 87 | ## Documentation 88 | 89 | In progress 90 | 91 | ## License 92 | 93 | This repository is licensed under the [MIT License](LICENSE). Feel free to use the code and modify it according to your needs. 94 | 95 | ## Contacts : 96 | 97 | 🔖 Mail: javachakir@gmail.com 98 | 99 | 💬 Discord: ChakAs3 100 | 101 | 🐦‍⬛ Twitter: [@ChakirQatab](https://twitter.com/ChakirQatab) 102 | 103 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook-vue/nuxt", 3 | "type": "module", 4 | "version": "0.2.7", 5 | "packageManager": "pnpm@8.6.12", 6 | "description": "Storybook for Nuxt and Vite: Develop Vue3 components in isolation with Hot Reloading.", 7 | "license": "MIT", 8 | "homepage": "https://github.com/storybook-vue/storybook-nuxt", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/storybook-vue/storybook-nuxt.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/storybook-vue/storybook-nuxt/issues" 15 | }, 16 | "keywords": [ 17 | "storybook", 18 | "nuxt", 19 | "vite", 20 | "vue3" 21 | ], 22 | "exports": { 23 | ".": { 24 | "types": "./dist/index.d.ts", 25 | "node": "./dist/index.cjs", 26 | "import": "./dist/index.mjs", 27 | "require": "./dist/index.cjs" 28 | }, 29 | "./preset": { 30 | "types": "./dist/preset.d.ts", 31 | "require": "./dist/preset.cjs" 32 | }, 33 | "./preview": { 34 | "types": "./dist/preview.d.ts", 35 | "import": "./dist/preview.mjs", 36 | "default": "./preview.js" 37 | }, 38 | "./package.json": "./package.json" 39 | }, 40 | "main": "dist/index.cjs", 41 | "module": "dist/index.mjs", 42 | "types": "dist/index.d.ts", 43 | "files": [ 44 | "dist/**/*", 45 | "template/**/*", 46 | "README.md", 47 | "*.js", 48 | "*.mjs", 49 | "*.cjs", 50 | "*.d.ts" 51 | ], 52 | "engines": { 53 | "node": ">=18.0.0" 54 | }, 55 | "scripts": { 56 | "build": "unbuild", 57 | "build:watch": "unbuild --stub", 58 | "test": "vitest run", 59 | "dev": "unbuild && cd playground && storybook dev -p 6006", 60 | "prepack": "unbuild", 61 | "release": "pnpm changelogen --release --push && pnpm publish" 62 | }, 63 | "peerDependencies": { 64 | "nuxt": "^3.6 || ^3.7 || ^3.8", 65 | "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", 66 | "vue": "^3.0.0" 67 | }, 68 | "dependencies": { 69 | "@nuxt/devtools-kit": "^1.0.6", 70 | "@nuxt/kit": "^3.11.1", 71 | "@nuxt/schema": "^3.11.1", 72 | "@nuxt/types": "2.17.2", 73 | "@nuxt/vite-builder": "^3.11.1", 74 | "@storybook/builder-vite": "^8.0.0", 75 | "@storybook/vue3": "^8.0.0", 76 | "@storybook/vue3-vite": "^8.0.0", 77 | "autoprefixer": "^10.4.16", 78 | "nuxt": "^3.11.1", 79 | "postcss": "^8.4.33", 80 | "postcss-import": "^15.1.0", 81 | "postcss-url": "^10.1.3", 82 | "typescript": "^5.4.3", 83 | "vue": "^3.4.21" 84 | }, 85 | "devDependencies": { 86 | "@storybook/types": "^8.0.0", 87 | "@vitejs/plugin-vue": "^5.0.4", 88 | "@vitejs/plugin-vue-jsx": "^3.1.0", 89 | "changelogen": "^0.5.5", 90 | "unbuild": "^2.0.0", 91 | "vite": "^5.2.2" 92 | }, 93 | "publishConfig": { 94 | "access": "public" 95 | }, 96 | "bundler": { 97 | "entries": [ 98 | "./src/index.ts", 99 | "./src/preview.ts", 100 | "./src/preset.ts" 101 | ], 102 | "platform": "node" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook for Nuxt framework 2 | 3 | 4 | ![sb-nuxt (2)](https://github.com/storybook-vue/nuxt/assets/711292/6cd06c77-1b6e-4a45-9666-a97d091a27c0) 5 | 6 | 7 | Storybook package for [**Nuxt framework**](https://nuxt.com/) with zero configs. seamless integration supporting all Nuxt fancy features 8 | 9 | 10 | https://www.veed.io/embed/1f9d58b6-3c76-43a1-a15e-ae95af8dff5f 11 | 12 | 13 | ## Demos 14 | 15 | - Simple Demo [storybook7-nuxt3-demo](https://github.com/storybook-vue/storybook-nuxt-demo) 16 | or try it on [Stackblitz](https://stackblitz.com/~/github.com/storybook-vue/storybook-nuxt-demo) 17 | 18 | 19 | - Demo with more plugins [i18n+pinia](https://github.com/chakAs3/nuxt-sb-app) try it on [Stackblitz](https://stackblitz.com/~/github.com/chakAs3/nuxt-sb-app) 20 | - Full Demo [i18n, Pinia, Composables,Vuetify,and Routing](https://github.com/storybook-vue/storybook-nuxt-starter) try it on [Stackblitz](https://stackblitz.com/~/github.com/storybook-vue/storybook-nuxt-starter) 21 | 22 | ## Storybook module for setup: 23 | 24 | [Storybook Nuxt-Module](https://github.com/storybook-vue/nuxt-storybook) 25 | 26 | [Storybook Nuxt-Module Demo](https://github.com/chakAs3/nuxt-storybook-module-demo) 27 | 28 | ## Supported Features 29 | 30 | 👉 [Nuxt Modules](#nuxts-image-component) 31 | 32 | 👉 [Nuxt Plugins](#nuxt-font-optimization) 33 | 34 | 👉 [All in-built Nuxt Components](#nuxt-components) 35 | 36 | 👉 [Sass/Scss](#sassscss) 37 | 38 | 👉 [Css/Sass/Scss Modules](#csssassscss-modules) 39 | 40 | 👉 [ JSX ](#styled-jsx) 41 | 42 | 👉 [Postcss](#postcss) 43 | 44 | 👉 [Auto Imports](#auto-imports) 45 | 46 | 👉 [Runtime Config](#runtime-config) 47 | 48 | 👉 [Composables](#composables) 49 | 50 | 👉 [Typescript](#typescript) (already supported out of the box by Storybook) 51 | 52 | 👉 [Nuxt DevTools](https://devtools.nuxtjs.org/) : finally as Bonus, Nuxt DevTools works amazingly with your Storybook, full features 53 | 54 | 55 | https://github.com/storybook-vue/nuxt/assets/711292/63cc1fb3-ec6b-4df2-ad61-d87e5692f385 56 | 57 | 58 | 59 | ## Requirements 60 | 61 | - [Nuxt](https://nuxt.com/) >= 3.x 62 | - [Storybook](https://storybook.js.org/) >= 7.x 63 | 64 | 65 | ## Getting Started 66 | 67 | ### In a project without Storybook 68 | 69 | Follow the prompts after running this command in your Nuxt project's root directory: 70 | 71 | ```bash 72 | npx storybook-nuxt init 73 | ``` 74 | 75 | [More on getting started with Storybook](https://storybook.js.org/docs/vue3/get-started/install) 76 | 77 | #### Automatic migration 78 | 79 | When running the `upgrade` command above, you should get a prompt asking you to migrate to `@storybook-vue/nuxt`, which should handle everything for you. In case auto-migration does not work for your project, refer to the manual migration below. 80 | 81 | 82 | 83 | Update your `main.js` to change the framework property: 84 | 85 | ```js 86 | // .storybook/main.js 87 | export default { 88 | // ... 89 | framework: { 90 | name: '@storybook-vue/nuxt', // Add this 91 | options: {}, 92 | }, 93 | } 94 | ``` 95 | 96 | ## Documentation 97 | 98 | check https://storybook.nuxtjs.org/ 99 | 100 | ## License 101 | 102 | This repository is licensed under the [MIT License](LICENSE). Feel free to use the code and modify it according to your needs. 103 | 104 | ## Contacts : 105 | 106 | 🔖 Mail: javachakir@gmail.com 107 | 108 | 💬 Discord: ChakAs3 109 | 110 | 🐦‍⬛ Twitter: [@ChakirQatab](https://twitter.com/ChakirQatab) 111 | 112 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/src/add-module.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'node:fs' 2 | import fsp from 'node:fs/promises' 3 | import { relative } from 'node:path' 4 | import { cwd } from 'node:process' 5 | 6 | // import { consola } from 'consola' 7 | import c from 'picocolors' 8 | import { parseModule } from 'magicast' 9 | import { diffLines } from 'diff' 10 | import path, { join } from 'pathe' 11 | 12 | const consola = console 13 | 14 | export async function addModuleToNuxtConfigFile(moduleName, cwd) { 15 | const nuxtConfig = findNuxtConfig(cwd) 16 | if (!nuxtConfig) { 17 | consola.error(c.red('Unable to find Nuxt config file in current directory:'), cwd) 18 | process.exitCode = 1 19 | printOutManual(moduleName) 20 | return false 21 | } 22 | 23 | try { 24 | const source = await fsp.readFile(nuxtConfig, 'utf-8') 25 | const mod = await parseModule(source, { sourceFileName: nuxtConfig }) 26 | const config = mod.exports.default.$type === 'function-call' 27 | ? mod.exports.default.$args[0] 28 | : mod.exports.default 29 | 30 | config.modules ||= [] 31 | if (typeof config.modules === 'object' && !config.modules.includes(moduleName)) 32 | config.modules.push(moduleName) 33 | 34 | const generated = mod.generate().code 35 | 36 | if (source.trim() === generated.trim()) { 37 | consola.info(c.yellow('x')) 38 | } 39 | else { 40 | consola.log('') 41 | consola.log('We are going to update the Nuxt config with with the following changes:') 42 | consola.log(c.bold(c.green(`./${relative(cwd, nuxtConfig)}`))) 43 | consola.log('') 44 | printDiffToCLI(source, generated) 45 | consola.log('') 46 | 47 | await fsp.writeFile(nuxtConfig, `${generated.trimEnd()}\n`, 'utf-8') 48 | } 49 | } 50 | catch (err) { 51 | consola.error(c.red('Unable to update Nuxt config file automatically')) 52 | process.exitCode = 1 53 | printOutManual(moduleName) 54 | return false 55 | } 56 | } 57 | 58 | function findNuxtConfig(cwd) { 59 | const names = [ 60 | 'nuxt.config.ts', 61 | 'nuxt.config.js', 62 | ] 63 | 64 | for (const name of names) { 65 | const path = join(cwd, name) 66 | if (existsSync(path)) 67 | return path 68 | } 69 | } 70 | function printOutManual(moduleName: boolean) { 71 | consola.info(c.yellow('To manually enable Storybook Module, add the following to your Nuxt config modules :')) 72 | consola.info(c.cyan(`\n ${moduleName} \n`)) 73 | } 74 | 75 | // diff `from` and `to` by line and pretty print to console with line numbers, using the `diff` package 76 | function printDiffToCLI(from, to) { 77 | const diffs = diffLines(from.trim(), to.trim()) 78 | let output = '' 79 | 80 | let no = 0 81 | 82 | // TODO: frame only the diff parts 83 | for (const diff of diffs) { 84 | const lines = diff.value.trimEnd().split('\n') 85 | for (const line of lines) { 86 | if (!diff.added) 87 | no += 1 88 | if (diff.added) 89 | output += c.green(`+ | ${line}\n`) 90 | else if (diff.removed) 91 | output += c.red(`-${no.toString().padStart(3, ' ')} | ${line}\n`) 92 | else 93 | output += c.gray(`${c.dim(`${no.toString().padStart(4, ' ')} |`)} ${line}\n`) 94 | } 95 | } 96 | 97 | consola.log(output.trimEnd()) 98 | } 99 | 100 | export async function updatePackageJsonFile(devDependencies) { 101 | try { 102 | const packageJsonPath = path.join(process.cwd(), 'package.json') 103 | const source = await fsp.readFile(packageJsonPath, 'utf-8') 104 | 105 | const packageJson = source ? JSON.parse(source) : {} 106 | 107 | packageJson.devDependencies ||= {} 108 | if (typeof packageJson.devDependencies === 'object') { 109 | for (const [name, version] of Object.entries(devDependencies)) 110 | packageJson.devDependencies[name] = version 111 | } 112 | 113 | const generated = JSON.stringify(packageJson, null, 2) 114 | 115 | if (source.trim() === generated.trim()) { 116 | consola.info(c.yellow('x')) 117 | } 118 | else { 119 | consola.log('') 120 | consola.log(`We are going to update ${c.blue('package.json')} with the following changes:`) 121 | consola.log(c.bold(c.green(`./${relative(cwd(), packageJsonPath)}`))) 122 | consola.log('') 123 | printDiffToCLI(source, generated) 124 | consola.log('') 125 | 126 | await fsp.writeFile(packageJsonPath, `${generated.trimEnd()}\n`, 'utf-8') 127 | } 128 | } 129 | catch (err) { 130 | consola.error(c.red('Unable to update package.json file automatically'), err) 131 | process.exitCode = 1 132 | 133 | return false 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/assets/typography.svg: -------------------------------------------------------------------------------- 1 | 2 | Typography 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/preset.ts: -------------------------------------------------------------------------------- 1 | import { dirname, join, resolve } from 'node:path' 2 | import { fileURLToPath, pathToFileURL } from 'node:url' 3 | import { createRequire } from 'node:module' 4 | 5 | import type { PresetProperty } from '@storybook/types' 6 | import { type UserConfig as ViteConfig, mergeConfig, searchForWorkspaceRoot } from 'vite' 7 | import type { Nuxt } from '@nuxt/schema' 8 | import vuePlugin from '@vitejs/plugin-vue' 9 | 10 | import replace from '@rollup/plugin-replace' 11 | import type { StorybookConfig } from './types' 12 | import { componentsDir, composablesDir, pluginsDir, runtimeDir } from './dirs' 13 | 14 | const packageDir = resolve(fileURLToPath( 15 | import.meta.url), '../..') 16 | const distDir = resolve(fileURLToPath( 17 | import.meta.url), '../..', 'dist') 18 | 19 | const dirs = [distDir, packageDir, pluginsDir, componentsDir] 20 | 21 | let nuxt: Nuxt 22 | 23 | /** 24 | * extend nuxt-link component to use storybook router 25 | * @param nuxt 26 | */ 27 | function extendComponents(nuxt: Nuxt) { 28 | nuxt.hook('components:extend', (components: any) => { 29 | const nuxtLink = components.find(({ name }: any) => name === 'NuxtLink') 30 | nuxtLink.filePath = join(runtimeDir, 'components/nuxt-link') 31 | nuxtLink.shortPath = join(runtimeDir, 'components/nuxt-link') 32 | nuxt.options.build.transpile.push(nuxtLink.filePath) 33 | }) 34 | } 35 | 36 | /** 37 | * extend composables to override router ( fix undefined router useNuxtApp ) 38 | * 39 | * @param nuxt 40 | */ 41 | 42 | async function extendComposables(nuxt: Nuxt) { 43 | const { addImportsSources } = await import('@nuxt/kit') 44 | nuxt.options.build.transpile.push(composablesDir) 45 | addImportsSources({ imports: ['useRouter'], from: join(composablesDir, 'router') }) 46 | } 47 | 48 | async function defineNuxtConfig(baseConfig: Record) { 49 | const { loadNuxt, buildNuxt, addPlugin, extendPages } = await import('@nuxt/kit') 50 | 51 | nuxt = await loadNuxt({ 52 | rootDir: baseConfig.root, 53 | ready: false, 54 | dev: false, 55 | 56 | 57 | overrides: { 58 | buildDir: '.nuxt-storybook', 59 | }, 60 | 61 | }) 62 | 63 | if ((nuxt.options.builder as string) !== '@nuxt/vite-builder') 64 | throw new Error(`Storybook-Nuxt does not support '${nuxt.options.builder}' for now.`) 65 | 66 | let extendedConfig: ViteConfig = {} 67 | nuxt.options.build.transpile.push(join(packageDir, 'preview')) 68 | 69 | nuxt.hook('modules:done', () => { 70 | extendComposables(nuxt) 71 | // Override nuxt-link component to use storybook router 72 | extendComponents(nuxt) 73 | // nuxt.options.build.transpile.push('@storybook-vue/nuxt') 74 | addPlugin({ 75 | src: join(pluginsDir, 'storybook'), 76 | mode: 'client', 77 | }) 78 | // Add iframe page 79 | extendPages((pages: any) => { 80 | pages.push({ 81 | name: 'storybook-iframe', 82 | path: '/iframe.html', 83 | }) 84 | }) 85 | 86 | nuxt.hook( 87 | 'vite:extendConfig', 88 | ( 89 | config: ViteConfig | PromiseLike | Record, 90 | { isClient }: any, 91 | ) => { 92 | if (isClient) { 93 | const plugins = baseConfig.plugins 94 | 95 | // Find the index of the plugin with name 'vite:vue' 96 | const index = plugins.findIndex((plugin: any) => plugin.name === 'vite:vue') 97 | 98 | // Check if the plugin was found 99 | if (index !== -1) { 100 | // Replace the plugin with the new one using vuePlugin() 101 | plugins[index] = vuePlugin() 102 | } 103 | else { 104 | plugins.push(vuePlugin()) 105 | } 106 | baseConfig.plugins = plugins 107 | extendedConfig = mergeConfig(config, baseConfig) 108 | } 109 | }, 110 | ) 111 | }) 112 | 113 | await nuxt.ready() 114 | 115 | try { 116 | await buildNuxt(nuxt) 117 | 118 | return { 119 | viteConfig: extendedConfig, 120 | nuxt, 121 | } 122 | } 123 | catch (e: any) { 124 | throw new Error(e) 125 | } 126 | } 127 | export const core: PresetProperty<'core', StorybookConfig> = async (config: any) => { 128 | return ({ 129 | ...config, 130 | builder: '@storybook/builder-vite', 131 | renderer: '@storybook/vue3', 132 | }) 133 | } 134 | /** 135 | * 136 | * @param entry preview entries 137 | * @returns preview entries with nuxt runtime 138 | */ 139 | export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (entry = []) => { 140 | return [...entry, resolve(packageDir, 'preview')] 141 | } 142 | 143 | export const viteFinal: StorybookConfig['viteFinal'] = async ( 144 | config: Record, 145 | options: any, 146 | ) => { 147 | const getStorybookViteConfig = async (c: Record, o: any) => { 148 | // const pkgPath = await getPackageDir('@storybook/vue3-vite') 149 | const presetURL = pathToFileURL(join(await getPackageDir('@storybook/vue3-vite'), 'preset.js')) 150 | const { viteFinal: ViteFile } = await import(presetURL.href) 151 | 152 | if (!ViteFile) 153 | throw new Error('ViteFile not found') 154 | return ViteFile(c, o) 155 | } 156 | const nuxtConfig = await defineNuxtConfig(await getStorybookViteConfig(config, options)) 157 | 158 | return mergeConfig(nuxtConfig.viteConfig, { 159 | // build: { rollupOptions: { external: ['vue', 'vue-demi'] } }, 160 | define: { 161 | '__NUXT__': JSON.stringify({ config: nuxtConfig.nuxt.options.runtimeConfig }), 162 | 'import.meta.client': 'true', 163 | }, 164 | 165 | plugins: [replace({ 166 | values: { 167 | 'import.meta.server': 'false', 168 | 'import.meta.client': 'true', 169 | }, 170 | preventAssignment: true, 171 | })], 172 | server: { 173 | cors: true, 174 | proxy: { 175 | ...getPreviewProxy(), 176 | ...getNuxtProxyConfig(nuxt).proxy, 177 | }, 178 | fs: { allow: [searchForWorkspaceRoot(process.cwd()), ...dirs] }, 179 | }, 180 | envPrefix: ['NUXT_'], 181 | }) 182 | } 183 | 184 | async function getPackageDir(frameworkPackageName: any) { 185 | // const packageJsonPath = join(frameworkPackageName, 'package.json') 186 | 187 | try { 188 | const require = createRequire(import.meta.url) 189 | const packageDir = dirname(require.resolve(join(frameworkPackageName, 'package.json'), { paths: [process.cwd()] })) 190 | 191 | return packageDir 192 | } 193 | catch (e) { 194 | // logger.error(e) 195 | } 196 | throw new Error(`Cannot find ${frameworkPackageName},`) 197 | } 198 | 199 | export function getNuxtProxyConfig(nuxt: Nuxt) { 200 | const port = nuxt.options.runtimeConfig.app.port ?? 3000 201 | const route = '^/(_nuxt|_ipx|_icon|__nuxt_devtools__)' 202 | const proxy = { 203 | [route]: 204 | { 205 | target: `http://localhost:${port}`, 206 | changeOrigin: true, 207 | secure: false, 208 | ws: true, 209 | }, 210 | } 211 | return { 212 | port, 213 | route, 214 | proxy, 215 | } 216 | } 217 | 218 | function getPreviewProxy() { 219 | return { 220 | '/__storybook_preview__': { 221 | target: '/', 222 | changeOrigin: false, 223 | secure: false, 224 | rewrite: (path: string) => path.replace('/__storybook_preview__', ''), 225 | ws: true, 226 | }, 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/.storybook/rendererAssets/common/Configure.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from "@storybook/blocks"; 2 | 3 | import Accessibility from "./assets/accessibility.svg"; 4 | import Checkmark from "./assets/checkmark.svg"; 5 | import Document from "./assets/document.svg"; 6 | import Typography from "./assets/typography.svg"; 7 | import Github from "./assets/github.svg"; 8 | import Discord from "./assets/discord.svg"; 9 | import Youtube from "./assets/youtube.svg"; 10 | import Chromatic from "./assets/chromatic.svg"; 11 | import Figma from "./assets/figma.svg"; 12 | import Tutorials from "./assets/tutorials.svg"; 13 | import Styling from "./assets/styling.jpg"; 14 | import Context from "./assets/context.jpg"; 15 | import Assets from "./assets/assets.jpg"; 16 | 17 | 18 | 19 | # Configure your project 20 | 21 | Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community. 22 | 23 |
24 |
25 | A wall of logos representing different styling technologies 29 |
30 |

Add styling and CSS

31 |

Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.

32 | 36 | Read more on how to set up styling 37 | 38 | 39 |
40 |
41 |
42 | An abstraction representing the composition of data for a component 46 |
47 |

Provide context and mocking

48 |

Often when a story doesn't render, it's because your component is 49 | expecting a specific environment or context (like a theme provider) to be available. 50 | Learn more about solving these issues by providing context and mocking to Storybook.

51 | Read more on how to set up context 55 |
56 |
57 |
58 | A representation of typography and image assets 59 |
60 |

Load assets and resources

61 |

To link static files (like fonts) to your projects and stories, use the `staticDirs` configuration option to specify folders to load when starting Storybook.

62 | Read more on how to load assets 66 |
67 |
68 |
69 | 70 | # Do more with Storybook 71 | 72 | Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs. 73 | 74 |
75 | 76 |
77 |
78 |
79 | Document 80 |

Autodocs

81 |

Auto-generate living, interactive reference documentation from your components and stories.

82 | Learn more 86 |
87 |
88 | Checkmark 89 |

Testing

90 |

Use stories to test a component in all its variations, no matter how complex.

91 | Learn more 95 |
96 |
97 | Chromatic 98 |

Publish to Chromatic

99 |

Publish your Storybook to review and collaborate with your entire team.

100 | Learn more 104 |
105 |
106 | Figma 107 |

Figma Plugin

108 |

Embed your stories into Figma to cross-reference the design and live implementation in one place.

109 | Learn more 113 |
114 |
115 | Accessibility 116 |

Accessibility

117 |

Automatically test your components for a11y issues as you develop.

118 | Learn more 122 |
123 |
124 | Typography 125 |

Theming

126 |

Theme Storybook's UI to personalize it to your project.

127 | Learn more 131 |
132 |
133 |
134 | 135 | # Explore and Connect 136 | 137 | Connect with our community on Discord or start contributing directly on Github. You might also be interesting in watching some videos on Youtube explaining how to take full advantage of Storybook. 138 | 139 |
140 | 141 | 177 | 178 | 287 | -------------------------------------------------------------------------------- /packages/storybook-nuxt-cli/src/init.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'node:child_process' 2 | import fsp from 'node:fs/promises' 3 | import path from 'node:path' 4 | import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs' 5 | import { createRequire } from 'node:module' 6 | import { fileURLToPath } from 'node:url' 7 | import c from 'picocolors' 8 | import { consola } from 'consola' 9 | import { addModuleToNuxtConfigFile, updatePackageJsonFile } from './add-module' 10 | 11 | // Unicode icons for better display 12 | const CHECKMARK = '\u2714' // ✔ 13 | const CROSSMARK = '\u274C' // ❌ 14 | const STARTMARK = '\u25B6' // ▶ 15 | 16 | const sbVersion = '8.0.8' 17 | const nuxtSbVersion = '0.2.6' 18 | const nuxtSbModuleVersion = '7.0.2' 19 | 20 | const logger = console 21 | let packageManager 22 | 23 | async function initStorybook(start = false, port = 6006, ci = true, enableModule = false) { 24 | logger.log(`${STARTMARK} Initializing Storybook configuration...`) 25 | logger.log() 26 | // Path to the project root 27 | const projectRoot = process.cwd() 28 | // Path to the root of the Storybook Nuxt CLI 29 | const rootDir = path.join(fileURLToPath(import.meta.url), '../..') 30 | // Path to the .storybook directory 31 | const storybookDir = path.join(projectRoot, '.storybook') 32 | 33 | // Determine if the project is using TypeScript 34 | const isTypeScriptProject = existsSync(path.join(projectRoot, 'tsconfig.json')) 35 | 36 | // Determine if the project has a ./src directory 37 | const hasSrcDirectory = existsSync(path.join(projectRoot, 'src')) 38 | const sourceFolder = hasSrcDirectory ? 'src' : '.' 39 | 40 | // Choose the appropriate file extension for the configuration files 41 | const configFileExtension = isTypeScriptProject ? 'ts' : 'js' 42 | const extensions = isTypeScriptProject ? 'js|jsx|mjs|ts|tsx' : 'js|jsx|mjs' 43 | // Build the paths for stories based on the source folder 44 | const storiesPath = path.join(sourceFolder, '../stories') 45 | const storiesGlob = `${storiesPath}/**/*.stories.@(${extensions})` 46 | 47 | // Load the main and preview template files 48 | const mainTemplatePath = path.join(rootDir, '.storybook', `main-${configFileExtension}`) 49 | const previewTemplatePath = path.join(rootDir, '.storybook', `preview-${configFileExtension}`) 50 | 51 | const mainTemplate = readFileSync(mainTemplatePath, 'utf8') 52 | const previewTemplate = readFileSync(previewTemplatePath, 'utf8') 53 | 54 | // Replace placeholders in the templates with dynamic values 55 | const mainConfigContent = mainTemplate 56 | .replace(/\$storiesPath/g, storiesPath) 57 | .replace(/\$storiesGlob/g, storiesGlob) 58 | 59 | const previewConfigContent = previewTemplate 60 | 61 | // Create the .storybook directory if it doesn't exist 62 | if (!existsSync(storybookDir)) 63 | mkdirSync(storybookDir) 64 | 65 | // Create the Storybook main config file 66 | writeFileSync(path.join(storybookDir, `main.${configFileExtension}`), mainConfigContent) 67 | 68 | // Create the Storybook preview config file 69 | writeFileSync(path.join(storybookDir, `preview.${configFileExtension}`), previewConfigContent) 70 | 71 | logger.log('Install dependencies 📦️') 72 | logger.log() 73 | 74 | packageManager = detectPackageManager() 75 | if (!packageManager) { 76 | // Prompt user to select package manager 77 | const selectedPackageManager = await consola.prompt<{ 78 | type: 'select' 79 | options: string[] 80 | }>('Which package manager would you like to use?', { 81 | type: 'select', 82 | options: ['npm', 'pnpm', 'yarn', 'bun'], 83 | }) 84 | 85 | packageManager = selectedPackageManager 86 | } 87 | 88 | addDevDependencies() 89 | consola.info('🔌 enableModule ', enableModule) 90 | if (enableModule) 91 | addModuleToNuxtConfigFile('@nuxtjs/storybook', projectRoot) 92 | // Install required packages using pnpm 93 | const installProcess = spawn(packageManager, ['install'], { 94 | cwd: projectRoot, 95 | stdio: 'inherit', 96 | }) 97 | 98 | installProcess.on('close', async (code) => { 99 | if (code !== 0) { 100 | logger.error(`${CROSSMARK} Package installation failed with code ${code}`) 101 | } 102 | else { 103 | logger.log(`${CHECKMARK} Packages installed successfully!`) 104 | 105 | await addScripts() 106 | await copyTemplateFiles(configFileExtension, path.join(sourceFolder, 'stories')) 107 | 108 | logger.log() 109 | logger.log('📕 Storybook is ready to go! 🚀') 110 | logger.log() 111 | logger.log('To start Storybook, run:') 112 | logger.log() 113 | logger.log(` ${c.blue(`${packageManager} run storybook`)} `) 114 | logger.log() 115 | if (start) { 116 | const startProcess = spawn(packageManager, ['storybook', 'dev', '--ci', '--port', `${port}`], { 117 | cwd: projectRoot, 118 | stdio: 'inherit', 119 | }) 120 | 121 | startProcess.on('close', (code) => { 122 | if (code !== 0) 123 | logger.error(`${CROSSMARK} Storybook failed to start with code ${code}`) 124 | else 125 | logger.log(`${CHECKMARK} Storybook started successfully!`) 126 | }) 127 | } 128 | } 129 | }) 130 | } 131 | 132 | // Function to detect the package manager 133 | function detectPackageManager() { 134 | if (existsSync(path.join(process.cwd(), 'package-lock.json'))) 135 | return 'npm' 136 | 137 | else if (existsSync(path.join(process.cwd(), 'yarn.lock'))) 138 | return 'yarn' 139 | 140 | else if (existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'))) 141 | return 'pnpm' 142 | 143 | else if (existsSync(path.join(process.cwd(), 'bun.lock'))) 144 | return 'bun' 145 | 146 | return undefined 147 | } 148 | 149 | async function addDevDependencies() { 150 | const devDependencies = { 151 | 'storybook': sbVersion, 152 | '@types/node': '^18.17.5', 153 | '@storybook/vue3': sbVersion, 154 | '@storybook-vue/nuxt': nuxtSbVersion, 155 | '@nuxtjs/storybook': nuxtSbModuleVersion, 156 | '@storybook/addon-links': sbVersion, 157 | '@storybook/builder-vite': sbVersion, 158 | '@storybook/addon-essentials': sbVersion, 159 | '@storybook/addon-interactions': sbVersion, 160 | '@storybook/test': sbVersion, 161 | '@storybook/blocks': sbVersion, 162 | } 163 | 164 | updatePackageJsonFile(devDependencies) 165 | } 166 | 167 | async function addScripts() { 168 | // Update package.json with the script 169 | const packageJsonPath = path.join(process.cwd(), 'package.json') 170 | const source = await fsp.readFile(packageJsonPath, 'utf-8') 171 | const packageJson = JSON.parse(source) 172 | 173 | if (packageJson) { 174 | packageJson.scripts = packageJson.scripts || {} 175 | packageJson.scripts.storybook = 'storybook dev --port 6006' 176 | packageJson.scripts['build-storybook'] = 'storybook build' 177 | writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) 178 | logger.log() 179 | logger.log(`${CHECKMARK} Storybook scripts added to package.json `) 180 | } 181 | else { 182 | logger.log(`${CROSSMARK} Sorry, this feature is currently only supported with pnpm.`) 183 | } 184 | } 185 | 186 | async function copyTemplateFiles(extension, storiesPath) { 187 | // Copy the template files to the project root 188 | const packagePath = await getPackageDir('@storybook-vue/nuxt') 189 | const templateDir = path.join(packagePath, 'template', 'cli', extension) 190 | const targetDir = path.join(process.cwd(), storiesPath) 191 | copyFolderRecursive(templateDir, targetDir) 192 | // Copy the common assets to the project root 193 | const rootDir = path.join(fileURLToPath(import.meta.url), '../..') 194 | const commonAssetsDir = path.join(rootDir, '.storybook', 'rendererAssets/common') 195 | copyFolderRecursive(commonAssetsDir, targetDir) 196 | } 197 | 198 | function copyFolderRecursive(sourceFolder, destinationFolder) { 199 | // Create the destination folder if it doesn't exist 200 | if (!existsSync(destinationFolder)) 201 | mkdirSync(destinationFolder) 202 | 203 | // Read the contents of the source folder 204 | const files = readdirSync(sourceFolder) 205 | 206 | // Loop through the files in the source folder 207 | for (const file of files) { 208 | const sourceFilePath = path.join(sourceFolder, file) 209 | const destinationFilePath = path.join(destinationFolder, file) 210 | 211 | // Get the file's stats to check if it's a directory or a file 212 | const stats = statSync(sourceFilePath) 213 | 214 | if (stats.isFile()) { 215 | // If it's a file, copy it to the destination folder 216 | copyFileSync(sourceFilePath, destinationFilePath) 217 | } 218 | else if (stats.isDirectory()) { 219 | // If it's a directory, recursively copy it 220 | copyFolderRecursive(sourceFilePath, destinationFilePath) 221 | } 222 | } 223 | } 224 | 225 | async function getPackageDir(frameworkPackageName) { 226 | // const packageJsonPath = join(frameworkPackageName, 'package.json') 227 | 228 | try { 229 | const require = createRequire(import.meta.url) 230 | const packageDir = path.dirname(require.resolve(path.join(frameworkPackageName, 'package.json'), { paths: [process.cwd()] })) 231 | 232 | return packageDir 233 | } 234 | catch (e) { 235 | logger.error(e) 236 | } 237 | throw new Error(`Cannot find ${frameworkPackageName},`) 238 | } 239 | 240 | async function initNuxtProject() { 241 | const isEmpty = readdirSync(process.cwd()).length === 0 242 | if (!isEmpty) { 243 | logger.error(' Directory is not empty') 244 | return true 245 | } 246 | const startProcess = spawn('npx', ['nuxi', 'init', '.'], { 247 | cwd: process.cwd(), 248 | stdio: 'inherit', 249 | }) 250 | 251 | return new Promise((resolve, reject) => { 252 | startProcess.on('close', (code) => { 253 | if (code !== 0) { 254 | logger.error(`${CROSSMARK} Nuxt failed to init ${code}`) 255 | if (code === 1) 256 | resolve(true) 257 | else 258 | reject(code) 259 | } 260 | else { 261 | logger.log(`${CHECKMARK} Nuxt started successfully!`) 262 | resolve(true) 263 | } 264 | }) 265 | }) 266 | } 267 | async function installDependencies() { 268 | const installProcess = spawn(packageManager, ['install'], { 269 | cwd: process.cwd(), 270 | stdio: 'inherit', 271 | }) 272 | return new Promise((resolve, reject) => { 273 | installProcess.on('close', (code) => { 274 | if (code !== 0) { 275 | logger.error(`${CROSSMARK} Package installation failed with code ${code}`) 276 | reject(code) 277 | } 278 | else { 279 | logger.log(`${CHECKMARK} Packages installed successfully!`) 280 | resolve(true) 281 | } 282 | }) 283 | }) 284 | } 285 | 286 | export { initStorybook, initNuxtProject as initNuxt, installDependencies } 287 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/playground/components/PiniaLogo.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 257 | 258 | 309 | 310 | 330 | -------------------------------------------------------------------------------- /packages/storybook-nuxt/src/runtime/components/nuxt-link.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-statements-per-line */ 2 | import type { ComputedRef, DefineComponent, PropType } from 'vue' 3 | import { 4 | computed, defineComponent, h, onBeforeUnmount, onMounted, ref, resolveComponent, 5 | } from 'vue' 6 | import { useRouter as useVueRouter } from 'vue-router' 7 | import type { RouteLocation, RouteLocationRaw } from 'vue-router' 8 | import { 9 | hasProtocol, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash, 10 | } from 'ufo' 11 | 12 | import { 13 | navigateTo, onNuxtReady, preloadRouteComponents, useNuxtApp, useRouter, 14 | } from 'nuxt/app' 15 | 16 | const firstNonUndefined = (...args: (T | undefined)[]) => args.find(arg => arg !== undefined) 17 | 18 | const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer' 19 | 20 | export interface NuxtLinkOptions { 21 | componentName?: string 22 | externalRelAttribute?: string | null 23 | activeClass?: string 24 | exactActiveClass?: string 25 | prefetchedClass?: string 26 | trailingSlash?: 'append' | 'remove' 27 | } 28 | 29 | export interface NuxtLinkProps { 30 | // Routing 31 | to?: RouteLocationRaw 32 | href?: RouteLocationRaw 33 | external?: boolean 34 | replace?: boolean 35 | custom?: boolean 36 | 37 | // Attributes 38 | target?: '_blank' | '_parent' | '_self' | '_top' | (string & object) | null 39 | rel?: string | null 40 | noRel?: boolean 41 | 42 | prefetch?: boolean 43 | noPrefetch?: boolean 44 | 45 | // Styling 46 | activeClass?: string 47 | exactActiveClass?: string 48 | 49 | // Vue Router's `` additional props 50 | ariaCurrentValue?: string 51 | } 52 | 53 | // Polyfills for Safari support 54 | // https://caniuse.com/requestidlecallback 55 | export const requestIdleCallback: Window['requestIdleCallback'] = import.meta.server 56 | ? (() => {}) as any 57 | : (globalThis.requestIdleCallback || ((cb) => { 58 | const start = Date.now() 59 | const idleDeadline = { 60 | didTimeout: false, 61 | timeRemaining: () => Math.max(0, 50 - (Date.now() - start)), 62 | } 63 | return setTimeout(() => { cb(idleDeadline) }, 1) 64 | })) 65 | 66 | export const cancelIdleCallback: Window['cancelIdleCallback'] = import.meta.server 67 | ? (() => {}) as any 68 | : (globalThis.cancelIdleCallback || ((id) => { clearTimeout(id) })) 69 | 70 | /*! @__NO_SIDE_EFFECTS__ */ 71 | export function defineNuxtLink(options: NuxtLinkOptions) { 72 | const componentName = options.componentName || 'NuxtLink' 73 | 74 | const checkPropConflicts = (props: NuxtLinkProps, main: keyof NuxtLinkProps, sub: keyof NuxtLinkProps): void => { 75 | if (import.meta.dev && props[main] !== undefined && props[sub] !== undefined) 76 | console.warn(`[${componentName}] \`${main}\` and \`${sub}\` cannot be used together. \`${sub}\` will be ignored.`) 77 | } 78 | const resolveTrailingSlashBehavior = ( 79 | to: RouteLocationRaw, 80 | resolve: (to: RouteLocationRaw) => RouteLocation & { href?: string }, 81 | ): RouteLocationRaw | RouteLocation => { 82 | if (!to || (options.trailingSlash !== 'append' && options.trailingSlash !== 'remove')) 83 | return to 84 | 85 | const normalizeTrailingSlash = options.trailingSlash === 'append' ? withTrailingSlash : withoutTrailingSlash 86 | if (typeof to === 'string') 87 | return normalizeTrailingSlash(to, true) 88 | 89 | const path = 'path' in to ? to.path : resolve(to).path 90 | 91 | return { 92 | ...to, 93 | name: undefined, // named routes would otherwise always override trailing slash behavior 94 | path: normalizeTrailingSlash(path, true), 95 | } 96 | } 97 | 98 | return defineComponent({ 99 | name: componentName, 100 | props: { 101 | // Routing 102 | to: { 103 | type: [String, Object] as PropType, 104 | default: undefined, 105 | required: false, 106 | }, 107 | href: { 108 | type: [String, Object] as PropType, 109 | default: undefined, 110 | required: false, 111 | }, 112 | 113 | // Attributes 114 | target: { 115 | type: String as PropType, 116 | default: undefined, 117 | required: false, 118 | }, 119 | rel: { 120 | type: String as PropType, 121 | default: undefined, 122 | required: false, 123 | }, 124 | noRel: { 125 | type: Boolean as PropType, 126 | default: undefined, 127 | required: false, 128 | }, 129 | 130 | // Prefetching 131 | prefetch: { 132 | type: Boolean as PropType, 133 | default: undefined, 134 | required: false, 135 | }, 136 | noPrefetch: { 137 | type: Boolean as PropType, 138 | default: undefined, 139 | required: false, 140 | }, 141 | 142 | // Styling 143 | activeClass: { 144 | type: String as PropType, 145 | default: undefined, 146 | required: false, 147 | }, 148 | exactActiveClass: { 149 | type: String as PropType, 150 | default: undefined, 151 | required: false, 152 | }, 153 | prefetchedClass: { 154 | type: String as PropType, 155 | default: undefined, 156 | required: false, 157 | }, 158 | 159 | // Vue Router's `` additional props 160 | replace: { 161 | type: Boolean as PropType, 162 | default: undefined, 163 | required: false, 164 | }, 165 | ariaCurrentValue: { 166 | type: String as PropType, 167 | default: undefined, 168 | required: false, 169 | }, 170 | 171 | // Edge cases handling 172 | external: { 173 | type: Boolean as PropType, 174 | default: undefined, 175 | required: false, 176 | }, 177 | 178 | // Slot API 179 | custom: { 180 | type: Boolean as PropType, 181 | default: undefined, 182 | required: false, 183 | }, 184 | }, 185 | setup(props, { slots }) { 186 | const router = useRouter() ?? useVueRouter() 187 | // Resolving `to` value from `to` and `href` props 188 | const to: ComputedRef = computed(() => { 189 | checkPropConflicts(props, 'to', 'href') 190 | 191 | const path = props.to || props.href || '' // Defaults to empty string (won't render any `href` attribute) 192 | 193 | return resolveTrailingSlashBehavior(path, router.resolve) 194 | }) 195 | 196 | // Resolving link type 197 | const isExternal = computed(() => { 198 | // External prop is explicitly set 199 | if (props.external) 200 | return true 201 | 202 | // When `target` prop is set, link is external 203 | if (props.target && props.target !== '_self') 204 | return true 205 | 206 | // When `to` is a route object then it's an internal link 207 | if (typeof to.value === 'object') 208 | return false 209 | 210 | return to.value === '' || hasProtocol(to.value, { acceptRelative: true }) 211 | }) 212 | 213 | // Prefetching 214 | const prefetched = ref(false) 215 | const el = import.meta.server ? undefined : ref(null) 216 | const elRef = import.meta.server 217 | ? undefined 218 | : (ref: any) => { el!.value = props.custom ? ref?.$el?.nextElementSibling : ref?.$el } 219 | 220 | if (import.meta.client) { 221 | checkPropConflicts(props, 'prefetch', 'noPrefetch') 222 | const shouldPrefetch = props.prefetch !== false && props.noPrefetch !== true && props.target !== '_blank' && !isSlowConnection() 223 | if (shouldPrefetch) { 224 | const nuxtApp = useNuxtApp() 225 | let idleId: number 226 | let unobserve: (() => void) | null = null 227 | onMounted(() => { 228 | const observer = useObserver() 229 | onNuxtReady(() => { 230 | idleId = requestIdleCallback(() => { 231 | if (el?.value?.tagName) { 232 | unobserve = observer!.observe(el.value as HTMLElement, async () => { 233 | unobserve?.() 234 | unobserve = null 235 | 236 | const path = typeof to.value === 'string' ? to.value : router.resolve(to.value).fullPath 237 | await Promise.all([ 238 | nuxtApp.hooks.callHook('link:prefetch', path).catch(() => {}), 239 | !isExternal.value && preloadRouteComponents(to.value as string, router).catch(() => {}), 240 | ]) 241 | prefetched.value = true 242 | }) 243 | } 244 | }) 245 | }) 246 | }) 247 | onBeforeUnmount(() => { 248 | if (idleId) 249 | cancelIdleCallback(idleId) 250 | unobserve?.() 251 | unobserve = null 252 | }) 253 | } 254 | } 255 | 256 | return () => { 257 | if (!isExternal.value) { 258 | const routerLinkProps: Record = { 259 | ref: elRef, 260 | to: to.value, 261 | activeClass: props.activeClass || options.activeClass, 262 | exactActiveClass: props.exactActiveClass || options.exactActiveClass, 263 | replace: props.replace, 264 | ariaCurrentValue: props.ariaCurrentValue, 265 | custom: props.custom, 266 | } 267 | 268 | // `custom` API cannot support fallthrough attributes as the slot 269 | // may render fragment or text root nodes (#14897, #19375) 270 | if (!props.custom) { 271 | if (prefetched.value) 272 | routerLinkProps.class = props.prefetchedClass || options.prefetchedClass 273 | 274 | routerLinkProps.rel = props.rel 275 | } 276 | 277 | // Internal link 278 | return h( 279 | resolveComponent('RouterLink'), 280 | routerLinkProps, 281 | slots.default, 282 | ) 283 | } 284 | 285 | // Resolves `to` value if it's a route location object 286 | // converts `""` to `null` to prevent the attribute from being added as empty (`href=""`) 287 | const href = typeof to.value === 'object' ? router.resolve(to.value)?.href ?? null : to.value || null 288 | 289 | // Resolves `target` value 290 | const target = props.target || null 291 | 292 | // Resolves `rel` 293 | checkPropConflicts(props, 'noRel', 'rel') 294 | const rel = (props.noRel) 295 | ? null 296 | // converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`) 297 | : firstNonUndefined(props.rel, options.externalRelAttribute, href ? DEFAULT_EXTERNAL_REL_ATTRIBUTE : '') || null 298 | 299 | const navigate = () => navigateTo(href, { replace: props.replace }) 300 | 301 | // https://router.vuejs.org/api/#custom 302 | if (props.custom) { 303 | if (!slots.default) 304 | return null 305 | 306 | return slots.default({ 307 | href, 308 | navigate, 309 | get route() { 310 | if (!href) 311 | return undefined 312 | 313 | const url = parseURL(href) 314 | return { 315 | path: url.pathname, 316 | fullPath: url.pathname, 317 | get query() { return parseQuery(url.search) }, 318 | hash: url.hash, 319 | // stub properties for compat with vue-router 320 | params: {}, 321 | name: undefined, 322 | matched: [], 323 | redirectedFrom: undefined, 324 | meta: {}, 325 | href, 326 | } 327 | }, 328 | rel, 329 | target, 330 | isExternal: isExternal.value, 331 | isActive: false, 332 | isExactActive: false, 333 | }) 334 | } 335 | 336 | return h('a', { 337 | ref: el, href, rel, target, 338 | }, slots.default?.()) 339 | } 340 | }, 341 | }) as unknown as DefineComponent 342 | } 343 | 344 | export default defineNuxtLink({ componentName: 'NuxtLink' }) 345 | 346 | // --- Prefetching utils --- 347 | type CallbackFn = () => void 348 | type ObserveFn = (element: Element, callback: CallbackFn) => () => void 349 | 350 | function useObserver(): { observe: ObserveFn } | undefined { 351 | if (import.meta.server) 352 | return 353 | 354 | const nuxtApp = useNuxtApp() 355 | if (nuxtApp._observer) 356 | return nuxtApp._observer 357 | 358 | let observer: IntersectionObserver | null = null 359 | 360 | const callbacks = new Map() 361 | 362 | const observe: ObserveFn = (element, callback) => { 363 | if (!observer) { 364 | observer = new IntersectionObserver((entries) => { 365 | for (const entry of entries) { 366 | const callback = callbacks.get(entry.target) 367 | const isVisible = entry.isIntersecting || entry.intersectionRatio > 0 368 | if (isVisible && callback) 369 | callback() 370 | } 371 | }) 372 | } 373 | callbacks.set(element, callback) 374 | observer.observe(element) 375 | return () => { 376 | callbacks.delete(element) 377 | observer!.unobserve(element) 378 | if (callbacks.size === 0) { 379 | observer!.disconnect() 380 | observer = null 381 | } 382 | } 383 | } 384 | 385 | const _observer = nuxtApp._observer = { 386 | observe, 387 | } 388 | 389 | return _observer 390 | } 391 | 392 | function isSlowConnection() { 393 | if (import.meta.server) 394 | return 395 | 396 | // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection 397 | const cn = (navigator as any).connection as { saveData: boolean; effectiveType: string } | null 398 | if (cn && (cn.saveData || /2g/.test(cn.effectiveType))) 399 | return true 400 | return false 401 | } 402 | --------------------------------------------------------------------------------