4

My app stack is Node14, Vite, Typescript, Vue3, UnoCSS (Antfu's Vitesse). I'm using Storybook 6.5 alpha for Vue3 with the storybook-builder-vite plugin.

My goal is that my Storybook config and stack are the same as the app, so Storybook will accurately reflect implemented components. Maintaining these configs separately will make it difficult to keep them in sync.

Is there a way to simplify this?

vite.config.ts:

import path from 'path'
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
import generateSitemap from 'vite-ssg-sitemap'
import Layouts from 'vite-plugin-vue-layouts'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Markdown from 'vite-plugin-md'
import { VitePWA } from 'vite-plugin-pwa'
import VueI18n from '@intlify/vite-plugin-vue-i18n'
import Inspect from 'vite-plugin-inspect'
import Prism from 'markdown-it-prism'
import LinkAttributes from 'markdown-it-link-attributes'
import Unocss from 'unocss/vite'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { FileSystemIconLoader } from 'unplugin-icons/loaders' 

const markdownWrapperClasses = 'prose prose-sm m-auto text-left'

export default defineConfig({
  resolve: {
    alias: {
      '~/': `${path.resolve(__dirname, 'src')}/`,
    },
  },
  plugins: [
    Vue({
      include: [/\.vue$/, /\.md$/],
    }),

    // https://github.com/hannoeru/vite-plugin-pages
    Pages({
      extensions: ['vue', 'md'],
    }),

    // https://github.com/JohnCampionJr/vite-plugin-vue-layouts
    Layouts(),

    // https://github.com/antfu/unplugin-auto-import
    AutoImport({
      imports: [
        'vue',
        'vue-router',
        'vue-i18n',
        '@vueuse/head',
        '@vueuse/core',
      ],
      dts: 'src/auto-imports.d.ts',
    }),

    // https://github.com/antfu/unplugin-vue-components
    Components({
      // allow auto load markdown components under `./src/components/`
      extensions: ['vue', 'md'],
      // allow auto import and register components used in markdown
      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
      dts: 'src/components.d.ts',
      resolvers: [
        IconsResolver({
          customCollections: [
            'fab',
            'fas',
            'far',
            'fal',
            'fat',
            'fad',
          ]
        }),
      ]
    }),

    Icons({
      compiler: 'vue3',
      customCollections: {
        'fab': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/brands',
          svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),  
        'fas': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/solid',
          svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),  
        'far': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/regular',
          svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),  
        'fal': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/light',
          svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),  
        'fat': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/thin',
          svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),  
        'fad': FileSystemIconLoader(
          './src/core/assets/icons/font-awesome/duotone',
          svg => svg.replace(/<path /g, '<path fill="currentColor" ')
        ),  
      }
    }),
    // https://github.com/antfu/unocss
    // see unocss.config.ts for config
    Unocss(),


    // https://github.com/antfu/vite-plugin-md
    // Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite
    Markdown({
      wrapperClasses: markdownWrapperClasses,
      headEnabled: true,
      markdownItSetup(md) {
        // https://prismjs.com/
        md.use(Prism)
        md.use(LinkAttributes, {
          matcher: (link: string) => /^https?:\/\//.test(link),
          attrs: {
            target: '_blank',
            rel: 'noopener',
          },
        })
      },
    }),

    // https://github.com/antfu/vite-plugin-pwa
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.svg', 'safari-pinned-tab.svg'],
      manifest: {
        name: 'Vitesse',
        short_name: 'Vitesse',
        theme_color: '#ffffff',
        icons: [
          {
            src: '/pwa-192x192.png',
            sizes: '192x192',
            type: 'image/png',
          },
          {
            src: '/pwa-512x512.png',
            sizes: '512x512',
            type: 'image/png',
          },
          {
            src: '/pwa-512x512.png',
            sizes: '512x512',
            type: 'image/png',
            purpose: 'any maskable',
          },
        ],
      },
    }),

    // https://github.com/intlify/bundle-tools/tree/main/packages/vite-plugin-vue-i18n
    VueI18n({
      runtimeOnly: true,
      compositionOnly: true,
      include: [path.resolve(__dirname, 'locales/**')],
    }),

    // https://github.com/antfu/vite-plugin-inspect
    // Visit http://localhost:3333/__inspect/ to see the inspector
    Inspect(),
  ],

  // https://github.com/antfu/vite-ssg
  ssgOptions: {
    script: 'async',
    formatting: 'minify',
    onFinished() { generateSitemap() },
  },

  optimizeDeps: {
    include: [
      'vue',
      'vue-router',
      '@vueuse/core',
      '@vueuse/head',
    ],
    exclude: [
      'vue-demi',
    ],
  },

  // https://github.com/vitest-dev/vitest
  test: {
    include: ['test/**/*.test.ts'],
    environment: 'jsdom',
    deps: {
      inline: ['@vue', '@vueuse', 'vue-demi'],
    },
  },
})

UnoCSS.config.ts:

import {
  defineConfig,
  presetAttributify,
  presetIcons,
  presetTypography,
  presetUno,
  presetWebFonts,
  transformerDirectives,
  transformerVariantGroup,
} from 'unocss'

export default defineConfig({
  shortcuts: [
    ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
    ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600'],
  ],
  presets: [
    presetUno(),
    // presetAttributify(),
    presetIcons({
      scale: 1.2,
      warn: true,
      extraProperties: {
        'display': 'inline-block',
        'vertical-align': 'middle',
        // ...
      },
    }),
    presetTypography(),
    // presetWebFonts({
    //   // provider: 'google',
    //   // fonts: {
    //   //   sans: ['Hind:300,400,500,600,700'],
    //   //   serif: ['Roboto Slab:200,300,400,500,600,700']
    //   // }
    // }),
  ],
  transformers: [
    transformerDirectives(),
    transformerVariantGroup(),
  ],
  safelist: 'prose prose-sm m-auto text-left'.split(' '),
  theme: {
    fontFamily: {
      serif: ['"Roboto Slab"', 'serif'],
      sans: ['"Hind"', 'sans-serif'],
    },
  },
})

.storybook/main.ts:

const { resolve } = require('path');
const Inspect = require('vite-plugin-inspect');
const Unocss = require('unocss/vite').default;

import {
  defineConfig,
} from 'unocss'

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "storybook-addon-designs"
  ],
  "framework": "@storybook/vue3",
  "core": {
    "builder": "storybook-builder-vite"
  },
  async viteFinal(config, { configType }) {
    console.log(defineConfig)
    config.resolve.alias = {
      ...config.resolve.alias,
      '~': resolve(__dirname, 'src')
    };
    config.resolve.modules = [resolve(__dirname, '@', '../src'), 'node_modules'];
    config.plugins = config.plugins ?? [];
    config.plugins.push(Unocss())
    config.plugins.push(Inspect());
    return config;
  }
}

I understand that the code will need to be different because the storybook config modifies an existing config rather than creating a new one, but I'm wondering if perhaps the unocss config could be imported wholesale.

I'm also having issues with imports as IIUC storybook uses commonjs and my other config files use ts/esm -- "Cannot use import outside module" and async imports seem to cause build errors.

Anyone?

  • Feel free to hop into the #vite channel of the storybook discord, and we'll do our best to help answer your questions. – IanVS Apr 22 '22 at 03:51

0 Answers0