@leadconnector/vibe-tagger 0.1.0 → 0.2.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 +4 -50
- package/dist/index.cjs +205 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +195 -7
- package/dist/index.js.map +1 -1
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -1,55 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Vite Plugin Component Tagger
|
|
2
2
|
|
|
3
|
-
Vite plugin that
|
|
3
|
+
A Vite plugin that automatically tags JSX/TSX components with source location metadata for easier debugging and tracking.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install
|
|
9
|
-
# or
|
|
10
|
-
bun add -D vibe-tagger
|
|
11
|
-
# or
|
|
12
|
-
yarn add -D vibe-tagger
|
|
8
|
+
npm install @leadconnector/vibe-tagger
|
|
13
9
|
```
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
// vite.config.ts
|
|
19
|
-
import { defineConfig } from "vite";
|
|
20
|
-
import react from "@vitejs/plugin-react-swc";
|
|
21
|
-
import { componentTagger } from "vibe-tagger";
|
|
22
|
-
|
|
23
|
-
export default defineConfig(({ mode }) => ({
|
|
24
|
-
plugins: [
|
|
25
|
-
react(),
|
|
26
|
-
mode === "development" && componentTagger(),
|
|
27
|
-
].filter(Boolean),
|
|
28
|
-
}));
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## What it does
|
|
32
|
-
|
|
33
|
-
In development mode, the plugin intercepts React's `jsx-dev-runtime` to:
|
|
34
|
-
|
|
35
|
-
1. Attach `Symbol.for("__jsxSource__")` to every DOM element with `{ fileName, lineNumber, columnNumber, displayName }`
|
|
36
|
-
2. Maintain a global `window.sourceElementMap` (`Map<string, Set<WeakRef<Element>>>`) for fast source-to-element lookups
|
|
37
|
-
|
|
38
|
-
This enables visual editing tools to map clicked elements back to their source code location.
|
|
39
|
-
|
|
40
|
-
## API
|
|
41
|
-
|
|
42
|
-
### `componentTagger(options?): Plugin`
|
|
43
|
-
|
|
44
|
-
Returns a Vite plugin. Uses `enforce: "pre"` to run before other plugins.
|
|
45
|
-
|
|
46
|
-
#### Options
|
|
47
|
-
|
|
48
|
-
| Option | Type | Default | Description |
|
|
49
|
-
|--------|------|---------|-------------|
|
|
50
|
-
| `jsxSource` | `boolean` | `true` | Enable JSX source tagging |
|
|
51
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
52
|
-
|
|
53
|
-
## License
|
|
54
|
-
|
|
55
|
-
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
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
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -181,33 +191,221 @@ function createJsxTaggerFeature() {
|
|
|
181
191
|
};
|
|
182
192
|
}
|
|
183
193
|
|
|
194
|
+
// src/features/tailwindConfig.ts
|
|
195
|
+
var esbuild = __toESM(require("esbuild"), 1);
|
|
196
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
197
|
+
var import_path = __toESM(require("path"), 1);
|
|
198
|
+
var import_resolveConfig = __toESM(require("tailwindcss/resolveConfig.js"), 1);
|
|
199
|
+
var V4_CSS_CANDIDATES = [
|
|
200
|
+
"src/styles.css",
|
|
201
|
+
"src/index.css",
|
|
202
|
+
"src/globals.css",
|
|
203
|
+
"src/app.css"
|
|
204
|
+
];
|
|
205
|
+
var V4_NAMESPACES = {
|
|
206
|
+
colors: "--color",
|
|
207
|
+
screens: "--breakpoint",
|
|
208
|
+
spacing: "--spacing",
|
|
209
|
+
borderRadius: "--radius",
|
|
210
|
+
fontFamily: "--font",
|
|
211
|
+
opacity: "--opacity"
|
|
212
|
+
};
|
|
213
|
+
var OUTFILE_RELATIVE = "./src/tailwind.config.vibe.json";
|
|
214
|
+
var INTERMEDIATE_RELATIVE = "./.vibe.tailwind.config.js";
|
|
215
|
+
var V3_CONFIG_RELATIVE = "./tailwind.config.ts";
|
|
216
|
+
async function findV4CssEntry(projectRoot) {
|
|
217
|
+
for (const candidate of V4_CSS_CANDIDATES) {
|
|
218
|
+
const abs = import_path.default.resolve(projectRoot, candidate);
|
|
219
|
+
try {
|
|
220
|
+
const contents = await import_promises.default.readFile(abs, "utf8");
|
|
221
|
+
if (/@import\s+["']tailwindcss/.test(contents))
|
|
222
|
+
return abs;
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
async function loadDesignSystemFromProject() {
|
|
229
|
+
try {
|
|
230
|
+
const modName = "@tailwindcss/node";
|
|
231
|
+
const mod = await import(modName);
|
|
232
|
+
return mod.__unstable__loadDesignSystem ?? null;
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function parseRootVars(css) {
|
|
238
|
+
const vars = {};
|
|
239
|
+
const rootMatch = css.match(/:root\s*\{([^}]*)\}/s);
|
|
240
|
+
if (!rootMatch)
|
|
241
|
+
return vars;
|
|
242
|
+
for (const decl of rootMatch[1].split(";")) {
|
|
243
|
+
const trimmed = decl.trim();
|
|
244
|
+
if (!trimmed.startsWith("--"))
|
|
245
|
+
continue;
|
|
246
|
+
const colon = trimmed.indexOf(":");
|
|
247
|
+
if (colon === -1)
|
|
248
|
+
continue;
|
|
249
|
+
const key = trimmed.slice(0, colon).trim();
|
|
250
|
+
const value = trimmed.slice(colon + 1).trim();
|
|
251
|
+
if (key && value)
|
|
252
|
+
vars[key] = value;
|
|
253
|
+
}
|
|
254
|
+
return vars;
|
|
255
|
+
}
|
|
256
|
+
function resolveVars(value, rootVars, depth = 0) {
|
|
257
|
+
if (depth > 8)
|
|
258
|
+
return value;
|
|
259
|
+
return value.replace(
|
|
260
|
+
/var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/g,
|
|
261
|
+
(match, name, fallback) => {
|
|
262
|
+
const resolved = rootVars[name];
|
|
263
|
+
if (resolved !== void 0)
|
|
264
|
+
return resolveVars(resolved, rootVars, depth + 1);
|
|
265
|
+
return fallback ?? match;
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
async function generateV4Config(cssEntry, outfile, load) {
|
|
270
|
+
const css = await import_promises.default.readFile(cssEntry, "utf8");
|
|
271
|
+
const ds = await load(css, { base: import_path.default.dirname(cssEntry) });
|
|
272
|
+
const rootVars = parseRootVars(css);
|
|
273
|
+
const theme = {};
|
|
274
|
+
for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {
|
|
275
|
+
const entries = {};
|
|
276
|
+
for (const [key, rawValue] of ds.theme.namespace(namespace)) {
|
|
277
|
+
if (key === null)
|
|
278
|
+
continue;
|
|
279
|
+
entries[key] = resolveVars(rawValue, rootVars);
|
|
280
|
+
}
|
|
281
|
+
theme[configKey] = entries;
|
|
282
|
+
}
|
|
283
|
+
await import_promises.default.mkdir(import_path.default.dirname(outfile), { recursive: true });
|
|
284
|
+
await import_promises.default.writeFile(outfile, JSON.stringify({ theme }, null, 2));
|
|
285
|
+
}
|
|
286
|
+
async function generateV3Config(tailwindInputFile, tailwindIntermediateFile, tailwindJsonOutfile) {
|
|
287
|
+
await esbuild.build({
|
|
288
|
+
entryPoints: [tailwindInputFile],
|
|
289
|
+
outfile: tailwindIntermediateFile,
|
|
290
|
+
bundle: true,
|
|
291
|
+
format: "esm",
|
|
292
|
+
banner: {
|
|
293
|
+
js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);'
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
try {
|
|
297
|
+
const userConfig = await import(tailwindIntermediateFile + "?update=" + Date.now());
|
|
298
|
+
if (!userConfig || !userConfig.default) {
|
|
299
|
+
throw new Error("Invalid Tailwind config structure");
|
|
300
|
+
}
|
|
301
|
+
const resolved = (0, import_resolveConfig.default)(userConfig.default);
|
|
302
|
+
await import_promises.default.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));
|
|
303
|
+
} finally {
|
|
304
|
+
await import_promises.default.unlink(tailwindIntermediateFile).catch(() => {
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function createTailwindConfigFeature() {
|
|
309
|
+
let projectRoot = "";
|
|
310
|
+
const run = async () => {
|
|
311
|
+
if (!projectRoot)
|
|
312
|
+
return;
|
|
313
|
+
const outfile = import_path.default.resolve(projectRoot, OUTFILE_RELATIVE);
|
|
314
|
+
const cssEntry = await findV4CssEntry(projectRoot);
|
|
315
|
+
if (cssEntry) {
|
|
316
|
+
const load = await loadDesignSystemFromProject();
|
|
317
|
+
if (load) {
|
|
318
|
+
try {
|
|
319
|
+
await generateV4Config(cssEntry, outfile, load);
|
|
320
|
+
console.log(`[vibe-tagger] wrote ${outfile}`);
|
|
321
|
+
return;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error("[vibe-tagger] Error generating v4 tailwind config:", error);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const v3ConfigFile = import_path.default.resolve(projectRoot, V3_CONFIG_RELATIVE);
|
|
329
|
+
try {
|
|
330
|
+
await import_promises.default.access(v3ConfigFile);
|
|
331
|
+
} catch {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const intermediate = import_path.default.resolve(projectRoot, INTERMEDIATE_RELATIVE);
|
|
335
|
+
try {
|
|
336
|
+
await generateV3Config(v3ConfigFile, intermediate, outfile);
|
|
337
|
+
console.log(`[vibe-tagger] wrote ${outfile}`);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error("[vibe-tagger] Error generating v3 tailwind config:", error);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
onConfigResolved(config) {
|
|
344
|
+
projectRoot = config.root;
|
|
345
|
+
},
|
|
346
|
+
async onBuildStart() {
|
|
347
|
+
await run();
|
|
348
|
+
},
|
|
349
|
+
onConfigureServer(server) {
|
|
350
|
+
try {
|
|
351
|
+
const v3ConfigFile = import_path.default.resolve(projectRoot, V3_CONFIG_RELATIVE);
|
|
352
|
+
const v4Candidates = V4_CSS_CANDIDATES.map(
|
|
353
|
+
(c) => import_path.default.resolve(projectRoot, c)
|
|
354
|
+
);
|
|
355
|
+
const watchPaths = [v3ConfigFile, ...v4Candidates];
|
|
356
|
+
for (const p of watchPaths)
|
|
357
|
+
server.watcher.add(p);
|
|
358
|
+
const normalized = new Set(watchPaths.map((p) => import_path.default.normalize(p)));
|
|
359
|
+
server.watcher.on("change", async (changedPath) => {
|
|
360
|
+
if (normalized.has(import_path.default.normalize(changedPath)))
|
|
361
|
+
await run();
|
|
362
|
+
});
|
|
363
|
+
} catch (error) {
|
|
364
|
+
console.error("[vibe-tagger] Error adding tailwind watcher:", error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
184
370
|
// src/plugin.ts
|
|
185
371
|
function vibeTagger({
|
|
186
372
|
jsxSource = true,
|
|
187
|
-
|
|
373
|
+
tailwindConfig = false,
|
|
374
|
+
debug: _debug = false
|
|
188
375
|
} = {}) {
|
|
189
376
|
const features = [];
|
|
190
|
-
if (jsxSource)
|
|
377
|
+
if (jsxSource)
|
|
191
378
|
features.push(createJsxTaggerFeature());
|
|
192
|
-
|
|
379
|
+
if (tailwindConfig)
|
|
380
|
+
features.push(createTailwindConfigFeature());
|
|
193
381
|
return {
|
|
194
382
|
name: "vibe-tagger",
|
|
195
383
|
enforce: "pre",
|
|
384
|
+
configResolved(config) {
|
|
385
|
+
for (const feature of features)
|
|
386
|
+
feature.onConfigResolved?.(config);
|
|
387
|
+
},
|
|
388
|
+
async buildStart() {
|
|
389
|
+
for (const feature of features)
|
|
390
|
+
await feature.onBuildStart?.();
|
|
391
|
+
},
|
|
392
|
+
configureServer(server) {
|
|
393
|
+
for (const feature of features)
|
|
394
|
+
feature.onConfigureServer?.(server);
|
|
395
|
+
},
|
|
196
396
|
resolveId(id, importer) {
|
|
197
397
|
for (const feature of features) {
|
|
198
398
|
const result = feature.resolveId?.(id, importer);
|
|
199
|
-
if (result !== null && result !== void 0)
|
|
399
|
+
if (result !== null && result !== void 0)
|
|
200
400
|
return result;
|
|
201
|
-
}
|
|
202
401
|
}
|
|
203
402
|
return null;
|
|
204
403
|
},
|
|
205
404
|
load(id) {
|
|
206
405
|
for (const feature of features) {
|
|
207
406
|
const result = feature.load?.(id);
|
|
208
|
-
if (result !== null && result !== void 0)
|
|
407
|
+
if (result !== null && result !== void 0)
|
|
209
408
|
return result;
|
|
210
|
-
}
|
|
211
409
|
}
|
|
212
410
|
return null;
|
|
213
411
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/devRuntimeCode.ts","../src/features/jsxSource.ts","../src/plugin.ts"],"sourcesContent":["export { vibeTagger as componentTagger } from \"./plugin\";\nexport type { VibeTaggerOptions } from \"./plugin\";\n","// Runtime code that replaces react/jsx-dev-runtime in the browser.\n// Served as a virtual module by the Vite plugin.\n\nconst devRuntimeCode = [\n 'import * as React from \"react\";',\n 'import * as ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n 'const SOURCE_KEY = Symbol.for(\"__jsxSource__\");',\n \"\",\n \"const cleanFileName = (fileName) => {\",\n ' if (!fileName) return \"\";',\n ' if (fileName.includes(\"dev_server\")) {',\n ' fileName = fileName.split(\"dev_server\")[1].slice(1);',\n \" }\",\n ' if (fileName.includes(\"sandbox-scheduler/sandbox\")) {',\n ' const sandboxPart = fileName.split(\"sandbox-scheduler/\")[1];',\n ' fileName = sandboxPart.split(\"/\").slice(1).join(\"/\");',\n \" }\",\n ' return fileName.replace(/^\\\\/dev-server\\\\//, \"\");',\n \"};\",\n \"\",\n \"const sourceElementMap = new Map();\",\n \"window.sourceElementMap = sourceElementMap;\",\n \"\",\n \"function getSourceKey(sourceInfo) {\",\n \" return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;\",\n \"}\",\n \"\",\n \"function unregisterElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" const refs = sourceElementMap.get(key);\",\n \" if (refs) {\",\n \" for (const ref of refs) {\",\n \" if (ref.deref() === node) {\",\n \" refs.delete(ref);\",\n \" break;\",\n \" }\",\n \" }\",\n \" if (refs.size === 0) {\",\n \" sourceElementMap.delete(key);\",\n \" }\",\n \" }\",\n \"}\",\n \"\",\n \"function registerElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" if (!sourceElementMap.has(key)) {\",\n \" sourceElementMap.set(key, new Set());\",\n \" }\",\n \" sourceElementMap.get(key).add(new WeakRef(node));\",\n \"}\",\n \"\",\n \"function getTypeName(type) {\",\n ' if (typeof type === \"string\") return type;',\n ' if (typeof type === \"function\") return type.displayName || type.name || \"Unknown\";',\n ' if (typeof type === \"object\" && type !== null) {',\n ' return type.displayName || type.render?.displayName || type.render?.name || \"Unknown\";',\n \" }\",\n ' return \"Unknown\";',\n \"}\",\n \"\",\n \"export function jsxDEV(type, props, key, isStatic, source, self) {\",\n \" // For custom components (like <Icon />, <Button />), tag their rendered output\",\n \" // This captures the JSX element name for library components that don't have source info\",\n ' if (source?.fileName && typeof type !== \"string\" && type !== Fragment) {',\n \" const typeName = getTypeName(type);\",\n \" const jsxSourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: typeName,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" // Only tag if this element doesn't already have source info\",\n \" // (library components won't have it, user components will)\",\n \" if (!node[SOURCE_KEY]) {\",\n \" node[SOURCE_KEY] = jsxSourceInfo;\",\n \" registerElement(node, jsxSourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \"\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" // For host elements (div, span, etc.), tag with component context\",\n ' if (source?.fileName && typeof type === \"string\") {',\n \" const sourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: type,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \"\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" const existingSource = node[SOURCE_KEY];\",\n \" if (existingSource) {\",\n \" if (getSourceKey(existingSource) !== getSourceKey(sourceInfo)) {\",\n \" unregisterElement(node, existingSource);\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" } else {\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \"}\",\n].join(\"\\n\");\n\nexport default devRuntimeCode;\n","import devRuntimeCode from \"../devRuntimeCode\";\n\nexport interface JsxSourceFeature {\n resolveId(id: string, importer?: string): string | null;\n load(id: string): string | null;\n}\n\nexport function createJsxTaggerFeature(): JsxSourceFeature {\n return {\n resolveId(id: string, importer?: string): string | null {\n if (id === \"react/jsx-dev-runtime\" && !importer?.includes(\"\\0jsx-source\")) {\n return \"\\0jsx-source/jsx-dev-runtime\";\n }\n return null;\n },\n load(id: string): string | null {\n if (id === \"\\0jsx-source/jsx-dev-runtime\") {\n return devRuntimeCode;\n }\n return null;\n },\n };\n}\n","import type { Plugin } from \"vite\";\nimport { createJsxTaggerFeature } from \"./features/jsxSource\";\n\nexport interface VibeTaggerOptions {\n /** Enable JSX source tagging. Default: true */\n jsxSource?: boolean;\n /** Enable debug logging. Default: false */\n debug?: boolean;\n}\n\nexport function vibeTagger({\n jsxSource = true,\n debug = false,\n}: VibeTaggerOptions = {}): Plugin {\n const features: ReturnType<typeof createJsxTaggerFeature>[] = [];\n\n if (jsxSource) {\n features.push(createJsxTaggerFeature());\n }\n\n return {\n name: \"vibe-tagger\",\n enforce: \"pre\",\n\n resolveId(id: string, importer?: string) {\n for (const feature of features) {\n const result = feature.resolveId?.(id, importer);\n if (result !== null && result !== undefined) {\n return result;\n }\n }\n return null;\n },\n\n load(id: string) {\n for (const feature of features) {\n const result = feature.load?.(id);\n if (result !== null && result !== undefined) {\n return result;\n }\n }\n return null;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAO,yBAAQ;;;ACrIR,SAAS,yBAA2C;AACzD,SAAO;AAAA,IACL,UAAU,IAAY,UAAkC;AACtD,UAAI,OAAO,2BAA2B,CAAC,UAAU,SAAS,cAAc,GAAG;AACzE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK,IAA2B;AAC9B,UAAI,OAAO,gCAAgC;AACzC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACZO,SAAS,WAAW;AAAA,EACzB,YAAY;AAAA,EACZ,QAAQ;AACV,IAAuB,CAAC,GAAW;AACjC,QAAM,WAAwD,CAAC;AAE/D,MAAI,WAAW;AACb,aAAS,KAAK,uBAAuB,CAAC;AAAA,EACxC;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,UAAU,IAAY,UAAmB;AACvC,iBAAW,WAAW,UAAU;AAC9B,cAAM,SAAS,QAAQ,YAAY,IAAI,QAAQ;AAC/C,YAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAY;AACf,iBAAW,WAAW,UAAU;AAC9B,cAAM,SAAS,QAAQ,OAAO,EAAE;AAChC,YAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/devRuntimeCode.ts","../src/features/jsxSource.ts","../src/features/tailwindConfig.ts","../src/plugin.ts"],"sourcesContent":["export { vibeTagger as componentTagger } from \"./plugin\";\nexport type { VibeTaggerOptions } from \"./plugin\";\n","// Runtime code that replaces react/jsx-dev-runtime in the browser.\n// Served as a virtual module by the Vite plugin.\n\nconst devRuntimeCode = [\n 'import * as React from \"react\";',\n 'import * as ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n 'const SOURCE_KEY = Symbol.for(\"__jsxSource__\");',\n \"\",\n \"const cleanFileName = (fileName) => {\",\n ' if (!fileName) return \"\";',\n ' if (fileName.includes(\"dev_server\")) {',\n ' fileName = fileName.split(\"dev_server\")[1].slice(1);',\n \" }\",\n ' if (fileName.includes(\"sandbox-scheduler/sandbox\")) {',\n ' const sandboxPart = fileName.split(\"sandbox-scheduler/\")[1];',\n ' fileName = sandboxPart.split(\"/\").slice(1).join(\"/\");',\n \" }\",\n ' return fileName.replace(/^\\\\/dev-server\\\\//, \"\");',\n \"};\",\n \"\",\n \"const sourceElementMap = new Map();\",\n \"window.sourceElementMap = sourceElementMap;\",\n \"\",\n \"function getSourceKey(sourceInfo) {\",\n \" return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;\",\n \"}\",\n \"\",\n \"function unregisterElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" const refs = sourceElementMap.get(key);\",\n \" if (refs) {\",\n \" for (const ref of refs) {\",\n \" if (ref.deref() === node) {\",\n \" refs.delete(ref);\",\n \" break;\",\n \" }\",\n \" }\",\n \" if (refs.size === 0) {\",\n \" sourceElementMap.delete(key);\",\n \" }\",\n \" }\",\n \"}\",\n \"\",\n \"function registerElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" if (!sourceElementMap.has(key)) {\",\n \" sourceElementMap.set(key, new Set());\",\n \" }\",\n \" sourceElementMap.get(key).add(new WeakRef(node));\",\n \"}\",\n \"\",\n \"function getTypeName(type) {\",\n ' if (typeof type === \"string\") return type;',\n ' if (typeof type === \"function\") return type.displayName || type.name || \"Unknown\";',\n ' if (typeof type === \"object\" && type !== null) {',\n ' return type.displayName || type.render?.displayName || type.render?.name || \"Unknown\";',\n \" }\",\n ' return \"Unknown\";',\n \"}\",\n \"\",\n \"export function jsxDEV(type, props, key, isStatic, source, self) {\",\n \" // For custom components (like <Icon />, <Button />), tag their rendered output\",\n \" // This captures the JSX element name for library components that don't have source info\",\n ' if (source?.fileName && typeof type !== \"string\" && type !== Fragment) {',\n \" const typeName = getTypeName(type);\",\n \" const jsxSourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: typeName,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" // Only tag if this element doesn't already have source info\",\n \" // (library components won't have it, user components will)\",\n \" if (!node[SOURCE_KEY]) {\",\n \" node[SOURCE_KEY] = jsxSourceInfo;\",\n \" registerElement(node, jsxSourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \"\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" // For host elements (div, span, etc.), tag with component context\",\n ' if (source?.fileName && typeof type === \"string\") {',\n \" const sourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: type,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \"\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" const existingSource = node[SOURCE_KEY];\",\n \" if (existingSource) {\",\n \" if (getSourceKey(existingSource) !== getSourceKey(sourceInfo)) {\",\n \" unregisterElement(node, existingSource);\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" } else {\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \"}\",\n].join(\"\\n\");\n\nexport default devRuntimeCode;\n","import devRuntimeCode from \"../devRuntimeCode\";\n\nexport interface JsxSourceFeature {\n resolveId(id: string, importer?: string): string | null;\n load(id: string): string | null;\n}\n\nexport function createJsxTaggerFeature(): JsxSourceFeature {\n return {\n resolveId(id: string, importer?: string): string | null {\n if (id === \"react/jsx-dev-runtime\" && !importer?.includes(\"\\0jsx-source\")) {\n return \"\\0jsx-source/jsx-dev-runtime\";\n }\n return null;\n },\n load(id: string): string | null {\n if (id === \"\\0jsx-source/jsx-dev-runtime\") {\n return devRuntimeCode;\n }\n return null;\n },\n };\n}\n","import type { ResolvedConfig, ViteDevServer } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport resolveConfig from \"tailwindcss/resolveConfig.js\";\n\nexport interface TailwindConfigFeature {\n onConfigResolved(config: ResolvedConfig): void;\n onBuildStart(): Promise<void>;\n onConfigureServer(server: ViteDevServer): void;\n}\n\nconst V4_CSS_CANDIDATES = [\n \"src/styles.css\",\n \"src/index.css\",\n \"src/globals.css\",\n \"src/app.css\",\n];\n\nconst V4_NAMESPACES: Record<string, string> = {\n colors: \"--color\",\n screens: \"--breakpoint\",\n spacing: \"--spacing\",\n borderRadius: \"--radius\",\n fontFamily: \"--font\",\n opacity: \"--opacity\",\n};\n\nconst OUTFILE_RELATIVE = \"./src/tailwind.config.vibe.json\";\nconst INTERMEDIATE_RELATIVE = \"./.vibe.tailwind.config.js\";\nconst V3_CONFIG_RELATIVE = \"./tailwind.config.ts\";\n\nasync function findV4CssEntry(projectRoot: string): Promise<string | null> {\n for (const candidate of V4_CSS_CANDIDATES) {\n const abs = path.resolve(projectRoot, candidate);\n try {\n const contents = await fs.readFile(abs, \"utf8\");\n if (/@import\\s+[\"']tailwindcss/.test(contents)) return abs;\n } catch {\n // missing file — try next\n }\n }\n return null;\n}\n\ntype LoadDesignSystem = (\n css: string,\n opts: { base: string },\n) => Promise<{\n theme: {\n namespace(ns: string): Iterable<[string | null, string]>;\n };\n}>;\n\nasync function loadDesignSystemFromProject(): Promise<LoadDesignSystem | null> {\n try {\n // Dynamic import so consumers not on v4 don't need the package installed.\n const modName = \"@tailwindcss/node\";\n const mod = (await import(modName)) as {\n __unstable__loadDesignSystem?: LoadDesignSystem;\n };\n return mod.__unstable__loadDesignSystem ?? null;\n } catch {\n return null;\n }\n}\n\nfunction parseRootVars(css: string): Record<string, string> {\n const vars: Record<string, string> = {};\n const rootMatch = css.match(/:root\\s*\\{([^}]*)\\}/s);\n if (!rootMatch) return vars;\n for (const decl of rootMatch[1]!.split(\";\")) {\n const trimmed = decl.trim();\n if (!trimmed.startsWith(\"--\")) continue;\n const colon = trimmed.indexOf(\":\");\n if (colon === -1) continue;\n const key = trimmed.slice(0, colon).trim();\n const value = trimmed.slice(colon + 1).trim();\n if (key && value) vars[key] = value;\n }\n return vars;\n}\n\nfunction resolveVars(\n value: string,\n rootVars: Record<string, string>,\n depth = 0,\n): string {\n if (depth > 8) return value;\n return value.replace(\n /var\\(\\s*(--[\\w-]+)(?:\\s*,\\s*([^)]+))?\\s*\\)/g,\n (match, name: string, fallback: string | undefined) => {\n const resolved = rootVars[name];\n if (resolved !== undefined) return resolveVars(resolved, rootVars, depth + 1);\n return fallback ?? match;\n },\n );\n}\n\nasync function generateV4Config(\n cssEntry: string,\n outfile: string,\n load: LoadDesignSystem,\n): Promise<void> {\n const css = await fs.readFile(cssEntry, \"utf8\");\n const ds = await load(css, { base: path.dirname(cssEntry) });\n const rootVars = parseRootVars(css);\n const theme: Record<string, Record<string, string>> = {};\n for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {\n const entries: Record<string, string> = {};\n for (const [key, rawValue] of ds.theme.namespace(namespace)) {\n if (key === null) continue;\n entries[key] = resolveVars(rawValue, rootVars);\n }\n theme[configKey] = entries;\n }\n await fs.mkdir(path.dirname(outfile), { recursive: true });\n await fs.writeFile(outfile, JSON.stringify({ theme }, null, 2));\n}\n\nasync function generateV3Config(\n tailwindInputFile: string,\n tailwindIntermediateFile: string,\n tailwindJsonOutfile: string,\n): Promise<void> {\n await esbuild.build({\n entryPoints: [tailwindInputFile],\n outfile: tailwindIntermediateFile,\n bundle: true,\n format: \"esm\",\n banner: {\n js: 'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);',\n },\n });\n try {\n const userConfig = (await import(\n tailwindIntermediateFile + \"?update=\" + Date.now()\n )) as { default?: unknown };\n if (!userConfig || !userConfig.default) {\n throw new Error(\"Invalid Tailwind config structure\");\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const resolved = (resolveConfig as any)(userConfig.default);\n await fs.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));\n } finally {\n await fs.unlink(tailwindIntermediateFile).catch(() => {});\n }\n}\n\nexport function createTailwindConfigFeature(): TailwindConfigFeature {\n let projectRoot = \"\";\n\n const run = async (): Promise<void> => {\n if (!projectRoot) return;\n const outfile = path.resolve(projectRoot, OUTFILE_RELATIVE);\n\n // Prefer v4 when the CSS entry uses `@import \"tailwindcss\"` and\n // @tailwindcss/node is available.\n const cssEntry = await findV4CssEntry(projectRoot);\n if (cssEntry) {\n const load = await loadDesignSystemFromProject();\n if (load) {\n try {\n await generateV4Config(cssEntry, outfile, load);\n console.log(`[vibe-tagger] wrote ${outfile}`);\n return;\n } catch (error) {\n console.error(\"[vibe-tagger] Error generating v4 tailwind config:\", error);\n return;\n }\n }\n }\n\n // Fall back to v3: compile tailwind.config.ts via esbuild, then resolveConfig.\n const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);\n try {\n await fs.access(v3ConfigFile);\n } catch {\n return;\n }\n const intermediate = path.resolve(projectRoot, INTERMEDIATE_RELATIVE);\n try {\n await generateV3Config(v3ConfigFile, intermediate, outfile);\n console.log(`[vibe-tagger] wrote ${outfile}`);\n } catch (error) {\n console.error(\"[vibe-tagger] Error generating v3 tailwind config:\", error);\n }\n };\n\n return {\n onConfigResolved(config) {\n projectRoot = config.root;\n },\n async onBuildStart() {\n await run();\n },\n onConfigureServer(server) {\n try {\n const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);\n const v4Candidates = V4_CSS_CANDIDATES.map((c) =>\n path.resolve(projectRoot, c),\n );\n const watchPaths = [v3ConfigFile, ...v4Candidates];\n for (const p of watchPaths) server.watcher.add(p);\n const normalized = new Set(watchPaths.map((p) => path.normalize(p)));\n server.watcher.on(\"change\", async (changedPath) => {\n if (normalized.has(path.normalize(changedPath))) await run();\n });\n } catch (error) {\n console.error(\"[vibe-tagger] Error adding tailwind watcher:\", error);\n }\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer } from \"vite\";\nimport { createJsxTaggerFeature } from \"./features/jsxSource\";\nimport { createTailwindConfigFeature } from \"./features/tailwindConfig\";\n\nexport interface VibeTaggerOptions {\n /** Enable JSX source tagging. Default: true */\n jsxSource?: boolean;\n /**\n * Emit a resolved Tailwind theme JSON to `src/tailwind.config.vibe.json`\n * on build start and on config-file changes. Supports Tailwind v3\n * (reads `tailwind.config.ts`) and v4 (reads `src/styles.css` etc.).\n * Default: false\n */\n tailwindConfig?: boolean;\n /** Enable debug logging. Default: false */\n debug?: boolean;\n}\n\ninterface Feature {\n resolveId?(id: string, importer?: string): string | null;\n load?(id: string): string | null;\n onConfigResolved?(config: ResolvedConfig): void;\n onBuildStart?(): void | Promise<void>;\n onConfigureServer?(server: ViteDevServer): void;\n}\n\nexport function vibeTagger({\n jsxSource = true,\n tailwindConfig = false,\n debug: _debug = false,\n}: VibeTaggerOptions = {}): Plugin {\n const features: Feature[] = [];\n\n if (jsxSource) features.push(createJsxTaggerFeature());\n if (tailwindConfig) features.push(createTailwindConfigFeature());\n\n return {\n name: \"vibe-tagger\",\n enforce: \"pre\",\n\n configResolved(config) {\n for (const feature of features) feature.onConfigResolved?.(config);\n },\n\n async buildStart() {\n for (const feature of features) await feature.onBuildStart?.();\n },\n\n configureServer(server) {\n for (const feature of features) feature.onConfigureServer?.(server);\n },\n\n resolveId(id: string, importer?: string) {\n for (const feature of features) {\n const result = feature.resolveId?.(id, importer);\n if (result !== null && result !== undefined) return result;\n }\n return null;\n },\n\n load(id: string) {\n for (const feature of features) {\n const result = feature.load?.(id);\n if (result !== null && result !== undefined) return result;\n }\n return null;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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","names":["path","fs","resolveConfig"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,9 +3,16 @@ import { Plugin } from 'vite';
|
|
|
3
3
|
interface VibeTaggerOptions {
|
|
4
4
|
/** Enable JSX source tagging. Default: true */
|
|
5
5
|
jsxSource?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Emit a resolved Tailwind theme JSON to `src/tailwind.config.vibe.json`
|
|
8
|
+
* on build start and on config-file changes. Supports Tailwind v3
|
|
9
|
+
* (reads `tailwind.config.ts`) and v4 (reads `src/styles.css` etc.).
|
|
10
|
+
* Default: false
|
|
11
|
+
*/
|
|
12
|
+
tailwindConfig?: boolean;
|
|
6
13
|
/** Enable debug logging. Default: false */
|
|
7
14
|
debug?: boolean;
|
|
8
15
|
}
|
|
9
|
-
declare function vibeTagger({ jsxSource, debug, }?: VibeTaggerOptions): Plugin;
|
|
16
|
+
declare function vibeTagger({ jsxSource, tailwindConfig, debug: _debug, }?: VibeTaggerOptions): Plugin;
|
|
10
17
|
|
|
11
18
|
export { type VibeTaggerOptions, vibeTagger as componentTagger };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,16 @@ import { Plugin } from 'vite';
|
|
|
3
3
|
interface VibeTaggerOptions {
|
|
4
4
|
/** Enable JSX source tagging. Default: true */
|
|
5
5
|
jsxSource?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Emit a resolved Tailwind theme JSON to `src/tailwind.config.vibe.json`
|
|
8
|
+
* on build start and on config-file changes. Supports Tailwind v3
|
|
9
|
+
* (reads `tailwind.config.ts`) and v4 (reads `src/styles.css` etc.).
|
|
10
|
+
* Default: false
|
|
11
|
+
*/
|
|
12
|
+
tailwindConfig?: boolean;
|
|
6
13
|
/** Enable debug logging. Default: false */
|
|
7
14
|
debug?: boolean;
|
|
8
15
|
}
|
|
9
|
-
declare function vibeTagger({ jsxSource, debug, }?: VibeTaggerOptions): Plugin;
|
|
16
|
+
declare function vibeTagger({ jsxSource, tailwindConfig, debug: _debug, }?: VibeTaggerOptions): Plugin;
|
|
10
17
|
|
|
11
18
|
export { type VibeTaggerOptions, vibeTagger as componentTagger };
|
package/dist/index.js
CHANGED
|
@@ -155,33 +155,221 @@ function createJsxTaggerFeature() {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// src/features/tailwindConfig.ts
|
|
159
|
+
import * as esbuild from "esbuild";
|
|
160
|
+
import fs from "fs/promises";
|
|
161
|
+
import path from "path";
|
|
162
|
+
import resolveConfig from "tailwindcss/resolveConfig.js";
|
|
163
|
+
var V4_CSS_CANDIDATES = [
|
|
164
|
+
"src/styles.css",
|
|
165
|
+
"src/index.css",
|
|
166
|
+
"src/globals.css",
|
|
167
|
+
"src/app.css"
|
|
168
|
+
];
|
|
169
|
+
var V4_NAMESPACES = {
|
|
170
|
+
colors: "--color",
|
|
171
|
+
screens: "--breakpoint",
|
|
172
|
+
spacing: "--spacing",
|
|
173
|
+
borderRadius: "--radius",
|
|
174
|
+
fontFamily: "--font",
|
|
175
|
+
opacity: "--opacity"
|
|
176
|
+
};
|
|
177
|
+
var OUTFILE_RELATIVE = "./src/tailwind.config.vibe.json";
|
|
178
|
+
var INTERMEDIATE_RELATIVE = "./.vibe.tailwind.config.js";
|
|
179
|
+
var V3_CONFIG_RELATIVE = "./tailwind.config.ts";
|
|
180
|
+
async function findV4CssEntry(projectRoot) {
|
|
181
|
+
for (const candidate of V4_CSS_CANDIDATES) {
|
|
182
|
+
const abs = path.resolve(projectRoot, candidate);
|
|
183
|
+
try {
|
|
184
|
+
const contents = await fs.readFile(abs, "utf8");
|
|
185
|
+
if (/@import\s+["']tailwindcss/.test(contents))
|
|
186
|
+
return abs;
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
async function loadDesignSystemFromProject() {
|
|
193
|
+
try {
|
|
194
|
+
const modName = "@tailwindcss/node";
|
|
195
|
+
const mod = await import(modName);
|
|
196
|
+
return mod.__unstable__loadDesignSystem ?? null;
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function parseRootVars(css) {
|
|
202
|
+
const vars = {};
|
|
203
|
+
const rootMatch = css.match(/:root\s*\{([^}]*)\}/s);
|
|
204
|
+
if (!rootMatch)
|
|
205
|
+
return vars;
|
|
206
|
+
for (const decl of rootMatch[1].split(";")) {
|
|
207
|
+
const trimmed = decl.trim();
|
|
208
|
+
if (!trimmed.startsWith("--"))
|
|
209
|
+
continue;
|
|
210
|
+
const colon = trimmed.indexOf(":");
|
|
211
|
+
if (colon === -1)
|
|
212
|
+
continue;
|
|
213
|
+
const key = trimmed.slice(0, colon).trim();
|
|
214
|
+
const value = trimmed.slice(colon + 1).trim();
|
|
215
|
+
if (key && value)
|
|
216
|
+
vars[key] = value;
|
|
217
|
+
}
|
|
218
|
+
return vars;
|
|
219
|
+
}
|
|
220
|
+
function resolveVars(value, rootVars, depth = 0) {
|
|
221
|
+
if (depth > 8)
|
|
222
|
+
return value;
|
|
223
|
+
return value.replace(
|
|
224
|
+
/var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/g,
|
|
225
|
+
(match, name, fallback) => {
|
|
226
|
+
const resolved = rootVars[name];
|
|
227
|
+
if (resolved !== void 0)
|
|
228
|
+
return resolveVars(resolved, rootVars, depth + 1);
|
|
229
|
+
return fallback ?? match;
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
async function generateV4Config(cssEntry, outfile, load) {
|
|
234
|
+
const css = await fs.readFile(cssEntry, "utf8");
|
|
235
|
+
const ds = await load(css, { base: path.dirname(cssEntry) });
|
|
236
|
+
const rootVars = parseRootVars(css);
|
|
237
|
+
const theme = {};
|
|
238
|
+
for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {
|
|
239
|
+
const entries = {};
|
|
240
|
+
for (const [key, rawValue] of ds.theme.namespace(namespace)) {
|
|
241
|
+
if (key === null)
|
|
242
|
+
continue;
|
|
243
|
+
entries[key] = resolveVars(rawValue, rootVars);
|
|
244
|
+
}
|
|
245
|
+
theme[configKey] = entries;
|
|
246
|
+
}
|
|
247
|
+
await fs.mkdir(path.dirname(outfile), { recursive: true });
|
|
248
|
+
await fs.writeFile(outfile, JSON.stringify({ theme }, null, 2));
|
|
249
|
+
}
|
|
250
|
+
async function generateV3Config(tailwindInputFile, tailwindIntermediateFile, tailwindJsonOutfile) {
|
|
251
|
+
await esbuild.build({
|
|
252
|
+
entryPoints: [tailwindInputFile],
|
|
253
|
+
outfile: tailwindIntermediateFile,
|
|
254
|
+
bundle: true,
|
|
255
|
+
format: "esm",
|
|
256
|
+
banner: {
|
|
257
|
+
js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);'
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
try {
|
|
261
|
+
const userConfig = await import(tailwindIntermediateFile + "?update=" + Date.now());
|
|
262
|
+
if (!userConfig || !userConfig.default) {
|
|
263
|
+
throw new Error("Invalid Tailwind config structure");
|
|
264
|
+
}
|
|
265
|
+
const resolved = resolveConfig(userConfig.default);
|
|
266
|
+
await fs.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));
|
|
267
|
+
} finally {
|
|
268
|
+
await fs.unlink(tailwindIntermediateFile).catch(() => {
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function createTailwindConfigFeature() {
|
|
273
|
+
let projectRoot = "";
|
|
274
|
+
const run = async () => {
|
|
275
|
+
if (!projectRoot)
|
|
276
|
+
return;
|
|
277
|
+
const outfile = path.resolve(projectRoot, OUTFILE_RELATIVE);
|
|
278
|
+
const cssEntry = await findV4CssEntry(projectRoot);
|
|
279
|
+
if (cssEntry) {
|
|
280
|
+
const load = await loadDesignSystemFromProject();
|
|
281
|
+
if (load) {
|
|
282
|
+
try {
|
|
283
|
+
await generateV4Config(cssEntry, outfile, load);
|
|
284
|
+
console.log(`[vibe-tagger] wrote ${outfile}`);
|
|
285
|
+
return;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error("[vibe-tagger] Error generating v4 tailwind config:", error);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);
|
|
293
|
+
try {
|
|
294
|
+
await fs.access(v3ConfigFile);
|
|
295
|
+
} catch {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const intermediate = path.resolve(projectRoot, INTERMEDIATE_RELATIVE);
|
|
299
|
+
try {
|
|
300
|
+
await generateV3Config(v3ConfigFile, intermediate, outfile);
|
|
301
|
+
console.log(`[vibe-tagger] wrote ${outfile}`);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error("[vibe-tagger] Error generating v3 tailwind config:", error);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
return {
|
|
307
|
+
onConfigResolved(config) {
|
|
308
|
+
projectRoot = config.root;
|
|
309
|
+
},
|
|
310
|
+
async onBuildStart() {
|
|
311
|
+
await run();
|
|
312
|
+
},
|
|
313
|
+
onConfigureServer(server) {
|
|
314
|
+
try {
|
|
315
|
+
const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);
|
|
316
|
+
const v4Candidates = V4_CSS_CANDIDATES.map(
|
|
317
|
+
(c) => path.resolve(projectRoot, c)
|
|
318
|
+
);
|
|
319
|
+
const watchPaths = [v3ConfigFile, ...v4Candidates];
|
|
320
|
+
for (const p of watchPaths)
|
|
321
|
+
server.watcher.add(p);
|
|
322
|
+
const normalized = new Set(watchPaths.map((p) => path.normalize(p)));
|
|
323
|
+
server.watcher.on("change", async (changedPath) => {
|
|
324
|
+
if (normalized.has(path.normalize(changedPath)))
|
|
325
|
+
await run();
|
|
326
|
+
});
|
|
327
|
+
} catch (error) {
|
|
328
|
+
console.error("[vibe-tagger] Error adding tailwind watcher:", error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
158
334
|
// src/plugin.ts
|
|
159
335
|
function vibeTagger({
|
|
160
336
|
jsxSource = true,
|
|
161
|
-
|
|
337
|
+
tailwindConfig = false,
|
|
338
|
+
debug: _debug = false
|
|
162
339
|
} = {}) {
|
|
163
340
|
const features = [];
|
|
164
|
-
if (jsxSource)
|
|
341
|
+
if (jsxSource)
|
|
165
342
|
features.push(createJsxTaggerFeature());
|
|
166
|
-
|
|
343
|
+
if (tailwindConfig)
|
|
344
|
+
features.push(createTailwindConfigFeature());
|
|
167
345
|
return {
|
|
168
346
|
name: "vibe-tagger",
|
|
169
347
|
enforce: "pre",
|
|
348
|
+
configResolved(config) {
|
|
349
|
+
for (const feature of features)
|
|
350
|
+
feature.onConfigResolved?.(config);
|
|
351
|
+
},
|
|
352
|
+
async buildStart() {
|
|
353
|
+
for (const feature of features)
|
|
354
|
+
await feature.onBuildStart?.();
|
|
355
|
+
},
|
|
356
|
+
configureServer(server) {
|
|
357
|
+
for (const feature of features)
|
|
358
|
+
feature.onConfigureServer?.(server);
|
|
359
|
+
},
|
|
170
360
|
resolveId(id, importer) {
|
|
171
361
|
for (const feature of features) {
|
|
172
362
|
const result = feature.resolveId?.(id, importer);
|
|
173
|
-
if (result !== null && result !== void 0)
|
|
363
|
+
if (result !== null && result !== void 0)
|
|
174
364
|
return result;
|
|
175
|
-
}
|
|
176
365
|
}
|
|
177
366
|
return null;
|
|
178
367
|
},
|
|
179
368
|
load(id) {
|
|
180
369
|
for (const feature of features) {
|
|
181
370
|
const result = feature.load?.(id);
|
|
182
|
-
if (result !== null && result !== void 0)
|
|
371
|
+
if (result !== null && result !== void 0)
|
|
183
372
|
return result;
|
|
184
|
-
}
|
|
185
373
|
}
|
|
186
374
|
return null;
|
|
187
375
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/devRuntimeCode.ts","../src/features/jsxSource.ts","../src/plugin.ts"],"sourcesContent":["// Runtime code that replaces react/jsx-dev-runtime in the browser.\n// Served as a virtual module by the Vite plugin.\n\nconst devRuntimeCode = [\n 'import * as React from \"react\";',\n 'import * as ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n 'const SOURCE_KEY = Symbol.for(\"__jsxSource__\");',\n \"\",\n \"const cleanFileName = (fileName) => {\",\n ' if (!fileName) return \"\";',\n ' if (fileName.includes(\"dev_server\")) {',\n ' fileName = fileName.split(\"dev_server\")[1].slice(1);',\n \" }\",\n ' if (fileName.includes(\"sandbox-scheduler/sandbox\")) {',\n ' const sandboxPart = fileName.split(\"sandbox-scheduler/\")[1];',\n ' fileName = sandboxPart.split(\"/\").slice(1).join(\"/\");',\n \" }\",\n ' return fileName.replace(/^\\\\/dev-server\\\\//, \"\");',\n \"};\",\n \"\",\n \"const sourceElementMap = new Map();\",\n \"window.sourceElementMap = sourceElementMap;\",\n \"\",\n \"function getSourceKey(sourceInfo) {\",\n \" return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;\",\n \"}\",\n \"\",\n \"function unregisterElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" const refs = sourceElementMap.get(key);\",\n \" if (refs) {\",\n \" for (const ref of refs) {\",\n \" if (ref.deref() === node) {\",\n \" refs.delete(ref);\",\n \" break;\",\n \" }\",\n \" }\",\n \" if (refs.size === 0) {\",\n \" sourceElementMap.delete(key);\",\n \" }\",\n \" }\",\n \"}\",\n \"\",\n \"function registerElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" if (!sourceElementMap.has(key)) {\",\n \" sourceElementMap.set(key, new Set());\",\n \" }\",\n \" sourceElementMap.get(key).add(new WeakRef(node));\",\n \"}\",\n \"\",\n \"function getTypeName(type) {\",\n ' if (typeof type === \"string\") return type;',\n ' if (typeof type === \"function\") return type.displayName || type.name || \"Unknown\";',\n ' if (typeof type === \"object\" && type !== null) {',\n ' return type.displayName || type.render?.displayName || type.render?.name || \"Unknown\";',\n \" }\",\n ' return \"Unknown\";',\n \"}\",\n \"\",\n \"export function jsxDEV(type, props, key, isStatic, source, self) {\",\n \" // For custom components (like <Icon />, <Button />), tag their rendered output\",\n \" // This captures the JSX element name for library components that don't have source info\",\n ' if (source?.fileName && typeof type !== \"string\" && type !== Fragment) {',\n \" const typeName = getTypeName(type);\",\n \" const jsxSourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: typeName,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" // Only tag if this element doesn't already have source info\",\n \" // (library components won't have it, user components will)\",\n \" if (!node[SOURCE_KEY]) {\",\n \" node[SOURCE_KEY] = jsxSourceInfo;\",\n \" registerElement(node, jsxSourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \"\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" // For host elements (div, span, etc.), tag with component context\",\n ' if (source?.fileName && typeof type === \"string\") {',\n \" const sourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: type,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \"\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" const existingSource = node[SOURCE_KEY];\",\n \" if (existingSource) {\",\n \" if (getSourceKey(existingSource) !== getSourceKey(sourceInfo)) {\",\n \" unregisterElement(node, existingSource);\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" } else {\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \"}\",\n].join(\"\\n\");\n\nexport default devRuntimeCode;\n","import devRuntimeCode from \"../devRuntimeCode\";\n\nexport interface JsxSourceFeature {\n resolveId(id: string, importer?: string): string | null;\n load(id: string): string | null;\n}\n\nexport function createJsxTaggerFeature(): JsxSourceFeature {\n return {\n resolveId(id: string, importer?: string): string | null {\n if (id === \"react/jsx-dev-runtime\" && !importer?.includes(\"\\0jsx-source\")) {\n return \"\\0jsx-source/jsx-dev-runtime\";\n }\n return null;\n },\n load(id: string): string | null {\n if (id === \"\\0jsx-source/jsx-dev-runtime\") {\n return devRuntimeCode;\n }\n return null;\n },\n };\n}\n","import type { Plugin } from \"vite\";\nimport { createJsxTaggerFeature } from \"./features/jsxSource\";\n\nexport interface VibeTaggerOptions {\n /** Enable JSX source tagging. Default: true */\n jsxSource?: boolean;\n /** Enable debug logging. Default: false */\n debug?: boolean;\n}\n\nexport function vibeTagger({\n jsxSource = true,\n debug = false,\n}: VibeTaggerOptions = {}): Plugin {\n const features: ReturnType<typeof createJsxTaggerFeature>[] = [];\n\n if (jsxSource) {\n features.push(createJsxTaggerFeature());\n }\n\n return {\n name: \"vibe-tagger\",\n enforce: \"pre\",\n\n resolveId(id: string, importer?: string) {\n for (const feature of features) {\n const result = feature.resolveId?.(id, importer);\n if (result !== null && result !== undefined) {\n return result;\n }\n }\n return null;\n },\n\n load(id: string) {\n for (const feature of features) {\n const result = feature.load?.(id);\n if (result !== null && result !== undefined) {\n return result;\n }\n }\n return null;\n },\n };\n}\n"],"mappings":";AAGA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAO,yBAAQ;;;ACrIR,SAAS,yBAA2C;AACzD,SAAO;AAAA,IACL,UAAU,IAAY,UAAkC;AACtD,UAAI,OAAO,2BAA2B,CAAC,UAAU,SAAS,cAAc,GAAG;AACzE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK,IAA2B;AAC9B,UAAI,OAAO,gCAAgC;AACzC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACZO,SAAS,WAAW;AAAA,EACzB,YAAY;AAAA,EACZ,QAAQ;AACV,IAAuB,CAAC,GAAW;AACjC,QAAM,WAAwD,CAAC;AAE/D,MAAI,WAAW;AACb,aAAS,KAAK,uBAAuB,CAAC;AAAA,EACxC;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,UAAU,IAAY,UAAmB;AACvC,iBAAW,WAAW,UAAU;AAC9B,cAAM,SAAS,QAAQ,YAAY,IAAI,QAAQ;AAC/C,YAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAY;AACf,iBAAW,WAAW,UAAU;AAC9B,cAAM,SAAS,QAAQ,OAAO,EAAE;AAChC,YAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/devRuntimeCode.ts","../src/features/jsxSource.ts","../src/features/tailwindConfig.ts","../src/plugin.ts"],"sourcesContent":["// Runtime code that replaces react/jsx-dev-runtime in the browser.\n// Served as a virtual module by the Vite plugin.\n\nconst devRuntimeCode = [\n 'import * as React from \"react\";',\n 'import * as ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n 'const SOURCE_KEY = Symbol.for(\"__jsxSource__\");',\n \"\",\n \"const cleanFileName = (fileName) => {\",\n ' if (!fileName) return \"\";',\n ' if (fileName.includes(\"dev_server\")) {',\n ' fileName = fileName.split(\"dev_server\")[1].slice(1);',\n \" }\",\n ' if (fileName.includes(\"sandbox-scheduler/sandbox\")) {',\n ' const sandboxPart = fileName.split(\"sandbox-scheduler/\")[1];',\n ' fileName = sandboxPart.split(\"/\").slice(1).join(\"/\");',\n \" }\",\n ' return fileName.replace(/^\\\\/dev-server\\\\//, \"\");',\n \"};\",\n \"\",\n \"const sourceElementMap = new Map();\",\n \"window.sourceElementMap = sourceElementMap;\",\n \"\",\n \"function getSourceKey(sourceInfo) {\",\n \" return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;\",\n \"}\",\n \"\",\n \"function unregisterElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" const refs = sourceElementMap.get(key);\",\n \" if (refs) {\",\n \" for (const ref of refs) {\",\n \" if (ref.deref() === node) {\",\n \" refs.delete(ref);\",\n \" break;\",\n \" }\",\n \" }\",\n \" if (refs.size === 0) {\",\n \" sourceElementMap.delete(key);\",\n \" }\",\n \" }\",\n \"}\",\n \"\",\n \"function registerElement(node, sourceInfo) {\",\n \" const key = getSourceKey(sourceInfo);\",\n \" if (!sourceElementMap.has(key)) {\",\n \" sourceElementMap.set(key, new Set());\",\n \" }\",\n \" sourceElementMap.get(key).add(new WeakRef(node));\",\n \"}\",\n \"\",\n \"function getTypeName(type) {\",\n ' if (typeof type === \"string\") return type;',\n ' if (typeof type === \"function\") return type.displayName || type.name || \"Unknown\";',\n ' if (typeof type === \"object\" && type !== null) {',\n ' return type.displayName || type.render?.displayName || type.render?.name || \"Unknown\";',\n \" }\",\n ' return \"Unknown\";',\n \"}\",\n \"\",\n \"export function jsxDEV(type, props, key, isStatic, source, self) {\",\n \" // For custom components (like <Icon />, <Button />), tag their rendered output\",\n \" // This captures the JSX element name for library components that don't have source info\",\n ' if (source?.fileName && typeof type !== \"string\" && type !== Fragment) {',\n \" const typeName = getTypeName(type);\",\n \" const jsxSourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: typeName,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" // Only tag if this element doesn't already have source info\",\n \" // (library components won't have it, user components will)\",\n \" if (!node[SOURCE_KEY]) {\",\n \" node[SOURCE_KEY] = jsxSourceInfo;\",\n \" registerElement(node, jsxSourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \"\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" // For host elements (div, span, etc.), tag with component context\",\n ' if (source?.fileName && typeof type === \"string\") {',\n \" const sourceInfo = {\",\n \" fileName: cleanFileName(source.fileName),\",\n \" lineNumber: source.lineNumber,\",\n \" columnNumber: source.columnNumber,\",\n \" displayName: type,\",\n \" };\",\n \"\",\n \" const originalRef = props?.ref;\",\n \"\",\n \" const enhancedProps = {\",\n \" ...props,\",\n \" ref: (node) => {\",\n \" if (node) {\",\n \" const existingSource = node[SOURCE_KEY];\",\n \" if (existingSource) {\",\n \" if (getSourceKey(existingSource) !== getSourceKey(sourceInfo)) {\",\n \" unregisterElement(node, existingSource);\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" } else {\",\n \" node[SOURCE_KEY] = sourceInfo;\",\n \" registerElement(node, sourceInfo);\",\n \" }\",\n \" }\",\n ' if (typeof originalRef === \"function\") {',\n \" originalRef(node);\",\n ' } else if (originalRef && typeof originalRef === \"object\") {',\n \" originalRef.current = node;\",\n \" }\",\n \" },\",\n \" };\",\n \" return _jsxDEV(type, enhancedProps, key, isStatic, source, self);\",\n \" }\",\n \"\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \"}\",\n].join(\"\\n\");\n\nexport default devRuntimeCode;\n","import devRuntimeCode from \"../devRuntimeCode\";\n\nexport interface JsxSourceFeature {\n resolveId(id: string, importer?: string): string | null;\n load(id: string): string | null;\n}\n\nexport function createJsxTaggerFeature(): JsxSourceFeature {\n return {\n resolveId(id: string, importer?: string): string | null {\n if (id === \"react/jsx-dev-runtime\" && !importer?.includes(\"\\0jsx-source\")) {\n return \"\\0jsx-source/jsx-dev-runtime\";\n }\n return null;\n },\n load(id: string): string | null {\n if (id === \"\\0jsx-source/jsx-dev-runtime\") {\n return devRuntimeCode;\n }\n return null;\n },\n };\n}\n","import type { ResolvedConfig, ViteDevServer } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport resolveConfig from \"tailwindcss/resolveConfig.js\";\n\nexport interface TailwindConfigFeature {\n onConfigResolved(config: ResolvedConfig): void;\n onBuildStart(): Promise<void>;\n onConfigureServer(server: ViteDevServer): void;\n}\n\nconst V4_CSS_CANDIDATES = [\n \"src/styles.css\",\n \"src/index.css\",\n \"src/globals.css\",\n \"src/app.css\",\n];\n\nconst V4_NAMESPACES: Record<string, string> = {\n colors: \"--color\",\n screens: \"--breakpoint\",\n spacing: \"--spacing\",\n borderRadius: \"--radius\",\n fontFamily: \"--font\",\n opacity: \"--opacity\",\n};\n\nconst OUTFILE_RELATIVE = \"./src/tailwind.config.vibe.json\";\nconst INTERMEDIATE_RELATIVE = \"./.vibe.tailwind.config.js\";\nconst V3_CONFIG_RELATIVE = \"./tailwind.config.ts\";\n\nasync function findV4CssEntry(projectRoot: string): Promise<string | null> {\n for (const candidate of V4_CSS_CANDIDATES) {\n const abs = path.resolve(projectRoot, candidate);\n try {\n const contents = await fs.readFile(abs, \"utf8\");\n if (/@import\\s+[\"']tailwindcss/.test(contents)) return abs;\n } catch {\n // missing file — try next\n }\n }\n return null;\n}\n\ntype LoadDesignSystem = (\n css: string,\n opts: { base: string },\n) => Promise<{\n theme: {\n namespace(ns: string): Iterable<[string | null, string]>;\n };\n}>;\n\nasync function loadDesignSystemFromProject(): Promise<LoadDesignSystem | null> {\n try {\n // Dynamic import so consumers not on v4 don't need the package installed.\n const modName = \"@tailwindcss/node\";\n const mod = (await import(modName)) as {\n __unstable__loadDesignSystem?: LoadDesignSystem;\n };\n return mod.__unstable__loadDesignSystem ?? null;\n } catch {\n return null;\n }\n}\n\nfunction parseRootVars(css: string): Record<string, string> {\n const vars: Record<string, string> = {};\n const rootMatch = css.match(/:root\\s*\\{([^}]*)\\}/s);\n if (!rootMatch) return vars;\n for (const decl of rootMatch[1]!.split(\";\")) {\n const trimmed = decl.trim();\n if (!trimmed.startsWith(\"--\")) continue;\n const colon = trimmed.indexOf(\":\");\n if (colon === -1) continue;\n const key = trimmed.slice(0, colon).trim();\n const value = trimmed.slice(colon + 1).trim();\n if (key && value) vars[key] = value;\n }\n return vars;\n}\n\nfunction resolveVars(\n value: string,\n rootVars: Record<string, string>,\n depth = 0,\n): string {\n if (depth > 8) return value;\n return value.replace(\n /var\\(\\s*(--[\\w-]+)(?:\\s*,\\s*([^)]+))?\\s*\\)/g,\n (match, name: string, fallback: string | undefined) => {\n const resolved = rootVars[name];\n if (resolved !== undefined) return resolveVars(resolved, rootVars, depth + 1);\n return fallback ?? match;\n },\n );\n}\n\nasync function generateV4Config(\n cssEntry: string,\n outfile: string,\n load: LoadDesignSystem,\n): Promise<void> {\n const css = await fs.readFile(cssEntry, \"utf8\");\n const ds = await load(css, { base: path.dirname(cssEntry) });\n const rootVars = parseRootVars(css);\n const theme: Record<string, Record<string, string>> = {};\n for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {\n const entries: Record<string, string> = {};\n for (const [key, rawValue] of ds.theme.namespace(namespace)) {\n if (key === null) continue;\n entries[key] = resolveVars(rawValue, rootVars);\n }\n theme[configKey] = entries;\n }\n await fs.mkdir(path.dirname(outfile), { recursive: true });\n await fs.writeFile(outfile, JSON.stringify({ theme }, null, 2));\n}\n\nasync function generateV3Config(\n tailwindInputFile: string,\n tailwindIntermediateFile: string,\n tailwindJsonOutfile: string,\n): Promise<void> {\n await esbuild.build({\n entryPoints: [tailwindInputFile],\n outfile: tailwindIntermediateFile,\n bundle: true,\n format: \"esm\",\n banner: {\n js: 'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);',\n },\n });\n try {\n const userConfig = (await import(\n tailwindIntermediateFile + \"?update=\" + Date.now()\n )) as { default?: unknown };\n if (!userConfig || !userConfig.default) {\n throw new Error(\"Invalid Tailwind config structure\");\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const resolved = (resolveConfig as any)(userConfig.default);\n await fs.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));\n } finally {\n await fs.unlink(tailwindIntermediateFile).catch(() => {});\n }\n}\n\nexport function createTailwindConfigFeature(): TailwindConfigFeature {\n let projectRoot = \"\";\n\n const run = async (): Promise<void> => {\n if (!projectRoot) return;\n const outfile = path.resolve(projectRoot, OUTFILE_RELATIVE);\n\n // Prefer v4 when the CSS entry uses `@import \"tailwindcss\"` and\n // @tailwindcss/node is available.\n const cssEntry = await findV4CssEntry(projectRoot);\n if (cssEntry) {\n const load = await loadDesignSystemFromProject();\n if (load) {\n try {\n await generateV4Config(cssEntry, outfile, load);\n console.log(`[vibe-tagger] wrote ${outfile}`);\n return;\n } catch (error) {\n console.error(\"[vibe-tagger] Error generating v4 tailwind config:\", error);\n return;\n }\n }\n }\n\n // Fall back to v3: compile tailwind.config.ts via esbuild, then resolveConfig.\n const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);\n try {\n await fs.access(v3ConfigFile);\n } catch {\n return;\n }\n const intermediate = path.resolve(projectRoot, INTERMEDIATE_RELATIVE);\n try {\n await generateV3Config(v3ConfigFile, intermediate, outfile);\n console.log(`[vibe-tagger] wrote ${outfile}`);\n } catch (error) {\n console.error(\"[vibe-tagger] Error generating v3 tailwind config:\", error);\n }\n };\n\n return {\n onConfigResolved(config) {\n projectRoot = config.root;\n },\n async onBuildStart() {\n await run();\n },\n onConfigureServer(server) {\n try {\n const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);\n const v4Candidates = V4_CSS_CANDIDATES.map((c) =>\n path.resolve(projectRoot, c),\n );\n const watchPaths = [v3ConfigFile, ...v4Candidates];\n for (const p of watchPaths) server.watcher.add(p);\n const normalized = new Set(watchPaths.map((p) => path.normalize(p)));\n server.watcher.on(\"change\", async (changedPath) => {\n if (normalized.has(path.normalize(changedPath))) await run();\n });\n } catch (error) {\n console.error(\"[vibe-tagger] Error adding tailwind watcher:\", error);\n }\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer } from \"vite\";\nimport { createJsxTaggerFeature } from \"./features/jsxSource\";\nimport { createTailwindConfigFeature } from \"./features/tailwindConfig\";\n\nexport interface VibeTaggerOptions {\n /** Enable JSX source tagging. Default: true */\n jsxSource?: boolean;\n /**\n * Emit a resolved Tailwind theme JSON to `src/tailwind.config.vibe.json`\n * on build start and on config-file changes. Supports Tailwind v3\n * (reads `tailwind.config.ts`) and v4 (reads `src/styles.css` etc.).\n * Default: false\n */\n tailwindConfig?: boolean;\n /** Enable debug logging. Default: false */\n debug?: boolean;\n}\n\ninterface Feature {\n resolveId?(id: string, importer?: string): string | null;\n load?(id: string): string | null;\n onConfigResolved?(config: ResolvedConfig): void;\n onBuildStart?(): void | Promise<void>;\n onConfigureServer?(server: ViteDevServer): void;\n}\n\nexport function vibeTagger({\n jsxSource = true,\n tailwindConfig = false,\n debug: _debug = false,\n}: VibeTaggerOptions = {}): Plugin {\n const features: Feature[] = [];\n\n if (jsxSource) features.push(createJsxTaggerFeature());\n if (tailwindConfig) features.push(createTailwindConfigFeature());\n\n return {\n name: \"vibe-tagger\",\n enforce: \"pre\",\n\n configResolved(config) {\n for (const feature of features) feature.onConfigResolved?.(config);\n },\n\n async buildStart() {\n for (const feature of features) await feature.onBuildStart?.();\n },\n\n configureServer(server) {\n for (const feature of features) feature.onConfigureServer?.(server);\n },\n\n resolveId(id: string, importer?: string) {\n for (const feature of features) {\n const result = feature.resolveId?.(id, importer);\n if (result !== null && result !== undefined) return result;\n }\n return null;\n },\n\n load(id: string) {\n for (const feature of features) {\n const result = feature.load?.(id);\n if (result !== null && result !== undefined) return result;\n }\n return null;\n },\n };\n}\n"],"mappings":";AAGA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;A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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadconnector/vibe-tagger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vite plugin that instruments React JSX elements with source-location metadata for visual editing",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -31,6 +31,15 @@
|
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"vite": ">=5.0.0 <8.0.0"
|
|
33
33
|
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@tailwindcss/node": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"esbuild": "^0.25.0",
|
|
41
|
+
"tailwindcss": "^3.4.0"
|
|
42
|
+
},
|
|
34
43
|
"devDependencies": {
|
|
35
44
|
"@types/node": "^22.5.5",
|
|
36
45
|
"rimraf": "^5.0.10",
|