Skip to content

Commit b4e9924

Browse files
committed
feat(vscode): add Vuetify component snippets generation
Add a script to generate Vuetify component snippets for various file types (Vue, HTML, JSX, TSX) from the official web-types data. The script fetches the latest component definitions and creates useful snippets with common attributes. Update package.json to include the new script command and register the snippets for multiple languages.
1 parent 3dbbccf commit b4e9924

File tree

3 files changed

+1864
-4
lines changed

3 files changed

+1864
-4
lines changed

packages/vscode/package.json

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,37 @@
3737
"title": "Create Vuetify Project",
3838
"category": "Vuetify"
3939
},
40+
{
41+
"command": "vuetify.openApiDocs",
42+
"title": "Open Vuetify API Documentation",
43+
"category": "Vuetify"
44+
},
4045
{
4146
"command": "vuetify.openComponentDocs",
4247
"title": "Open Vuetify Component Documentation",
4348
"category": "Vuetify"
49+
}
50+
],
51+
"snippets": [
52+
{
53+
"language": "vue",
54+
"path": "./snippets/vuetify.json"
4455
},
4556
{
46-
"command": "vuetify.openApiDocs",
47-
"title": "Open Vuetify API Documentation",
48-
"category": "Vuetify"
57+
"language": "vue-html",
58+
"path": "./snippets/vuetify.json"
59+
},
60+
{
61+
"language": "html",
62+
"path": "./snippets/vuetify.json"
63+
},
64+
{
65+
"language": "javascriptreact",
66+
"path": "./snippets/vuetify.json"
67+
},
68+
{
69+
"language": "typescriptreact",
70+
"path": "./snippets/vuetify.json"
4971
}
5072
],
5173
"menus": {
@@ -78,7 +100,8 @@
78100
"dev": "tsdown --watch ./src --env.NODE_ENV development",
79101
"typecheck": "tsc --noEmit",
80102
"vscode:prepublish": "pnpm run build",
81-
"generate:map": "node scripts/generate-map.mjs"
103+
"generate:map": "node scripts/generate-map.mjs",
104+
"generate:snippets": "node scripts/generate-snippets.mjs"
82105
},
83106
"devDependencies": {
84107
"@types/node": "catalog:",
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { promises as fs } from 'node:fs'
2+
import { dirname, resolve } from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
5+
const __dirname = dirname(fileURLToPath(import.meta.url))
6+
const ROOT = resolve(__dirname, '..')
7+
const OUTPUT_FILE = resolve(ROOT, 'snippets/vuetify.json')
8+
9+
async function fetchWebTypes () {
10+
const urls = [
11+
'https://unpkg.com/vuetify/dist/json/web-types.json',
12+
'https://cdn.jsdelivr.net/npm/vuetify/dist/json/web-types.json',
13+
]
14+
for (const url of urls) {
15+
try {
16+
const res = await fetch(url)
17+
if (res.ok) {
18+
return await res.json()
19+
}
20+
} catch (error) {
21+
console.error(`Failed to fetch ${url}`, error)
22+
}
23+
}
24+
throw new Error('Failed to fetch web-types')
25+
}
26+
27+
function kebabCase (str) {
28+
return str
29+
.replace(/^V([A-Z])/, 'v-$1')
30+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
31+
.toLowerCase()
32+
}
33+
34+
function getAttributes (component) {
35+
const attributes = []
36+
const props = component.attributes || []
37+
38+
// Common useful props
39+
// We don't want to overload the snippet, so we pick high-value props
40+
const common = [
41+
'text', // Cards, Alerts, etc.
42+
'title', // Cards, Alerts
43+
'label', // Inputs
44+
'items', // Selects
45+
'src', // Images
46+
]
47+
48+
let index = 1
49+
50+
for (const name of common) {
51+
if (props.some(p => p.name === name)) {
52+
attributes.push(`${name}="\${${index}}"`)
53+
index++
54+
}
55+
}
56+
57+
for (const prop of props) {
58+
if (prop.required && !common.includes(prop.name) && prop.name !== 'modelValue') {
59+
attributes.push(`${prop.name}="\${${index}}"`)
60+
index++
61+
}
62+
}
63+
64+
return attributes.length > 0 ? ' ' + attributes.join(' ') : ''
65+
}
66+
67+
async function main () {
68+
console.log('Fetching web-types...')
69+
const webTypes = await fetchWebTypes()
70+
if (!webTypes || !webTypes.contributions || !webTypes.contributions.html || !webTypes.contributions.html.tags) {
71+
console.error('Invalid web-types structure', webTypes)
72+
return
73+
}
74+
const components = webTypes.contributions.html.tags
75+
console.log(`Found ${components.length} components in web-types`)
76+
const snippets = {}
77+
78+
for (const component of components) {
79+
const name = component.name
80+
81+
if (!name.startsWith('V')) {
82+
continue
83+
}
84+
85+
const kebab = kebabCase(name)
86+
87+
const pascal = name
88+
89+
const attrs = getAttributes(component)
90+
91+
snippets[pascal] = {
92+
prefix: kebab,
93+
body: [
94+
`<${kebab}${attrs}>`,
95+
'\t$0',
96+
`</${kebab}>`,
97+
],
98+
description: component.description || `Vuetify ${name} Component`,
99+
}
100+
}
101+
102+
await fs.mkdir(dirname(OUTPUT_FILE), { recursive: true })
103+
await fs.writeFile(OUTPUT_FILE, JSON.stringify(snippets, null, 2))
104+
console.log(`Generated snippets for ${Object.keys(snippets).length} components at ${OUTPUT_FILE}`)
105+
}
106+
107+
main().catch(console.error)

0 commit comments

Comments
 (0)