@leadconnector/vibe-tagger 0.1.1 → 0.3.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/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
@@ -26,12 +36,18 @@ module.exports = __toCommonJS(src_exports);
26
36
 
27
37
  // src/devRuntimeCode.ts
28
38
  var devRuntimeCode = [
29
- 'import * as React from "react";',
30
39
  'import * as ReactJSXDevRuntime from "react/jsx-dev-runtime";',
31
40
  "",
32
41
  "const _jsxDEV = ReactJSXDevRuntime.jsxDEV;",
33
42
  "export const Fragment = ReactJSXDevRuntime.Fragment;",
34
43
  "",
44
+ // SSR/test guard. Vite SSR, vitest, and Node prerender contexts have no
45
+ // `window` — touching it at module init would crash before any component
46
+ // renders. We still install the wrapper so React's JSX dev runtime stays
47
+ // wired up, but skip the per-element tagging path entirely. Matches
48
+ // lovable-tagger >=1.3.0.
49
+ 'const _isBrowser = typeof window !== "undefined";',
50
+ "",
35
51
  'const SOURCE_KEY = Symbol.for("__jsxSource__");',
36
52
  "",
37
53
  "const cleanFileName = (fileName) => {",
@@ -47,7 +63,9 @@ var devRuntimeCode = [
47
63
  "};",
48
64
  "",
49
65
  "const sourceElementMap = new Map();",
50
- "window.sourceElementMap = sourceElementMap;",
66
+ "if (_isBrowser) {",
67
+ " window.sourceElementMap = sourceElementMap;",
68
+ "}",
51
69
  "",
52
70
  "function getSourceKey(sourceInfo) {",
53
71
  " return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;",
@@ -87,6 +105,13 @@ var devRuntimeCode = [
87
105
  "}",
88
106
  "",
89
107
  "export function jsxDEV(type, props, key, isStatic, source, self) {",
108
+ " // During SSR / non-browser execution, skip all tagging and pass",
109
+ " // straight through to React. Touching DOM refs or the source map",
110
+ " // here would crash on Node-only paths (Vite SSR, vitest, etc.).",
111
+ " if (!_isBrowser) {",
112
+ " return _jsxDEV(type, props, key, isStatic, source, self);",
113
+ " }",
114
+ "",
90
115
  " // For custom components (like <Icon />, <Button />), tag their rendered output",
91
116
  " // This captures the JSX element name for library components that don't have source info",
92
117
  ' if (source?.fileName && typeof type !== "string" && type !== Fragment) {',
@@ -181,33 +206,221 @@ function createJsxTaggerFeature() {
181
206
  };
182
207
  }
183
208
 
209
+ // src/features/tailwindConfig.ts
210
+ var esbuild = __toESM(require("esbuild"), 1);
211
+ var import_promises = __toESM(require("fs/promises"), 1);
212
+ var import_path = __toESM(require("path"), 1);
213
+ var import_resolveConfig = __toESM(require("tailwindcss/resolveConfig.js"), 1);
214
+ var V4_CSS_CANDIDATES = [
215
+ "src/styles.css",
216
+ "src/index.css",
217
+ "src/globals.css",
218
+ "src/app.css"
219
+ ];
220
+ var V4_NAMESPACES = {
221
+ colors: "--color",
222
+ screens: "--breakpoint",
223
+ spacing: "--spacing",
224
+ borderRadius: "--radius",
225
+ fontFamily: "--font",
226
+ opacity: "--opacity"
227
+ };
228
+ var OUTFILE_RELATIVE = "./src/tailwind.config.vibe.json";
229
+ var INTERMEDIATE_RELATIVE = "./.vibe.tailwind.config.js";
230
+ var V3_CONFIG_RELATIVE = "./tailwind.config.ts";
231
+ async function findV4CssEntry(projectRoot) {
232
+ for (const candidate of V4_CSS_CANDIDATES) {
233
+ const abs = import_path.default.resolve(projectRoot, candidate);
234
+ try {
235
+ const contents = await import_promises.default.readFile(abs, "utf8");
236
+ if (/@import\s+["']tailwindcss/.test(contents))
237
+ return abs;
238
+ } catch {
239
+ }
240
+ }
241
+ return null;
242
+ }
243
+ async function loadDesignSystemFromProject() {
244
+ try {
245
+ const modName = "@tailwindcss/node";
246
+ const mod = await import(modName);
247
+ return mod.__unstable__loadDesignSystem ?? null;
248
+ } catch {
249
+ return null;
250
+ }
251
+ }
252
+ function parseRootVars(css) {
253
+ const vars = {};
254
+ const rootMatch = css.match(/:root\s*\{([^}]*)\}/s);
255
+ if (!rootMatch)
256
+ return vars;
257
+ for (const decl of rootMatch[1].split(";")) {
258
+ const trimmed = decl.trim();
259
+ if (!trimmed.startsWith("--"))
260
+ continue;
261
+ const colon = trimmed.indexOf(":");
262
+ if (colon === -1)
263
+ continue;
264
+ const key = trimmed.slice(0, colon).trim();
265
+ const value = trimmed.slice(colon + 1).trim();
266
+ if (key && value)
267
+ vars[key] = value;
268
+ }
269
+ return vars;
270
+ }
271
+ function resolveVars(value, rootVars, depth = 0) {
272
+ if (depth > 8)
273
+ return value;
274
+ return value.replace(
275
+ /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/g,
276
+ (match, name, fallback) => {
277
+ const resolved = rootVars[name];
278
+ if (resolved !== void 0)
279
+ return resolveVars(resolved, rootVars, depth + 1);
280
+ return fallback ?? match;
281
+ }
282
+ );
283
+ }
284
+ async function generateV4Config(cssEntry, outfile, load) {
285
+ const css = await import_promises.default.readFile(cssEntry, "utf8");
286
+ const ds = await load(css, { base: import_path.default.dirname(cssEntry) });
287
+ const rootVars = parseRootVars(css);
288
+ const theme = {};
289
+ for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {
290
+ const entries = {};
291
+ for (const [key, rawValue] of ds.theme.namespace(namespace)) {
292
+ if (key === null)
293
+ continue;
294
+ entries[key] = resolveVars(rawValue, rootVars);
295
+ }
296
+ theme[configKey] = entries;
297
+ }
298
+ await import_promises.default.mkdir(import_path.default.dirname(outfile), { recursive: true });
299
+ await import_promises.default.writeFile(outfile, JSON.stringify({ theme }, null, 2));
300
+ }
301
+ async function generateV3Config(tailwindInputFile, tailwindIntermediateFile, tailwindJsonOutfile) {
302
+ await esbuild.build({
303
+ entryPoints: [tailwindInputFile],
304
+ outfile: tailwindIntermediateFile,
305
+ bundle: true,
306
+ format: "esm",
307
+ banner: {
308
+ js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);'
309
+ }
310
+ });
311
+ try {
312
+ const userConfig = await import(tailwindIntermediateFile + "?update=" + Date.now());
313
+ if (!userConfig || !userConfig.default) {
314
+ throw new Error("Invalid Tailwind config structure");
315
+ }
316
+ const resolved = (0, import_resolveConfig.default)(userConfig.default);
317
+ await import_promises.default.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));
318
+ } finally {
319
+ await import_promises.default.unlink(tailwindIntermediateFile).catch(() => {
320
+ });
321
+ }
322
+ }
323
+ function createTailwindConfigFeature() {
324
+ let projectRoot = "";
325
+ const run = async () => {
326
+ if (!projectRoot)
327
+ return;
328
+ const outfile = import_path.default.resolve(projectRoot, OUTFILE_RELATIVE);
329
+ const cssEntry = await findV4CssEntry(projectRoot);
330
+ if (cssEntry) {
331
+ const load = await loadDesignSystemFromProject();
332
+ if (load) {
333
+ try {
334
+ await generateV4Config(cssEntry, outfile, load);
335
+ console.log(`[vibe-tagger] wrote ${outfile}`);
336
+ return;
337
+ } catch (error) {
338
+ console.error("[vibe-tagger] Error generating v4 tailwind config:", error);
339
+ return;
340
+ }
341
+ }
342
+ }
343
+ const v3ConfigFile = import_path.default.resolve(projectRoot, V3_CONFIG_RELATIVE);
344
+ try {
345
+ await import_promises.default.access(v3ConfigFile);
346
+ } catch {
347
+ return;
348
+ }
349
+ const intermediate = import_path.default.resolve(projectRoot, INTERMEDIATE_RELATIVE);
350
+ try {
351
+ await generateV3Config(v3ConfigFile, intermediate, outfile);
352
+ console.log(`[vibe-tagger] wrote ${outfile}`);
353
+ } catch (error) {
354
+ console.error("[vibe-tagger] Error generating v3 tailwind config:", error);
355
+ }
356
+ };
357
+ return {
358
+ onConfigResolved(config) {
359
+ projectRoot = config.root;
360
+ },
361
+ async onBuildStart() {
362
+ await run();
363
+ },
364
+ onConfigureServer(server) {
365
+ try {
366
+ const v3ConfigFile = import_path.default.resolve(projectRoot, V3_CONFIG_RELATIVE);
367
+ const v4Candidates = V4_CSS_CANDIDATES.map(
368
+ (c) => import_path.default.resolve(projectRoot, c)
369
+ );
370
+ const watchPaths = [v3ConfigFile, ...v4Candidates];
371
+ for (const p of watchPaths)
372
+ server.watcher.add(p);
373
+ const normalized = new Set(watchPaths.map((p) => import_path.default.normalize(p)));
374
+ server.watcher.on("change", async (changedPath) => {
375
+ if (normalized.has(import_path.default.normalize(changedPath)))
376
+ await run();
377
+ });
378
+ } catch (error) {
379
+ console.error("[vibe-tagger] Error adding tailwind watcher:", error);
380
+ }
381
+ }
382
+ };
383
+ }
384
+
184
385
  // src/plugin.ts
