jsx-loc-plugin 0.2.1 → 0.2.3
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/dist/chunk-QPTFKQPZ.js +170 -0
- package/dist/loader.js +4 -165
- package/dist/vite.cjs +241 -0
- package/dist/vite.d.cts +25 -0
- package/dist/vite.d.ts +25 -0
- package/dist/vite.js +43 -0
- package/package.json +10 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/transform.ts
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
import { relative, resolve } from "path";
|
|
5
|
+
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx"]);
|
|
6
|
+
var DEFAULT_PARSER_OPTIONS = {
|
|
7
|
+
sourceType: "module",
|
|
8
|
+
plugins: [
|
|
9
|
+
"jsx",
|
|
10
|
+
"typescript",
|
|
11
|
+
"decorators-legacy",
|
|
12
|
+
"classProperties",
|
|
13
|
+
"classPrivateProperties",
|
|
14
|
+
"classPrivateMethods",
|
|
15
|
+
"exportDefaultFrom",
|
|
16
|
+
"exportNamespaceFrom",
|
|
17
|
+
"asyncGenerators",
|
|
18
|
+
"functionBind",
|
|
19
|
+
"functionSent",
|
|
20
|
+
"dynamicImport",
|
|
21
|
+
"numericSeparator",
|
|
22
|
+
"optionalChaining",
|
|
23
|
+
"importMeta",
|
|
24
|
+
"bigInt",
|
|
25
|
+
"optionalCatchBinding",
|
|
26
|
+
"throwExpressions",
|
|
27
|
+
"nullishCoalescingOperator",
|
|
28
|
+
"topLevelAwait"
|
|
29
|
+
],
|
|
30
|
+
errorRecovery: true
|
|
31
|
+
};
|
|
32
|
+
var SKIP_ELEMENTS = /* @__PURE__ */ new Set([
|
|
33
|
+
"Fragment",
|
|
34
|
+
"React.Fragment",
|
|
35
|
+
"Suspense",
|
|
36
|
+
"React.Suspense",
|
|
37
|
+
"StrictMode",
|
|
38
|
+
"React.StrictMode",
|
|
39
|
+
"Profiler",
|
|
40
|
+
"React.Profiler"
|
|
41
|
+
]);
|
|
42
|
+
function shouldProcessFile(filePath) {
|
|
43
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
44
|
+
return VALID_EXTENSIONS.has(ext);
|
|
45
|
+
}
|
|
46
|
+
function getElementName(node) {
|
|
47
|
+
if (!node || !node.name) return null;
|
|
48
|
+
const name = node.name;
|
|
49
|
+
if (name.type === "JSXIdentifier") {
|
|
50
|
+
return name.name;
|
|
51
|
+
}
|
|
52
|
+
if (name.type === "JSXMemberExpression") {
|
|
53
|
+
const parts = [];
|
|
54
|
+
let current = name;
|
|
55
|
+
while (current) {
|
|
56
|
+
if (current.type === "JSXMemberExpression") {
|
|
57
|
+
parts.unshift(current.property.name);
|
|
58
|
+
current = current.object;
|
|
59
|
+
} else if (current.type === "JSXIdentifier") {
|
|
60
|
+
parts.unshift(current.name);
|
|
61
|
+
break;
|
|
62
|
+
} else {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return parts.join(".");
|
|
67
|
+
}
|
|
68
|
+
if (name.type === "JSXNamespacedName") {
|
|
69
|
+
return `${name.namespace.name}:${name.name.name}`;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function shouldSkipElement(elementName) {
|
|
74
|
+
if (!elementName) return true;
|
|
75
|
+
return SKIP_ELEMENTS.has(elementName);
|
|
76
|
+
}
|
|
77
|
+
function hasDataLocAttribute(node) {
|
|
78
|
+
if (!node.attributes) return false;
|
|
79
|
+
return node.attributes.some(
|
|
80
|
+
(attr) => attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === "data-loc"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
function findInsertionPoint(node, source) {
|
|
84
|
+
let insertPos = node.name.end;
|
|
85
|
+
if (node.typeParameters) {
|
|
86
|
+
insertPos = node.typeParameters.end;
|
|
87
|
+
}
|
|
88
|
+
if (node.attributes && node.attributes.length > 0) {
|
|
89
|
+
const firstAttr = node.attributes[0];
|
|
90
|
+
const nameEnd = node.typeParameters ? node.typeParameters.end : node.name.end;
|
|
91
|
+
let pos = nameEnd;
|
|
92
|
+
while (pos < firstAttr.start && /\s/.test(source[pos])) {
|
|
93
|
+
pos++;
|
|
94
|
+
}
|
|
95
|
+
insertPos = pos;
|
|
96
|
+
}
|
|
97
|
+
return insertPos;
|
|
98
|
+
}
|
|
99
|
+
function normalizeFilePath(filePath) {
|
|
100
|
+
const abs = resolve(filePath).replace(/\\/g, "/");
|
|
101
|
+
const cwd = resolve(process.cwd()).replace(/\\/g, "/");
|
|
102
|
+
if (abs === cwd || abs.startsWith(cwd + "/")) {
|
|
103
|
+
return relative(cwd, abs).replace(/\\/g, "/");
|
|
104
|
+
}
|
|
105
|
+
return abs;
|
|
106
|
+
}
|
|
107
|
+
function transformJsxCode(source, filePath, options = {}) {
|
|
108
|
+
const parserOptions = {
|
|
109
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
110
|
+
...options.parserOptions
|
|
111
|
+
};
|
|
112
|
+
let ast;
|
|
113
|
+
try {
|
|
114
|
+
ast = parse(source, parserOptions);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`[jsx-loc] Failed to parse ${filePath}:`, error);
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const magicString = new MagicString(source);
|
|
120
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
121
|
+
let hasChanges = false;
|
|
122
|
+
function walk(node) {
|
|
123
|
+
if (!node || typeof node !== "object") return;
|
|
124
|
+
if (node.type === "JSXOpeningElement") {
|
|
125
|
+
const elementName = getElementName(node);
|
|
126
|
+
if (!shouldSkipElement(elementName) && !hasDataLocAttribute(node)) {
|
|
127
|
+
const loc = node.loc;
|
|
128
|
+
if (loc && loc.start) {
|
|
129
|
+
const line = loc.start.line;
|
|
130
|
+
const column = loc.start.column;
|
|
131
|
+
const locValue = `${normalizedPath}:${line}:${column}`;
|
|
132
|
+
const insertPos = findInsertionPoint(node, source);
|
|
133
|
+
magicString.appendLeft(insertPos, ` data-loc="${locValue}"`);
|
|
134
|
+
hasChanges = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const key of Object.keys(node)) {
|
|
139
|
+
const child = node[key];
|
|
140
|
+
if (Array.isArray(child)) {
|
|
141
|
+
for (const item of child) {
|
|
142
|
+
walk(item);
|
|
143
|
+
}
|
|
144
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
145
|
+
walk(child);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (ast.program && ast.program.body) {
|
|
150
|
+
for (const node of ast.program.body) {
|
|
151
|
+
walk(node);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!hasChanges) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
code: magicString.toString(),
|
|
159
|
+
map: magicString.generateMap({
|
|
160
|
+
source: filePath,
|
|
161
|
+
file: filePath,
|
|
162
|
+
includeContent: true
|
|
163
|
+
})
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export {
|
|
168
|
+
shouldProcessFile,
|
|
169
|
+
transformJsxCode
|
|
170
|
+
};
|
package/dist/loader.js
CHANGED
|
@@ -1,168 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx"]);
|
|
6
|
-
var DEFAULT_PARSER_OPTIONS = {
|
|
7
|
-
sourceType: "module",
|
|
8
|
-
plugins: [
|
|
9
|
-
"jsx",
|
|
10
|
-
"typescript",
|
|
11
|
-
"decorators-legacy",
|
|
12
|
-
"classProperties",
|
|
13
|
-
"classPrivateProperties",
|
|
14
|
-
"classPrivateMethods",
|
|
15
|
-
"exportDefaultFrom",
|
|
16
|
-
"exportNamespaceFrom",
|
|
17
|
-
"asyncGenerators",
|
|
18
|
-
"functionBind",
|
|
19
|
-
"functionSent",
|
|
20
|
-
"dynamicImport",
|
|
21
|
-
"numericSeparator",
|
|
22
|
-
"optionalChaining",
|
|
23
|
-
"importMeta",
|
|
24
|
-
"bigInt",
|
|
25
|
-
"optionalCatchBinding",
|
|
26
|
-
"throwExpressions",
|
|
27
|
-
"nullishCoalescingOperator",
|
|
28
|
-
"topLevelAwait"
|
|
29
|
-
],
|
|
30
|
-
errorRecovery: true
|
|
31
|
-
};
|
|
32
|
-
var SKIP_ELEMENTS = /* @__PURE__ */ new Set([
|
|
33
|
-
"Fragment",
|
|
34
|
-
"React.Fragment",
|
|
35
|
-
"Suspense",
|
|
36
|
-
"React.Suspense",
|
|
37
|
-
"StrictMode",
|
|
38
|
-
"React.StrictMode",
|
|
39
|
-
"Profiler",
|
|
40
|
-
"React.Profiler"
|
|
41
|
-
]);
|
|
42
|
-
function shouldProcessFile(filePath) {
|
|
43
|
-
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
44
|
-
return VALID_EXTENSIONS.has(ext);
|
|
45
|
-
}
|
|
46
|
-
function getElementName(node) {
|
|
47
|
-
if (!node || !node.name) return null;
|
|
48
|
-
const name = node.name;
|
|
49
|
-
if (name.type === "JSXIdentifier") {
|
|
50
|
-
return name.name;
|
|
51
|
-
}
|
|
52
|
-
if (name.type === "JSXMemberExpression") {
|
|
53
|
-
const parts = [];
|
|
54
|
-
let current = name;
|
|
55
|
-
while (current) {
|
|
56
|
-
if (current.type === "JSXMemberExpression") {
|
|
57
|
-
parts.unshift(current.property.name);
|
|
58
|
-
current = current.object;
|
|
59
|
-
} else if (current.type === "JSXIdentifier") {
|
|
60
|
-
parts.unshift(current.name);
|
|
61
|
-
break;
|
|
62
|
-
} else {
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return parts.join(".");
|
|
67
|
-
}
|
|
68
|
-
if (name.type === "JSXNamespacedName") {
|
|
69
|
-
return `${name.namespace.name}:${name.name.name}`;
|
|
70
|
-
}
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
function shouldSkipElement(elementName) {
|
|
74
|
-
if (!elementName) return true;
|
|
75
|
-
return SKIP_ELEMENTS.has(elementName);
|
|
76
|
-
}
|
|
77
|
-
function hasDataLocAttribute(node) {
|
|
78
|
-
if (!node.attributes) return false;
|
|
79
|
-
return node.attributes.some(
|
|
80
|
-
(attr) => attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === "data-loc"
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
function findInsertionPoint(node, source) {
|
|
84
|
-
let insertPos = node.name.end;
|
|
85
|
-
if (node.typeParameters) {
|
|
86
|
-
insertPos = node.typeParameters.end;
|
|
87
|
-
}
|
|
88
|
-
if (node.attributes && node.attributes.length > 0) {
|
|
89
|
-
const firstAttr = node.attributes[0];
|
|
90
|
-
const nameEnd = node.typeParameters ? node.typeParameters.end : node.name.end;
|
|
91
|
-
let pos = nameEnd;
|
|
92
|
-
while (pos < firstAttr.start && /\s/.test(source[pos])) {
|
|
93
|
-
pos++;
|
|
94
|
-
}
|
|
95
|
-
insertPos = pos;
|
|
96
|
-
}
|
|
97
|
-
return insertPos;
|
|
98
|
-
}
|
|
99
|
-
function normalizeFilePath(filePath) {
|
|
100
|
-
const abs = resolve(filePath).replace(/\\/g, "/");
|
|
101
|
-
const cwd = resolve(process.cwd()).replace(/\\/g, "/");
|
|
102
|
-
if (abs === cwd || abs.startsWith(cwd + "/")) {
|
|
103
|
-
return relative(cwd, abs).replace(/\\/g, "/");
|
|
104
|
-
}
|
|
105
|
-
return abs;
|
|
106
|
-
}
|
|
107
|
-
function transformJsxCode(source, filePath, options = {}) {
|
|
108
|
-
const parserOptions = {
|
|
109
|
-
...DEFAULT_PARSER_OPTIONS,
|
|
110
|
-
...options.parserOptions
|
|
111
|
-
};
|
|
112
|
-
let ast;
|
|
113
|
-
try {
|
|
114
|
-
ast = parse(source, parserOptions);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`[jsx-loc] Failed to parse ${filePath}:`, error);
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
const magicString = new MagicString(source);
|
|
120
|
-
const normalizedPath = normalizeFilePath(filePath);
|
|
121
|
-
let hasChanges = false;
|
|
122
|
-
function walk(node) {
|
|
123
|
-
if (!node || typeof node !== "object") return;
|
|
124
|
-
if (node.type === "JSXOpeningElement") {
|
|
125
|
-
const elementName = getElementName(node);
|
|
126
|
-
if (!shouldSkipElement(elementName) && !hasDataLocAttribute(node)) {
|
|
127
|
-
const loc = node.loc;
|
|
128
|
-
if (loc && loc.start) {
|
|
129
|
-
const line = loc.start.line;
|
|
130
|
-
const column = loc.start.column;
|
|
131
|
-
const locValue = `${normalizedPath}:${line}:${column}`;
|
|
132
|
-
const insertPos = findInsertionPoint(node, source);
|
|
133
|
-
magicString.appendLeft(insertPos, ` data-loc="${locValue}"`);
|
|
134
|
-
hasChanges = true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
for (const key of Object.keys(node)) {
|
|
139
|
-
const child = node[key];
|
|
140
|
-
if (Array.isArray(child)) {
|
|
141
|
-
for (const item of child) {
|
|
142
|
-
walk(item);
|
|
143
|
-
}
|
|
144
|
-
} else if (child && typeof child === "object" && child.type) {
|
|
145
|
-
walk(child);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (ast.program && ast.program.body) {
|
|
150
|
-
for (const node of ast.program.body) {
|
|
151
|
-
walk(node);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (!hasChanges) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
code: magicString.toString(),
|
|
159
|
-
map: magicString.generateMap({
|
|
160
|
-
source: filePath,
|
|
161
|
-
file: filePath,
|
|
162
|
-
includeContent: true
|
|
163
|
-
})
|
|
164
|
-
};
|
|
165
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
shouldProcessFile,
|
|
3
|
+
transformJsxCode
|
|
4
|
+
} from "./chunk-QPTFKQPZ.js";
|
|
166
5
|
|
|
167
6
|
// src/loader.ts
|
|
168
7
|
import fs from "fs";
|
package/dist/vite.cjs
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
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/vite.ts
|
|
31
|
+
var vite_exports = {};
|
|
32
|
+
__export(vite_exports, {
|
|
33
|
+
default: () => vite_default,
|
|
34
|
+
jsxLoc: () => jsxLoc
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(vite_exports);
|
|
37
|
+
|
|
38
|
+
// src/transform.ts
|
|
39
|
+
var import_parser = require("@babel/parser");
|
|
40
|
+
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
41
|
+
var import_path = require("path");
|
|
42
|
+
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx"]);
|
|
43
|
+
var DEFAULT_PARSER_OPTIONS = {
|
|
44
|
+
sourceType: "module",
|
|
45
|
+
plugins: [
|
|
46
|
+
"jsx",
|
|
47
|
+
"typescript",
|
|
48
|
+
"decorators-legacy",
|
|
49
|
+
"classProperties",
|
|
50
|
+
"classPrivateProperties",
|
|
51
|
+
"classPrivateMethods",
|
|
52
|
+
"exportDefaultFrom",
|
|
53
|
+
"exportNamespaceFrom",
|
|
54
|
+
"asyncGenerators",
|
|
55
|
+
"functionBind",
|
|
56
|
+
"functionSent",
|
|
57
|
+
"dynamicImport",
|
|
58
|
+
"numericSeparator",
|
|
59
|
+
"optionalChaining",
|
|
60
|
+
"importMeta",
|
|
61
|
+
"bigInt",
|
|
62
|
+
"optionalCatchBinding",
|
|
63
|
+
"throwExpressions",
|
|
64
|
+
"nullishCoalescingOperator",
|
|
65
|
+
"topLevelAwait"
|
|
66
|
+
],
|
|
67
|
+
errorRecovery: true
|
|
68
|
+
};
|
|
69
|
+
var SKIP_ELEMENTS = /* @__PURE__ */ new Set([
|
|
70
|
+
"Fragment",
|
|
71
|
+
"React.Fragment",
|
|
72
|
+
"Suspense",
|
|
73
|
+
"React.Suspense",
|
|
74
|
+
"StrictMode",
|
|
75
|
+
"React.StrictMode",
|
|
76
|
+
"Profiler",
|
|
77
|
+
"React.Profiler"
|
|
78
|
+
]);
|
|
79
|
+
function shouldProcessFile(filePath) {
|
|
80
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
81
|
+
return VALID_EXTENSIONS.has(ext);
|
|
82
|
+
}
|
|
83
|
+
function getElementName(node) {
|
|
84
|
+
if (!node || !node.name) return null;
|
|
85
|
+
const name = node.name;
|
|
86
|
+
if (name.type === "JSXIdentifier") {
|
|
87
|
+
return name.name;
|
|
88
|
+
}
|
|
89
|
+
if (name.type === "JSXMemberExpression") {
|
|
90
|
+
const parts = [];
|
|
91
|
+
let current = name;
|
|
92
|
+
while (current) {
|
|
93
|
+
if (current.type === "JSXMemberExpression") {
|
|
94
|
+
parts.unshift(current.property.name);
|
|
95
|
+
current = current.object;
|
|
96
|
+
} else if (current.type === "JSXIdentifier") {
|
|
97
|
+
parts.unshift(current.name);
|
|
98
|
+
break;
|
|
99
|
+
} else {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return parts.join(".");
|
|
104
|
+
}
|
|
105
|
+
if (name.type === "JSXNamespacedName") {
|
|
106
|
+
return `${name.namespace.name}:${name.name.name}`;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
function shouldSkipElement(elementName) {
|
|
111
|
+
if (!elementName) return true;
|
|
112
|
+
return SKIP_ELEMENTS.has(elementName);
|
|
113
|
+
}
|
|
114
|
+
function hasDataLocAttribute(node) {
|
|
115
|
+
if (!node.attributes) return false;
|
|
116
|
+
return node.attributes.some(
|
|
117
|
+
(attr) => attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === "data-loc"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function findInsertionPoint(node, source) {
|
|
121
|
+
let insertPos = node.name.end;
|
|
122
|
+
if (node.typeParameters) {
|
|
123
|
+
insertPos = node.typeParameters.end;
|
|
124
|
+
}
|
|
125
|
+
if (node.attributes && node.attributes.length > 0) {
|
|
126
|
+
const firstAttr = node.attributes[0];
|
|
127
|
+
const nameEnd = node.typeParameters ? node.typeParameters.end : node.name.end;
|
|
128
|
+
let pos = nameEnd;
|
|
129
|
+
while (pos < firstAttr.start && /\s/.test(source[pos])) {
|
|
130
|
+
pos++;
|
|
131
|
+
}
|
|
132
|
+
insertPos = pos;
|
|
133
|
+
}
|
|
134
|
+
return insertPos;
|
|
135
|
+
}
|
|
136
|
+
function normalizeFilePath(filePath) {
|
|
137
|
+
const abs = (0, import_path.resolve)(filePath).replace(/\\/g, "/");
|
|
138
|
+
const cwd = (0, import_path.resolve)(process.cwd()).replace(/\\/g, "/");
|
|
139
|
+
if (abs === cwd || abs.startsWith(cwd + "/")) {
|
|
140
|
+
return (0, import_path.relative)(cwd, abs).replace(/\\/g, "/");
|
|
141
|
+
}
|
|
142
|
+
return abs;
|
|
143
|
+
}
|
|
144
|
+
function transformJsxCode(source, filePath, options = {}) {
|
|
145
|
+
const parserOptions = {
|
|
146
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
147
|
+
...options.parserOptions
|
|
148
|
+
};
|
|
149
|
+
let ast;
|
|
150
|
+
try {
|
|
151
|
+
ast = (0, import_parser.parse)(source, parserOptions);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(`[jsx-loc] Failed to parse ${filePath}:`, error);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const magicString = new import_magic_string.default(source);
|
|
157
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
158
|
+
let hasChanges = false;
|
|
159
|
+
function walk(node) {
|
|
160
|
+
if (!node || typeof node !== "object") return;
|
|
161
|
+
if (node.type === "JSXOpeningElement") {
|
|
162
|
+
const elementName = getElementName(node);
|
|
163
|
+
if (!shouldSkipElement(elementName) && !hasDataLocAttribute(node)) {
|
|
164
|
+
const loc = node.loc;
|
|
165
|
+
if (loc && loc.start) {
|
|
166
|
+
const line = loc.start.line;
|
|
167
|
+
const column = loc.start.column;
|
|
168
|
+
const locValue = `${normalizedPath}:${line}:${column}`;
|
|
169
|
+
const insertPos = findInsertionPoint(node, source);
|
|
170
|
+
magicString.appendLeft(insertPos, ` data-loc="${locValue}"`);
|
|
171
|
+
hasChanges = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const key of Object.keys(node)) {
|
|
176
|
+
const child = node[key];
|
|
177
|
+
if (Array.isArray(child)) {
|
|
178
|
+
for (const item of child) {
|
|
179
|
+
walk(item);
|
|
180
|
+
}
|
|
181
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
182
|
+
walk(child);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (ast.program && ast.program.body) {
|
|
187
|
+
for (const node of ast.program.body) {
|
|
188
|
+
walk(node);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!hasChanges) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
code: magicString.toString(),
|
|
196
|
+
map: magicString.generateMap({
|
|
197
|
+
source: filePath,
|
|
198
|
+
file: filePath,
|
|
199
|
+
includeContent: true
|
|
200
|
+
})
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/vite.ts
|
|
205
|
+
function stripQuery(id) {
|
|
206
|
+
const q = id.indexOf("?");
|
|
207
|
+
return q === -1 ? id : id.slice(0, q);
|
|
208
|
+
}
|
|
209
|
+
function asArray(v) {
|
|
210
|
+
if (!v) return [];
|
|
211
|
+
return Array.isArray(v) ? v : [v];
|
|
212
|
+
}
|
|
213
|
+
function matchesAny(re, value) {
|
|
214
|
+
const list = asArray(re);
|
|
215
|
+
if (list.length === 0) return true;
|
|
216
|
+
return list.some((r) => r.test(value));
|
|
217
|
+
}
|
|
218
|
+
function jsxLoc(options = {}) {
|
|
219
|
+
const exclude = options.exclude ?? /node_modules/;
|
|
220
|
+
return {
|
|
221
|
+
name: "jsx-loc-plugin",
|
|
222
|
+
enforce: "pre",
|
|
223
|
+
transform(code, id) {
|
|
224
|
+
const filePath = stripQuery(id);
|
|
225
|
+
if (!shouldProcessFile(filePath)) return null;
|
|
226
|
+
if (!matchesAny(options.include, filePath)) return null;
|
|
227
|
+
if (asArray(exclude).some((r) => r.test(filePath))) return null;
|
|
228
|
+
const result = transformJsxCode(code, filePath);
|
|
229
|
+
if (!result) return null;
|
|
230
|
+
return {
|
|
231
|
+
code: result.code,
|
|
232
|
+
map: result.map
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
var vite_default = jsxLoc;
|
|
238
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
239
|
+
0 && (module.exports = {
|
|
240
|
+
jsxLoc
|
|
241
|
+
});
|
package/dist/vite.d.cts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { JsxLocPluginOptions } from './index.cjs';
|
|
3
|
+
import 'next';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Vite plugin that adds `data-loc` attributes to JSX elements.
|
|
7
|
+
*
|
|
8
|
+
* This powers UILint's in-browser inspection overlay for Vite projects.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a Vite plugin that injects `data-loc` on JSX elements.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { defineConfig } from "vite";
|
|
16
|
+
* import react from "@vitejs/plugin-react";
|
|
17
|
+
* import { jsxLoc } from "jsx-loc-plugin/vite";
|
|
18
|
+
*
|
|
19
|
+
* export default defineConfig({
|
|
20
|
+
* plugins: [react(), jsxLoc()],
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
declare function jsxLoc(options?: JsxLocPluginOptions): Plugin;
|
|
24
|
+
|
|
25
|
+
export { jsxLoc as default, jsxLoc };
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { JsxLocPluginOptions } from './index.js';
|
|
3
|
+
import 'next';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Vite plugin that adds `data-loc` attributes to JSX elements.
|
|
7
|
+
*
|
|
8
|
+
* This powers UILint's in-browser inspection overlay for Vite projects.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a Vite plugin that injects `data-loc` on JSX elements.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { defineConfig } from "vite";
|
|
16
|
+
* import react from "@vitejs/plugin-react";
|
|
17
|
+
* import { jsxLoc } from "jsx-loc-plugin/vite";
|
|
18
|
+
*
|
|
19
|
+
* export default defineConfig({
|
|
20
|
+
* plugins: [react(), jsxLoc()],
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
declare function jsxLoc(options?: JsxLocPluginOptions): Plugin;
|
|
24
|
+
|
|
25
|
+
export { jsxLoc as default, jsxLoc };
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shouldProcessFile,
|
|
3
|
+
transformJsxCode
|
|
4
|
+
} from "./chunk-QPTFKQPZ.js";
|
|
5
|
+
|
|
6
|
+
// src/vite.ts
|
|
7
|
+
function stripQuery(id) {
|
|
8
|
+
const q = id.indexOf("?");
|
|
9
|
+
return q === -1 ? id : id.slice(0, q);
|
|
10
|
+
}
|
|
11
|
+
function asArray(v) {
|
|
12
|
+
if (!v) return [];
|
|
13
|
+
return Array.isArray(v) ? v : [v];
|
|
14
|
+
}
|
|
15
|
+
function matchesAny(re, value) {
|
|
16
|
+
const list = asArray(re);
|
|
17
|
+
if (list.length === 0) return true;
|
|
18
|
+
return list.some((r) => r.test(value));
|
|
19
|
+
}
|
|
20
|
+
function jsxLoc(options = {}) {
|
|
21
|
+
const exclude = options.exclude ?? /node_modules/;
|
|
22
|
+
return {
|
|
23
|
+
name: "jsx-loc-plugin",
|
|
24
|
+
enforce: "pre",
|
|
25
|
+
transform(code, id) {
|
|
26
|
+
const filePath = stripQuery(id);
|
|
27
|
+
if (!shouldProcessFile(filePath)) return null;
|
|
28
|
+
if (!matchesAny(options.include, filePath)) return null;
|
|
29
|
+
if (asArray(exclude).some((r) => r.test(filePath))) return null;
|
|
30
|
+
const result = transformJsxCode(code, filePath);
|
|
31
|
+
if (!result) return null;
|
|
32
|
+
return {
|
|
33
|
+
code: result.code,
|
|
34
|
+
map: result.map
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
var vite_default = jsxLoc;
|
|
40
|
+
export {
|
|
41
|
+
vite_default as default,
|
|
42
|
+
jsxLoc
|
|
43
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsx-loc-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Next.js plugin that adds data-loc attributes to JSX elements for UILint inspection",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"import": "./dist/index.js",
|
|
33
33
|
"require": "./dist/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./vite": {
|
|
36
|
+
"types": "./dist/vite.d.ts",
|
|
37
|
+
"import": "./dist/vite.js",
|
|
38
|
+
"require": "./dist/vite.cjs"
|
|
34
39
|
}
|
|
35
40
|
},
|
|
36
41
|
"files": [
|
|
@@ -43,10 +48,12 @@
|
|
|
43
48
|
"devDependencies": {
|
|
44
49
|
"next": "^16.1.1",
|
|
45
50
|
"tsup": "^8.0.0",
|
|
46
|
-
"typescript": "^5.0.0"
|
|
51
|
+
"typescript": "^5.0.0",
|
|
52
|
+
"vite": "^6.0.0"
|
|
47
53
|
},
|
|
48
54
|
"peerDependencies": {
|
|
49
|
-
"next": ">=14.0.0"
|
|
55
|
+
"next": ">=14.0.0",
|
|
56
|
+
"vite": ">=5.0.0"
|
|
50
57
|
},
|
|
51
58
|
"scripts": {
|
|
52
59
|
"build": "tsup",
|