vite-vue-tagger
Version:
112 lines • 3.84 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = ComponentTagger;
// src/index.ts
const compiler_sfc_1 = require("@vue/compiler-sfc");
const compiler_dom_1 = require("@vue/compiler-dom");
const magic_string_1 = __importDefault(require("magic-string"));
const crypto_1 = require("crypto");
const DEFAULT_OPTIONS = {
mode: 'development',
hashSalt: 'vite-vue-tagger',
minify: false,
include: /\.(vue|tsx|jsx)$/,
exclude: /node_modules/,
};
function ComponentTagger(options) {
const opts = { ...DEFAULT_OPTIONS, ...options };
return {
name: 'vite-vue-tagger',
enforce: 'pre',
async transform(code, id) {
if (opts.mode === 'production')
return;
if (opts.exclude.test(id))
return;
if (!opts.include.test(id))
return;
try {
const s = await processCode(code, id, opts);
return {
code: opts.minify ? minifyAttributes(s.toString()) : s.toString(),
map: s.generateMap({ source: id }),
};
}
catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
console.error(`[vue-tagger] Error processing ${id}:\n${errorMessage}`);
}
},
};
}
async function processCode(code, id, opts) {
const s = new magic_string_1.default(code);
const { descriptor } = (0, compiler_sfc_1.parse)(code, { sourceMap: true });
if (!descriptor.template)
return s;
const template = descriptor.template;
const ast = (0, compiler_dom_1.parse)(template.content, {
parseMode: 'html',
isNativeTag: () => true, // 假设全部为原生标签
});
ast.children.forEach((node) => {
if (node.type === 1) {
// ElementNode
processElement(node, template.loc.start.line, s, id, opts);
}
});
return s;
}
function processElement(node, lineOffset, s, id, opts) {
const source = {
line: node.loc.start.line + lineOffset,
column: node.loc.start.column,
tag: node.tag,
};
const attrs = [
`data-lov-id="${id}:${source.line}:${source.column}"`,
`data-lov-name="${node.tag}"`,
`data-component-path="${id}"`,
`data-component-file="${id.split('/').pop()}"`,
`data-component-line="${source.line}"`,
`data-component-name="${getComponentName(id)}"`,
`data-component-content="${encodeContent(node, opts)}"`,
].join(' ');
s.appendLeft(node.loc.start.offset + 1, // 在标签名后插入
` ${attrs} `);
}
function getComponentName(filePath) {
return (filePath
.split('/')
.pop()
?.replace(/\.(vue|tsx|jsx)$/, '')
?.replace(/([a-z])([A-Z])/g, '$1 $2') // 驼峰转空格
?.replace(/[-_]/g, ' ') || 'Anonymous');
}
function encodeContent(node, opts) {
const props = extractProps(node);
const contentHash = (0, crypto_1.createHash)('sha256')
.update(opts.hashSalt + node.loc.source)
.digest('hex')
.substr(0, 8);
return encodeURIComponent(JSON.stringify({
props,
hash: contentHash,
}));
}
function extractProps(node) {
return node.props.reduce((acc, prop) => {
if (prop.type === 6) {
// 属性节点
acc[prop.name] = prop.value?.content || '';
}
return acc;
}, {});
}
function minifyAttributes(code) {
return code.replace(/\s+data-[\w-]+="[^"]*"/g, (match) => match.replace(/\s+/g, ' ').trim());
}
//# sourceMappingURL=index.js.map