@promakeai/inspector-hook 1.0.2 → 1.1.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/dist/utils/jsxUpdater.d.ts +2 -1
- package/dist/utils/jsxUpdater.d.ts.map +1 -1
- package/dist/utils/jsxUpdater.js +318 -122
- package/package.json +1 -9
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSX Source Code Updater
|
|
3
|
-
*
|
|
3
|
+
* Lightweight regex-based utility for updating styles and classNames in JSX/TSX source code
|
|
4
|
+
* No Babel dependencies - works perfectly in browser environments!
|
|
4
5
|
*/
|
|
5
6
|
export interface UpdateJSXSourceOptions {
|
|
6
7
|
sourceCode: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsxUpdater.d.ts","sourceRoot":"","sources":["../../src/utils/jsxUpdater.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"jsxUpdater.d.ts","sourceRoot":"","sources":["../../src/utils/jsxUpdater.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4YD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAkFvB"}
|
package/dist/utils/jsxUpdater.js
CHANGED
|
@@ -1,125 +1,320 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSX Source Code Updater
|
|
3
|
-
*
|
|
3
|
+
* Lightweight regex-based utility for updating styles and classNames in JSX/TSX source code
|
|
4
|
+
* No Babel dependencies - works perfectly in browser environments!
|
|
4
5
|
*/
|
|
5
|
-
import * as parser from '@babel/parser';
|
|
6
|
-
import * as traverseModule from '@babel/traverse';
|
|
7
|
-
import * as generateModule from '@babel/generator';
|
|
8
|
-
import * as t from '@babel/types';
|
|
9
|
-
// Browser compatibility: Handle both CommonJS and ESM default exports
|
|
10
|
-
// @ts-ignore - Babel modules have complex type definitions
|
|
11
|
-
const traverse = traverseModule.default || traverseModule;
|
|
12
|
-
// @ts-ignore - Babel modules have complex type definitions
|
|
13
|
-
const generate = generateModule.default || generateModule;
|
|
14
6
|
/**
|
|
15
|
-
*
|
|
7
|
+
* Extract tag name from JSX opening tag (handles generics like Component<T>)
|
|
16
8
|
*/
|
|
17
|
-
function
|
|
18
|
-
|
|
9
|
+
function extractTagName(tagContent) {
|
|
10
|
+
// Match tag name, supporting generics: <Component<T> or <div
|
|
11
|
+
const match = tagContent.match(/^<([A-Za-z_$][A-Za-z0-9_$]*)/);
|
|
12
|
+
return match ? match[1] : null;
|
|
19
13
|
}
|
|
20
14
|
/**
|
|
21
|
-
* Find JSX
|
|
15
|
+
* Find the JSX opening tag at the specified line and column
|
|
22
16
|
*/
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
17
|
+
function findJSXTagAtPosition(sourceCode, lineNumber, columnNumber, tagName) {
|
|
18
|
+
const lines = sourceCode.split("\n");
|
|
19
|
+
if (lineNumber < 1 || lineNumber > lines.length) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// Calculate absolute position in source code
|
|
23
|
+
let absolutePosition = 0;
|
|
24
|
+
for (let i = 0; i < lineNumber - 1; i++) {
|
|
25
|
+
absolutePosition += lines[i].length + 1; // +1 for newline
|
|
26
|
+
}
|
|
27
|
+
absolutePosition += columnNumber;
|
|
28
|
+
// Check if we're at the start of a tag (flexible - allows any tag)
|
|
29
|
+
const remainingCode = sourceCode.slice(absolutePosition);
|
|
30
|
+
const tagPattern = /^<[A-Za-z_$][A-Za-z0-9_$]*/;
|
|
31
|
+
if (!tagPattern.test(remainingCode)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Extract actual tag name found at this position
|
|
35
|
+
const foundTagName = extractTagName(remainingCode);
|
|
36
|
+
if (!foundTagName) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// Find the end of the opening tag
|
|
40
|
+
let depth = 0;
|
|
41
|
+
let angleBracketDepth = 0; // For TypeScript generics
|
|
42
|
+
let inString = false;
|
|
43
|
+
let stringChar = "";
|
|
44
|
+
let inExpression = false;
|
|
45
|
+
let i = absolutePosition + 1; // Skip the '<'
|
|
46
|
+
let hasSeenTagName = false;
|
|
47
|
+
while (i < sourceCode.length) {
|
|
48
|
+
const char = sourceCode[i];
|
|
49
|
+
const prevChar = i > 0 ? sourceCode[i - 1] : "";
|
|
50
|
+
// Handle strings
|
|
51
|
+
if ((char === '"' || char === "'" || char === "`") && prevChar !== "\\") {
|
|
52
|
+
if (!inString) {
|
|
53
|
+
inString = true;
|
|
54
|
+
stringChar = char;
|
|
55
|
+
}
|
|
56
|
+
else if (char === stringChar) {
|
|
57
|
+
inString = false;
|
|
58
|
+
stringChar = "";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Handle JSX expressions and TypeScript generics
|
|
62
|
+
if (!inString) {
|
|
63
|
+
if (char === "{") {
|
|
64
|
+
inExpression = true;
|
|
65
|
+
depth++;
|
|
66
|
+
}
|
|
67
|
+
else if (char === "}") {
|
|
68
|
+
depth--;
|
|
69
|
+
if (depth === 0) {
|
|
70
|
+
inExpression = false;
|
|
71
|
+
}
|
|
72
|
+
// Detect unterminated expressions
|
|
73
|
+
if (depth < 0) {
|
|
74
|
+
return null; // Invalid JSX
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (char === "<" && !hasSeenTagName) {
|
|
78
|
+
// TypeScript generic opening
|
|
79
|
+
angleBracketDepth++;
|
|
80
|
+
}
|
|
81
|
+
else if (char === ">" && angleBracketDepth > 0) {
|
|
82
|
+
// TypeScript generic closing
|
|
83
|
+
angleBracketDepth--;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Mark that we've passed the tag name
|
|
87
|
+
if (!hasSeenTagName &&
|
|
88
|
+
(char === " " || char === "\t" || char === "\n" || char === "<")) {
|
|
89
|
+
hasSeenTagName = true;
|
|
90
|
+
}
|
|
91
|
+
// Check for end of opening tag
|
|
92
|
+
if (!inString && !inExpression && depth === 0 && angleBracketDepth === 0) {
|
|
93
|
+
if (char === ">" ||
|
|
94
|
+
(char === "/" && i + 1 < sourceCode.length && sourceCode[i + 1] === ">")) {
|
|
95
|
+
const end = char === "/" ? i + 2 : i + 1;
|
|
96
|
+
const content = sourceCode.slice(absolutePosition, end);
|
|
97
|
+
// Additional validation: check for incomplete/malformed closing tag
|
|
98
|
+
if (!content.trim().endsWith(">") && !content.trim().endsWith("/>")) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
start: absolutePosition,
|
|
103
|
+
end: end,
|
|
104
|
+
content: content,
|
|
105
|
+
foundTagName: foundTagName,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
i++;
|
|
110
|
+
}
|
|
111
|
+
// If we reach here, the tag was not properly closed
|
|
112
|
+
return null;
|
|
37
113
|
}
|
|
38
114
|
/**
|
|
39
|
-
*
|
|
115
|
+
* Parse inline style object from style attribute
|
|
40
116
|
*/
|
|
41
|
-
function
|
|
42
|
-
const
|
|
43
|
-
//
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
117
|
+
function parseStyleObject(styleValue) {
|
|
118
|
+
const styles = {};
|
|
119
|
+
// Remove outer {{ }}
|
|
120
|
+
const objectMatch = styleValue.match(/\{\s*\{([^}]*)\}\s*\}/s);
|
|
121
|
+
if (!objectMatch) {
|
|
122
|
+
return styles;
|
|
123
|
+
}
|
|
124
|
+
const content = objectMatch[1];
|
|
125
|
+
// Parse property: value pairs
|
|
126
|
+
const propRegex = /(\w+):\s*['"]([^'"]*)['"]/g;
|
|
127
|
+
let match;
|
|
128
|
+
while ((match = propRegex.exec(content)) !== null) {
|
|
129
|
+
styles[match[1]] = match[2];
|
|
130
|
+
}
|
|
131
|
+
return styles;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create style object string from styles
|
|
135
|
+
*/
|
|
136
|
+
function createStyleString(styles) {
|
|
137
|
+
const entries = Object.entries(styles).map(([key, value]) => `${key}: "${value}"`);
|
|
138
|
+
return `{{ ${entries.join(", ")} }}`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validate that the tag content has proper syntax
|
|
142
|
+
*/
|
|
143
|
+
function validateTagSyntax(tagContent) {
|
|
144
|
+
// Check for incomplete attributes (e.g., className= without value)
|
|
145
|
+
if (/\s+\w+=$/.test(tagContent) || /\s+\w+=\s*>/.test(tagContent)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
// Check for proper closing
|
|
149
|
+
if (!tagContent.endsWith(">") && !tagContent.endsWith("/>")) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if a JSX element has a proper closing tag and valid content (basic check)
|
|
156
|
+
*/
|
|
157
|
+
function hasProperClosingTag(sourceCode, tagStart, tagEnd, tagName) {
|
|
158
|
+
// For self-closing tags, no closing tag needed
|
|
159
|
+
const openingTag = sourceCode.slice(tagStart, tagEnd);
|
|
160
|
+
if (openingTag.trim().endsWith("/>")) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
// Look for closing tag after the opening tag
|
|
164
|
+
const afterOpening = sourceCode.slice(tagEnd);
|
|
165
|
+
const closingTagPattern = new RegExp(`</${tagName}\\s*>`);
|
|
166
|
+
// Check if there's a closing tag somewhere
|
|
167
|
+
const closingMatch = closingTagPattern.exec(afterOpening);
|
|
168
|
+
if (!closingMatch) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
// Check the content between opening and closing tags for unmatched braces
|
|
172
|
+
const contentBetween = afterOpening.slice(0, closingMatch.index);
|
|
173
|
+
let braceDepth = 0;
|
|
174
|
+
let inString = false;
|
|
175
|
+
let stringChar = "";
|
|
176
|
+
for (let i = 0; i < contentBetween.length; i++) {
|
|
177
|
+
const char = contentBetween[i];
|
|
178
|
+
const prevChar = i > 0 ? contentBetween[i - 1] : "";
|
|
179
|
+
// Handle strings
|
|
180
|
+
if ((char === '"' || char === "'" || char === "`") && prevChar !== "\\") {
|
|
181
|
+
if (!inString) {
|
|
182
|
+
inString = true;
|
|
183
|
+
stringChar = char;
|
|
184
|
+
}
|
|
185
|
+
else if (char === stringChar) {
|
|
186
|
+
inString = false;
|
|
187
|
+
stringChar = "";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Count braces outside of strings
|
|
191
|
+
if (!inString) {
|
|
192
|
+
if (char === "{") {
|
|
193
|
+
braceDepth++;
|
|
194
|
+
}
|
|
195
|
+
else if (char === "}") {
|
|
196
|
+
braceDepth--;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// If braces don't match, JSX is invalid
|
|
201
|
+
if (braceDepth !== 0) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
// Validate proper nesting by tracking all opening and closing tags
|
|
205
|
+
let tagStack = [tagName];
|
|
206
|
+
let i = 0;
|
|
207
|
+
while (i < afterOpening.length && tagStack.length > 0) {
|
|
208
|
+
// Look for next tag (opening or closing)
|
|
209
|
+
const remainingCode = afterOpening.slice(i);
|
|
210
|
+
const nextTag = remainingCode.match(/^<\/?([A-Za-z_$][A-Za-z0-9_$]*)/);
|
|
211
|
+
if (!nextTag) {
|
|
212
|
+
// No more tags, move forward
|
|
213
|
+
i++;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const isClosing = remainingCode.startsWith("</");
|
|
217
|
+
const foundTagName = nextTag[1];
|
|
218
|
+
if (isClosing) {
|
|
219
|
+
// Closing tag - should match top of stack
|
|
220
|
+
if (tagStack.length === 0 ||
|
|
221
|
+
tagStack[tagStack.length - 1] !== foundTagName) {
|
|
222
|
+
return false; // Mismatched closing tag
|
|
223
|
+
}
|
|
224
|
+
tagStack.pop();
|
|
225
|
+
// If this was our target tag's closing, we're done
|
|
226
|
+
if (tagStack.length === 0) {
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
67
229
|
}
|
|
68
230
|
else {
|
|
69
|
-
//
|
|
70
|
-
|
|
231
|
+
// Opening tag - check if it's self-closing
|
|
232
|
+
const tagEndMatch = remainingCode.match(/^<[^>]+?(\/?)>/);
|
|
233
|
+
if (tagEndMatch && !tagEndMatch[1]) {
|
|
234
|
+
// Not self-closing, add to stack
|
|
235
|
+
tagStack.push(foundTagName);
|
|
236
|
+
}
|
|
237
|
+
// If self-closing (/>), don't add to stack
|
|
71
238
|
}
|
|
239
|
+
// Move past this tag
|
|
240
|
+
const tagEnd = remainingCode.indexOf(">");
|
|
241
|
+
if (tagEnd === -1)
|
|
242
|
+
break;
|
|
243
|
+
i += tagEnd + 1;
|
|
244
|
+
}
|
|
245
|
+
// Stack should be empty if all tags matched properly
|
|
246
|
+
if (tagStack.length > 0) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Update or add style attribute in JSX tag
|
|
253
|
+
*/
|
|
254
|
+
function updateStyleAttribute(tagContent, newStyles) {
|
|
255
|
+
// Validate syntax before attempting to update
|
|
256
|
+
if (!validateTagSyntax(tagContent)) {
|
|
257
|
+
throw new Error("Invalid JSX syntax: malformed attributes or incomplete tag");
|
|
258
|
+
}
|
|
259
|
+
const styleMatch = tagContent.match(/style=\{\s*\{[^}]*\}\s*\}/s);
|
|
260
|
+
if (styleMatch) {
|
|
261
|
+
// Merge with existing styles
|
|
262
|
+
const existingStyles = parseStyleObject(styleMatch[0]);
|
|
263
|
+
const mergedStyles = { ...existingStyles, ...newStyles };
|
|
264
|
+
const newStyleAttr = `style=${createStyleString(mergedStyles)}`;
|
|
265
|
+
return tagContent.replace(/style=\{\s*\{[^}]*\}\s*\}/s, newStyleAttr);
|
|
72
266
|
}
|
|
73
267
|
else {
|
|
74
268
|
// Add new style attribute
|
|
75
|
-
const
|
|
76
|
-
|
|
269
|
+
const newStyleAttr = ` style=${createStyleString(newStyles)}`;
|
|
270
|
+
// Insert before closing > or />
|
|
271
|
+
if (tagContent.endsWith("/>")) {
|
|
272
|
+
return tagContent.slice(0, -2) + newStyleAttr + " />";
|
|
273
|
+
}
|
|
274
|
+
else if (tagContent.endsWith(">")) {
|
|
275
|
+
return tagContent.slice(0, -1) + newStyleAttr + ">";
|
|
276
|
+
}
|
|
77
277
|
}
|
|
278
|
+
return tagContent;
|
|
78
279
|
}
|
|
79
280
|
/**
|
|
80
|
-
*
|
|
281
|
+
* Update or add className attribute in JSX tag
|
|
81
282
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
283
|
+
function updateClassNameAttribute(tagContent, newClassName) {
|
|
284
|
+
// Validate syntax before attempting to update
|
|
285
|
+
if (!validateTagSyntax(tagContent)) {
|
|
286
|
+
throw new Error("Invalid JSX syntax: malformed attributes or incomplete tag");
|
|
287
|
+
}
|
|
288
|
+
// Check for existing className
|
|
289
|
+
const classNameMatch = tagContent.match(/className=["']([^"']*)["']/);
|
|
290
|
+
if (classNameMatch) {
|
|
89
291
|
// Merge with existing className
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
else if (t.isJSXExpressionContainer(classNameAttr.value) &&
|
|
103
|
-
t.isTemplateLiteral(classNameAttr.value.expression)) {
|
|
104
|
-
// Template literal className
|
|
105
|
-
const templateLiteral = classNameAttr.value.expression;
|
|
106
|
-
const lastQuasi = templateLiteral.quasis[templateLiteral.quasis.length - 1];
|
|
107
|
-
lastQuasi.value.raw += ` ${newClassName}`;
|
|
108
|
-
lastQuasi.value.cooked = lastQuasi.value.raw;
|
|
292
|
+
const existingClasses = classNameMatch[1];
|
|
293
|
+
const allClasses = `${existingClasses} ${newClassName}`.trim();
|
|
294
|
+
return tagContent.replace(/className=["'][^"']*["']/, `className="${allClasses}"`);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
// Check for className with JSX expression
|
|
298
|
+
const classNameExprMatch = tagContent.match(/className=\{[^}]*\}/);
|
|
299
|
+
if (classNameExprMatch) {
|
|
300
|
+
// Wrap existing expression in template literal
|
|
301
|
+
const existingExpr = classNameExprMatch[0].slice(11, -1); // Remove className={ and }
|
|
302
|
+
const newExpr = `className={\`\${${existingExpr}} ${newClassName}\`}`;
|
|
303
|
+
return tagContent.replace(/className=\{[^}]*\}/, newExpr);
|
|
109
304
|
}
|
|
110
305
|
else {
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
306
|
+
// Add new className attribute
|
|
307
|
+
const newClassNameAttr = ` className="${newClassName}"`;
|
|
308
|
+
// Insert before closing > or />
|
|
309
|
+
if (tagContent.endsWith("/>")) {
|
|
310
|
+
return tagContent.slice(0, -2) + newClassNameAttr + " />";
|
|
311
|
+
}
|
|
312
|
+
else if (tagContent.endsWith(">")) {
|
|
313
|
+
return tagContent.slice(0, -1) + newClassNameAttr + ">";
|
|
314
|
+
}
|
|
116
315
|
}
|
|
117
316
|
}
|
|
118
|
-
|
|
119
|
-
// Add new className attribute
|
|
120
|
-
const classNameAttr = t.jsxAttribute(t.jsxIdentifier('className'), t.stringLiteral(newClassName));
|
|
121
|
-
attributes.push(classNameAttr);
|
|
122
|
-
}
|
|
317
|
+
return tagContent;
|
|
123
318
|
}
|
|
124
319
|
/**
|
|
125
320
|
* Update JSX source code with new styles and/or className
|
|
@@ -143,56 +338,57 @@ function mergeClassNameProp(jsxOpeningElement, newClassName) {
|
|
|
143
338
|
export function updateJSXSource(options) {
|
|
144
339
|
const { sourceCode, lineNumber, columnNumber, tagName, styles, className } = options;
|
|
145
340
|
try {
|
|
146
|
-
//
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
plugins: ['jsx', 'typescript', 'decorators-legacy'],
|
|
150
|
-
});
|
|
151
|
-
// Find the target JSX element at the specified position
|
|
152
|
-
const elementPath = findJSXElementAtPosition(ast, lineNumber, columnNumber);
|
|
153
|
-
if (!elementPath) {
|
|
341
|
+
// Find the JSX tag at the specified position
|
|
342
|
+
const tagInfo = findJSXTagAtPosition(sourceCode, lineNumber, columnNumber, tagName);
|
|
343
|
+
if (!tagInfo) {
|
|
154
344
|
return {
|
|
155
345
|
success: false,
|
|
156
346
|
code: sourceCode,
|
|
157
347
|
message: `Could not find JSX element <${tagName}> at line ${lineNumber}, column ${columnNumber}`,
|
|
158
348
|
};
|
|
159
349
|
}
|
|
160
|
-
// Verify tag name matches
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
350
|
+
// Verify tag name matches
|
|
351
|
+
if (tagInfo.foundTagName.toLowerCase() !== tagName.toLowerCase()) {
|
|
352
|
+
return {
|
|
353
|
+
success: false,
|
|
354
|
+
code: sourceCode,
|
|
355
|
+
message: `Tag name mismatch: expected <${tagName}>, found <${tagInfo.foundTagName}>`,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
// Validate that the element has proper closing tag
|
|
359
|
+
if (!hasProperClosingTag(sourceCode, tagInfo.start, tagInfo.end, tagInfo.foundTagName)) {
|
|
165
360
|
return {
|
|
166
361
|
success: false,
|
|
167
362
|
code: sourceCode,
|
|
168
|
-
message: `
|
|
363
|
+
message: `Invalid JSX: element <${tagInfo.foundTagName}> is missing or has mismatched closing tag`,
|
|
169
364
|
};
|
|
170
365
|
}
|
|
366
|
+
let updatedTagContent = tagInfo.content;
|
|
171
367
|
// Apply style updates if provided
|
|
172
368
|
if (styles && Object.keys(styles).length > 0) {
|
|
173
|
-
|
|
369
|
+
updatedTagContent = updateStyleAttribute(updatedTagContent, styles);
|
|
174
370
|
}
|
|
175
371
|
// Apply className updates if provided
|
|
176
372
|
if (className && className.trim()) {
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
//
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
concise: false,
|
|
184
|
-
});
|
|
373
|
+
updatedTagContent = updateClassNameAttribute(updatedTagContent, className.trim());
|
|
374
|
+
}
|
|
375
|
+
// Replace the tag in the source code
|
|
376
|
+
const updatedCode = sourceCode.slice(0, tagInfo.start) +
|
|
377
|
+
updatedTagContent +
|
|
378
|
+
sourceCode.slice(tagInfo.end);
|
|
185
379
|
return {
|
|
186
380
|
success: true,
|
|
187
|
-
code:
|
|
188
|
-
message:
|
|
381
|
+
code: updatedCode,
|
|
382
|
+
message: "JSX source updated successfully",
|
|
189
383
|
};
|
|
190
384
|
}
|
|
191
385
|
catch (error) {
|
|
192
386
|
return {
|
|
193
387
|
success: false,
|
|
194
388
|
code: sourceCode,
|
|
195
|
-
message: error instanceof Error
|
|
389
|
+
message: error instanceof Error
|
|
390
|
+
? error.message
|
|
391
|
+
: "Unknown error occurred during JSX update",
|
|
196
392
|
};
|
|
197
393
|
}
|
|
198
394
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promakeai/inspector-hook",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "React hook for controlling inspector in parent applications",
|
|
5
5
|
"author": "Promake",
|
|
6
6
|
"type": "module",
|
|
@@ -45,16 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@promakeai/inspector-types": "^1.0.1",
|
|
48
|
-
"@types/babel__traverse": "^7.20.0",
|
|
49
|
-
"@types/babel__generator": "^7.6.0",
|
|
50
48
|
"vitest": "^1.0.0"
|
|
51
49
|
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@babel/parser": "^7.23.0",
|
|
54
|
-
"@babel/traverse": "^7.23.0",
|
|
55
|
-
"@babel/generator": "^7.23.0",
|
|
56
|
-
"@babel/types": "^7.23.0"
|
|
57
|
-
},
|
|
58
50
|
"publishConfig": {
|
|
59
51
|
"access": "public"
|
|
60
52
|
}
|