185
386
  function vibeTagger({
186
387
  jsxSource = true,
187
- debug = false
388
+ tailwindConfig = false,
389
+ debug: _debug = false
188
390
  } = {}) {
189
391
  const features = [];
190
- if (jsxSource) {
392
+ if (jsxSource)
191
393
  features.push(createJsxTaggerFeature());
192
- }
394
+ if (tailwindConfig)
395
+ features.push(createTailwindConfigFeature());
193
396
  return {
194
397
  name: "vibe-tagger",
195
398
  enforce: "pre",
399
+ configResolved(config) {
400
+ for (const feature of features)
401
+ feature.onConfigResolved?.(config);
402
+ },
403
+ async buildStart() {
404
+ for (const feature of features)
405
+ await feature.onBuildStart?.();
406
+ },
407
+ configureServer(server) {
408
+ for (const feature of features)
409
+ feature.onConfigureServer?.(server);
410
+ },
196
411
  resolveId(id, importer) {
197
412
  for (const feature of features) {
198
413
  const result = feature.resolveId?.(id, importer);
199
- if (result !== null && result !== void 0) {
414
+ if (result !== null && result !== void 0)
200
415
  return result;
201
- }
202
416
  }
203
417
  return null;
204
418
  },
205
419
  load(id) {
206
420
  for (const feature of features) {
207
421
  const result = feature.load?.(id);
208
- if (result !== null && result !== void 0) {
422
+ if (result !== null && result !== void 0)
209
423
  return result;
210
- }
211
424
  }
212
425
  return null;
213
426
  }
@@ -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 ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n // SSR/test guard. Vite SSR, vitest, and Node prerender contexts have no\n // `window` — touching it at module init would crash before any component\n // renders. We still install the wrapper so React's JSX dev runtime stays\n // wired up, but skip the per-element tagging path entirely. Matches\n // lovable-tagger >=1.3.0.\n 'const _isBrowser = typeof window !== \"undefined\";',\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 \"if (_isBrowser) {\",\n \" window.sourceElementMap = sourceElementMap;\",\n \"}\",\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 \" // During SSR / non-browser execution, skip all tagging and pass\",\n \" // straight through to React. Touching DOM refs or the source map\",\n \" // here would crash on Node-only paths (Vite SSR, vitest, etc.).\",\n \" if (!_isBrowser) {\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \" }\",\n \"\",\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;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;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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
@@ -1,11 +1,17 @@
1
1
  // src/devRuntimeCode.ts
2
2
  var devRuntimeCode = [
3
- 'import * as React from "react";',
4
3
  'import * as ReactJSXDevRuntime from "react/jsx-dev-runtime";',
5
4
  "",
6
5
  "const _jsxDEV = ReactJSXDevRuntime.jsxDEV;",
7
6
  "export const Fragment = ReactJSXDevRuntime.Fragment;",
8
7
  "",
8
+ // SSR/test guard. Vite SSR, vitest, and Node prerender contexts have no
9
+ // `window` — touching it at module init would crash before any component
10
+ // renders. We still install the wrapper so React's JSX dev runtime stays
11
+ // wired up, but skip the per-element tagging path entirely. Matches
12
+ // lovable-tagger >=1.3.0.
13
+ 'const _isBrowser = typeof window !== "undefined";',
14
+ "",
9
15
  'const SOURCE_KEY = Symbol.for("__jsxSource__");',
10
16
  "",
11
17
  "const cleanFileName = (fileName) => {",
@@ -21,7 +27,9 @@ var devRuntimeCode = [
21
27
  "};",
22
28
  "",
23
29
  "const sourceElementMap = new Map();",
24
- "window.sourceElementMap = sourceElementMap;",
30
+ "if (_isBrowser) {",
31
+ " window.sourceElementMap = sourceElementMap;",
32
+ "}",
25
33
  "",
26
34
  "function getSourceKey(sourceInfo) {",
27
35
  " return `${cleanFileName(sourceInfo.fileName)}:${sourceInfo.lineNumber}:${sourceInfo.columnNumber}`;",
@@ -61,6 +69,13 @@ var devRuntimeCode = [
61
69
  "}",
62
70
  "",
63
71
  "export function jsxDEV(type, props, key, isStatic, source, self) {",
72
+ " // During SSR / non-browser execution, skip all tagging and pass",
73
+ " // straight through to React. Touching DOM refs or the source map",
74
+ " // here would crash on Node-only paths (Vite SSR, vitest, etc.).",
75
+ " if (!_isBrowser) {",
76
+ " return _jsxDEV(type, props, key, isStatic, source, self);",
77
+ " }",
78
+ "",
64
79
  " // For custom components (like <Icon />, <Button />), tag their rendered output",
65
80
  " // This captures the JSX element name for library components that don't have source info",
66
81
  ' if (source?.fileName && typeof type !== "string" && type !== Fragment) {',
@@ -155,33 +170,221 @@ function createJsxTaggerFeature() {
155
170
  };
156
171
  }
157
172
 
173
+ // src/features/tailwindConfig.ts
174
+ import * as esbuild from "esbuild";
175
+ import fs from "fs/promises";
176
+ import path from "path";
177
+ import resolveConfig from "tailwindcss/resolveConfig.js";
178
+ var V4_CSS_CANDIDATES = [
179
+ "src/styles.css",
180
+ "src/index.css",
181
+ "src/globals.css",
182
+ "src/app.css"
183
+ ];
184
+ var V4_NAMESPACES = {
185
+ colors: "--color",
186
+ screens: "--breakpoint",
187
+ spacing: "--spacing",
188
+ borderRadius: "--radius",
189
+ fontFamily: "--font",
190
+ opacity: "--opacity"
191
+ };
192
+ var OUTFILE_RELATIVE = "./src/tailwind.config.vibe.json";
193
+ var INTERMEDIATE_RELATIVE = "./.vibe.tailwind.config.js";
194
+ var V3_CONFIG_RELATIVE = "./tailwind.config.ts";
195
+ async function findV4CssEntry(projectRoot) {
196
+ for (const candidate of V4_CSS_CANDIDATES) {
197
+ const abs = path.resolve(projectRoot, candidate);
198
+ try {
199
+ const contents = await fs.readFile(abs, "utf8");
200
+ if (/@import\s+["']tailwindcss/.test(contents))
201
+ return abs;
202
+ } catch {
203
+ }
204
+ }
205
+ return null;
206
+ }
207
+ async function loadDesignSystemFromProject() {
208
+ try {
209
+ const modName = "@tailwindcss/node";
210
+ const mod = await import(modName);
211
+ return mod.__unstable__loadDesignSystem ?? null;
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+ function parseRootVars(css) {
217
+ const vars = {};
218
+ const rootMatch = css.match(/:root\s*\{([^}]*)\}/s);
219
+ if (!rootMatch)
220
+ return vars;
221
+ for (const decl of rootMatch[1].split(";")) {
222
+ const trimmed = decl.trim();
223
+ if (!trimmed.startsWith("--"))
224
+ continue;
225
+ const colon = trimmed.indexOf(":");
226
+ if (colon === -1)
227
+ continue;
228
+ const key = trimmed.slice(0, colon).trim();
229
+ const value = trimmed.slice(colon + 1).trim();
230
+ if (key && value)
231
+ vars[key] = value;
232
+ }
233
+ return vars;
234
+ }
235
+ function resolveVars(value, rootVars, depth = 0) {
236
+ if (depth > 8)
237
+ return value;
238
+ return value.replace(
239
+ /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/g,
240
+ (match, name, fallback) => {
241
+ const resolved = rootVars[name];
242
+ if (resolved !== void 0)
243
+ return resolveVars(resolved, rootVars, depth + 1);
244
+ return fallback ?? match;
245
+ }
246
+ );
247
+ }
248
+ async function generateV4Config(cssEntry, outfile, load) {
249
+ const css = await fs.readFile(cssEntry, "utf8");
250
+ const ds = await load(css, { base: path.dirname(cssEntry) });
251
+ const rootVars = parseRootVars(css);
252
+ const theme = {};
253
+ for (const [configKey, namespace] of Object.entries(V4_NAMESPACES)) {
254
+ const entries = {};
255
+ for (const [key, rawValue] of ds.theme.namespace(namespace)) {
256
+ if (key === null)
257
+ continue;
258
+ entries[key] = resolveVars(rawValue, rootVars);
259
+ }
260
+ theme[configKey] = entries;
261
+ }
262
+ await fs.mkdir(path.dirname(outfile), { recursive: true });
263
+ await fs.writeFile(outfile, JSON.stringify({ theme }, null, 2));
264
+ }
265
+ async function generateV3Config(tailwindInputFile, tailwindIntermediateFile, tailwindJsonOutfile) {
266
+ await esbuild.build({
267
+ entryPoints: [tailwindInputFile],
268
+ outfile: tailwindIntermediateFile,
269
+ bundle: true,
270
+ format: "esm",
271
+ banner: {
272
+ js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);'
273
+ }
274
+ });
275
+ try {
276
+ const userConfig = await import(tailwindIntermediateFile + "?update=" + Date.now());
277
+ if (!userConfig || !userConfig.default) {
278
+ throw new Error("Invalid Tailwind config structure");
279
+ }
280
+ const resolved = resolveConfig(userConfig.default);
281
+ await fs.writeFile(tailwindJsonOutfile, JSON.stringify(resolved, null, 2));
282
+ } finally {
283
+ await fs.unlink(tailwindIntermediateFile).catch(() => {
284
+ });
285
+ }
286
+ }
287
+ function createTailwindConfigFeature() {
288
+ let projectRoot = "";
289
+ const run = async () => {
290
+ if (!projectRoot)
291
+ return;
292
+ const outfile = path.resolve(projectRoot, OUTFILE_RELATIVE);
293
+ const cssEntry = await findV4CssEntry(projectRoot);
294
+ if (cssEntry) {
295
+ const load = await loadDesignSystemFromProject();
296
+ if (load) {
297
+ try {
298
+ await generateV4Config(cssEntry, outfile, load);
299
+ console.log(`[vibe-tagger] wrote ${outfile}`);
300
+ return;
301
+ } catch (error) {
302
+ console.error("[vibe-tagger] Error generating v4 tailwind config:", error);
303
+ return;
304
+ }
305
+ }
306
+ }
307
+ const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);
308
+ try {
309
+ await fs.access(v3ConfigFile);
310
+ } catch {
311
+ return;
312
+ }
313
+ const intermediate = path.resolve(projectRoot, INTERMEDIATE_RELATIVE);
314
+ try {
315
+ await generateV3Config(v3ConfigFile, intermediate, outfile);
316
+ console.log(`[vibe-tagger] wrote ${outfile}`);
317
+ } catch (error) {
318
+ console.error("[vibe-tagger] Error generating v3 tailwind config:", error);
319
+ }
320
+ };
321
+ return {
322
+ onConfigResolved(config) {
323
+ projectRoot = config.root;
324
+ },
325
+ async onBuildStart() {
326
+ await run();
327
+ },
328
+ onConfigureServer(server) {
329
+ try {
330
+ const v3ConfigFile = path.resolve(projectRoot, V3_CONFIG_RELATIVE);
331
+ const v4Candidates = V4_CSS_CANDIDATES.map(
332
+ (c) => path.resolve(projectRoot, c)
333
+ );
334
+ const watchPaths = [v3ConfigFile, ...v4Candidates];
335
+ for (const p of watchPaths)
336
+ server.watcher.add(p);
337
+ const normalized = new Set(watchPaths.map((p) => path.normalize(p)));
338
+ server.watcher.on("change", async (changedPath) => {
339
+ if (normalized.has(path.normalize(changedPath)))
340
+ await run();
341
+ });
342
+ } catch (error) {
343
+ console.error("[vibe-tagger] Error adding tailwind watcher:", error);
344
+ }
345
+ }
346
+ };
347
+ }
348
+
158
349
  // src/plugin.ts
159
350
  function vibeTagger({
160
351
  jsxSource = true,
161
- debug = false
352
+ tailwindConfig = false,
353
+ debug: _debug = false
162
354
  } = {}) {
163
355
  const features = [];
164
- if (jsxSource) {
356
+ if (jsxSource)
165
357
  features.push(createJsxTaggerFeature());
166
- }
358
+ if (tailwindConfig)
359
+ features.push(createTailwindConfigFeature());
167
360
  return {
168
361
  name: "vibe-tagger",
169
362
  enforce: "pre",
363
+ configResolved(config) {
364
+ for (const feature of features)
365
+ feature.onConfigResolved?.(config);
366
+ },
367
+ async buildStart() {
368
+ for (const feature of features)
369
+ await feature.onBuildStart?.();
370
+ },
371
+ configureServer(server) {
372
+ for (const feature of features)
373
+ feature.onConfigureServer?.(server);
374
+ },
170
375
  resolveId(id, importer) {
171
376
  for (const feature of features) {
172
377
  const result = feature.resolveId?.(id, importer);
173
- if (result !== null && result !== void 0) {
378
+ if (result !== null && result !== void 0)
174
379
  return result;
175
- }
176
380
  }
177
381
  return null;
178
382
  },
179
383
  load(id) {
180
384
  for (const feature of features) {
181
385
  const result = feature.load?.(id);
182
- if (result !== null && result !== void 0) {
386
+ if (result !== null && result !== void 0)
183
387
  return result;
184
- }
185
388
  }
186
389
  return null;
187
390
  }
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 ReactJSXDevRuntime from \"react/jsx-dev-runtime\";',\n \"\",\n \"const _jsxDEV = ReactJSXDevRuntime.jsxDEV;\",\n \"export const Fragment = ReactJSXDevRuntime.Fragment;\",\n \"\",\n // SSR/test guard. Vite SSR, vitest, and Node prerender contexts have no\n // `window` — touching it at module init would crash before any component\n // renders. We still install the wrapper so React's JSX dev runtime stays\n // wired up, but skip the per-element tagging path entirely. Matches\n // lovable-tagger >=1.3.0.\n 'const _isBrowser = typeof window !== \"undefined\";',\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 \"if (_isBrowser) {\",\n \" window.sourceElementMap = sourceElementMap;\",\n \"}\",\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 \" // During SSR / non-browser execution, skip all tagging and pass\",\n \" // straight through to React. Touching DOM refs or the source map\",\n \" // here would crash on Node-only paths (Vite SSR, vitest, etc.).\",\n \" if (!_isBrowser) {\",\n \" return _jsxDEV(type, props, key, isStatic, source, self);\",\n \" }\",\n \"\",\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;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,E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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadconnector/vibe-tagger",
3
- "version": "0.1.1",
3
+ "version": "0.3.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",