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.
6.3 KiB
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.tsfiles - 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
- Always externalize Vue - Don't bundle Vue itself
- Enable vue: true in dts - For proper type generation
- Use platform: 'neutral' - Maximum compatibility
- Install vue-tsc - Required for type generation
- Set isProduction: true - Optimize for production
- 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
Related
- Plugins - Plugin system
- Dependencies - External packages
- DTS - Type declarations
- React Recipe - React component libraries