Skip to content

Commit a4e0e2b

Browse files
committed
feat: generate components.d.ts for global components
1 parent d3edbce commit a4e0e2b

File tree

6 files changed

+95
-4
lines changed

6 files changed

+95
-4
lines changed

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Use components in templates as you would usually do but NO `import` and `compone
4949

5050
Basically, it will automatically turn this
5151

52-
```vue
52+
```html
5353
<template>
5454
<div>
5555
<HelloWorld msg="Hello Vue 3.0 + Vite" />
@@ -65,7 +65,7 @@ export default {
6565

6666
into this
6767

68-
```vue
68+
```html
6969
<template>
7070
<div>
7171
<HelloWorld msg="Hello Vue 3.0 + Vite" />
@@ -84,6 +84,24 @@ export default {
8484
</script>
8585
```
8686

87+
## TypeScript
88+
89+
To have TypeScript support for auto-imported components, there is [a PR](https://github.com/vuejs/vue-next/pull/3399) to Vue 3 extending the interface of global components. Currently, [Volar](https://github.com/johnsoncodehk/volar) has supported this usage already, if you are using Volar, you can change the config as following to get the support.
90+
91+
```ts
92+
// vite.config.js
93+
import ViteComponents from 'vite-plugin-components'
94+
95+
export default {
96+
plugins: [
97+
/* ... */
98+
ViteComponents({
99+
globalComponentsDeclaration: true,
100+
}),
101+
],
102+
}
103+
```
104+
87105
## Vue 2 Support
88106

89107
It just works.

examples/vue3/components.d.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// generated by vite-plugin-components
2+
// read more https://github.com/vuejs/vue-next/pull/3399
3+
4+
declare module 'vue' {
5+
export interface GlobalComponents {
6+
CarbonSvg: typeof import('./src/components/CarbonSvg.svg')
7+
ComponentA: typeof import('./src/components/ComponentA.vue')
8+
ComponentB: typeof import('./src/components/ComponentB.vue')
9+
ComponentD: typeof import('./src/components/ComponentD.vue')
10+
MarkdownA: typeof import('./src/components/MarkdownA.md')
11+
MarkdownB: typeof import('./src/components/MarkdownB.md')
12+
Recursive: typeof import('./src/components/Recursive.vue')
13+
ComponentC: typeof import('./src/components/component-c.vue')
14+
Book: typeof import('./src/components/book/index.vue')
15+
Avatar: typeof import('./src/components/global/avatar.vue')
16+
UiButton: typeof import('./src/components/ui/button.vue')
17+
UiNestedCheckbox: typeof import('./src/components/ui/nested/checkbox.vue')
18+
}
19+
}
20+
21+
export { }

examples/vue3/vite.config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import Markdown from 'vite-plugin-md'
77
import SVG from 'vite-plugin-vue-svg'
88

99
const config: UserConfig = {
10-
alias: {
11-
'/~/': `${path.resolve(__dirname, 'src')}/`,
10+
resolve: {
11+
alias: {
12+
'/~/': `${path.resolve(__dirname, 'src')}/`,
13+
},
1214
},
1315
plugins: [
1416
Vue({
@@ -19,6 +21,7 @@ const config: UserConfig = {
1921
ViteComponents({
2022
extensions: ['vue', 'md', 'svg'],
2123
directoryAsNamespace: true,
24+
globalComponentsDeclaration: true,
2225
globalNamespaces: ['global'],
2326
importPathTransform: path => path.endsWith('.svg') ? `${path}?component` : undefined,
2427
customLoaderMatcher: path => path.endsWith('.md'),

src/declaration.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { resolve, dirname, relative } from 'path'
2+
import fs from 'fs/promises'
3+
import { Context } from './context'
4+
5+
export async function generateDeclaration(ctx: Context, root: string, filepath: string) {
6+
const lines = Object.values(ctx.componentNameMap)
7+
.map(({ path, name, importName }) => {
8+
const related = path.startsWith('/')
9+
? `./${relative(dirname(filepath), resolve(root, path.slice(1)))}`
10+
: path
11+
let entry = `${name}: typeof import('${related}')`
12+
if (importName)
13+
entry += `['${importName}']`
14+
return entry
15+
})
16+
17+
const code = `// generated by vite-plugin-components
18+
// read more https://github.com/vuejs/vue-next/pull/3399
19+
20+
declare module 'vue' {
21+
export interface GlobalComponents {
22+
${lines.join('\n ')}
23+
}
24+
}
25+
26+
export { }
27+
`
28+
await fs.writeFile(filepath, code, 'utf-8')
29+
}

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import { resolve } from 'path'
2+
import fs from 'fs/promises'
13
import type { Plugin } from 'vite'
24
import { Options, Transformer } from './types'
35
import { Context } from './context'
46
import { parseId } from './utils'
57
import { Vue3Transformer } from './transforms/vue3'
68
import { Vue2Transformer } from './transforms/vue2'
9+
import { generateDeclaration } from './declaration'
710

811
function VitePluginComponents(options: Options = {}): Plugin {
912
let ctx: Context
@@ -20,6 +23,12 @@ function VitePluginComponents(options: Options = {}): Plugin {
2023
transformer = ctx.options.transformer === 'vue2'
2124
? Vue2Transformer(ctx)
2225
: Vue3Transformer(ctx)
26+
27+
if (options.globalComponentsDeclaration) {
28+
ctx.searchGlob()
29+
const path = resolve(config.root, typeof options.globalComponentsDeclaration === 'string' ? options.globalComponentsDeclaration : 'components.d.ts')
30+
generateDeclaration(ctx, config.root, path)
31+
}
2332
},
2433
configureServer(server) {
2534
ctx.setServer(server)

src/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ export interface Options {
8989
* @default 'vue3'
9090
*/
9191
transformer?: 'vue3' | 'vue2'
92+
93+
/**
94+
* Generate TypeScript declaration for global components
95+
*
96+
* Accept boolean or a path related to project root
97+
*
98+
* @see https://github.com/vuejs/vue-next/pull/3399
99+
* @see https://github.com/johnsoncodehk/volar#using
100+
* @default false
101+
*/
102+
globalComponentsDeclaration?: boolean | string
92103
}
93104

94105
export type ResolvedOptions = Omit<

0 commit comments

Comments
 (0)