Skip to content

Commit 7a1fc4c

Browse files
authored
feat: add a feature option to support custom component id generator (#461)
1 parent 23ea2f9 commit 7a1fc4c

File tree

10 files changed

+138
-2
lines changed

10 files changed

+138
-2
lines changed

packages/plugin-vue/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ export interface Options {
5858
* - **default:** `false`
5959
*/
6060
prodHydrationMismatchDetails?: boolean
61+
/**
62+
* Customize the component ID generation strategy.
63+
* - `'filepath'`: hash the file path (relative to the project root)
64+
* - `'filepath-source'`: hash the file path and the source code
65+
* - `function`: custom function that takes the file path, source code,
66+
* whether in production mode, and the default hash function as arguments
67+
* - **default:** `'filepath'` in development, `'filepath-source'` in production
68+
*/
69+
componentIdGenerator?:
70+
| 'filepath'
71+
| 'filepath-source'
72+
| ((
73+
filepath: string,
74+
source: string,
75+
isProduction: boolean | undefined,
76+
getHash: (text: string) => string,
77+
) => string)
6178
}
6279

6380
// `script`, `template` and `style` are lower-level compiler options

packages/plugin-vue/src/index.ts

+17
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,23 @@ export interface Options {
138138
* - **default:** `false`
139139
*/
140140
prodHydrationMismatchDetails?: boolean
141+
/**
142+
* Customize the component ID generation strategy.
143+
* - `'filepath'`: hash the file path (relative to the project root)
144+
* - `'filepath-source'`: hash the file path and the source code
145+
* - `function`: custom function that takes the file path, source code,
146+
* whether in production mode, and the default hash function as arguments
147+
* - **default:** `'filepath'` in development, `'filepath-source'` in production
148+
*/
149+
componentIdGenerator?:
150+
| 'filepath'
151+
| 'filepath-source'
152+
| ((
153+
filepath: string,
154+
source: string,
155+
isProduction: boolean | undefined,
156+
getHash: (text: string) => string,
157+
) => string)
141158
}
142159

143160
/**

packages/plugin-vue/src/utils/descriptorCache.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ const prevCache = new Map<string, SFCDescriptor | undefined>()
2222
export function createDescriptor(
2323
filename: string,
2424
source: string,
25-
{ root, isProduction, sourceMap, compiler, template }: ResolvedOptions,
25+
{
26+
root,
27+
isProduction,
28+
sourceMap,
29+
compiler,
30+
template,
31+
features,
32+
}: ResolvedOptions,
2633
hmr = false,
2734
): SFCParseResult {
2835
const { descriptor, errors } = compiler.parse(source, {
@@ -34,7 +41,23 @@ export function createDescriptor(
3441
// ensure the path is normalized in a way that is consistent inside
3542
// project (relative to root) and on different systems.
3643
const normalizedPath = normalizePath(path.relative(root, filename))
37-
descriptor.id = getHash(normalizedPath + (isProduction ? source : ''))
44+
45+
const componentIdGenerator = features?.componentIdGenerator
46+
if (componentIdGenerator === 'filepath') {
47+
descriptor.id = getHash(normalizedPath)
48+
} else if (componentIdGenerator === 'filepath-source') {
49+
descriptor.id = getHash(normalizedPath + source)
50+
} else if (typeof componentIdGenerator === 'function') {
51+
descriptor.id = componentIdGenerator(
52+
normalizedPath,
53+
source,
54+
isProduction,
55+
getHash,
56+
)
57+
} else {
58+
descriptor.id = getHash(normalizedPath + (isProduction ? source : ''))
59+
}
60+
3861
;(hmr ? hmrCache : cache).set(filename, descriptor)
3962
return { descriptor, errors }
4063
}

playground/vue-custom-id/Main.vue

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script setup>
2+
import Foo from './components/Foo.vue'
3+
</script>
4+
5+
<template>
6+
<Foo />
7+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, test } from 'vitest'
2+
import { page } from '~utils'
3+
4+
test('should render', async () => {
5+
expect(await page.innerHTML('div')).toMatch(
6+
'<h1 data-v-components-foo="">Foo</h1>',
7+
)
8+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<template>
2+
<h1>Foo</h1>
3+
</template>
4+
5+
<style scoped>
6+
h1 {
7+
color: red;
8+
}
9+
</style>

playground/vue-custom-id/index.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div id="app"></div>
2+
<script type="module">
3+
import { createApp, defineCustomElement } from 'vue'
4+
import Main from './Main.vue'
5+
6+
createApp(Main).mount('#app')
7+
</script>

playground/vue-custom-id/package.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@vitejs/test-vue-custom-id",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"debug": "node --inspect-brk vite",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"vue": "catalog:"
13+
},
14+
"devDependencies": {
15+
"@vitejs/plugin-vue": "workspace:*"
16+
}
17+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineConfig } from 'vite'
2+
import vuePlugin from '@vitejs/plugin-vue'
3+
4+
export default defineConfig({
5+
plugins: [
6+
vuePlugin({
7+
features: {
8+
componentIdGenerator: (filename) => {
9+
return filename
10+
.replace(/\.\w+$/, '')
11+
.replace(/[^a-z0-9]/gi, '-')
12+
.toLowerCase()
13+
},
14+
},
15+
}),
16+
],
17+
build: {
18+
// to make tests faster
19+
minify: false,
20+
},
21+
})

pnpm-lock.yaml

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)