@vibexdotnew/inspector 0.0.10 → 0.0.12
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/next/index.cjs +2 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +2 -2
- package/dist/next/index.js.map +1 -1
- package/dist/next/loader.cjs +2 -2
- package/dist/next/loader.cjs.map +1 -1
- package/dist/next/loader.js +2 -2
- package/dist/next/loader.js.map +1 -1
- package/next/element-tagger.cjs +372 -0
- package/package.json +2 -2
- package/next/element-tagger.js +0 -461
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FlexApp Element Tagger - Turbopack/Webpack Loader
|
|
5
|
+
*
|
|
6
|
+
* Transforms JSX to inject location attributes for visual editing.
|
|
7
|
+
* Injects: data-flex-loc="filepath:line:col" data-flex-name="tagName"
|
|
8
|
+
*
|
|
9
|
+
* SAFE: Never throws errors - returns original source on any failure
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
// Safe imports with fallbacks
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
let parse, MagicString, walk;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
parse = require("@babel/parser").parse;
|
|
20
|
+
} catch {
|
|
21
|
+
parse = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
MagicString = require("magic-string");
|
|
26
|
+
} catch {
|
|
27
|
+
MagicString = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// estree-walker v3 is ESM-only, try multiple import strategies
|
|
31
|
+
try {
|
|
32
|
+
walk = require("estree-walker").walk;
|
|
33
|
+
} catch {
|
|
34
|
+
try {
|
|
35
|
+
// Try the sync version
|
|
36
|
+
const mod = require("estree-walker/src/sync.js");
|
|
37
|
+
walk = mod.walk || mod.default?.walk;
|
|
38
|
+
} catch {
|
|
39
|
+
walk = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const path = require("path");
|
|
44
|
+
const fs = require("fs");
|
|
45
|
+
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
// Element Exclusion Lists
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const EXCLUDED_ELEMENTS = new Set([
|
|
51
|
+
// React Three Fiber primitives
|
|
52
|
+
"primitive", "mesh", "group", "scene", "object3D",
|
|
53
|
+
"camera", "perspectiveCamera", "orthographicCamera", "cubeCamera", "arrayCamera",
|
|
54
|
+
"instancedMesh", "batchedMesh", "skinnedMesh", "sprite", "lOD",
|
|
55
|
+
"lineSegments", "lineLoop", "points", "skeleton", "bone",
|
|
56
|
+
"bufferGeometry", "instancedBufferGeometry",
|
|
57
|
+
"boxGeometry", "sphereGeometry", "planeGeometry", "cylinderGeometry",
|
|
58
|
+
"coneGeometry", "torusGeometry", "torusKnotGeometry", "ringGeometry",
|
|
59
|
+
"circleGeometry", "tubeGeometry", "extrudeGeometry", "latheGeometry",
|
|
60
|
+
"shapeGeometry", "polyhedronGeometry", "icosahedronGeometry",
|
|
61
|
+
"octahedronGeometry", "tetrahedronGeometry", "dodecahedronGeometry",
|
|
62
|
+
"capsuleGeometry", "edgesGeometry", "wireframeGeometry",
|
|
63
|
+
"material", "meshBasicMaterial", "meshStandardMaterial", "meshPhongMaterial",
|
|
64
|
+
"meshLambertMaterial", "meshToonMaterial", "meshNormalMaterial",
|
|
65
|
+
"meshPhysicalMaterial", "meshDepthMaterial", "meshDistanceMaterial",
|
|
66
|
+
"meshMatcapMaterial", "shaderMaterial", "rawShaderMaterial",
|
|
67
|
+
"pointsMaterial", "lineBasicMaterial", "lineDashedMaterial",
|
|
68
|
+
"spriteMaterial", "shadowMaterial",
|
|
69
|
+
"light", "ambientLight", "directionalLight", "pointLight", "spotLight",
|
|
70
|
+
"hemisphereLight", "rectAreaLight", "lightProbe",
|
|
71
|
+
"texture", "videoTexture", "canvasTexture", "cubeTexture",
|
|
72
|
+
"dataTexture", "compressedTexture", "depthTexture",
|
|
73
|
+
"vector2", "vector3", "vector4", "euler", "quaternion",
|
|
74
|
+
"matrix3", "matrix4", "color", "raycaster", "fog", "fogExp2",
|
|
75
|
+
// Drei components
|
|
76
|
+
"Billboard", "Text", "Text3D", "Html", "Trail", "Decal", "Edges", "Outlines",
|
|
77
|
+
"PerspectiveCamera", "OrthographicCamera", "CubeCamera",
|
|
78
|
+
"OrbitControls", "CameraControls", "TransformControls", "PivotControls",
|
|
79
|
+
"Environment", "Sky", "Stars", "Cloud", "Sparkles", "Float", "Stage",
|
|
80
|
+
"RoundedBox", "Line", "Points", "Instances", "Merged",
|
|
81
|
+
"MeshReflectorMaterial", "MeshTransmissionMaterial", "shaderMaterial",
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
// Simple AST Walker (fallback if estree-walker fails)
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
function simpleWalk(node, callbacks, parent = null) {
|
|
89
|
+
if (!node || typeof node !== "object") return;
|
|
90
|
+
|
|
91
|
+
// Add parent reference
|
|
92
|
+
if (parent && !Object.prototype.hasOwnProperty.call(node, "parent")) {
|
|
93
|
+
Object.defineProperty(node, "parent", {
|
|
94
|
+
value: parent,
|
|
95
|
+
enumerable: false,
|
|
96
|
+
writable: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (callbacks.enter) {
|
|
101
|
+
callbacks.enter(node, parent);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Walk children
|
|
105
|
+
for (const key of Object.keys(node)) {
|
|
106
|
+
if (key === "parent") continue;
|
|
107
|
+
const child = node[key];
|
|
108
|
+
if (Array.isArray(child)) {
|
|
109
|
+
for (const item of child) {
|
|
110
|
+
if (item && typeof item === "object" && item.type) {
|
|
111
|
+
simpleWalk(item, callbacks, node);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
115
|
+
simpleWalk(child, callbacks, node);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Use estree-walker if available, otherwise use simple walker
|
|
121
|
+
const walkAst = walk || simpleWalk;
|
|
122
|
+
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
// Helpers
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Quick check if source likely contains JSX
|
|
129
|
+
*/
|
|
130
|
+
function hasJSX(source) {
|
|
131
|
+
// Look for JSX patterns: <Component, <div, etc.
|
|
132
|
+
return /<[A-Za-z]/.test(source);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if element should receive location attributes
|
|
137
|
+
*/
|
|
138
|
+
function shouldInjectAttrs(tagName) {
|
|
139
|
+
if (!tagName) return false;
|
|
140
|
+
if (tagName === "Fragment" || tagName === "React.Fragment") return false;
|
|
141
|
+
if (EXCLUDED_ELEMENTS.has(tagName)) return false;
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Extract tag name from JSX opening element
|
|
147
|
+
*/
|
|
148
|
+
function getTagName(node) {
|
|
149
|
+
try {
|
|
150
|
+
const { name } = node;
|
|
151
|
+
if (name.type === "JSXIdentifier") {
|
|
152
|
+
return name.name;
|
|
153
|
+
}
|
|
154
|
+
if (name.type === "JSXMemberExpression") {
|
|
155
|
+
return `${name.object.name}.${name.property.name}`;
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Detect if JSX element is inside a .map() call
|
|
165
|
+
*/
|
|
166
|
+
function detectMapContext(node) {
|
|
167
|
+
try {
|
|
168
|
+
let current = node;
|
|
169
|
+
let depth = 0;
|
|
170
|
+
|
|
171
|
+
while (current && depth < 15) {
|
|
172
|
+
if (
|
|
173
|
+
current.type === "CallExpression" &&
|
|
174
|
+
current.callee?.type === "MemberExpression" &&
|
|
175
|
+
current.callee.property?.name === "map"
|
|
176
|
+
) {
|
|
177
|
+
const arrayRef = current.callee.object?.name;
|
|
178
|
+
const callback = current.arguments?.[0];
|
|
179
|
+
|
|
180
|
+
if (arrayRef && callback?.type === "ArrowFunctionExpression") {
|
|
181
|
+
const indexArg = callback.params?.[1];
|
|
182
|
+
return {
|
|
183
|
+
arrayName: arrayRef,
|
|
184
|
+
indexName: indexArg?.type === "Identifier" ? indexArg.name : null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
current = current.parent;
|
|
189
|
+
depth++;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
// Ignore errors
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Collect Next.js Image import aliases
|
|
199
|
+
*/
|
|
200
|
+
function collectImageAliases(ast) {
|
|
201
|
+
const aliases = new Set();
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
walkAst(ast, {
|
|
205
|
+
enter(node) {
|
|
206
|
+
if (
|
|
207
|
+
node.type === "ImportDeclaration" &&
|
|
208
|
+
node.source?.value === "next/image"
|
|
209
|
+
) {
|
|
210
|
+
for (const spec of node.specifiers || []) {
|
|
211
|
+
if (spec.local?.name) {
|
|
212
|
+
aliases.add(spec.local.name);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
} catch {
|
|
219
|
+
// Ignore errors
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return aliases;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
226
|
+
// Main Loader
|
|
227
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* FlexApp Element Tagger Loader
|
|
231
|
+
* SAFE: Returns original source on any error
|
|
232
|
+
*/
|
|
233
|
+
function flexElementTagger(source, sourceMap) {
|
|
234
|
+
const callback = this.async();
|
|
235
|
+
// return callback(null, source, sourceMap);
|
|
236
|
+
// Early exit: missing dependencies
|
|
237
|
+
if (!parse || !MagicString) {
|
|
238
|
+
return callback(null, source, sourceMap);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Early exit: no resource path or it's a directory
|
|
242
|
+
if (!this.resourcePath) {
|
|
243
|
+
return callback(null, source, sourceMap);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Early exit: path doesn't look like a file (no extension)
|
|
247
|
+
const ext = path.extname(this.resourcePath);
|
|
248
|
+
if (!ext || !['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
249
|
+
return callback(null, source, sourceMap);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Early exit: check if it's actually a file (not a directory)
|
|
253
|
+
try {
|
|
254
|
+
const stat = fs.statSync(this.resourcePath);
|
|
255
|
+
if (!stat.isFile()) {
|
|
256
|
+
return callback(null, source, sourceMap);
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
// If stat fails, still try to process (might be virtual file)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Early exit: node_modules
|
|
263
|
+
if (/node_modules/.test(this.resourcePath)) {
|
|
264
|
+
return callback(null, source, sourceMap);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Early exit: no JSX detected
|
|
268
|
+
if (!hasJSX(source)) {
|
|
269
|
+
return callback(null, source, sourceMap);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
// Parse source
|
|
274
|
+
const ast = parse(source, {
|
|
275
|
+
sourceType: "module",
|
|
276
|
+
plugins: ["jsx", "typescript"],
|
|
277
|
+
errorRecovery: true, // Don't fail on syntax errors
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!ast || !ast.program) {
|
|
281
|
+
return callback(null, source, sourceMap);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const magicStr = new MagicString(source);
|
|
285
|
+
const relativePath = path.relative(process.cwd(), this.resourcePath);
|
|
286
|
+
const imageAliases = collectImageAliases(ast);
|
|
287
|
+
let hasChanges = false;
|
|
288
|
+
|
|
289
|
+
// Add parent references
|
|
290
|
+
walkAst(ast.program, {
|
|
291
|
+
enter() { }, // Just to trigger parent ref addition
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Process JSX elements
|
|
295
|
+
walkAst(ast.program, {
|
|
296
|
+
enter(node) {
|
|
297
|
+
try {
|
|
298
|
+
if (node.type !== "JSXOpeningElement") return;
|
|
299
|
+
|
|
300
|
+
let tagName = getTagName(node);
|
|
301
|
+
if (!tagName) return;
|
|
302
|
+
|
|
303
|
+
// Check if already has data-flex-loc
|
|
304
|
+
const hasAttr = node.attributes?.some(
|
|
305
|
+
(attr) =>
|
|
306
|
+
attr.type === "JSXAttribute" &&
|
|
307
|
+
attr.name?.name === "data-flex-loc"
|
|
308
|
+
);
|
|
309
|
+
if (hasAttr) return;
|
|
310
|
+
|
|
311
|
+
// Normalize Next.js Image to img
|
|
312
|
+
const isNextImage = imageAliases.has(tagName.split(".")[0]);
|
|
313
|
+
const displayName = isNextImage ? "img" : tagName;
|
|
314
|
+
|
|
315
|
+
// Check if we should tag this element
|
|
316
|
+
if (!shouldInjectAttrs(tagName) && !isNextImage) return;
|
|
317
|
+
|
|
318
|
+
// Ensure we have location info
|
|
319
|
+
if (!node.loc?.start || !node.name?.end) return;
|
|
320
|
+
|
|
321
|
+
// Build location identifier
|
|
322
|
+
const { line, column } = node.loc.start;
|
|
323
|
+
let locId = `${relativePath}:${line}:${column}`;
|
|
324
|
+
|
|
325
|
+
// Add iteration context if inside .map()
|
|
326
|
+
const mapCtx = detectMapContext(node);
|
|
327
|
+
if (mapCtx?.arrayName) {
|
|
328
|
+
locId += `#${mapCtx.arrayName}`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Inject data-flex-iter for map index if available
|
|
332
|
+
if (mapCtx?.indexName) {
|
|
333
|
+
magicStr.appendLeft(
|
|
334
|
+
node.name.end,
|
|
335
|
+
` data-flex-iter=${mapCtx.indexName}`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Inject location and name attributes
|
|
340
|
+
magicStr.appendLeft(
|
|
341
|
+
node.name.end,
|
|
342
|
+
` data-flex-loc="${locId}" data-flex-name="${displayName}"`
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
hasChanges = true;
|
|
346
|
+
} catch {
|
|
347
|
+
// Ignore errors for individual nodes
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Return original if no changes
|
|
353
|
+
if (!hasChanges) {
|
|
354
|
+
return callback(null, source, sourceMap);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Generate output
|
|
358
|
+
const output = magicStr.toString();
|
|
359
|
+
const outputMap = magicStr.generateMap({ hires: true });
|
|
360
|
+
|
|
361
|
+
// Turbopack expects sourcemap as JSON string
|
|
362
|
+
callback(null, output, JSON.stringify(outputMap));
|
|
363
|
+
|
|
364
|
+
} catch (error) {
|
|
365
|
+
// On ANY error, return original source unchanged
|
|
366
|
+
console.warn("[flex-element-tagger] Skipping file due to error:", this.resourcePath);
|
|
367
|
+
callback(null, source, sourceMap);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
module.exports = flexElementTagger;
|
|
372
|
+
module.exports.default = flexElementTagger;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibexdotnew/inspector",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Visual inspection and element tagging for Next.js and Expo applications",
|
|
5
5
|
"author": "Vibex",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
29
|
"./package.json": "./package.json",
|
|
30
|
-
"./loader.js": "./next/element-tagger.
|
|
30
|
+
"./loader.js": "./next/element-tagger.cjs",
|
|
31
31
|
"./transformer.js": "./expo/metro-transformer.cjs",
|
|
32
32
|
"./next": {
|
|
33
33
|
"types": "./dist/next/index.d.ts",
|