@upstart.gg/vite-plugins 0.1.4 → 0.1.6

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.
Files changed (62) hide show
  1. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts +10 -0
  2. package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
  3. package/dist/src/vite-plugin-upstart-editor/runtime/state.js +18 -0
  4. package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
  5. package/dist/upstart-editor-api.d.ts +0 -3
  6. package/dist/upstart-editor-api.d.ts.map +1 -1
  7. package/dist/upstart-editor-api.js +1 -2
  8. package/dist/upstart-editor-api.js.map +1 -1
  9. package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
  10. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  11. package/dist/vite-plugin-upstart-attrs.js +22 -22
  12. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  13. package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -4
  14. package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
  15. package/dist/vite-plugin-upstart-branding/plugin.js +1 -2
  16. package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
  17. package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -1
  18. package/dist/vite-plugin-upstart-branding/runtime.js +2 -2
  19. package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
  20. package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -1
  21. package/dist/vite-plugin-upstart-branding/types.js +1 -1
  22. package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -4
  23. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
  24. package/dist/vite-plugin-upstart-editor/plugin.js +2 -3
  25. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
  26. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -1
  27. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +84 -16
  28. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  29. package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -1
  30. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +1 -2
  31. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
  32. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -1
  33. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +78 -21
  34. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  35. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +3 -5
  36. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
  37. package/dist/vite-plugin-upstart-editor/runtime/index.js +30 -15
  38. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  39. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +0 -1
  40. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  41. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +9 -37
  42. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  43. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +7 -9
  44. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  45. package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -1
  46. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +0 -1
  47. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -1
  48. package/dist/vite-plugin-upstart-editor/runtime/utils.js +1 -1
  49. package/dist/vite-plugin-upstart-theme.d.ts +3 -3
  50. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
  51. package/dist/vite-plugin-upstart-theme.js +1 -2
  52. package/dist/vite-plugin-upstart-theme.js.map +1 -1
  53. package/package.json +14 -13
  54. package/src/vite-plugin-upstart-attrs.ts +1 -0
  55. package/src/vite-plugin-upstart-branding/runtime.ts +2 -3
  56. package/src/vite-plugin-upstart-editor/plugin.ts +2 -2
  57. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +94 -18
  58. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +94 -20
  59. package/src/vite-plugin-upstart-editor/runtime/index.ts +42 -13
  60. package/src/vite-plugin-upstart-editor/runtime/state.ts +17 -0
  61. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +49 -81
  62. package/src/vite-plugin-upstart-editor/runtime/types.ts +6 -3
