Files
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

6.3 KiB

Vue Support

Build Vue component libraries with tsdown.

Overview

tsdown provides first-class support for Vue libraries through integration with unplugin-vue and rolldown-plugin-dts for type generation.

Quick Start

Use Starter Template

npx create-tsdown@latest -t vue

Basic Configuration

Install Dependencies

pnpm add -D unplugin-vue vue-tsc

Minimal Setup

// tsdown.config.ts
import { defineConfig } from 'tsdown';
import Vue from 'unplugin-vue/rolldown';

export default defineConfig({
  entry: ['./src/index.ts'],
  format: ['esm', 'cjs'],
  platform: 'neutral',
  external: ['vue'],
  plugins: [Vue({ isProduction: true })],
  dts: {
    vue: true, // Enable Vue type generation
  },
});

How It Works

unplugin-vue

Compiles .vue single-file components:

  • Transforms template to render functions
  • Handles scoped styles
  • Processes script setup

vue-tsc

Generates TypeScript declarations:

  • Type-checks Vue components
  • Creates .d.ts files
  • Preserves component props types
  • Exports component types

Component Example

Single File Component

<!-- src/Button.vue -->
<script setup lang="ts">
interface Props {
  type?: 'primary' | 'secondary';
  disabled?: boolean;
}

defineProps<Props>();
defineEmits<{
  click: [];
}>();
</script>

<template>
  <button :class="['btn', `btn-${type}`]" :disabled="disabled" @click="$emit('click')">
    <slot />
  </button>
</template>

<style scoped>
.btn {
  padding: 8px 16px;
  border-radius: 4px;
}

.btn-primary {
  background: blue;
  color: white;
}
</style>

Export Components

// src/index.ts
export { default as Button } from './Button.vue';
export { default as Input } from './Input.vue';
export { default as Modal } from './Modal.vue';

// Re-export types
export type { ButtonProps } from './Button.vue';

Common Patterns

Component Library

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  platform: 'neutral',
  external: ['vue'],
  plugins: [
    Vue({
      isProduction: true,
      style: {
        trim: true,
      },
    }),
  ],
  dts: {
    vue: true,
  },
  clean: true,
});

Multiple Components

export default defineConfig({
  entry: {
    index: 'src/index.ts',
    Button: 'src/Button.vue',
    Input: 'src/Input.vue',
    Modal: 'src/Modal.vue',
  },
  format: ['esm', 'cjs'],
  external: ['vue'],
  plugins: [Vue({ isProduction: true })],
  dts: { vue: true },
});

With Composition Utilities

// src/composables/useCounter.ts
import { ref } from 'vue';

export function useCounter(initial = 0) {
  const count = ref(initial);
  const increment = () => count.value++;
  const decrement = () => count.value--;
  return { count, increment, decrement };
}
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  external: ['vue'],
  plugins: [Vue({ isProduction: true })],
  dts: { vue: true },
});

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "preserve",
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "strict": true,
    "isolatedDeclarations": true,
    "skipLibCheck": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Package.json Configuration

{
  "name": "my-vue-library",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  },
  "files": ["dist"],
  "peerDependencies": {
    "vue": "^3.0.0"
  },
  "devDependencies": {
    "tsdown": "^0.9.0",
    "typescript": "^5.0.0",
    "unplugin-vue": "^5.0.0",
    "vue": "^3.4.0",
    "vue-tsc": "^2.0.0"
  }
}

Advanced Patterns

With Vite Plugins

Some Vite Vue plugins may work:

import Vue from 'unplugin-vue/rolldown';
import Components from 'unplugin-vue-components/rolldown';

export default defineConfig({
  entry: ['src/index.ts'],
  external: ['vue'],
  plugins: [
    Vue({ isProduction: true }),
    Components({
      dts: 'src/components.d.ts',
    }),
  ],
  dts: { vue: true },
});

JSX Support

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  external: ['vue'],
  plugins: [
    Vue({
      isProduction: true,
      script: {
        propsDestructure: true,
      },
    }),
  ],
  inputOptions: {
    transform: {
      jsx: 'automatic',
      jsxImportSource: 'vue',
    },
  },
  dts: { vue: true },
});

Monorepo Vue Packages

export default defineConfig({
  workspace: 'packages/*',
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  external: ['vue', /^@mycompany\//],
  plugins: [Vue({ isProduction: true })],
  dts: { vue: true },
});

Plugin Options

unplugin-vue Options

Vue({
  isProduction: true,
  script: {
    defineModel: true,
    propsDestructure: true,
  },
  style: {
    trim: true,
  },
  template: {
    compilerOptions: {
      isCustomElement: (tag) => tag.startsWith('custom-'),
    },
  },
});

Tips

  1. Always externalize Vue - Don't bundle Vue itself
  2. Enable vue: true in dts - For proper type generation
  3. Use platform: 'neutral' - Maximum compatibility
  4. Install vue-tsc - Required for type generation
  5. Set isProduction: true - Optimize for production
  6. Add peer dependency - Vue as peer dependency

Troubleshooting

Type Generation Fails

Ensure vue-tsc is installed:

pnpm add -D vue-tsc

Enable in config:

dts: {
  vue: true;
}

Component Types Missing

Check TypeScript config:

{
  "compilerOptions": {
    "jsx": "preserve",
    "moduleResolution": "bundler"
  }
}

Vue Not Externalized

Add to external:

external: ['vue'];

SFC Compilation Errors

Check unplugin-vue version:

pnpm add -D unplugin-vue@latest