vue3-vite-component-tagger-plugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/index.cjs +97 -0
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +66 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Vue 3 Vite Component Tagger
|
|
2
|
+
|
|
3
|
+
A Vite plugin that automatically adds `data-soeasy-id` and `data-soeasy-name` attributes to your Vue 3 components. This is useful for identifying components in the DOM, for example for testing or analytics.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vue3-vite-component-tagger
|
|
9
|
+
# or
|
|
10
|
+
yarn add vue3-vite-component-tagger
|
|
11
|
+
# or
|
|
12
|
+
pnpm add vue3-vite-component-tagger
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Add the plugin to your `vite.config.ts` file:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineConfig } from "vite";
|
|
21
|
+
import vue from "@vitejs/plugin-vue";
|
|
22
|
+
import componentTagger from "vue3-vite-component-tagger";
|
|
23
|
+
|
|
24
|
+
// https://vitejs.dev/config/
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
plugins: [vue(), componentTagger()],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The plugin will automatically add `data-soeasy-id` and `data-soeasy-name` to all your Vue components in development mode.
|
|
31
|
+
|
|
32
|
+
### Example
|
|
33
|
+
|
|
34
|
+
Given a Vue component:
|
|
35
|
+
|
|
36
|
+
```vue
|
|
37
|
+
<template>
|
|
38
|
+
<div>
|
|
39
|
+
<header>
|
|
40
|
+
<h1>Welcome</h1>
|
|
41
|
+
</header>
|
|
42
|
+
<button @click="handleClick">Click me</button>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The plugin will transform it to:
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<div data-soeasy-id="src/App.vue:2:3" data-soeasy-name="div">
|
|
52
|
+
<header data-soeasy-id="src/App.vue:3:5" data-soeasy-name="header">
|
|
53
|
+
<h1 data-soeasy-id="src/App.vue:4:7" data-soeasy-name="h1">Welcome</h1>
|
|
54
|
+
</header>
|
|
55
|
+
<button data-soeasy-id="src/App.vue:6:5" data-soeasy-name="button" @click="handleClick">Click me</button>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The `data-soeasy-id` will be a unique identifier for each component instance, in the format `path/to/file.vue:line:column`.
|
|
61
|
+
|
|
62
|
+
The `data-soeasy-name` will be the tag name of the element.
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- Automatically tags all Vue template elements in `.vue` files
|
|
67
|
+
- Adds unique `data-soeasy-id` based on file path and location
|
|
68
|
+
- Adds `data-soeasy-name` with the element's tag name
|
|
69
|
+
- Only runs in development mode (`serve`)
|
|
70
|
+
- Skips elements that already have `data-soeasy-id`
|
|
71
|
+
- Ignores `node_modules` files
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => dyadTagger
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_compiler_sfc = require("@vue/compiler-sfc");
|
|
37
|
+
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
38
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
39
|
+
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".vue"]);
|
|
40
|
+
function dyadTagger(options = {}) {
|
|
41
|
+
const { idAttribute = "data-soeasy-id", nameAttribute = "data-soeasy-name" } = options;
|
|
42
|
+
return {
|
|
43
|
+
name: "vite-plugin-vue-dyad-tagger",
|
|
44
|
+
apply: "serve",
|
|
45
|
+
enforce: "pre",
|
|
46
|
+
async transform(code, id) {
|
|
47
|
+
try {
|
|
48
|
+
if (!VALID_EXTENSIONS.has(import_node_path.default.extname(id)) || id.includes("node_modules"))
|
|
49
|
+
return null;
|
|
50
|
+
const { descriptor } = (0, import_compiler_sfc.parse)(code, { filename: id });
|
|
51
|
+
if (!descriptor.template) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const templateContent = descriptor.template.content;
|
|
55
|
+
const templateStart = descriptor.template.loc.start.offset;
|
|
56
|
+
const tagRegex = /<([a-zA-Z][a-zA-Z0-9-]*(?:\.[a-zA-Z][a-zA-Z0-9-]*)*)(\s+[^>]*?)?(\s*\/)?>/g;
|
|
57
|
+
const ms = new import_magic_string.default(code);
|
|
58
|
+
let match;
|
|
59
|
+
let processedTags = 0;
|
|
60
|
+
while ((match = tagRegex.exec(templateContent)) !== null) {
|
|
61
|
+
const [, tagName, attributes = ""] = match;
|
|
62
|
+
const tagStartInTemplate = match.index;
|
|
63
|
+
if (attributes.includes(idAttribute)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (tagName === "template") {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const tagPositionInFile = templateStart + tagStartInTemplate;
|
|
70
|
+
const lines = code.substring(0, tagPositionInFile).split("\n");
|
|
71
|
+
const line = lines.length;
|
|
72
|
+
const column = lines[lines.length - 1].length + 1;
|
|
73
|
+
const elementId = `${id}:${line}:${column}`;
|
|
74
|
+
let insertPosition = templateStart + tagStartInTemplate + 1 + tagName.length;
|
|
75
|
+
const charAfterTagName = code[insertPosition];
|
|
76
|
+
const needsSpace = charAfterTagName !== " " && charAfterTagName !== "\n" && charAfterTagName !== " ";
|
|
77
|
+
const attributesToAdd = needsSpace ? ` ${idAttribute}="${elementId}" ${nameAttribute}="${tagName}"` : ` ${idAttribute}="${elementId}" ${nameAttribute}="${tagName}"`;
|
|
78
|
+
ms.appendLeft(insertPosition, attributesToAdd);
|
|
79
|
+
processedTags++;
|
|
80
|
+
}
|
|
81
|
+
if (processedTags === 0) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
code: ms.toString(),
|
|
86
|
+
map: ms.generateMap({ hires: true })
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn(
|
|
90
|
+
`[vue-dyad-tagger] Warning: Failed to transform ${id}:`,
|
|
91
|
+
error
|
|
92
|
+
);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { parse } from "@vue/compiler-sfc";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".vue"]);
|
|
6
|
+
function dyadTagger(options = {}) {
|
|
7
|
+
const { idAttribute = "data-soeasy-id", nameAttribute = "data-soeasy-name" } = options;
|
|
8
|
+
return {
|
|
9
|
+
name: "vite-plugin-vue-dyad-tagger",
|
|
10
|
+
apply: "serve",
|
|
11
|
+
enforce: "pre",
|
|
12
|
+
async transform(code, id) {
|
|
13
|
+
try {
|
|
14
|
+
if (!VALID_EXTENSIONS.has(path.extname(id)) || id.includes("node_modules"))
|
|
15
|
+
return null;
|
|
16
|
+
const { descriptor } = parse(code, { filename: id });
|
|
17
|
+
if (!descriptor.template) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const templateContent = descriptor.template.content;
|
|
21
|
+
const templateStart = descriptor.template.loc.start.offset;
|
|
22
|
+
const tagRegex = /<([a-zA-Z][a-zA-Z0-9-]*(?:\.[a-zA-Z][a-zA-Z0-9-]*)*)(\s+[^>]*?)?(\s*\/)?>/g;
|
|
23
|
+
const ms = new MagicString(code);
|
|
24
|
+
let match;
|
|
25
|
+
let processedTags = 0;
|
|
26
|
+
while ((match = tagRegex.exec(templateContent)) !== null) {
|
|
27
|
+
const [, tagName, attributes = ""] = match;
|
|
28
|
+
const tagStartInTemplate = match.index;
|
|
29
|
+
if (attributes.includes(idAttribute)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (tagName === "template") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const tagPositionInFile = templateStart + tagStartInTemplate;
|
|
36
|
+
const lines = code.substring(0, tagPositionInFile).split("\n");
|
|
37
|
+
const line = lines.length;
|
|
38
|
+
const column = lines[lines.length - 1].length + 1;
|
|
39
|
+
const elementId = `${id}:${line}:${column}`;
|
|
40
|
+
let insertPosition = templateStart + tagStartInTemplate + 1 + tagName.length;
|
|
41
|
+
const charAfterTagName = code[insertPosition];
|
|
42
|
+
const needsSpace = charAfterTagName !== " " && charAfterTagName !== "\n" && charAfterTagName !== " ";
|
|
43
|
+
const attributesToAdd = needsSpace ? ` ${idAttribute}="${elementId}" ${nameAttribute}="${tagName}"` : ` ${idAttribute}="${elementId}" ${nameAttribute}="${tagName}"`;
|
|
44
|
+
ms.appendLeft(insertPosition, attributesToAdd);
|
|
45
|
+
processedTags++;
|
|
46
|
+
}
|
|
47
|
+
if (processedTags === 0) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
code: ms.toString(),
|
|
52
|
+
map: ms.generateMap({ hires: true })
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.warn(
|
|
56
|
+
`[vue-dyad-tagger] Warning: Failed to transform ${id}:`,
|
|
57
|
+
error
|
|
58
|
+
);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
dyadTagger as default
|
|
66
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vue3-vite-component-tagger-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Vite plugin that automatically adds data attributes to your Vue 3 components.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
14
|
+
"dev": "npm run build -- --watch",
|
|
15
|
+
"lint": "eslint . --max-warnings 0",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"vite",
|
|
20
|
+
"vite-plugin",
|
|
21
|
+
"vue",
|
|
22
|
+
"vue3"
|
|
23
|
+
],
|
|
24
|
+
"author": "Yaob1990",
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@vue/compiler-sfc": "^3.4.0",
|
|
31
|
+
"magic-string": "^0.30.5"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.8.9",
|
|
35
|
+
"eslint": "^8.52.0",
|
|
36
|
+
"tsup": "^8.0.2",
|
|
37
|
+
"typescript": "^5.2.2",
|
|
38
|
+
"vite": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
}
|
|
43
|
+
}
|