@@ -0,0 +1,10 @@
1
+ import { EditorMode } from "../../../vite-plugin-upstart-editor/runtime/types.js";
2
+
3
+ //#region src/vite-plugin-upstart-editor/runtime/state.d.ts
4
+ /**
5
+ * Get the current editor mode.
6
+ */
7
+ declare function getCurrentMode(): EditorMode;
8
+ //#endregion
9
+ export { getCurrentMode };
10
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","names":[],"sources":["../../../../src/vite-plugin-upstart-editor/runtime/state.ts"],"mappings":";;;;;AAOA;iBAAgB,cAAA,CAAA,GAAkB,UAAA"}
@@ -0,0 +1,18 @@
1
+ //#region src/vite-plugin-upstart-editor/runtime/state.ts
2
+ let currentMode = "preview";
3
+ /**
4
+ * Get the current editor mode.
5
+ */
6
+ function getCurrentMode() {
7
+ return currentMode;
8
+ }
9
+ /**
10
+ * Set the current editor mode (internal use only).
11
+ */
12
+ function setCurrentMode(mode) {
13
+ currentMode = mode;
14
+ }
15
+ //#endregion
16
+ export { getCurrentMode, setCurrentMode };
17
+
18
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","names":[],"sources":["../../../../src/vite-plugin-upstart-editor/runtime/state.ts"],"sourcesContent":["import type { EditorMode } from \"./types.js\";\n\nlet currentMode: EditorMode = \"preview\";\n\n/**\n * Get the current editor mode.\n */\nexport function getCurrentMode(): EditorMode {\n return currentMode;\n}\n\n/**\n * Set the current editor mode (internal use only).\n */\nexport function setCurrentMode(mode: EditorMode): void {\n currentMode = mode;\n}\n"],"mappings":";AAEA,IAAI,cAA0B;;;;AAK9B,SAAgB,iBAA6B;AAC3C,QAAO;;;;;AAMT,SAAgB,eAAe,MAAwB;AACrD,eAAc"}
@@ -57,9 +57,6 @@ declare class UpstartEditorAPI {
57
57
  * Edit the className of an element
58
58
  */
59
59
  editClassName(params: PayloadEditClassName): Promise<EditResult>;
60
- /**
61
- * Apply an edit to a source file
62
- */
63
60
  private applyEdit;
64
61
  /**
65
62
  * Get element info by ID
@@ -1 +1 @@
1
- {"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"sourcesContent":[],"mappings":";;;;cAMa,iBAAe,CAAA,CAAA;;EAAf,QAAA,aASX;;;;;KAEU,eAAA,GAAkB,CAAA,CAAE,aAAa;cAEhC,sBAAoB,CAAA,CAAA;EAbL,MAAA,cAAA,CAAA,eAAA,CAAA;EAAA,EAAA,aAAA;EAWhB,SAAA,aAAe;AAE3B,CAAA,eAAa,CAAA;KAMD,oBAAA,GAAuB,CAAA,CAAE,aAAa;UAEjC,gBAAA;;;EARgB,QAAA,EAWrB,MAXqB,CAAA,MAAA,EAWN,aAXM,CAAA;;AAMrB,KAQA,UAAA,GARA;EAEK,OAAA,EAAA,IAAA;EAML,KAAA,CAAA,EAAA,KAAU;EAYT,QAAA,EAAA,MAAA;CAaW,GAAA;EAQP,OAAA,EAAA,KAAA;EAOO,KAAA,EAAA,MAAA;EASC,QAAA,CAAA,EAAA,KAAA;CAA0B;AAAR,cArC9B,gBAAA,CAqC8B;EAwDb,QAAA,QAAA;EAA+B,QAAA,WAAA;EAAR,QAAA,YAAA;EAyF3B,WAAA,CAAA,WAAA,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA;EAOsC;;;EAiB7B,YAAA,CAAA,CAAA,EAjMX,OAiMW,CAAA,IAAA,CAAA;EAAM;;;iBAzLxB;;;;wBAOO;;;;;;mBASC,kBAAkB,QAAQ;;;;wBAwDrB,uBAAuB,QAAQ;;;;;;;;0BAyFnC;;;;iDAOuB,eAAe;;;;mCAiB7B,eAAe"}
1
+ {"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"mappings":";;;;cAMa,eAAA,EAAe,CAAA,CAAA,SAAA;;;;;;;KAWhB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,eAAA;AAAA,cAEhC,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;KAMrB,oBAAA,GAAuB,CAAA,CAAE,KAAA,QAAa,oBAAA;AAAA,UAEjC,gBAAA;EACf,OAAA;EACA,WAAA;EACA,QAAA,EAAU,MAAA,SAAe,aAAA;AAAA;AAAA,KAGf,UAAA;EAEN,OAAA;EACA,KAAA;EACA,QAAA;AAAA;EAGA,OAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,cAGO,gBAAA;EAAA,QACH,QAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAER,WAAA,CAAY,WAAA,UAAqB,YAAA;;;;EAQ3B,YAAA,CAAA,GAAgB,OAAA;EAzCG;;;EAiDzB,WAAA,CAAA,GAAe,gBAAA;EAjDe;;;EAwD9B,WAAA,CAAY,QAAA,EAAU,gBAAA;EAtDX;;;;;EA+DL,QAAA,CAAS,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,UAAA;;;;EAwD3C,aAAA,CAAc,MAAA,EAAQ,oBAAA,GAAuB,OAAA,CAAQ,UAAA;EAAA,QA6B7C,SAAA;EApJiB;;;EAgN/B,UAAA,CAAW,EAAA,WAAa,aAAA;;;;EAOxB,iBAAA,CAAkB,IAAA,yBAA6B,MAAA,SAAe,aAAA;;;;EAiB9D,iBAAA,CAAkB,IAAA,WAAe,MAAA,SAAe,aAAA;AAAA"}
@@ -2,7 +2,6 @@ import MagicString from "magic-string";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import z from "zod";
5
-
6
5
  //#region src/upstart-editor-api.ts
7
6
  const payloadEditText = z.object({
8
7
  action: z.literal("editText"),
@@ -202,7 +201,7 @@ var UpstartEditorAPI = class {
202
201
  return result;
203
202
  }
204
203
  };
205
-
206
204
  //#endregion
207
205
  export { UpstartEditorAPI, payloadEditClassName, payloadEditText };
206
+
208
207
  //# sourceMappingURL=upstart-editor-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upstart-editor-api.js","names":["raw: string","data: Record<string, unknown>","current: Record<string, unknown>","result: Record<string, EditableEntry>"],"sources":["../src/upstart-editor-api.ts"],"sourcesContent":["import MagicString from \"magic-string\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport z from \"zod\";\nimport type { EditableEntry } from \"./vite-plugin-upstart-attrs\";\n\nexport const payloadEditText = z.object({\n action: z.literal(\"editText\"),\n language: z\n .string()\n .length(2)\n .regex(/^[a-z]{2}$/),\n namespace: z.string().regex(/^[a-z0-9_-]+$/),\n key: z.string().regex(/^[a-zA-Z0-9_.-]+$/),\n content: z.string(),\n});\n\nexport type PayloadEditText = z.infer<typeof payloadEditText>;\n\nexport const payloadEditClassName = z.object({\n action: z.literal(\"editClassName\"),\n id: z.string().min(1),\n className: z.string(),\n});\n\nexport type PayloadEditClassName = z.infer<typeof payloadEditClassName>;\n\nexport interface EditableRegistry {\n version: number;\n generatedAt: string;\n elements: Record<string, EditableEntry>;\n}\n\nexport type EditResult =\n | {\n success: true;\n error?: never;\n filePath: string;\n }\n | {\n success: false;\n error: string;\n filePath?: never;\n };\n\nexport class UpstartEditorAPI {\n private registry: EditableRegistry | null = null;\n private projectRoot: string;\n private registryPath: string;\n\n constructor(projectRoot: string, registryPath: string) {\n this.projectRoot = projectRoot;\n this.registryPath = registryPath;\n }\n\n /**\n * Load the registry from disk\n */\n async loadRegistry(): Promise<void> {\n const content = await fs.readFile(this.registryPath, \"utf-8\");\n this.registry = JSON.parse(content);\n }\n\n /**\n * Get the current registry (for testing/debugging)\n */\n getRegistry(): EditableRegistry | null {\n return this.registry;\n }\n\n /**\n * Set the registry directly (for testing)\n */\n setRegistry(registry: EditableRegistry): void {\n this.registry = registry;\n }\n\n /**\n * Edit a translation value in an i18next locale file.\n * Auto-detects flat keys (e.g. \"nav.home\" as literal key) vs nested keys (e.g. nav -> home).\n * Only updates existing keys — returns an error if the key is not found.\n */\n async editText(params: PayloadEditText): Promise<EditResult> {\n const parsed = payloadEditText.safeParse(params);\n if (!parsed.success) {\n return { success: false, error: `Invalid payload: ${parsed.error.message}` };\n }\n const { language, namespace, key, content: newContent } = parsed.data;\n const filePath = path.join(this.projectRoot, \"app\", \"locales\", language, `${namespace}.json`);\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, \"utf-8\");\n } catch (err) {\n return { success: false, error: `Failed to read locale file: ${filePath}` };\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(raw);\n } catch (err) {\n return { success: false, error: `Failed to parse locale file: ${filePath}` };\n }\n\n // Strategy 1: check for flat/literal key at top level\n if (key in data && typeof data[key] === \"string\") {\n data[key] = newContent;\n } else {\n // Strategy 2: nested traversal via dot notation\n const parts = key.split(\".\");\n let current: Record<string, unknown> = data;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (current[part] == null || typeof current[part] !== \"object\") {\n return { success: false, error: `Key \"${key}\" not found in locale file ${filePath}` };\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const leafKey = parts[parts.length - 1];\n if (!(leafKey in current) || typeof current[leafKey] !== \"string\") {\n return { success: false, error: `Key \"${key}\" not found in locale file ${filePath}` };\n }\n current[leafKey] = newContent;\n }\n\n try {\n await fs.writeFile(filePath, JSON.stringify(data, null, 2) + \"\\n\");\n } catch (err) {\n return { success: false, error: `Failed to write locale file: ${filePath}` };\n }\n\n return { success: true, filePath };\n }\n\n /**\n * Edit the className of an element\n */\n async editClassName(params: PayloadEditClassName): Promise<EditResult> {\n const parsed = payloadEditClassName.safeParse(params);\n if (!parsed.success) {\n return { success: false, error: `Invalid payload: ${parsed.error.message}` };\n }\n const { id, className: newClassName } = parsed.data;\n if (!this.registry) {\n try {\n await this.loadRegistry();\n } catch (err) {\n return { success: false, error: `Failed to load registry: ${err}` };\n }\n }\n\n const entry = this.registry!.elements[id];\n if (!entry) {\n return { success: false, error: `Element ${id} not found in registry` };\n }\n\n if (entry.type !== \"className\") {\n return { success: false, error: `Element ${id} is not a className element (type: ${entry.type})` };\n }\n\n return this.applyEdit(id, entry, newClassName);\n }\n\n /**\n * Apply an edit to a source file\n */\n private async applyEdit(id: string, entry: EditableEntry, newContent: string): Promise<EditResult> {\n const filePath = path.join(this.projectRoot, entry.file);\n\n try {\n const code = await fs.readFile(filePath, \"utf-8\");\n\n // Verify content at expected location\n const currentContent = code.slice(entry.startOffset, entry.endOffset);\n\n let actualStart = entry.startOffset;\n let actualEnd = entry.endOffset;\n\n if (currentContent !== entry.originalContent) {\n // Content has shifted - try to find it by searching\n const searchIndex = code.indexOf(entry.originalContent);\n if (searchIndex === -1) {\n return {\n success: false,\n error: `Original content \"${entry.originalContent}\" not found in file ${entry.file}. The file may have been modified.`,\n };\n }\n actualStart = searchIndex;\n actualEnd = searchIndex + entry.originalContent.length;\n }\n\n // Apply the edit using MagicString\n const s = new MagicString(code);\n s.overwrite(actualStart, actualEnd, newContent);\n\n // Write the modified file\n await fs.writeFile(filePath, s.toString());\n\n // Calculate the length difference for offset adjustments\n const lengthDiff = newContent.length - entry.originalContent.length;\n\n // Update the registry entry\n entry.startOffset = actualStart;\n entry.endOffset = actualStart + newContent.length;\n entry.originalContent = newContent;\n\n // Shift all subsequent entries in the same file\n for (const [otherId, otherEntry] of Object.entries(this.registry!.elements)) {\n if (otherId !== id && otherEntry.file === entry.file && otherEntry.startOffset > actualStart) {\n otherEntry.startOffset += lengthDiff;\n otherEntry.endOffset += lengthDiff;\n }\n }\n\n // Save the updated registry\n await fs.writeFile(this.registryPath, JSON.stringify(this.registry, null, 2));\n\n return { success: true, filePath };\n } catch (err) {\n return { success: false, error: String(err) };\n }\n }\n\n /**\n * Get element info by ID\n */\n getElement(id: string): EditableEntry | undefined {\n return this.registry?.elements[id];\n }\n\n /**\n * Get all elements of a specific type\n */\n getElementsByType(type: \"text\" | \"className\"): Record<string, EditableEntry> {\n if (!this.registry) {\n return {};\n }\n\n const result: Record<string, EditableEntry> = {};\n for (const [id, entry] of Object.entries(this.registry.elements)) {\n if (entry.type === type) {\n result[id] = entry;\n }\n }\n return result;\n }\n\n /**\n * Get all elements in a specific file\n */\n getElementsByFile(file: string): Record<string, EditableEntry> {\n if (!this.registry) {\n return {};\n }\n\n const result: Record<string, EditableEntry> = {};\n for (const [id, entry] of Object.entries(this.registry.elements)) {\n if (entry.file === file) {\n result[id] = entry;\n }\n }\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"upstart-editor-api.js","names":[],"sources":["../src/upstart-editor-api.ts"],"sourcesContent":["import MagicString from \"magic-string\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport z from \"zod\";\nimport type { EditableEntry } from \"./vite-plugin-upstart-attrs\";\n\nexport const payloadEditText = z.object({\n action: z.literal(\"editText\"),\n language: z\n .string()\n .length(2)\n .regex(/^[a-z]{2}$/),\n namespace: z.string().regex(/^[a-z0-9_-]+$/),\n key: z.string().regex(/^[a-zA-Z0-9_.-]+$/),\n content: z.string(),\n});\n\nexport type PayloadEditText = z.infer<typeof payloadEditText>;\n\nexport const payloadEditClassName = z.object({\n action: z.literal(\"editClassName\"),\n id: z.string().min(1),\n className: z.string(),\n});\n\nexport type PayloadEditClassName = z.infer<typeof payloadEditClassName>;\n\nexport interface EditableRegistry {\n version: number;\n generatedAt: string;\n elements: Record<string, EditableEntry>;\n}\n\nexport type EditResult =\n | {\n success: true;\n error?: never;\n filePath: string;\n }\n | {\n success: false;\n error: string;\n filePath?: never;\n };\n\nexport class UpstartEditorAPI {\n private registry: EditableRegistry | null = null;\n private projectRoot: string;\n private registryPath: string;\n\n constructor(projectRoot: string, registryPath: string) {\n this.projectRoot = projectRoot;\n this.registryPath = registryPath;\n }\n\n /**\n * Load the registry from disk\n */\n async loadRegistry(): Promise<void> {\n const content = await fs.readFile(this.registryPath, \"utf-8\");\n this.registry = JSON.parse(content);\n }\n\n /**\n * Get the current registry (for testing/debugging)\n */\n getRegistry(): EditableRegistry | null {\n return this.registry;\n }\n\n /**\n * Set the registry directly (for testing)\n */\n setRegistry(registry: EditableRegistry): void {\n this.registry = registry;\n }\n\n /**\n * Edit a translation value in an i18next locale file.\n * Auto-detects flat keys (e.g. \"nav.home\" as literal key) vs nested keys (e.g. nav -> home).\n * Only updates existing keys — returns an error if the key is not found.\n */\n async editText(params: PayloadEditText): Promise<EditResult> {\n const parsed = payloadEditText.safeParse(params);\n if (!parsed.success) {\n return { success: false, error: `Invalid payload: ${parsed.error.message}` };\n }\n const { language, namespace, key, content: newContent } = parsed.data;\n const filePath = path.join(this.projectRoot, \"app\", \"locales\", language, `${namespace}.json`);\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, \"utf-8\");\n } catch (err) {\n return { success: false, error: `Failed to read locale file: ${filePath}` };\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(raw);\n } catch (err) {\n return { success: false, error: `Failed to parse locale file: ${filePath}` };\n }\n\n // Strategy 1: check for flat/literal key at top level\n if (key in data && typeof data[key] === \"string\") {\n data[key] = newContent;\n } else {\n // Strategy 2: nested traversal via dot notation\n const parts = key.split(\".\");\n let current: Record<string, unknown> = data;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (current[part] == null || typeof current[part] !== \"object\") {\n return { success: false, error: `Key \"${key}\" not found in locale file ${filePath}` };\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const leafKey = parts[parts.length - 1];\n if (!(leafKey in current) || typeof current[leafKey] !== \"string\") {\n return { success: false, error: `Key \"${key}\" not found in locale file ${filePath}` };\n }\n current[leafKey] = newContent;\n }\n\n try {\n await fs.writeFile(filePath, JSON.stringify(data, null, 2) + \"\\n\");\n } catch (err) {\n return { success: false, error: `Failed to write locale file: ${filePath}` };\n }\n\n return { success: true, filePath };\n }\n\n /**\n * Edit the className of an element\n */\n async editClassName(params: PayloadEditClassName): Promise<EditResult> {\n const parsed = payloadEditClassName.safeParse(params);\n if (!parsed.success) {\n return { success: false, error: `Invalid payload: ${parsed.error.message}` };\n }\n const { id, className: newClassName } = parsed.data;\n if (!this.registry) {\n try {\n await this.loadRegistry();\n } catch (err) {\n return { success: false, error: `Failed to load registry: ${err}` };\n }\n }\n\n const entry = this.registry!.elements[id];\n if (!entry) {\n return { success: false, error: `Element ${id} not found in registry` };\n }\n\n if (entry.type !== \"className\") {\n return { success: false, error: `Element ${id} is not a className element (type: ${entry.type})` };\n }\n\n return this.applyEdit(id, entry, newClassName);\n }\n\n /**\n * Apply an edit to a source file\n */\n private async applyEdit(id: string, entry: EditableEntry, newContent: string): Promise<EditResult> {\n const filePath = path.join(this.projectRoot, entry.file);\n\n try {\n const code = await fs.readFile(filePath, \"utf-8\");\n\n // Verify content at expected location\n const currentContent = code.slice(entry.startOffset, entry.endOffset);\n\n let actualStart = entry.startOffset;\n let actualEnd = entry.endOffset;\n\n if (currentContent !== entry.originalContent) {\n // Content has shifted - try to find it by searching\n const searchIndex = code.indexOf(entry.originalContent);\n if (searchIndex === -1) {\n return {\n success: false,\n error: `Original content \"${entry.originalContent}\" not found in file ${entry.file}. The file may have been modified.`,\n };\n }\n actualStart = searchIndex;\n actualEnd = searchIndex + entry.originalContent.length;\n }\n\n // Apply the edit using MagicString\n const s = new MagicString(code);\n s.overwrite(actualStart, actualEnd, newContent);\n\n // Write the modified file\n await fs.writeFile(filePath, s.toString());\n\n // Calculate the length difference for offset adjustments\n const lengthDiff = newContent.length - entry.originalContent.length;\n\n // Update the registry entry\n entry.startOffset = actualStart;\n entry.endOffset = actualStart + newContent.length;\n entry.originalContent = newContent;\n\n // Shift all subsequent entries in the same file\n for (const [otherId, otherEntry] of Object.entries(this.registry!.elements)) {\n if (otherId !== id && otherEntry.file === entry.file && otherEntry.startOffset > actualStart) {\n otherEntry.startOffset += lengthDiff;\n otherEntry.endOffset += lengthDiff;\n }\n }\n\n // Save the updated registry\n await fs.writeFile(this.registryPath, JSON.stringify(this.registry, null, 2));\n\n return { success: true, filePath };\n } catch (err) {\n return { success: false, error: String(err) };\n }\n }\n\n /**\n * Get element info by ID\n */\n getElement(id: string): EditableEntry | undefined {\n return this.registry?.elements[id];\n }\n\n /**\n * Get all elements of a specific type\n */\n getElementsByType(type: \"text\" | \"className\"): Record<string, EditableEntry> {\n if (!this.registry) {\n return {};\n }\n\n const result: Record<string, EditableEntry> = {};\n for (const [id, entry] of Object.entries(this.registry.elements)) {\n if (entry.type === type) {\n result[id] = entry;\n }\n }\n return result;\n }\n\n /**\n * Get all elements in a specific file\n */\n getElementsByFile(file: string): Record<string, EditableEntry> {\n if (!this.registry) {\n return {};\n }\n\n const result: Record<string, EditableEntry> = {};\n for (const [id, entry] of Object.entries(this.registry.elements)) {\n if (entry.file === file) {\n result[id] = entry;\n }\n }\n return result;\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import * as magic_string0 from "magic-string";
2
- import * as unplugin0 from "unplugin";
2
+ import * as unplugin from "unplugin";
3
3
 
4
4
  //#region src/vite-plugin-upstart-attrs.d.ts
5
5
  interface Options {
@@ -18,12 +18,12 @@ interface EditableEntry {
18
18
  }
19
19
  declare function getRegistry(): Record<string, EditableEntry>;
20
20
  declare function clearRegistry(): void;
21
- declare const upstartEditor: unplugin0.UnpluginInstance<Options, boolean>;
21
+ declare const upstartEditor: unplugin.UnpluginInstance<Options, boolean>;
22
22
  declare function transformWithOxc(code: string, filePath: string): {
23
23
  code: string;
24
24
  map: magic_string0.SourceMap;
25
25
  } | null;
26
- declare const _default: (options: Options) => unplugin0.VitePlugin<any> | unplugin0.VitePlugin<any>[];
26
+ declare const _default: (options: Options) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
27
27
  //#endregion
28
28
  export { EditableEntry, clearRegistry, _default as default, getRegistry, transformWithOxc, upstartEditor };
29
29
  //# sourceMappingURL=vite-plugin-upstart-attrs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-attrs.d.ts","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"sourcesContent":[],"mappings":";;;;UAeU,OAAA;;;;AAAA,UA0CO,aAAA,CA1CA;EA0CA,IAAA,EAAA,MAAA;EAoBD,IAAA,EAAA,MAAA,GAAW,WAAmB;EAK9B,WAAA,EAAA,MAAa;EAehB,SAAA,EAAA,MAkEX;EAEc,eAAA,EAAA,MAAgB;EAgP/B,OAAA,EAAA;;;;iBAxUe,WAAA,CAAA,GAAe,eAAe;iBAK9B,aAAA,CAAA;cAeH,eAAa,SAAA,CAAA,iBAAA;iBAoEV,gBAAA;;OAAgB,aAAA,CAAA;;cAgP/B"}
1
+ {"version":3,"file":"vite-plugin-upstart-attrs.d.ts","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"mappings":";;;;UAeU,OAAA;EACR,OAAA;EACA,YAAA;AAAA;AAAA,UAwCe,aAAA;EACf,IAAA;EACA,IAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,OAAA;IAAW,SAAA;EAAA;AAAA;AAAA,iBAcG,WAAA,CAAA,GAAe,MAAA,SAAe,aAAA;AAAA,iBAK9B,aAAA,CAAA;AAAA,cAeH,aAAA,EAAa,QAAA,CAAA,gBAAA,CAAA,OAAA;AAAA,iBAoEV,gBAAA,CAAiB,IAAA,UAAc,QAAA;;OAAf,aAAA,CAAA,SAAA;AAAA;AAAA,cAiP/B,QAAA"}
@@ -3,7 +3,6 @@ import path from "node:path";
3
3
  import { createUnplugin } from "unplugin";
4
4
  import { parseSync } from "oxc-parser";
5
5
  import { walk } from "zimmerframe";
6
-
7
6
  //#region src/vite-plugin-upstart-attrs.ts
8
7
  function hasRange(node) {
9
8
  return node && typeof node.start === "number" && typeof node.end === "number";
@@ -81,17 +80,17 @@ function transformWithOxc(code, filePath) {
81
80
  tFunctions: /* @__PURE__ */ new Map()
82
81
  };
83
82
  let modified = false;
84
- walk(ast.program, state, { _(node, { state: state$1, next }) {
83
+ walk(ast.program, state, { _(node, { state, next }) {
85
84
  if (node.type === "VariableDeclaration") {
86
- trackUseTranslation(node, state$1);
87
- for (const decl of node.declarations) if (decl.type === "VariableDeclarator" && decl.id?.type === "Identifier" && decl.init?.type === "Literal" && typeof decl.init.value === "string") state$1.constants.set(decl.id.name, decl.init.value);
85
+ trackUseTranslation(node, state);
86
+ for (const decl of node.declarations) if (decl.type === "VariableDeclarator" && decl.id?.type === "Identifier" && decl.init?.type === "Literal" && typeof decl.init.value === "string") state.constants.set(decl.id.name, decl.init.value);
88
87
  }
89
88
  if (node.type === "CallExpression") {
90
- const loopInfo = detectMapCall(node, state$1.code);
89
+ const loopInfo = detectMapCall(node, state.code);
91
90
  if (loopInfo) {
92
- state$1.loopStack.push(loopInfo);
91
+ state.loopStack.push(loopInfo);
93
92
  next();
94
- state$1.loopStack.pop();
93
+ state.loopStack.pop();
95
94
  return;
96
95
  }
97
96
  }
@@ -103,22 +102,23 @@ function transformWithOxc(code, filePath) {
103
102
  next();
104
103
  return;
105
104
  }
106
- const insertPos = getAttributeInsertPosition(opening, state$1.code);
105
+ const insertPos = getAttributeInsertPosition(opening, state.code);
107
106
  const attributes = [];
108
107
  if (hasRange(jsxNode)) {
109
- const hash = hashContent(state$1.code.slice(jsxNode.start, jsxNode.end));
110
- const loopIndices = state$1.loopStack.map((l) => l.indexName).filter((n) => n !== null);
108
+ const hash = hashContent(state.code.slice(jsxNode.start, jsxNode.end));
109
+ const loopIndices = state.loopStack.map((l) => l.indexName).filter((n) => n !== null);
111
110
  if (loopIndices.length > 0) {
112
111
  const suffix = loopIndices.map((n) => `\${${n}}`).join("-");
113
112
  attributes.push(`data-upstart-hash={\`${hash}-${suffix}\`}`);
114
113
  } else attributes.push(`data-upstart-hash="${hash}"`);
115
114
  }
116
- const transChildren = findTransInChildren(jsxNode, state$1.code, state$1.constants);
117
- const tCallChildren = findTCallsInChildren(jsxNode, state$1.code, state$1);
115
+ const transChildren = findTransInChildren(jsxNode, state.code, state.constants);
116
+ const tCallChildren = findTCallsInChildren(jsxNode, state.code, state);
118
117
  const allI18nKeys = [...transChildren, ...tCallChildren];
119
118
  const hasI18n = allI18nKeys.length > 0;
120
119
  if (hasI18n) {
121
120
  attributes.push("data-upstart-editable-text=\"true\"");
121
+ attributes.push("data-upstart-editable-text-mode=\"plain\"");
122
122
  if (allI18nKeys.some((t) => t.keyExpr || t.nsExpr)) {
123
123
  const parts = allI18nKeys.map((t) => {
124
124
  return `${t.nsExpr ? `\${${t.nsExpr}}` : t.namespace}:${t.keyExpr ? `\${${t.keyExpr}}` : t.key}`;
@@ -133,12 +133,12 @@ function transformWithOxc(code, filePath) {
133
133
  if (!hasI18n) attributes.push("data-upstart-editable-text=\"false\"");
134
134
  const textChild = jsxNode.children.find((c) => c.type === "JSXText" && c.value?.trim());
135
135
  if (textChild && hasRange(textChild)) {
136
- const id = generateId(state$1.filePath, textChild);
136
+ const id = generateId(state.filePath, textChild);
137
137
  const textValue = textChild.value;
138
138
  const trimmedStart = textChild.start + (textValue.length - textValue.trimStart().length);
139
139
  const trimmedEnd = textChild.end - (textValue.length - textValue.trimEnd().length);
140
140
  editableRegistry.set(id, {
141
- file: state$1.filePath,
141
+ file: state.filePath,
142
142
  type: "text",
143
143
  startOffset: trimmedStart,
144
144
  endOffset: trimmedEnd,
@@ -150,10 +150,10 @@ function transformWithOxc(code, filePath) {
150
150
  } else if (!hasI18n && hasVisibleTextContent(jsxNode)) attributes.push("data-upstart-editable-text=\"false\"");
151
151
  const classNameAttr = opening.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "className" && attr.value?.type === "Literal" && typeof attr.value.value === "string");
152
152
  if (classNameAttr && classNameAttr.value && hasRange(classNameAttr.value)) {
153
- const id = generateId(state$1.filePath, classNameAttr.value);
153
+ const id = generateId(state.filePath, classNameAttr.value);
154
154
  const classValue = classNameAttr.value.value;
155
155
  editableRegistry.set(id, {
156
- file: state$1.filePath,
156
+ file: state.filePath,
157
157
  type: "className",
158
158
  startOffset: classNameAttr.value.start + 1,
159
159
  endOffset: classNameAttr.value.end - 1,
@@ -163,10 +163,10 @@ function transformWithOxc(code, filePath) {
163
163
  attributes.push(`data-upstart-classname-id="${id}"`);
164
164
  }
165
165
  if (tagName && /^[A-Z]/.test(tagName)) {
166
- attributes.push(`data-upstart-file="${escapeProp(state$1.filePath)}"`);
166
+ attributes.push(`data-upstart-file="${escapeProp(state.filePath)}"`);
167
167
  attributes.push(`data-upstart-component="${escapeProp(tagName)}"`);
168
- if (state$1.loopStack.length > 0) {
169
- const loop = state$1.loopStack[state$1.loopStack.length - 1];
168
+ if (state.loopStack.length > 0) {
169
+ const loop = state.loopStack[state.loopStack.length - 1];
170
170
  attributes.push(`data-upstart-loop-item="${escapeProp(loop.itemName)}"`);
171
171
  if (loop.indexName) attributes.push(`data-upstart-loop-index={${loop.indexName}}`);
172
172
  attributes.push(`data-upstart-loop-array="${escapeProp(loop.arrayExpr)}"`);
@@ -174,7 +174,7 @@ function transformWithOxc(code, filePath) {
174
174
  for (const attr of opening.attributes) {
175
175
  if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier") continue;
176
176
  const propName = attr.name.name;
177
- const binding = analyzeBinding(attr.value, state$1.code);
177
+ const binding = analyzeBinding(attr.value, state.code);
178
178
  if (binding) {
179
179
  attributes.push(`data-upstart-prop-${propName.toLowerCase()}="${escapeProp(binding.path)}"`);
180
180
  if (binding.isConditional) attributes.push(`data-upstart-conditional-${propName.toLowerCase()}="true"`);
@@ -183,7 +183,7 @@ function transformWithOxc(code, filePath) {
183
183
  }
184
184
  if (insertPos !== -1 && attributes.length > 0) {
185
185
  const attrString = " " + attributes.join(" ");
186
- state$1.s.appendLeft(insertPos, attrString);
186
+ state.s.appendLeft(insertPos, attrString);
187
187
  modified = true;
188
188
  }
189
189
  }
@@ -500,7 +500,7 @@ function hasVisibleTextContent(jsxElement) {
500
500
  return false;
501
501
  }
502
502
  var vite_plugin_upstart_attrs_default = upstartEditor.vite;
503
-
504
503
  //#endregion
505
504
  export { clearRegistry, vite_plugin_upstart_attrs_default as default, getRegistry, transformWithOxc, upstartEditor };
505
+
506
506
  //# sourceMappingURL=vite-plugin-upstart-attrs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-attrs.js","names":["state: TransformState","state","attributes: string[]","keyResult: { value: string; expr: string | null } | null","nsResult: { value: string; expr: string | null } | null","results: I18nKeyInfo[]"],"sources":["../src/vite-plugin-upstart-attrs.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport { parseSync } from \"oxc-parser\";\nimport { walk } from \"zimmerframe\";\nimport MagicString from \"magic-string\";\nimport path from \"path\";\nimport type {\n Program,\n Node,\n JSXElement,\n JSXOpeningElement,\n JSXAttribute,\n Expression,\n CallExpression,\n} from \"estree-jsx\";\n\ninterface Options {\n enabled: boolean;\n emitRegistry?: boolean;\n}\n\ntype NodeWithRange = Node & { start: number; end: number };\n\nfunction hasRange(node: any): node is NodeWithRange {\n return node && typeof node.start === \"number\" && typeof node.end === \"number\";\n}\n\n// Fast, stable hash function (djb2 variant)\n// Produces a short hex string that's stable across rebuilds\nfunction hashContent(content: string): string {\n let hash = 5381;\n for (let i = 0; i < content.length; i++) {\n hash = ((hash << 5) + hash) ^ content.charCodeAt(i);\n }\n // Convert to unsigned 32-bit and then to hex\n return (hash >>> 0).toString(16);\n}\n\ninterface LoopContext {\n itemName: string;\n indexName: string | null;\n arrayExpr: string;\n}\n\ninterface I18nKeyInfo {\n /** The resolved key with namespace, e.g. \"dashboard:features.title\" */\n fullKey: string;\n /** Just the translation key, e.g. \"features.title\" */\n key: string;\n /** The namespace, e.g. \"dashboard\" */\n namespace: string;\n /** Source expression for i18nKey when dynamic (e.g. \"stat.labelKey\"), null when static */\n keyExpr: string | null;\n /** Source expression for namespace when dynamic, null when static */\n nsExpr: string | null;\n}\n\n// Registry entry for editable elements (text and className)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"className\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n context: { parentTag: string };\n}\n\n// Module-level registry (collected across all files during build)\nconst editableRegistry = new Map<string, EditableEntry>();\n\n// Generate stable ID for editable elements\n// Replace the counter-based ID generation with position-based\nfunction generateId(filePath: string, node: NodeWithRange): string {\n // Use file path + start position for a stable, deterministic ID\n return `${filePath}:${node.start}`;\n}\n\n// Export for testing - get a copy of the current registry\nexport function getRegistry(): Record<string, EditableEntry> {\n return Object.fromEntries(editableRegistry);\n}\n\n// Export for testing - clear registry and counters\nexport function clearRegistry(): void {\n editableRegistry.clear();\n}\n\ninterface TransformState {\n filePath: string;\n code: string;\n s: MagicString;\n loopStack: LoopContext[];\n /** Tracks const varName = \"literal\" declarations for resolving dynamic i18nKey expressions */\n constants: Map<string, string>;\n /** Maps t-function variable names to their default namespace, e.g. { \"t\": \"dashboard\" } */\n tFunctions: Map<string, string>;\n}\n\nexport const upstartEditor = createUnplugin<Options>((options) => {\n if (!options.enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const emitRegistry = options.emitRegistry ?? true;\n let root = process.cwd();\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n },\n generateBundle() {\n if (!emitRegistry || editableRegistry.size === 0) {\n return;\n }\n\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n\n this.emitFile({\n type: \"asset\",\n fileName: \"upstart-registry.json\",\n source: JSON.stringify(registry, null, 2),\n });\n\n // Clear for next build\n editableRegistry.clear();\n },\n },\n\n transformInclude(id) {\n return /\\.(tsx|jsx)$/.test(id) && !id.includes(\"node_modules\");\n },\n\n transform(code, id) {\n // Fast path: skip files without JSX\n if (!code.includes(\"<\")) {\n return null;\n }\n\n try {\n const relativePath = path.relative(root, id);\n const result = transformWithOxc(code, relativePath);\n\n if (!result) {\n return null;\n }\n\n return {\n code: result.code,\n map: result.map,\n };\n } catch (error) {\n console.error(`Error transforming ${id}:`, error);\n return null;\n }\n },\n };\n});\n\nexport function transformWithOxc(code: string, filePath: string) {\n // Parse with oxc (super fast!)\n const ast = parseSync(filePath, code, {\n sourceType: \"module\",\n });\n\n if (!ast.program) {\n return null;\n }\n\n const s = new MagicString(code);\n const state: TransformState = {\n filePath,\n code,\n s,\n loopStack: [],\n constants: new Map(),\n tFunctions: new Map(),\n };\n\n let modified = false;\n\n // Use zimmerframe to walk and transform the AST\n walk(ast.program as Program, state, {\n _(node: Node, { state, next }: { state: TransformState; next: () => void }) {\n // Track useTranslation() calls to resolve t() function namespaces\n if (node.type === \"VariableDeclaration\") {\n trackUseTranslation(node, state);\n\n // Track const varName = \"literal\" for resolving dynamic i18nKey expressions\n for (const decl of (node as any).declarations) {\n if (\n decl.type === \"VariableDeclarator\" &&\n decl.id?.type === \"Identifier\" &&\n decl.init?.type === \"Literal\" &&\n typeof decl.init.value === \"string\"\n ) {\n state.constants.set(decl.id.name, decl.init.value);\n }\n }\n }\n\n // Handle .map() calls to track loops (must be before JSXElement)\n if (node.type === \"CallExpression\") {\n const loopInfo = detectMapCall(node as CallExpression, state.code);\n\n if (loopInfo) {\n // Push loop context\n state.loopStack.push(loopInfo);\n\n // Continue walking into the callback\n next();\n\n // Pop loop context after traversing\n state.loopStack.pop();\n return;\n }\n }\n\n // Handle JSX elements\n if (node.type === \"JSXElement\") {\n const jsxNode = node as JSXElement;\n const opening = jsxNode.openingElement;\n const tagName = getJSXElementName(opening);\n\n // Skip ALL attribute injection for <Trans> elements.\n // <Trans> renders to a text node at runtime, so DOM attributes are lost.\n // The i18n attributes are promoted to the parent element instead.\n if (tagName === \"Trans\") {\n next();\n return;\n }\n\n const insertPos = getAttributeInsertPosition(opening, state.code);\n\n // Track attributes to inject\n const attributes: string[] = [];\n\n // Compute stable hash from the element's source code (includes all children)\n // This hash changes if ANY part of the element or its children change\n if (hasRange(jsxNode)) {\n const elementSource = state.code.slice(jsxNode.start, jsxNode.end);\n const hash = hashContent(elementSource);\n\n // Collect index variables from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n const loopIndices = state.loopStack.map((l) => l.indexName).filter((n): n is string => n !== null);\n\n if (loopIndices.length > 0) {\n const suffix = loopIndices.map((n) => `\\${${n}}`).join(\"-\");\n attributes.push(`data-upstart-hash={\\`${hash}-${suffix}\\`}`);\n } else {\n attributes.push(`data-upstart-hash=\"${hash}\"`);\n }\n }\n\n // --- Editable text detection ---\n // Priority: i18n (true) > non-i18n text (false) > no text (no attribute)\n\n // Step 1: Check for Trans children AND t() calls (editable via i18n, gets \"true\")\n const transChildren = findTransInChildren(jsxNode, state.code, state.constants);\n const tCallChildren = findTCallsInChildren(jsxNode, state.code, state);\n const allI18nKeys = [...transChildren, ...tCallChildren];\n const hasI18n = allI18nKeys.length > 0;\n\n if (hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n\n const hasDynamic = allI18nKeys.some((t) => t.keyExpr || t.nsExpr);\n if (hasDynamic) {\n // Build a runtime JSX template expression for dynamic i18n keys\n const parts = allI18nKeys.map((t) => {\n const nsPart = t.nsExpr ? `\\${${t.nsExpr}}` : t.namespace;\n const keyPart = t.keyExpr ? `\\${${t.keyExpr}}` : t.key;\n return `${nsPart}:${keyPart}`;\n });\n attributes.push(`data-upstart-i18n={\\`${parts.join(\",\")}\\`}`);\n } else {\n const keys = allI18nKeys.map((t: I18nKeyInfo) => escapeProp(t.fullKey)).join(\",\");\n attributes.push(`data-upstart-i18n=\"${keys}\"`);\n }\n }\n\n // Step 2: Text leaf elements — registry tracking + non-editable flag\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Find the actual JSXText node to track its location\n const textChild = jsxNode.children.find((c) => c.type === \"JSXText\" && (c as any).value?.trim());\n\n if (textChild && hasRange(textChild)) {\n const id = generateId(state.filePath, textChild);\n const textValue = (textChild as any).value as string;\n\n // Calculate trimmed offsets (exclude leading/trailing whitespace)\n const trimmedStart = textChild.start + (textValue.length - textValue.trimStart().length);\n const trimmedEnd = textChild.end - (textValue.length - textValue.trimEnd().length);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"text\",\n startOffset: trimmedStart,\n endOffset: trimmedEnd,\n originalContent: textValue.trim(),\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 3: Non-leaf elements with visible text content (expressions, mixed content)\n else if (!hasI18n && hasVisibleTextContent(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Track className attribute if it's a string literal\n const classNameAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n attr.name.name === \"className\" &&\n attr.value?.type === \"Literal\" &&\n typeof (attr.value as any).value === \"string\",\n );\n\n if (classNameAttr && classNameAttr.value && hasRange(classNameAttr.value)) {\n const id = generateId(state.filePath, classNameAttr.value);\n const classValue = (classNameAttr.value as any).value as string;\n\n // +1 and -1 to exclude the quotes\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"className\",\n startOffset: classNameAttr.value.start + 1,\n endOffset: classNameAttr.value.end - 1,\n originalContent: classValue,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-classname-id=\"${id}\"`);\n }\n\n // Process PascalCase components for additional tracking\n if (tagName && /^[A-Z]/.test(tagName)) {\n // File and component tracking\n attributes.push(`data-upstart-file=\"${escapeProp(state.filePath)}\"`);\n attributes.push(`data-upstart-component=\"${escapeProp(tagName)}\"`);\n\n // Loop context tracking\n if (state.loopStack.length > 0) {\n const loop = state.loopStack[state.loopStack.length - 1];\n attributes.push(`data-upstart-loop-item=\"${escapeProp(loop.itemName)}\"`);\n if (loop.indexName) {\n attributes.push(`data-upstart-loop-index={${loop.indexName}}`);\n }\n attributes.push(`data-upstart-loop-array=\"${escapeProp(loop.arrayExpr)}\"`);\n }\n\n // Analyze each prop for data bindings\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") {\n continue;\n }\n\n const propName = attr.name.name;\n const binding = analyzeBinding(attr.value, state.code);\n\n if (binding) {\n attributes.push(`data-upstart-prop-${propName.toLowerCase()}=\"${escapeProp(binding.path)}\"`);\n\n // Track conditional expressions\n if (binding.isConditional) {\n attributes.push(`data-upstart-conditional-${propName.toLowerCase()}=\"true\"`);\n }\n }\n }\n }\n\n // Inject attributes if any\n if (insertPos !== -1 && attributes.length > 0) {\n const attrString = \" \" + attributes.join(\" \");\n state.s.appendLeft(insertPos, attrString);\n modified = true;\n }\n }\n\n next();\n },\n });\n\n if (!modified) {\n return null;\n }\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: true }),\n };\n}\n\n// Helper: Get JSX element name\nfunction getJSXElementName(opening: JSXOpeningElement): string | null {\n if (opening.name.type === \"JSXIdentifier\") {\n return opening.name.name;\n }\n\n // Handle JSXMemberExpression like <Foo.Bar>\n if (opening.name.type === \"JSXMemberExpression\") {\n let current = opening.name;\n while (current.property) {\n if (current.property.type === \"JSXIdentifier\") {\n return current.property.name;\n }\n if (current.type === \"JSXMemberExpression\") {\n current = current.property as any;\n } else {\n break;\n }\n }\n }\n\n return null;\n}\n\n// Helper: Find where to insert attributes in JSX opening tag\nfunction getAttributeInsertPosition(opening: JSXOpeningElement, code: string): number {\n // If there are existing attributes, insert before the first one\n if (opening.attributes.length > 0) {\n const firstAttr = opening.attributes[0];\n if (hasRange(firstAttr)) {\n return firstAttr.start;\n }\n }\n\n // Otherwise, insert after the tag name\n if (opening.name.type === \"JSXIdentifier\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n if (opening.name.type === \"JSXMemberExpression\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n return -1;\n}\n\n// Helper: Analyze a prop value to extract data binding\nfunction analyzeBinding(\n value: JSXAttribute[\"value\"],\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n if (!value) {\n return null;\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n if (expr.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n return analyzeExpression(expr, code);\n }\n\n return null;\n}\n\n// Helper: Analyze any expression to extract binding info\nfunction analyzeExpression(\n expr: Expression,\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n // Handle member expressions: user.name, product.price, etc.\n if (expr.type === \"MemberExpression\") {\n const path = exprToString(expr, code);\n return { path };\n }\n\n // Handle identifiers: userName, price, etc.\n if (expr.type === \"Identifier\") {\n return { path: expr.name };\n }\n\n // Handle conditional expressions: condition ? value1 : value2\n if (expr.type === \"ConditionalExpression\") {\n // Try to extract from the consequent\n const consequent = analyzeExpression(expr.consequent, code);\n if (consequent) {\n return {\n ...consequent,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // Handle logical expressions: value1 && value2, value1 || value2\n if (expr.type === \"LogicalExpression\") {\n const right = analyzeExpression(expr.right, code);\n if (right) {\n return {\n ...right,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // For other complex expressions, just return the path\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return {\n path: code.slice(exprWithRange.start, exprWithRange.end),\n };\n }\n\n return null;\n}\n\n// Helper: Convert expression AST to string\nfunction exprToString(expr: Expression, code: string): string {\n // Use the source range to get the actual code\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return code.slice(exprWithRange.start, exprWithRange.end);\n }\n\n // Fallback: reconstruct from AST\n if (expr.type === \"Identifier\") {\n return expr.name;\n }\n\n if (expr.type === \"MemberExpression\") {\n const obj = exprToString(expr.object as Expression, code);\n const prop =\n expr.property.type === \"Identifier\" && !expr.computed\n ? expr.property.name\n : exprToString(expr.property as Expression, code);\n return expr.computed ? `${obj}[${prop}]` : `${obj}.${prop}`;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n const test = exprToString(expr.test, code);\n const consequent = exprToString(expr.consequent, code);\n const alternate = exprToString(expr.alternate, code);\n return `${test} ? ${consequent} : ${alternate}`;\n }\n\n if (expr.type === \"LogicalExpression\") {\n const left = exprToString(expr.left, code);\n const right = exprToString(expr.right, code);\n return `${left} ${expr.operator} ${right}`;\n }\n\n return \"\";\n}\n\n// Helper: Detect .map() calls and extract loop context\nfunction detectMapCall(node: CallExpression, code: string): LoopContext | null {\n // Check if this is a .map() call\n if (\n node.callee.type !== \"MemberExpression\" ||\n node.callee.property.type !== \"Identifier\" ||\n node.callee.property.name !== \"map\"\n ) {\n return null;\n }\n\n const callback = node.arguments[0];\n\n if (!callback) {\n return null;\n }\n\n // Check if callback is an arrow function or function expression\n if (callback.type !== \"ArrowFunctionExpression\" && callback.type !== \"FunctionExpression\") {\n return null;\n }\n\n const params = callback.params;\n const itemParam = params[0];\n const indexParam = params[1];\n\n if (!itemParam) {\n return null;\n }\n\n // Extract parameter names\n const itemName = itemParam.type === \"Identifier\" ? itemParam.name : \"item\";\n const indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\n// Helper: Track useTranslation() calls to map t-function variable names to their namespace\nfunction trackUseTranslation(node: Node, state: TransformState): void {\n if (node.type !== \"VariableDeclaration\") return;\n\n const decl = node as any;\n for (const declarator of decl.declarations) {\n if (\n declarator.id?.type !== \"ObjectPattern\" ||\n !declarator.init ||\n declarator.init.type !== \"CallExpression\"\n ) {\n continue;\n }\n\n const callExpr = declarator.init;\n if (callExpr.callee?.type !== \"Identifier\" || callExpr.callee.name !== \"useTranslation\") {\n continue;\n }\n\n const namespace = extractUseTranslationNamespace(callExpr);\n\n for (const prop of declarator.id.properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.key?.type !== \"Identifier\" || prop.key.name !== \"t\") continue;\n\n // Handle aliased destructuring: { t: translate } → maps \"translate\" to namespace\n const localName = prop.value?.type === \"Identifier\" ? prop.value.name : \"t\";\n state.tFunctions.set(localName, namespace);\n }\n }\n}\n\n// Helper: Extract namespace from useTranslation() call arguments\nfunction extractUseTranslationNamespace(callExpr: any): string {\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return \"translation\";\n\n // String literal: useTranslation(\"dashboard\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n return firstArg.value;\n }\n\n // Array expression: useTranslation([\"dashboard\", \"common\"])\n if (firstArg.type === \"ArrayExpression\" && firstArg.elements?.length > 0) {\n const first = firstArg.elements[0];\n if (first?.type === \"Literal\" && typeof first.value === \"string\") {\n return first.value;\n }\n }\n\n return \"translation\";\n}\n\n// Helper: Resolve a JSX attribute value to a string.\n// Handles string literals directly, and JSXExpressionContainer by resolving\n// Identifier references via the constants map, or returning the expression source for dynamic values.\nfunction resolveJSXAttrValue(\n value: any,\n code: string,\n constants: Map<string, string>,\n): { value: string; expr: string | null } | null {\n if (!value) return null;\n\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { value: value.value, expr: null };\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expression = value.expression;\n if (!expression) return null;\n\n // Resolve simple identifiers via constants map (e.g. i18nKey={labelKey} where const labelKey = \"...\")\n if (expression.type === \"Identifier\" && constants.has(expression.name)) {\n return { value: constants.get(expression.name)!, expr: null };\n }\n\n // Dynamic expression — return source text so caller can emit a runtime JSX expression\n if (hasRange(expression)) {\n const src = code.slice(expression.start, expression.end);\n return { value: src, expr: src };\n }\n }\n\n return null;\n}\n\n// Helper: Detect <Trans i18nKey=\"...\" /> component and extract i18n key info\nfunction detectTransComponent(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo | null {\n const opening = jsxElement.openingElement;\n const tagName = getJSXElementName(opening);\n\n if (tagName !== \"Trans\") return null;\n\n let keyResult: { value: string; expr: string | null } | null = null;\n let nsResult: { value: string; expr: string | null } | null = null;\n\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") continue;\n\n const attrName = attr.name.name;\n\n if (attrName === \"i18nKey\") {\n keyResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n\n if (attrName === \"ns\") {\n nsResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n }\n\n if (!keyResult) {\n return null;\n }\n\n const key = keyResult.value;\n const namespace = nsResult?.value ?? \"translation\";\n const keyExpr = keyResult.expr;\n const nsExpr = nsResult?.expr ?? null;\n\n return {\n fullKey: `${namespace}:${key}`,\n key,\n namespace,\n keyExpr,\n nsExpr,\n };\n}\n\n// Helper: Scan an element's direct children for <Trans> components and extract i18n key info.\n// Handles direct <Trans> children and <Trans> inside JSXExpressionContainer (e.g. {show && <Trans>}).\nfunction findTransInChildren(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo[] {\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTransInExpression(expr, code, results, constants);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for <Trans> JSXElements.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct JSXElement.\nfunction findTransInExpression(\n expr: any,\n code: string,\n results: I18nKeyInfo[],\n constants: Map<string, string>,\n): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"JSXElement\") {\n const info = detectTransComponent(expr as JSXElement, code, constants);\n if (info) results.push(info);\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTransInExpression(expr.left, code, results, constants);\n findTransInExpression(expr.right, code, results, constants);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTransInExpression(expr.consequent, code, results, constants);\n findTransInExpression(expr.alternate, code, results, constants);\n return;\n }\n\n // Handle .map() and other call expressions — recurse into arguments\n if (expr.type === \"CallExpression\") {\n for (const arg of expr.arguments) {\n findTransInExpression(arg, code, results, constants);\n }\n return;\n }\n\n // Handle arrow/function expressions — recurse into body\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTransInExpression(expr.body, code, results, constants);\n return;\n }\n\n // Handle block bodies (arrow functions with braces)\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTransInExpression(stmt.argument, code, results, constants);\n }\n }\n return;\n }\n}\n\n// Helper: Scan an element's direct children for t() function calls and extract i18n key info.\nfunction findTCallsInChildren(jsxElement: JSXElement, code: string, state: TransformState): I18nKeyInfo[] {\n if (state.tFunctions.size === 0) return [];\n\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(expr, code, results, state);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for t() calls.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct CallExpression.\nfunction findTCallInExpression(expr: any, code: string, results: I18nKeyInfo[], state: TransformState): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"CallExpression\") {\n const info = detectTCall(expr, code, state);\n if (info) {\n results.push(info);\n return;\n }\n // Not a t() call — recurse into arguments (for .map() callbacks etc.)\n for (const arg of expr.arguments) {\n findTCallInExpression(arg, code, results, state);\n }\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTCallInExpression(expr.left, code, results, state);\n findTCallInExpression(expr.right, code, results, state);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTCallInExpression(expr.consequent, code, results, state);\n findTCallInExpression(expr.alternate, code, results, state);\n return;\n }\n\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTCallInExpression(expr.body, code, results, state);\n return;\n }\n\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTCallInExpression(stmt.argument, code, results, state);\n }\n }\n return;\n }\n}\n\n// Helper: Detect a t(\"key\") call and extract i18n key info.\nfunction detectTCall(callExpr: any, code: string, state: TransformState): I18nKeyInfo | null {\n if (callExpr.callee?.type !== \"Identifier\") return null;\n\n const calleeName = callExpr.callee.name;\n const namespace = state.tFunctions.get(calleeName);\n if (namespace === undefined) return null;\n\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return null;\n\n // Static string key: t(\"features.title\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n const key = firstArg.value;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n // Dynamic key — try constant resolution first, then fall back to expression\n if (hasRange(firstArg)) {\n const exprSrc = code.slice(firstArg.start, firstArg.end);\n\n if (firstArg.type === \"Identifier\" && state.constants.has(firstArg.name)) {\n const key = state.constants.get(firstArg.name)!;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n return { fullKey: `${namespace}:${exprSrc}`, key: exprSrc, namespace, keyExpr: exprSrc, nsExpr: null };\n }\n\n return null;\n}\n\n// Helper: Check if element is a \"leaf\" with only static text (no nested elements or expressions)\nfunction isTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n hasText = true;\n }\n } else if (\n child.type === \"JSXElement\" ||\n child.type === \"JSXFragment\" ||\n child.type === \"JSXExpressionContainer\" ||\n child.type === \"JSXSpreadChild\"\n ) {\n // Has non-text children, not a leaf element\n return false;\n }\n }\n\n return hasText;\n}\n\n// Helper: Check if element has any visible text content (static or dynamic).\n// Broader than isTextLeafElement — returns true even if the element has other child types.\n// Detects JSXText with non-whitespace content and expression containers with\n// text-producing expressions (Identifier, MemberExpression, Literal, TemplateLiteral).\n// Skips CallExpression, ConditionalExpression, LogicalExpression (these typically produce elements).\nfunction hasVisibleTextContent(jsxElement: JSXElement): boolean {\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n return true;\n }\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") {\n continue;\n }\n if (\n expr.type === \"Identifier\" ||\n expr.type === \"MemberExpression\" ||\n expr.type === \"Literal\" ||\n expr.type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport default upstartEditor.vite;\n"],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin-upstart-attrs.js","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport { parseSync } from \"oxc-parser\";\nimport { walk } from \"zimmerframe\";\nimport MagicString from \"magic-string\";\nimport path from \"path\";\nimport type {\n Program,\n Node,\n JSXElement,\n JSXOpeningElement,\n JSXAttribute,\n Expression,\n CallExpression,\n} from \"estree-jsx\";\n\ninterface Options {\n enabled: boolean;\n emitRegistry?: boolean;\n}\n\ntype NodeWithRange = Node & { start: number; end: number };\n\nfunction hasRange(node: any): node is NodeWithRange {\n return node && typeof node.start === \"number\" && typeof node.end === \"number\";\n}\n\n// Fast, stable hash function (djb2 variant)\n// Produces a short hex string that's stable across rebuilds\nfunction hashContent(content: string): string {\n let hash = 5381;\n for (let i = 0; i < content.length; i++) {\n hash = ((hash << 5) + hash) ^ content.charCodeAt(i);\n }\n // Convert to unsigned 32-bit and then to hex\n return (hash >>> 0).toString(16);\n}\n\ninterface LoopContext {\n itemName: string;\n indexName: string | null;\n arrayExpr: string;\n}\n\ninterface I18nKeyInfo {\n /** The resolved key with namespace, e.g. \"dashboard:features.title\" */\n fullKey: string;\n /** Just the translation key, e.g. \"features.title\" */\n key: string;\n /** The namespace, e.g. \"dashboard\" */\n namespace: string;\n /** Source expression for i18nKey when dynamic (e.g. \"stat.labelKey\"), null when static */\n keyExpr: string | null;\n /** Source expression for namespace when dynamic, null when static */\n nsExpr: string | null;\n}\n\n// Registry entry for editable elements (text and className)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"className\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n context: { parentTag: string };\n}\n\n// Module-level registry (collected across all files during build)\nconst editableRegistry = new Map<string, EditableEntry>();\n\n// Generate stable ID for editable elements\n// Replace the counter-based ID generation with position-based\nfunction generateId(filePath: string, node: NodeWithRange): string {\n // Use file path + start position for a stable, deterministic ID\n return `${filePath}:${node.start}`;\n}\n\n// Export for testing - get a copy of the current registry\nexport function getRegistry(): Record<string, EditableEntry> {\n return Object.fromEntries(editableRegistry);\n}\n\n// Export for testing - clear registry and counters\nexport function clearRegistry(): void {\n editableRegistry.clear();\n}\n\ninterface TransformState {\n filePath: string;\n code: string;\n s: MagicString;\n loopStack: LoopContext[];\n /** Tracks const varName = \"literal\" declarations for resolving dynamic i18nKey expressions */\n constants: Map<string, string>;\n /** Maps t-function variable names to their default namespace, e.g. { \"t\": \"dashboard\" } */\n tFunctions: Map<string, string>;\n}\n\nexport const upstartEditor = createUnplugin<Options>((options) => {\n if (!options.enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const emitRegistry = options.emitRegistry ?? true;\n let root = process.cwd();\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n },\n generateBundle() {\n if (!emitRegistry || editableRegistry.size === 0) {\n return;\n }\n\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n\n this.emitFile({\n type: \"asset\",\n fileName: \"upstart-registry.json\",\n source: JSON.stringify(registry, null, 2),\n });\n\n // Clear for next build\n editableRegistry.clear();\n },\n },\n\n transformInclude(id) {\n return /\\.(tsx|jsx)$/.test(id) && !id.includes(\"node_modules\");\n },\n\n transform(code, id) {\n // Fast path: skip files without JSX\n if (!code.includes(\"<\")) {\n return null;\n }\n\n try {\n const relativePath = path.relative(root, id);\n const result = transformWithOxc(code, relativePath);\n\n if (!result) {\n return null;\n }\n\n return {\n code: result.code,\n map: result.map,\n };\n } catch (error) {\n console.error(`Error transforming ${id}:`, error);\n return null;\n }\n },\n };\n});\n\nexport function transformWithOxc(code: string, filePath: string) {\n // Parse with oxc (super fast!)\n const ast = parseSync(filePath, code, {\n sourceType: \"module\",\n });\n\n if (!ast.program) {\n return null;\n }\n\n const s = new MagicString(code);\n const state: TransformState = {\n filePath,\n code,\n s,\n loopStack: [],\n constants: new Map(),\n tFunctions: new Map(),\n };\n\n let modified = false;\n\n // Use zimmerframe to walk and transform the AST\n walk(ast.program as Program, state, {\n _(node: Node, { state, next }: { state: TransformState; next: () => void }) {\n // Track useTranslation() calls to resolve t() function namespaces\n if (node.type === \"VariableDeclaration\") {\n trackUseTranslation(node, state);\n\n // Track const varName = \"literal\" for resolving dynamic i18nKey expressions\n for (const decl of (node as any).declarations) {\n if (\n decl.type === \"VariableDeclarator\" &&\n decl.id?.type === \"Identifier\" &&\n decl.init?.type === \"Literal\" &&\n typeof decl.init.value === \"string\"\n ) {\n state.constants.set(decl.id.name, decl.init.value);\n }\n }\n }\n\n // Handle .map() calls to track loops (must be before JSXElement)\n if (node.type === \"CallExpression\") {\n const loopInfo = detectMapCall(node as CallExpression, state.code);\n\n if (loopInfo) {\n // Push loop context\n state.loopStack.push(loopInfo);\n\n // Continue walking into the callback\n next();\n\n // Pop loop context after traversing\n state.loopStack.pop();\n return;\n }\n }\n\n // Handle JSX elements\n if (node.type === \"JSXElement\") {\n const jsxNode = node as JSXElement;\n const opening = jsxNode.openingElement;\n const tagName = getJSXElementName(opening);\n\n // Skip ALL attribute injection for <Trans> elements.\n // <Trans> renders to a text node at runtime, so DOM attributes are lost.\n // The i18n attributes are promoted to the parent element instead.\n if (tagName === \"Trans\") {\n next();\n return;\n }\n\n const insertPos = getAttributeInsertPosition(opening, state.code);\n\n // Track attributes to inject\n const attributes: string[] = [];\n\n // Compute stable hash from the element's source code (includes all children)\n // This hash changes if ANY part of the element or its children change\n if (hasRange(jsxNode)) {\n const elementSource = state.code.slice(jsxNode.start, jsxNode.end);\n const hash = hashContent(elementSource);\n\n // Collect index variables from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n const loopIndices = state.loopStack.map((l) => l.indexName).filter((n): n is string => n !== null);\n\n if (loopIndices.length > 0) {\n const suffix = loopIndices.map((n) => `\\${${n}}`).join(\"-\");\n attributes.push(`data-upstart-hash={\\`${hash}-${suffix}\\`}`);\n } else {\n attributes.push(`data-upstart-hash=\"${hash}\"`);\n }\n }\n\n // --- Editable text detection ---\n // Priority: i18n (true) > non-i18n text (false) > no text (no attribute)\n\n // Step 1: Check for Trans children AND t() calls (editable via i18n, gets \"true\")\n const transChildren = findTransInChildren(jsxNode, state.code, state.constants);\n const tCallChildren = findTCallsInChildren(jsxNode, state.code, state);\n const allI18nKeys = [...transChildren, ...tCallChildren];\n const hasI18n = allI18nKeys.length > 0;\n\n if (hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const hasDynamic = allI18nKeys.some((t) => t.keyExpr || t.nsExpr);\n if (hasDynamic) {\n // Build a runtime JSX template expression for dynamic i18n keys\n const parts = allI18nKeys.map((t) => {\n const nsPart = t.nsExpr ? `\\${${t.nsExpr}}` : t.namespace;\n const keyPart = t.keyExpr ? `\\${${t.keyExpr}}` : t.key;\n return `${nsPart}:${keyPart}`;\n });\n attributes.push(`data-upstart-i18n={\\`${parts.join(\",\")}\\`}`);\n } else {\n const keys = allI18nKeys.map((t: I18nKeyInfo) => escapeProp(t.fullKey)).join(\",\");\n attributes.push(`data-upstart-i18n=\"${keys}\"`);\n }\n }\n\n // Step 2: Text leaf elements — registry tracking + non-editable flag\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Find the actual JSXText node to track its location\n const textChild = jsxNode.children.find((c) => c.type === \"JSXText\" && (c as any).value?.trim());\n\n if (textChild && hasRange(textChild)) {\n const id = generateId(state.filePath, textChild);\n const textValue = (textChild as any).value as string;\n\n // Calculate trimmed offsets (exclude leading/trailing whitespace)\n const trimmedStart = textChild.start + (textValue.length - textValue.trimStart().length);\n const trimmedEnd = textChild.end - (textValue.length - textValue.trimEnd().length);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"text\",\n startOffset: trimmedStart,\n endOffset: trimmedEnd,\n originalContent: textValue.trim(),\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 3: Non-leaf elements with visible text content (expressions, mixed content)\n else if (!hasI18n && hasVisibleTextContent(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Track className attribute if it's a string literal\n const classNameAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n attr.name.name === \"className\" &&\n attr.value?.type === \"Literal\" &&\n typeof (attr.value as any).value === \"string\",\n );\n\n if (classNameAttr && classNameAttr.value && hasRange(classNameAttr.value)) {\n const id = generateId(state.filePath, classNameAttr.value);\n const classValue = (classNameAttr.value as any).value as string;\n\n // +1 and -1 to exclude the quotes\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"className\",\n startOffset: classNameAttr.value.start + 1,\n endOffset: classNameAttr.value.end - 1,\n originalContent: classValue,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-classname-id=\"${id}\"`);\n }\n\n // Process PascalCase components for additional tracking\n if (tagName && /^[A-Z]/.test(tagName)) {\n // File and component tracking\n attributes.push(`data-upstart-file=\"${escapeProp(state.filePath)}\"`);\n attributes.push(`data-upstart-component=\"${escapeProp(tagName)}\"`);\n\n // Loop context tracking\n if (state.loopStack.length > 0) {\n const loop = state.loopStack[state.loopStack.length - 1];\n attributes.push(`data-upstart-loop-item=\"${escapeProp(loop.itemName)}\"`);\n if (loop.indexName) {\n attributes.push(`data-upstart-loop-index={${loop.indexName}}`);\n }\n attributes.push(`data-upstart-loop-array=\"${escapeProp(loop.arrayExpr)}\"`);\n }\n\n // Analyze each prop for data bindings\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") {\n continue;\n }\n\n const propName = attr.name.name;\n const binding = analyzeBinding(attr.value, state.code);\n\n if (binding) {\n attributes.push(`data-upstart-prop-${propName.toLowerCase()}=\"${escapeProp(binding.path)}\"`);\n\n // Track conditional expressions\n if (binding.isConditional) {\n attributes.push(`data-upstart-conditional-${propName.toLowerCase()}=\"true\"`);\n }\n }\n }\n }\n\n // Inject attributes if any\n if (insertPos !== -1 && attributes.length > 0) {\n const attrString = \" \" + attributes.join(\" \");\n state.s.appendLeft(insertPos, attrString);\n modified = true;\n }\n }\n\n next();\n },\n });\n\n if (!modified) {\n return null;\n }\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: true }),\n };\n}\n\n// Helper: Get JSX element name\nfunction getJSXElementName(opening: JSXOpeningElement): string | null {\n if (opening.name.type === \"JSXIdentifier\") {\n return opening.name.name;\n }\n\n // Handle JSXMemberExpression like <Foo.Bar>\n if (opening.name.type === \"JSXMemberExpression\") {\n let current = opening.name;\n while (current.property) {\n if (current.property.type === \"JSXIdentifier\") {\n return current.property.name;\n }\n if (current.type === \"JSXMemberExpression\") {\n current = current.property as any;\n } else {\n break;\n }\n }\n }\n\n return null;\n}\n\n// Helper: Find where to insert attributes in JSX opening tag\nfunction getAttributeInsertPosition(opening: JSXOpeningElement, code: string): number {\n // If there are existing attributes, insert before the first one\n if (opening.attributes.length > 0) {\n const firstAttr = opening.attributes[0];\n if (hasRange(firstAttr)) {\n return firstAttr.start;\n }\n }\n\n // Otherwise, insert after the tag name\n if (opening.name.type === \"JSXIdentifier\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n if (opening.name.type === \"JSXMemberExpression\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n return -1;\n}\n\n// Helper: Analyze a prop value to extract data binding\nfunction analyzeBinding(\n value: JSXAttribute[\"value\"],\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n if (!value) {\n return null;\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n if (expr.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n return analyzeExpression(expr, code);\n }\n\n return null;\n}\n\n// Helper: Analyze any expression to extract binding info\nfunction analyzeExpression(\n expr: Expression,\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n // Handle member expressions: user.name, product.price, etc.\n if (expr.type === \"MemberExpression\") {\n const path = exprToString(expr, code);\n return { path };\n }\n\n // Handle identifiers: userName, price, etc.\n if (expr.type === \"Identifier\") {\n return { path: expr.name };\n }\n\n // Handle conditional expressions: condition ? value1 : value2\n if (expr.type === \"ConditionalExpression\") {\n // Try to extract from the consequent\n const consequent = analyzeExpression(expr.consequent, code);\n if (consequent) {\n return {\n ...consequent,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // Handle logical expressions: value1 && value2, value1 || value2\n if (expr.type === \"LogicalExpression\") {\n const right = analyzeExpression(expr.right, code);\n if (right) {\n return {\n ...right,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // For other complex expressions, just return the path\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return {\n path: code.slice(exprWithRange.start, exprWithRange.end),\n };\n }\n\n return null;\n}\n\n// Helper: Convert expression AST to string\nfunction exprToString(expr: Expression, code: string): string {\n // Use the source range to get the actual code\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return code.slice(exprWithRange.start, exprWithRange.end);\n }\n\n // Fallback: reconstruct from AST\n if (expr.type === \"Identifier\") {\n return expr.name;\n }\n\n if (expr.type === \"MemberExpression\") {\n const obj = exprToString(expr.object as Expression, code);\n const prop =\n expr.property.type === \"Identifier\" && !expr.computed\n ? expr.property.name\n : exprToString(expr.property as Expression, code);\n return expr.computed ? `${obj}[${prop}]` : `${obj}.${prop}`;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n const test = exprToString(expr.test, code);\n const consequent = exprToString(expr.consequent, code);\n const alternate = exprToString(expr.alternate, code);\n return `${test} ? ${consequent} : ${alternate}`;\n }\n\n if (expr.type === \"LogicalExpression\") {\n const left = exprToString(expr.left, code);\n const right = exprToString(expr.right, code);\n return `${left} ${expr.operator} ${right}`;\n }\n\n return \"\";\n}\n\n// Helper: Detect .map() calls and extract loop context\nfunction detectMapCall(node: CallExpression, code: string): LoopContext | null {\n // Check if this is a .map() call\n if (\n node.callee.type !== \"MemberExpression\" ||\n node.callee.property.type !== \"Identifier\" ||\n node.callee.property.name !== \"map\"\n ) {\n return null;\n }\n\n const callback = node.arguments[0];\n\n if (!callback) {\n return null;\n }\n\n // Check if callback is an arrow function or function expression\n if (callback.type !== \"ArrowFunctionExpression\" && callback.type !== \"FunctionExpression\") {\n return null;\n }\n\n const params = callback.params;\n const itemParam = params[0];\n const indexParam = params[1];\n\n if (!itemParam) {\n return null;\n }\n\n // Extract parameter names\n const itemName = itemParam.type === \"Identifier\" ? itemParam.name : \"item\";\n const indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\n// Helper: Track useTranslation() calls to map t-function variable names to their namespace\nfunction trackUseTranslation(node: Node, state: TransformState): void {\n if (node.type !== \"VariableDeclaration\") return;\n\n const decl = node as any;\n for (const declarator of decl.declarations) {\n if (\n declarator.id?.type !== \"ObjectPattern\" ||\n !declarator.init ||\n declarator.init.type !== \"CallExpression\"\n ) {\n continue;\n }\n\n const callExpr = declarator.init;\n if (callExpr.callee?.type !== \"Identifier\" || callExpr.callee.name !== \"useTranslation\") {\n continue;\n }\n\n const namespace = extractUseTranslationNamespace(callExpr);\n\n for (const prop of declarator.id.properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.key?.type !== \"Identifier\" || prop.key.name !== \"t\") continue;\n\n // Handle aliased destructuring: { t: translate } → maps \"translate\" to namespace\n const localName = prop.value?.type === \"Identifier\" ? prop.value.name : \"t\";\n state.tFunctions.set(localName, namespace);\n }\n }\n}\n\n// Helper: Extract namespace from useTranslation() call arguments\nfunction extractUseTranslationNamespace(callExpr: any): string {\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return \"translation\";\n\n // String literal: useTranslation(\"dashboard\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n return firstArg.value;\n }\n\n // Array expression: useTranslation([\"dashboard\", \"common\"])\n if (firstArg.type === \"ArrayExpression\" && firstArg.elements?.length > 0) {\n const first = firstArg.elements[0];\n if (first?.type === \"Literal\" && typeof first.value === \"string\") {\n return first.value;\n }\n }\n\n return \"translation\";\n}\n\n// Helper: Resolve a JSX attribute value to a string.\n// Handles string literals directly, and JSXExpressionContainer by resolving\n// Identifier references via the constants map, or returning the expression source for dynamic values.\nfunction resolveJSXAttrValue(\n value: any,\n code: string,\n constants: Map<string, string>,\n): { value: string; expr: string | null } | null {\n if (!value) return null;\n\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { value: value.value, expr: null };\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expression = value.expression;\n if (!expression) return null;\n\n // Resolve simple identifiers via constants map (e.g. i18nKey={labelKey} where const labelKey = \"...\")\n if (expression.type === \"Identifier\" && constants.has(expression.name)) {\n return { value: constants.get(expression.name)!, expr: null };\n }\n\n // Dynamic expression — return source text so caller can emit a runtime JSX expression\n if (hasRange(expression)) {\n const src = code.slice(expression.start, expression.end);\n return { value: src, expr: src };\n }\n }\n\n return null;\n}\n\n// Helper: Detect <Trans i18nKey=\"...\" /> component and extract i18n key info\nfunction detectTransComponent(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo | null {\n const opening = jsxElement.openingElement;\n const tagName = getJSXElementName(opening);\n\n if (tagName !== \"Trans\") return null;\n\n let keyResult: { value: string; expr: string | null } | null = null;\n let nsResult: { value: string; expr: string | null } | null = null;\n\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") continue;\n\n const attrName = attr.name.name;\n\n if (attrName === \"i18nKey\") {\n keyResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n\n if (attrName === \"ns\") {\n nsResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n }\n\n if (!keyResult) {\n return null;\n }\n\n const key = keyResult.value;\n const namespace = nsResult?.value ?? \"translation\";\n const keyExpr = keyResult.expr;\n const nsExpr = nsResult?.expr ?? null;\n\n return {\n fullKey: `${namespace}:${key}`,\n key,\n namespace,\n keyExpr,\n nsExpr,\n };\n}\n\n// Helper: Scan an element's direct children for <Trans> components and extract i18n key info.\n// Handles direct <Trans> children and <Trans> inside JSXExpressionContainer (e.g. {show && <Trans>}).\nfunction findTransInChildren(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo[] {\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTransInExpression(expr, code, results, constants);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for <Trans> JSXElements.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct JSXElement.\nfunction findTransInExpression(\n expr: any,\n code: string,\n results: I18nKeyInfo[],\n constants: Map<string, string>,\n): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"JSXElement\") {\n const info = detectTransComponent(expr as JSXElement, code, constants);\n if (info) results.push(info);\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTransInExpression(expr.left, code, results, constants);\n findTransInExpression(expr.right, code, results, constants);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTransInExpression(expr.consequent, code, results, constants);\n findTransInExpression(expr.alternate, code, results, constants);\n return;\n }\n\n // Handle .map() and other call expressions — recurse into arguments\n if (expr.type === \"CallExpression\") {\n for (const arg of expr.arguments) {\n findTransInExpression(arg, code, results, constants);\n }\n return;\n }\n\n // Handle arrow/function expressions — recurse into body\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTransInExpression(expr.body, code, results, constants);\n return;\n }\n\n // Handle block bodies (arrow functions with braces)\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTransInExpression(stmt.argument, code, results, constants);\n }\n }\n return;\n }\n}\n\n// Helper: Scan an element's direct children for t() function calls and extract i18n key info.\nfunction findTCallsInChildren(jsxElement: JSXElement, code: string, state: TransformState): I18nKeyInfo[] {\n if (state.tFunctions.size === 0) return [];\n\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(expr, code, results, state);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for t() calls.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct CallExpression.\nfunction findTCallInExpression(expr: any, code: string, results: I18nKeyInfo[], state: TransformState): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"CallExpression\") {\n const info = detectTCall(expr, code, state);\n if (info) {\n results.push(info);\n return;\n }\n // Not a t() call — recurse into arguments (for .map() callbacks etc.)\n for (const arg of expr.arguments) {\n findTCallInExpression(arg, code, results, state);\n }\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTCallInExpression(expr.left, code, results, state);\n findTCallInExpression(expr.right, code, results, state);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTCallInExpression(expr.consequent, code, results, state);\n findTCallInExpression(expr.alternate, code, results, state);\n return;\n }\n\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTCallInExpression(expr.body, code, results, state);\n return;\n }\n\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTCallInExpression(stmt.argument, code, results, state);\n }\n }\n return;\n }\n}\n\n// Helper: Detect a t(\"key\") call and extract i18n key info.\nfunction detectTCall(callExpr: any, code: string, state: TransformState): I18nKeyInfo | null {\n if (callExpr.callee?.type !== \"Identifier\") return null;\n\n const calleeName = callExpr.callee.name;\n const namespace = state.tFunctions.get(calleeName);\n if (namespace === undefined) return null;\n\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return null;\n\n // Static string key: t(\"features.title\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n const key = firstArg.value;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n // Dynamic key — try constant resolution first, then fall back to expression\n if (hasRange(firstArg)) {\n const exprSrc = code.slice(firstArg.start, firstArg.end);\n\n if (firstArg.type === \"Identifier\" && state.constants.has(firstArg.name)) {\n const key = state.constants.get(firstArg.name)!;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n return { fullKey: `${namespace}:${exprSrc}`, key: exprSrc, namespace, keyExpr: exprSrc, nsExpr: null };\n }\n\n return null;\n}\n\n// Helper: Check if element is a \"leaf\" with only static text (no nested elements or expressions)\nfunction isTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n hasText = true;\n }\n } else if (\n child.type === \"JSXElement\" ||\n child.type === \"JSXFragment\" ||\n child.type === \"JSXExpressionContainer\" ||\n child.type === \"JSXSpreadChild\"\n ) {\n // Has non-text children, not a leaf element\n return false;\n }\n }\n\n return hasText;\n}\n\n// Helper: Check if element has any visible text content (static or dynamic).\n// Broader than isTextLeafElement — returns true even if the element has other child types.\n// Detects JSXText with non-whitespace content and expression containers with\n// text-producing expressions (Identifier, MemberExpression, Literal, TemplateLiteral).\n// Skips CallExpression, ConditionalExpression, LogicalExpression (these typically produce elements).\nfunction hasVisibleTextContent(jsxElement: JSXElement): boolean {\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n return true;\n }\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") {\n continue;\n }\n if (\n expr.type === \"Identifier\" ||\n expr.type === \"MemberExpression\" ||\n expr.type === \"Literal\" ||\n expr.type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport default upstartEditor.vite;\n"],"mappings":"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"}
@@ -1,8 +1,7 @@
1
1
  import { UpstartBrandingPluginOptions } from "./types.js";
2
- import * as unplugin5 from "unplugin";
2
+ import * as unplugin from "unplugin";
3
3
 
4
4
  //#region src/vite-plugin-upstart-branding/plugin.d.ts
5
-
6
5
  /**
7
6
  * Upstart Branding Vite plugin (build-time)
8
7
  *
@@ -10,8 +9,8 @@ import * as unplugin5 from "unplugin";
10
9
  * The badge appears at the bottom of the page, then fades out after a
11
10
  * few seconds or when the user scrolls.
12
11
  */
13
- declare const upstartBranding: unplugin5.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
14
- declare const _default: (options: UpstartBrandingPluginOptions) => unplugin5.VitePlugin<any> | unplugin5.VitePlugin<any>[];
12
+ declare const upstartBranding: unplugin.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
13
+ declare const _default: (options: UpstartBrandingPluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
15
14
  //#endregion
16
15
  export { _default as default, upstartBranding };
17
16
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAgBA;AAwCG;;;cAxCU,iBAAe,SAAA,CAAA,iBAAA;cAwCzB"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"mappings":";;;;;;;AAgBA;;;;cAAa,eAAA,EAAe,QAAA,CAAA,gBAAA,CAAA,4BAAA;AAAA,cAwCzB,QAAA"}
@@ -1,7 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { createUnplugin } from "unplugin";
3
3
  import { fileURLToPath } from "node:url";
4
-
5
4
  //#region src/vite-plugin-upstart-branding/plugin.ts
6
5
  const DEFAULT_OPTIONS = { enabled: false };
7
6
  /**
@@ -35,7 +34,7 @@ const upstartBranding = createUnplugin((options = {}) => {
35
34
  };
36
35
  });
37
36
  var plugin_default = upstartBranding.vite;
38
-
39
37
  //#endregion
40
38
  export { plugin_default as default, upstartBranding };
39
+
41
40
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["DEFAULT_OPTIONS: Required<UpstartBrandingPluginOptions>"],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartBrandingPluginOptions } from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartBrandingPluginOptions> = {\n enabled: false,\n};\n\n/**\n * Upstart Branding Vite plugin (build-time)\n *\n * Injects a \"Made with Upstart\" badge into the app entry during build.\n * The badge appears at the bottom of the page, then fades out after a\n * few seconds or when the user scrolls.\n */\nexport const upstartBranding = createUnplugin<UpstartBrandingPluginOptions>((options = {}) => {\n const { enabled } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-branding-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime\");\n\n return {\n name: \"upstart-branding\",\n enforce: \"pre\",\n\n transform(code, id) {\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!/\\.(t|j)sx?$/.test(cleanId)) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartBranding\")) {\n return null;\n }\n\n const imports = `import { initUpstartBranding } from ${JSON.stringify(runtimePath)};`;\n const injection = \"requestIdleCallback(initUpstartBranding);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartBranding.vite;\n"],"mappings":";;;;;AAKA,MAAMA,kBAA0D,EAC9D,SAAS,OACV;;;;;;;;AASD,MAAa,kBAAkB,gBAA8C,UAAU,EAAE,KAAK;CAC5F,MAAM,EAAE,YAAY;EAAE,GAAG;EAAiB,GAAG;EAAS;AAEtD,KAAI,CAAC,QACH,QAAO,EAAE,MAAM,6BAA6B;CAG9C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,YAAY;AAE3F,QAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;GAClB,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;AAC/B,OAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,CAC9C,QAAO;AAGT,OAAI,CAAC,cAAc,KAAK,QAAQ,CAC9B,QAAO;AAGT,OAAI,CAAC,QAAQ,SAAS,mBAAmB,CACvC,QAAO;AAGT,OAAI,KAAK,SAAS,sBAAsB,CACtC,QAAO;AAMT,UAAO;IACL,MAAM,GAJQ,uCAAuC,KAAK,UAAU,YAAY,CAAC,KAI9D;IACnB,KAAK;IACN;;EAEJ;EACD;AAEF,qBAAe,gBAAgB"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartBrandingPluginOptions } from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartBrandingPluginOptions> = {\n enabled: false,\n};\n\n/**\n * Upstart Branding Vite plugin (build-time)\n *\n * Injects a \"Made with Upstart\" badge into the app entry during build.\n * The badge appears at the bottom of the page, then fades out after a\n * few seconds or when the user scrolls.\n */\nexport const upstartBranding = createUnplugin<UpstartBrandingPluginOptions>((options = {}) => {\n const { enabled } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-branding-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime\");\n\n return {\n name: \"upstart-branding\",\n enforce: \"pre\",\n\n transform(code, id) {\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!/\\.(t|j)sx?$/.test(cleanId)) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartBranding\")) {\n return null;\n }\n\n const imports = `import { initUpstartBranding } from ${JSON.stringify(runtimePath)};`;\n const injection = \"requestIdleCallback(initUpstartBranding);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartBranding.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAA0D,EAC9D,SAAS,OACV;;;;;;;;AASD,MAAa,kBAAkB,gBAA8C,UAAU,EAAE,KAAK;CAC5F,MAAM,EAAE,YAAY;EAAE,GAAG;EAAiB,GAAG;EAAS;AAEtD,KAAI,CAAC,QACH,QAAO,EAAE,MAAM,6BAA6B;CAG9C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,YAAY;AAE3F,QAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;GAClB,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;AAC/B,OAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,CAC9C,QAAO;AAGT,OAAI,CAAC,cAAc,KAAK,QAAQ,CAC9B,QAAO;AAGT,OAAI,CAAC,QAAQ,SAAS,mBAAmB,CACvC,QAAO;AAGT,OAAI,KAAK,SAAS,sBAAsB,CACtC,QAAO;AAMT,UAAO;IACL,MAAM,GAJQ,uCAAuC,KAAK,UAAU,YAAY,CAAC,KAI9D,KAAA;IACnB,KAAK;IACN;;EAEJ;EACD;AAEF,IAAA,iBAAe,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"sourcesContent":[],"mappings":";;AAmBA;;;;iBAAgB,mBAAA,CAAA"}
1
+ {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"mappings":";;AAmBA;;;;iBAAgB,mBAAA,CAAA"}
@@ -19,7 +19,7 @@ function initUpstartBranding() {
19
19
  if (typeof window === "undefined" || typeof document === "undefined") return;
20
20
  if (isInitialized) return;
21
21
  isInitialized = true;
22
- if ("scheduler" in globalThis) globalThis.scheduler.postTask(() => injectBranding(), {
22
+ if ("scheduler" in window) window.scheduler.postTask(() => injectBranding(), {
23
23
  delay: 250,
24
24
  priority: "background"
25
25
  });
@@ -112,7 +112,7 @@ function injectBranding() {
112
112
  once: true
113
113
  });
114
114
  }
115
-
116
115
  //#endregion
117
116
  export { initUpstartBranding };
117
+
118
118
  //# sourceMappingURL=runtime.js.map