Skip to content

Commit fe0f4b8

Browse files
committed
feat: on-demand importing!
1 parent 05f57f6 commit fe0f4b8

16 files changed

+256
-134
lines changed

README.md

+38-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<h1 align='center'>vite-plugin-components</h1>
22

3-
<p align='center'>Components auto importing for Vite</p>
3+
<p align='center'>Components auto importing for Vite (on-demand!)</p>
44

55
<p align='center'>
66
<a href='https://www.npmjs.com/package/vite-plugin-components'>
@@ -22,36 +22,62 @@ Add it to `vite.config.js`
2222

2323
```ts
2424
// vite.config.js
25-
import { VitePluginComponents } from 'vite-plugin-components'
25+
import ViteComponents from 'vite-plugin-components'
2626

2727
export default {
2828
plugins: [
29-
VitePluginComponents()
29+
ViteComponents()
3030
]
3131
}
3232
```
3333

34-
Import and install `vite-plugin-components` in your `main.js`
34+
That's all.
3535

36-
```ts
37-
import { createApp } from 'vue'
38-
import App from './App.vue'
36+
Use components in templates as you would usually do but NO `import` and `component registration` required anymore! It will import components on demand, code spliting is also possible.
37+
38+
Basically, it automatically turns this
3939

40-
import components from 'vite-plugin-components' // <-- This
40+
```vue
41+
<template>
42+
<div>
43+
<HelloWorld msg="Hello Vue 3.0 + Vite" />
44+
</div>
45+
</template>
4146
42-
const app = createApp(App)
47+
<script>
48+
export default {
49+
name: 'App'
50+
}
51+
</script>
52+
```
4353

44-
app.use(components) // <-- and this
54+
into this
4555

46-
app.mount('#app')
56+
```vue
57+
<template>
58+
<div>
59+
<HelloWorld msg="Hello Vue 3.0 + Vite" />
60+
</div>
61+
</template>
62+
63+
<script>
64+
import HelloWorld from './src/components/HelloWorld.vue'
65+
66+
export default {
67+
name: 'App',
68+
components: {
69+
HelloWorld
70+
}
71+
}
72+
</script>
4773
```
4874

4975
## Configuration
5076

5177
The following show the default values of the configuration
5278

5379
```ts
54-
VitePluginComponents({
80+
ViteComponents({
5581
// Relative path to the directory to search for components.
5682
dirs: ['src/components'],
5783
// Valid file extensions for components.

src/build.ts

-20
This file was deleted.

src/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const MODULE_NAME = 'vite-plugin-components'
2+
export const RESOLVER_EXT = '.vite-plugin-components'

src/generated.ts

-6
This file was deleted.

src/generator.ts

-44
This file was deleted.

src/generator/resolver.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Context } from '../types'
2+
import { RESOLVER_EXT } from '../constants'
3+
4+
export function isResolverPath(reqPath: string) {
5+
return reqPath.endsWith(RESOLVER_EXT)
6+
}
7+
8+
export function generateResolver(ctx: Context, reqPath: string) {
9+
const sfcPath = reqPath.slice(0, -RESOLVER_EXT.length)
10+
const names = ctx.importMap[sfcPath] || []
11+
const components = ctx.components.filter(i => names.includes(i[0]) && i[1] !== sfcPath)
12+
13+
return `
14+
${components.map(([name, path]) => `import ${name} from "${path}"`).join('\n')}
15+
16+
export default (components) => {
17+
return Object.assign({}, { ${components.map(i => i[0]).join(', ')} }, components)
18+
}
19+
`
20+
}

src/glob.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from 'path'
2+
import fg from 'fast-glob'
3+
import { Context, ComponentsInfo } from './types'
4+
5+
function toArray<T>(arr: T | T[]): T[] {
6+
if (Array.isArray(arr))
7+
return arr
8+
return [arr]
9+
}
10+
11+
export async function searchComponents(ctx: Context, force = false) {
12+
if (force || !ctx._searchingPromise) {
13+
ctx._searchingPromise = (async() => {
14+
const { dirs, deep, extensions } = ctx.options
15+
const exts = toArray(extensions)
16+
17+
if (!exts.length)
18+
throw new Error('[vite-plugin-components] extensions are required to search for components')
19+
20+
const extsGlob = exts.length === 1 ? exts[0] : `{${exts.join(',')}}`
21+
const globs = toArray(dirs).map(i =>
22+
deep
23+
? `${i}/**/*.${extsGlob}`
24+
: `${i}/*.${extsGlob}`,
25+
)
26+
27+
const files = await fg(globs, {
28+
ignore: [
29+
'node_modules',
30+
],
31+
onlyFiles: true,
32+
})
33+
34+
if (!files.length)
35+
console.warn('[vite-plugin-components] no components found')
36+
37+
const components: ComponentsInfo[] = files.map(f => [path.parse(f).name, `/${f}`])
38+
39+
ctx.components = components
40+
ctx._searchingPromise = undefined
41+
})()
42+
}
43+
44+
return await ctx._searchingPromise
45+
}

src/index.ts

+34-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
1-
import { components } from './generated'
2-
export { Options } from './options'
3-
export { VitePluginComponents } from './plugin'
1+
import type { Plugin } from 'vite'
2+
import { createRollupPlugin } from './plugins/build'
3+
import { createServerPlugin } from './plugins/server'
4+
import { Options, Context } from './types'
5+
import { VueScriptTransformer } from './transforms/vueScript'
6+
import { VueTemplateTransformer } from './transforms/vueTemplate'
47

5-
export default components
8+
const defaultOptions: Options = {
9+
dirs: 'src/components',
10+
extensions: 'vue',
11+
deep: true,
12+
}
13+
14+
export type { Options }
15+
export function VitePluginComponents(options: Partial<Options> = {}): Plugin {
16+
const resolvedOptions: Options = Object.assign({}, options, defaultOptions)
17+
const ctx: Context = {
18+
options: resolvedOptions,
19+
importMap: {},
20+
components: [],
21+
}
22+
23+
return {
24+
configureServer: createServerPlugin(ctx),
25+
rollupInputOptions: {
26+
plugins: [
27+
createRollupPlugin(ctx),
28+
],
29+
},
30+
transforms: [
31+
VueScriptTransformer(ctx),
32+
VueTemplateTransformer(ctx),
33+
],
34+
}
35+
}

src/plugin.ts

-22
This file was deleted.

src/plugins/build.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Plugin } from 'rollup'
2+
import { isResolverPath, generateResolver } from '../generator/resolver'
3+
import { Context } from '../types'
4+
import { searchComponents } from '../glob'
5+
6+
export function createRollupPlugin(ctx: Context): Plugin {
7+
return {
8+
name: 'vite-plugin-components',
9+
resolveId(source) {
10+
if (isResolverPath(source))
11+
return source
12+
return null
13+
},
14+
async load(id) {
15+
if (isResolverPath(id)) {
16+
await searchComponents(ctx)
17+
return await generateResolver(ctx, id)
18+
}
19+
return null
20+
},
21+
}
22+
}

src/plugins/server.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ServerPlugin } from 'vite'
2+
import { Context } from '../types'
3+
import { isResolverPath, generateResolver } from '../generator/resolver'
4+
import { searchComponents } from '../glob'
5+
6+
export function createServerPlugin(context: Context): ServerPlugin {
7+
return ({ app }) => {
8+
app.use(async(ctx, next) => {
9+
if (!isResolverPath(ctx.path))
10+
return next()
11+
12+
try {
13+
await searchComponents(context, true)
14+
ctx.body = await generateResolver(context, ctx.path)
15+
ctx.type = 'js'
16+
ctx.status = 200
17+
}
18+
catch (e) {
19+
ctx.body = {
20+
error: e.toString(),
21+
}
22+
ctx.type = 'js'
23+
ctx.status = 500
24+
}
25+
})
26+
}
27+
}

src/server.ts

-26
This file was deleted.

src/state.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ComponentsImportMap } from './types'
2+
3+
export const importMap: ComponentsImportMap = {}

0 commit comments

Comments
 (0)