UNPKG

vue-plugin-component-id

Version:

_A drop-in replacement for the Vue plugin in Vite that adds a unique `data-component-id` to each component—useful for testing, debugging, or custom tooling._

77 lines (70 loc) • 2.33 kB
import vue from '@vitejs/plugin-vue'; import crypto from 'crypto'; import fs from 'fs'; import path from 'path'; import { merge } from 'lodash-es'; import { NodeTransform, NodeTypes } from '@vue/compiler-core'; import type { Options as VuePluginOptions, Api } from '@vitejs/plugin-vue'; export interface ComponentIdOptions { idMapPath?: string; vueOptions?: VuePluginOptions; } export function vueWithComponentIds(options: ComponentIdOptions = {}): import('vite').Plugin<Api> { const idMapPath = options.idMapPath || './component-id-map.json'; let idMap: Record<string, string> = {}; if (fs.existsSync(idMapPath)) { idMap = JSON.parse(fs.readFileSync(idMapPath, 'utf-8')); } function getOrCreateId(filePath: string): string { if (!idMap[filePath]) { const hash = crypto.createHash('md5').update(filePath).digest('hex'); idMap[filePath] = hash; fs.writeFileSync(idMapPath, JSON.stringify(idMap, null, 2)); } return idMap[filePath]; } function createDataComponentIdTransform(componentId: string): NodeTransform { return (node, _context) => { if (node.type === NodeTypes.ELEMENT && !node.tag.startsWith('svg')) { if (!node.props.some(p => p.type === 6 && p.name === 'data-component-id')) { node.props.push({ type: 6, name: 'data-component-id', value: { type: 2, content: componentId, loc: node.loc, }, loc: node.loc, nameLoc: node.loc, }); } } }; } return vue( merge( {}, options.vueOptions || {}, { template: { compilerOptions: { nodeTransforms: [ (node: any, context: any) => { if (node.type === NodeTypes.ROOT && node.children.length) { const filename = context.filename; const id = getOrCreateId(path.relative(process.cwd(), filename)); for (const child of node.children) { if (child.type === NodeTypes.ELEMENT) { createDataComponentIdTransform(id)(child, context); } } } }, ], }, }, } ) ); }