@upstart.gg/vite-plugins 0.1.74 → 0.1.75
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin-upstart-attrs.d.ts","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"mappings":";;;;UAgBU,OAAA;EACR,OAAA;EACA,YAAA;AAAA;AAAA,UA4Ce,eAAA;EACf,IAAA;EAGA,GAAA;AAAA;AAAA,UAIe,aAAA;EACf,IAAA;EACA,IAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EAEA,QAAA,GAAW,eAAA;EAIX,KAAA;EACA,OAAA;IAAW,SAAA;EAAA;AAAA;AAAA,iBAcG,WAAA,CAAA,GAAe,MAAA,SAAe,aAAA;AAAA,iBAK9B,aAAA,CAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"vite-plugin-upstart-attrs.d.ts","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"mappings":";;;;UAgBU,OAAA;EACR,OAAA;EACA,YAAA;AAAA;AAAA,UA4Ce,eAAA;EACf,IAAA;EAGA,GAAA;AAAA;AAAA,UAIe,aAAA;EACf,IAAA;EACA,IAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EAEA,QAAA,GAAW,eAAA;EAIX,KAAA;EACA,OAAA;IAAW,SAAA;EAAA;AAAA;AAAA,iBAcG,WAAA,CAAA,GAAe,MAAA,SAAe,aAAA;AAAA,iBAK9B,aAAA,CAAA;AAAA,cA6EH,aAAA,EAAa,UAAA,CAAA,gBAAA,CAAA,OAAA;AAAA,iBA8FV,gBAAA,CAAiB,IAAA,UAAc,QAAA;;OAAf,eAAA,CAAA,SAAA;AAAA;AAAA,cAid/B,QAAA"}
|
|
@@ -23,6 +23,61 @@ function getRegistry() {
|
|
|
23
23
|
function clearRegistry() {
|
|
24
24
|
editableRegistry.clear();
|
|
25
25
|
}
|
|
26
|
+
function isStringLiteralNode(node) {
|
|
27
|
+
return !!node && node.type === "Literal" && typeof node.value === "string" && hasRange(node);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Collect `{ name = "literal" }` defaults from a function's parameters. Only object-pattern
|
|
31
|
+
* params with a plain string default are tracked — that is the common "props with default"
|
|
32
|
+
* shape emitted for site components.
|
|
33
|
+
*/
|
|
34
|
+
function collectParamStringDefaults(params) {
|
|
35
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const param of params) {
|
|
37
|
+
const pattern = param?.type === "AssignmentPattern" ? param.left : param;
|
|
38
|
+
if (pattern?.type !== "ObjectPattern") continue;
|
|
39
|
+
for (const prop of pattern.properties) {
|
|
40
|
+
if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
|
|
41
|
+
const value = prop.value;
|
|
42
|
+
if (value?.type !== "AssignmentPattern" || value.left?.type !== "Identifier") continue;
|
|
43
|
+
if (isStringLiteralNode(value.right)) defaults.set(value.left.name, value.right);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return defaults;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the string Literal backing an `<img src>` attribute, so the editor can rewrite it.
|
|
50
|
+
* Handles `src="..."`, `src={"..."}`, `src={prop}` where `prop` has a string default in an
|
|
51
|
+
* enclosing function's parameters, and `src={CONST}` where `const CONST = "..."`.
|
|
52
|
+
* Returns the identifier name when the value was resolved through a variable, so the caller
|
|
53
|
+
* can guard the injected attribute at runtime (a parent may pass a different value).
|
|
54
|
+
*/
|
|
55
|
+
function resolveImageSrcLiteral(attr, state) {
|
|
56
|
+
const value = attr.value;
|
|
57
|
+
if (isStringLiteralNode(value)) return {
|
|
58
|
+
literal: value,
|
|
59
|
+
via: null
|
|
60
|
+
};
|
|
61
|
+
if (value?.type !== "JSXExpressionContainer") return null;
|
|
62
|
+
const expr = value.expression;
|
|
63
|
+
if (isStringLiteralNode(expr)) return {
|
|
64
|
+
literal: expr,
|
|
65
|
+
via: null
|
|
66
|
+
};
|
|
67
|
+
if (expr?.type !== "Identifier") return null;
|
|
68
|
+
for (let i = state.paramDefaults.length - 1; i >= 0; i--) {
|
|
69
|
+
const literal = state.paramDefaults[i].get(expr.name);
|
|
70
|
+
if (literal) return {
|
|
71
|
+
literal,
|
|
72
|
+
via: expr.name
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const constant = state.constantNodes.get(expr.name);
|
|
76
|
+
return constant ? {
|
|
77
|
+
literal: constant,
|
|
78
|
+
via: expr.name
|
|
79
|
+
} : null;
|
|
80
|
+
}
|
|
26
81
|
const upstartEditor = createUnplugin((options) => {
|
|
27
82
|
if (!options.enabled) return { name: "upstart-editor-disabled" };
|
|
28
83
|
const emitRegistry = options.emitRegistry ?? true;
|
|
@@ -102,13 +157,24 @@ function transformWithOxc(code, filePath) {
|
|
|
102
157
|
s,
|
|
103
158
|
loopStack: [],
|
|
104
159
|
constants: /* @__PURE__ */ new Map(),
|
|
105
|
-
tFunctions: /* @__PURE__ */ new Map()
|
|
160
|
+
tFunctions: /* @__PURE__ */ new Map(),
|
|
161
|
+
constantNodes: /* @__PURE__ */ new Map(),
|
|
162
|
+
paramDefaults: []
|
|
106
163
|
};
|
|
107
164
|
let modified = false;
|
|
108
165
|
walk(ast.program, state, { _(node, { state, next }) {
|
|
109
166
|
if (node.type === "VariableDeclaration") {
|
|
110
167
|
trackUseTranslation(node, state);
|
|
111
|
-
for (const decl of node.declarations) if (decl.type === "VariableDeclarator" && decl.id?.type === "Identifier" && decl.init?.type === "Literal" && typeof decl.init.value === "string")
|
|
168
|
+
for (const decl of node.declarations) if (decl.type === "VariableDeclarator" && decl.id?.type === "Identifier" && decl.init?.type === "Literal" && typeof decl.init.value === "string") {
|
|
169
|
+
state.constants.set(decl.id.name, decl.init.value);
|
|
170
|
+
if (isStringLiteralNode(decl.init)) state.constantNodes.set(decl.id.name, decl.init);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
174
|
+
state.paramDefaults.push(collectParamStringDefaults(node.params ?? []));
|
|
175
|
+
next();
|
|
176
|
+
state.paramDefaults.pop();
|
|
177
|
+
return;
|
|
112
178
|
}
|
|
113
179
|
if (node.type === "CallExpression") {
|
|
114
180
|
const mapCallInfo = detectAndPatchMapCall(node, state);
|
|
@@ -287,19 +353,21 @@ function transformWithOxc(code, filePath) {
|
|
|
287
353
|
attributes.push(`data-upstart-classname-id="${id}"`);
|
|
288
354
|
}
|
|
289
355
|
if (tagName === "img") {
|
|
290
|
-
const srcAttr = opening.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "src"
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
356
|
+
const srcAttr = opening.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "src");
|
|
357
|
+
const resolved = srcAttr ? resolveImageSrcLiteral(srcAttr, state) : null;
|
|
358
|
+
if (resolved) {
|
|
359
|
+
const { literal, via } = resolved;
|
|
360
|
+
const id = generateId(state.filePath, literal);
|
|
294
361
|
editableRegistry.set(id, {
|
|
295
362
|
file: state.filePath,
|
|
296
363
|
type: "image",
|
|
297
|
-
startOffset:
|
|
298
|
-
endOffset:
|
|
299
|
-
originalContent:
|
|
364
|
+
startOffset: literal.start + 1,
|
|
365
|
+
endOffset: literal.end - 1,
|
|
366
|
+
originalContent: literal.value,
|
|
300
367
|
context: { parentTag: tagName || "unknown" }
|
|
301
368
|
});
|
|
302
|
-
attributes.push(`data-upstart-image-id="${id}"`);
|
|
369
|
+
if (via) attributes.push(`data-upstart-image-id={${via} === ${JSON.stringify(literal.value)} ? "${id}" : undefined}`);
|
|
370
|
+
else attributes.push(`data-upstart-image-id="${id}"`);
|
|
303
371
|
}
|
|
304
372
|
}
|
|
305
373
|
if (tagName && /^[A-Z]/.test(tagName)) {
|
|
@@ -1 +1 @@
|
|
|
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 fs from \"node:fs\";\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 /** The `.map()` callee object AST node — used to edit inline array-literal items in place. */\n arrayNode: Node | null;\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 /** Variable names from the `values` prop (e.g. [\"year\"] from values={{ year }}) */\n valueKeys?: string[];\n}\n\n// A segment of a mixed-text element: either static text or a JSX expression\nexport interface EditableSegment {\n type: \"text\" | \"expr\";\n // For \"text\": the raw JSX text value (with whitespace/newlines as in source)\n // For \"expr\": the full expression source including braces, e.g. \"{new Date().getFullYear()}\"\n raw: string;\n}\n\n// Registry entry for editable elements (text, rich-text, className, mixed-text)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"rich-text\" | \"className\" | \"mixed-text\" | \"image\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n // Only present for mixed-text entries\n segments?: EditableSegment[];\n // For text entries that map to a JS string literal (e.g. an inline array item),\n // the source quote character (\" ' or `). When set, the server escapes the new\n // content for that quote style before writing it back between the quotes.\n quote?: 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 let isDevMode = false;\n let devWriteTimer: ReturnType<typeof setTimeout> | null = null;\n\n const flushDevRegistry = () => {\n if (!isDevMode || editableRegistry.size === 0) return;\n const registryDir = path.join(root, \"build\", \"server\");\n const registryPath = path.join(registryDir, \"upstart-registry.json\");\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n fs.mkdirSync(registryDir, { recursive: true });\n fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2));\n };\n\n const scheduleDevRegistryWrite = () => {\n if (!isDevMode) return;\n if (devWriteTimer) clearTimeout(devWriteTimer);\n devWriteTimer = setTimeout(flushDevRegistry, 150);\n };\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n isDevMode = config.command === \"serve\";\n },\n configureServer(_server) {\n isDevMode = true;\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 scheduleDevRegistryWrite();\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 mapCallInfo = detectAndPatchMapCall(node as CallExpression, state);\n\n if (mapCallInfo) {\n // Push loop context\n state.loopStack.push(mapCallInfo);\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 suffix expressions from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n // detectAndPatchMapCall() attempts to auto-inject an index param when missing.\n // If injection is not possible (no stable source range), fall back to \"0\".\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n\n if (loopSuffixes.length > 0) {\n const suffix = loopSuffixes.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 const isMixed = hasI18n && hasMixedNonI18nContent(jsxNode, state.code, state, state.constants);\n\n // Mixed content is normally disqualifying, but the very common\n // `<label><Trans i18nKey=\"…\" /> *</label>` shape is recoverable: the static\n // text around the single i18n node is exported as prefix/suffix so the editor\n // can keep it out of the saved translation.\n const i18nAffixes = isMixed\n ? detectI18nStaticAffixes(jsxNode, state.code, state, state.constants)\n : null;\n\n // The element must resolve to exactly ONE translation key at runtime, otherwise\n // an inline edit could not be routed to the right key.\n const i18nAttrValue = hasI18n\n ? buildI18nAttributeValue(jsxNode, allI18nKeys, state.code, state, state.constants)\n : null;\n\n // A single <Trans> next to element siblings without text (an <Icon>, an <svg>…): the\n // plain editor replaces the whole content of the editable element, so making the\n // container editable would destroy the icon, and React would later fight over it\n // (Iconify swaps its placeholder <span> for the <svg> once the icon data is loaded).\n // Wrap the <Trans> in its own editable <span> instead; the container stays untouched.\n const directTransNodes = jsxNode.children.filter(\n (c) =>\n c.type === \"JSXElement\" && detectTransComponent(c as JSXElement, state.code, state.constants),\n ) as JSXElement[];\n const elementSiblings = jsxNode.children.filter(\n (c) =>\n c.type === \"JSXElement\" && !detectTransComponent(c as JSXElement, state.code, state.constants),\n );\n const transToWrap =\n hasI18n &&\n i18nAttrValue &&\n !isMixed &&\n transChildren.length === 1 &&\n tCallChildren.length === 0 &&\n directTransNodes.length === 1 &&\n elementSiblings.length > 0 &&\n hasRange(directTransNodes[0])\n ? directTransNodes[0]\n : null;\n\n if (transToWrap) {\n const wrapperHash = hashContent(state.code.slice(transToWrap.start, transToWrap.end));\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n const wrapperAttrs = [\n loopSuffixes.length > 0\n ? `data-upstart-hash={\\`${wrapperHash}-${loopSuffixes.join(\"-\")}\\`}`\n : `data-upstart-hash=\"${wrapperHash}\"`,\n 'data-upstart-editable-text=\"true\"',\n 'data-upstart-editable-text-mode=\"plain\"',\n `data-upstart-i18n=${i18nAttrValue}`,\n ];\n const wrapperValueKeys = [...new Set(allI18nKeys.flatMap((t) => t.valueKeys ?? []))];\n if (wrapperValueKeys.length > 0) {\n wrapperAttrs.push(`data-i18n-values=\"${wrapperValueKeys.join(\",\")}\"`);\n }\n state.s.appendLeft(transToWrap.start, `<span ${wrapperAttrs.join(\" \")}>`);\n state.s.appendRight(transToWrap.end, \"</span>\");\n modified = true;\n } else if (hasI18n && i18nAttrValue && (!isMixed || i18nAffixes)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n if (i18nAffixes?.prefix) {\n attributes.push(`data-upstart-i18n-prefix=\"${escapeProp(i18nAffixes.prefix)}\"`);\n }\n if (i18nAffixes?.suffix) {\n attributes.push(`data-upstart-i18n-suffix=\"${escapeProp(i18nAffixes.suffix)}\"`);\n }\n\n attributes.push(`data-upstart-i18n=${i18nAttrValue}`);\n\n const allValueKeys = [...new Set(allI18nKeys.flatMap((t) => t.valueKeys ?? []))];\n if (allValueKeys.length > 0) {\n attributes.push(`data-i18n-values=\"${allValueKeys.join(\",\")}\"`);\n }\n }\n\n // Step 2a: Text leaf elements — plain text, editable directly in the TSX source\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"direct\"');\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 2b: Almost-leaf elements — inline formatting tags only, editable via InspectorPanel\n else if (!hasI18n && isAlmostLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"rich-panel\"');\n\n // Track the full children range so the server can overwrite them in the TSX source\n const children = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = children[0];\n const lastChild = children[children.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"rich-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 2c: Mixed-text leaf — JSXText + JSXExpressionContainer children only.\n // Expressions become non-editable chips; surrounding text is editable.\n else if (!hasI18n && isMixedTextLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const rangedChildren = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = rangedChildren[0];\n const lastChild = rangedChildren[rangedChildren.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const segments: EditableSegment[] = [];\n let exprIndex = 0;\n let templateStr = \"\";\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n const normalized = normalizeJSXText((child as any).value as string);\n segments.push({ type: \"text\", raw: (child as any).value as string });\n templateStr += normalized;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n const raw = hasRange(child) ? state.code.slice(child.start, child.end) : \"\";\n segments.push({ type: \"expr\", raw });\n templateStr += `{{${exprIndex++}}}`;\n }\n }\n\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"mixed-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n segments,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n attributes.push(`data-upstart-mixed-template=\"${escapeProp(templateStr.trim())}\"`);\n }\n }\n // Step 2d: Inline array-literal map item — <span>{item}</span> where `item` is the\n // param of a .map() over an inline array of literals. Each instance edits the\n // matching source string literal directly, addressed via the loop index.\n else if (!hasI18n) {\n const arrayItem = detectArrayLiteralItem(jsxNode, state);\n if (arrayItem) {\n attributes.push('data-upstart-editable-text=\"true\"');\n // \"plain\" mode + a data-upstart-id routes saves through editTextDirect while\n // keeping the editor plain-text (no rich HTML written into the string literal).\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n for (const el of arrayItem.elements) {\n editableRegistry.set(el.id, {\n file: state.filePath,\n type: \"text\",\n startOffset: el.startOffset,\n endOffset: el.endOffset,\n originalContent: el.originalContent,\n quote: el.quote,\n context: { parentTag: tagName || \"unknown\" },\n });\n }\n\n // Each rendered instance resolves its own id via the loop index, so the\n // edit targets the correct array element in source.\n const idsArray = `[${arrayItem.elements.map((e) => JSON.stringify(e.id)).join(\",\")}]`;\n attributes.push(`data-upstart-id={${idsArray}[${arrayItem.indexName}]}`);\n\n // Array identity + per-instance index drive the editor's add/delete (×/+)\n // controls, which add or remove elements of the inline array literal.\n const arrayId = `${state.filePath}:${arrayItem.arrayStart}`;\n attributes.push(`data-upstart-array-id=\"${arrayId}\"`);\n attributes.push(`data-upstart-array-index={${arrayItem.indexName}}`);\n } else if (hasVisibleTextContent(jsxNode)) {\n // Step 3: Non-leaf / dynamic text we cannot edit in place.\n attributes.push('data-upstart-editable-text=\"false\"');\n }\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 // Track <img src=\"...\"> when src is a string literal → makes the image\n // swappable from the editor (mirrors the className tracking above).\n if (tagName === \"img\") {\n const srcAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n attr.name.name === \"src\" &&\n attr.value?.type === \"Literal\" &&\n typeof (attr.value as any).value === \"string\",\n );\n\n if (srcAttr && srcAttr.value && hasRange(srcAttr.value)) {\n const id = generateId(state.filePath, srcAttr.value);\n const srcValue = (srcAttr.value as any).value as string;\n\n // +1 / -1 to exclude the surrounding quotes, like className above\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"image\",\n startOffset: srcAttr.value.start + 1,\n endOffset: srcAttr.value.end - 1,\n originalContent: srcValue,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-image-id=\"${id}\"`);\n }\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 auto-inject index parameter if missing\nfunction detectAndPatchMapCall(node: CallExpression, state: TransformState): 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 let indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // If no index parameter exists in arrow function, auto-inject it\n if (!indexName && callback.type === \"ArrowFunctionExpression\") {\n indexName = \"__i\";\n\n // Find the closing paren of the parameter list safely\n const lastParam = params[params.length - 1];\n\n // ESTree `Pattern` type doesn't guarantee source range fields in typings.\n // Guard before reading offsets.\n if (!hasRange(lastParam)) {\n // Can't patch callback params without a stable source offset.\n // Keep indexName as null so hash suffix generation can handle this case.\n indexName = null;\n return {\n itemName,\n indexName,\n arrayExpr: exprToString(node.callee.object as Expression, state.code),\n arrayNode: node.callee.object as Node,\n };\n }\n\n let searchPos = lastParam.end;\n\n // Find the next ) after the last parameter\n while (searchPos < state.code.length && state.code[searchPos] !== \")\") {\n searchPos++;\n }\n\n if (searchPos < state.code.length && state.code[searchPos] === \")\") {\n // Insert the parameter before the closing paren\n state.s.appendLeft(searchPos, \", __i\");\n }\n }\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, state.code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n arrayNode: node.callee.object as Node,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\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 let valueKeys: string[] | undefined;\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 if (attrName === \"values\") {\n const container = attr.value as any;\n if (container?.type === \"JSXExpressionContainer\") {\n const expr = container.expression;\n if (expr?.type === \"ObjectExpression\") {\n const keys: string[] = [];\n for (const prop of expr.properties) {\n // oxc-parser uses estree \"Property\"; Babel uses \"ObjectProperty\"\n if ((prop.type === \"Property\" || prop.type === \"ObjectProperty\") && prop.key) {\n if (prop.key.type === \"Identifier\") keys.push(prop.key.name);\n else if (prop.key.type === \"Literal\" || prop.key.type === \"StringLiteral\")\n keys.push(prop.key.value);\n }\n }\n if (keys.length > 0) valueKeys = keys;\n }\n }\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 valueKeys,\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 // A fragment renders all of its children at once — collect every key it holds\n if (expr.type === \"JSXFragment\") {\n for (const child of expr.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n } else if (child.type === \"JSXExpressionContainer\") {\n const inner = (child as any).expression;\n if (inner && inner.type !== \"JSXEmptyExpression\") {\n findTransInExpression(inner, code, results, constants);\n }\n }\n }\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// Source of a JS expression resolving to \"namespace:key\" for one i18n reference.\nfunction i18nKeyExpressionSource(info: I18nKeyInfo): string {\n if (info.keyExpr || info.nsExpr) {\n const nsPart = info.nsExpr ? `\\${${info.nsExpr}}` : info.namespace;\n const keyPart = info.keyExpr ? `\\${${info.keyExpr}}` : info.key;\n return `\\`${nsPart}:${keyPart}\\``;\n }\n return JSON.stringify(info.fullKey);\n}\n\ninterface I18nTernary {\n /** Source of the ternary condition, e.g. \"isSubmitting\" */\n test: string;\n consequent: I18nKeyInfo;\n alternate: I18nKeyInfo;\n}\n\n// Detects an element whose only i18n content is a ternary between two translations,\n// e.g. `<button>{isSubmitting ? <Trans i18nKey=\"a\" /> : <Trans i18nKey=\"b\" />}</button>`.\n// Only one branch is ever rendered, so the key can be resolved at runtime instead of\n// being ambiguous. Returns null as soon as anything else produces i18n content.\nfunction detectI18nTernary(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): I18nTernary | null {\n const keysOf = (expr: unknown): I18nKeyInfo[] => {\n const found: I18nKeyInfo[] = [];\n findTransInExpression(expr, code, found, constants);\n findTCallInExpression(expr, code, found, state);\n return found;\n };\n\n let ternary: I18nTernary | null = null;\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXElement\") {\n // A <Trans> sibling renders at the same time as the ternary → ambiguous\n if (detectTransComponent(child as JSXElement, code, constants)) return null;\n continue;\n }\n\n if (child.type !== \"JSXExpressionContainer\") continue;\n\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n if (keysOf(expr).length === 0) continue;\n\n // A second i18n-producing child renders alongside the first → ambiguous\n if (ternary) return null;\n if (expr.type !== \"ConditionalExpression\" || !hasRange(expr.test)) return null;\n\n const consequentKeys = keysOf(expr.consequent);\n const alternateKeys = keysOf(expr.alternate);\n if (consequentKeys.length !== 1 || alternateKeys.length !== 1) return null;\n\n ternary = {\n test: code.slice(expr.test.start, expr.test.end),\n consequent: consequentKeys[0],\n alternate: alternateKeys[0],\n };\n }\n\n return ternary;\n}\n\n// Builds the source of the `data-upstart-i18n` attribute, which must resolve to a\n// single \"namespace:key\" at runtime so an inline edit can be routed to the right\n// translation:\n// \"translation:home.title\" — single static key\n// {`${ns}:${keyVar}`} — dynamic key and/or namespace\n// {isSubmitting ? \"translation:a\" : \"translation:b\"} — ternary between two translations\n// Returns null when the element renders several translations at once — the element\n// then stays non-editable rather than saving edits to a bogus key.\nfunction buildI18nAttributeValue(\n jsxNode: JSXElement,\n keys: I18nKeyInfo[],\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): string | null {\n if (keys.length === 1) {\n const [info] = keys;\n if (info.keyExpr || info.nsExpr) {\n return `{${i18nKeyExpressionSource(info)}}`;\n }\n return `\"${escapeProp(info.fullKey)}\"`;\n }\n\n const ternary = detectI18nTernary(jsxNode, code, state, constants);\n if (!ternary) return null;\n\n const consequent = i18nKeyExpressionSource(ternary.consequent);\n const alternate = i18nKeyExpressionSource(ternary.alternate);\n return `{${ternary.test} ? ${consequent} : ${alternate}}`;\n}\n\n// Returns the visible text of an element whose children are exclusively JSXText\n// (e.g. the \"*\" of `<span aria-hidden=\"true\">*</span>`), or null when the element\n// contains anything dynamic. Such an element renders static text, so next to an\n// i18n node it must be treated as a static affix, never as editable content.\nfunction getStaticElementText(jsxElement: JSXElement): string | null {\n let text = \"\";\n for (const child of jsxElement.children) {\n if (child.type !== \"JSXText\") return null;\n text += normalizeJSXText((child as any).value as string);\n }\n return text.trim().length > 0 ? text : null;\n}\n\ninterface I18nStaticAffixes {\n /** Static text rendered before the i18n node, e.g. \"\" */\n prefix: string;\n /** Static text rendered after the i18n node, e.g. \" *\" */\n suffix: string;\n}\n\n// Detects the \"single i18n source surrounded by static text\" shape, e.g.\n// <label><Trans i18nKey=\"contact.form.phone\" /> *</label>\n// Returns the static text rendered before/after the i18n node (normalised the same\n// way React normalises JSXText), or null when the element does not match: several\n// i18n sources, non-i18n elements, or dynamic expressions among the children.\n// The affixes let the runtime editor strip the static parts from the editable\n// content so they never leak into the saved translation.\nfunction detectI18nStaticAffixes(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): I18nStaticAffixes | null {\n let prefix = \"\";\n let suffix = \"\";\n let seenI18n = false;\n\n const append = (text: string): void => {\n if (seenI18n) suffix += text;\n else prefix += text;\n };\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n // React only trims JSXText around newlines; a single-line text (e.g. the \" \"\n // or \" *\" between two elements) is rendered verbatim.\n const raw = (child as any).value as string;\n const text = /[\\r\\n]/.test(raw) ? normalizeJSXText(raw) : raw;\n if (text) append(text);\n continue;\n }\n\n if (child.type === \"JSXElement\") {\n if (detectTransComponent(child as JSXElement, code, constants)) {\n if (seenI18n) return null;\n seenI18n = true;\n continue;\n }\n // A purely static element (e.g. <span aria-hidden=\"true\">*</span>) is an affix\n const staticText = getStaticElementText(child as JSXElement);\n if (staticText === null) return null;\n append(staticText);\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n\n // Whitespace-only literal (e.g. {\" \"}) renders as static text\n if (expr.type === \"Literal\" && typeof expr.value === \"string\" && expr.value.trim() === \"\") {\n append(expr.value);\n continue;\n }\n\n const results: I18nKeyInfo[] = [];\n findTCallInExpression(expr, code, results, state);\n findTransInExpression(expr, code, results, constants);\n if (results.length === 0) return null;\n if (seenI18n) return null;\n seenI18n = true;\n continue;\n }\n\n // Fragments, spread children… — too complex to reason about\n return null;\n }\n\n if (!seenI18n || (!prefix && !suffix)) return null;\n\n return { prefix, suffix };\n}\n\n// Returns true if the element mixes i18n content with non-i18n text/expressions.\n// In that case, inline editing would incorrectly overwrite the translation with\n// the full rendered text (including static parts like \"© 2026 Company.\").\n//\n// Safe patterns (NOT mixed):\n// {showWelcome && <Trans>} — boolean guard around Trans\n// {cond ? <Trans a> : <Trans b>} — ternary between two Trans\n// {\" \"} — whitespace-only literal\n//\n// Mixed patterns (flagged):\n// \"© John Doe.\" + <Trans> — non-whitespace JSXText alongside i18n\n// {year} + <Trans> — text-producing Identifier/MemberExpression alongside i18n\n// <span>*</span> + <Trans> — static sibling element rendering its own text\n//\n// Flagged elements can still be recovered by detectI18nStaticAffixes() when the\n// static parts merely surround a single i18n node.\nfunction hasMixedNonI18nContent(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): boolean {\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n if (((child as any).value as string).trim().length > 0) return true;\n } else if (child.type === \"JSXElement\") {\n // A sibling element that renders its own text (e.g. <span aria-hidden=\"true\">*</span>)\n // is not part of the translation — same hazard as raw JSXText.\n if (detectTransComponent(child as JSXElement, code, constants)) continue;\n if (hasVisibleTextContent(child as JSXElement)) return true;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n // Whitespace-only string literal (e.g. {\" \"}) is harmless\n if (expr.type === \"Literal\" && typeof expr.value === \"string\" && expr.value.trim() === \"\") continue;\n // If expression contains i18n content (t() or Trans), it is not non-i18n text\n const tResults: I18nKeyInfo[] = [];\n findTCallInExpression(expr, code, tResults, state);\n if (tResults.length > 0) continue;\n const transResults: I18nKeyInfo[] = [];\n findTransInExpression(expr, code, transResults, constants);\n if (transResults.length > 0) continue;\n // Flag only expressions that produce visible text (same heuristic as hasVisibleTextContent)\n const type = expr.type;\n if (\n type === \"Identifier\" ||\n type === \"MemberExpression\" ||\n type === \"Literal\" ||\n type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n return false;\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 // A fragment renders all of its children at once — collect every key it holds\n if (expr.type === \"JSXFragment\") {\n for (const child of expr.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const inner = (child as any).expression;\n if (inner && inner.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(inner, code, results, state);\n }\n }\n }\n return;\n }\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// Inline HTML tags that purely style text without introducing block structure.\n// An element whose children are only JSXText + these tags is an \"almost-leaf\"\n// that can be edited as rich text via the InspectorPanel.\nconst INLINE_FORMATTING_TAGS = new Set([\n \"b\",\n \"i\",\n \"em\",\n \"strong\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"mark\",\n \"small\",\n \"sub\",\n \"sup\",\n \"code\",\n \"kbd\",\n \"abbr\",\n \"cite\",\n \"time\",\n \"span\",\n \"a\",\n]);\n\n// Returns true if the element's children are exclusively JSXText nodes and\n// known inline formatting tags (no expressions, no block elements, no fragments).\n// Must have at least one inline tag (otherwise it would be a plain text leaf).\n// The inline child tags must themselves be \"simple\" — only JSXText children,\n// no expressions, no attributes — so that <span className=\"...\">{expr}</span>\n// siblings are not mistaken for plain inline formatting wrappers.\nfunction isAlmostLeafElement(jsxElement: JSXElement): boolean {\n let hasInlineElement = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n continue;\n }\n if (child.type === \"JSXElement\") {\n const childEl = child as JSXElement;\n const tagName = getJSXElementName(childEl.openingElement);\n if (!tagName || !INLINE_FORMATTING_TAGS.has(tagName.toLowerCase())) return false;\n // The inline child must be truly simple: no attributes and only JSXText children.\n // A <span className=\"...\"> or <span>{expr}</span> is not a simple inline wrapper.\n if (childEl.openingElement.attributes.length > 0) return false;\n for (const grandchild of childEl.children) {\n if (grandchild.type !== \"JSXText\") return false;\n }\n hasInlineElement = true;\n } else {\n return false;\n }\n }\n\n return hasInlineElement;\n}\n\n// Normalise a raw JSX text node value the same way React/Babel does at compile time:\n// - Split by newlines\n// - For non-first lines, strip leading whitespace; for non-last lines, strip trailing whitespace\n// - Remove lines that are entirely whitespace\n// - Join surviving lines, appending a space after each non-last non-empty line\nfunction normalizeJSXText(raw: string): string {\n const lines = raw.split(/\\r\\n|\\n|\\r/);\n const lastNonEmptyIdx = lines.reduce((acc, line, i) => (/[^ \\t]/.test(line) ? i : acc), -1);\n let result = \"\";\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i].replace(/\\t/g, \" \");\n if (i > 0) line = line.replace(/^[ ]+/, \"\");\n if (i < lines.length - 1) line = line.replace(/[ ]+$/, \"\");\n if (!line) continue;\n if (i !== lastNonEmptyIdx) line += \" \";\n result += line;\n }\n return result;\n}\n\n// Returns true if the element's children are exclusively JSXText + JSXExpressionContainer nodes\n// (no nested elements), AND it has at least one of each — making the static text parts editable\n// while expressions are rendered as non-editable chips.\nfunction isMixedTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n let hasExpr = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n if (normalizeJSXText((child as any).value as string).length > 0) hasText = true;\n continue;\n }\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n hasExpr = true;\n continue;\n }\n // JSXElement, JSXFragment, JSXSpreadChild → not a pure mixed-text leaf\n return false;\n }\n\n return hasText && hasExpr;\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\n// Per-element data needed to make an inline array-literal map item editable.\ninterface ArrayLiteralItemInfo {\n /** Loop index variable name used to pick the matching id at runtime, e.g. \"__i\". */\n indexName: string;\n /** Source start offset of the `[` — identifies the array for add/delete operations. */\n arrayStart: number;\n elements: {\n id: string;\n startOffset: number;\n endOffset: number;\n originalContent: string;\n /** Source quote char for string literals (\" ' or `); undefined for numbers. */\n quote?: string;\n }[];\n}\n\n// Detect `<span>{item}</span>` where `item` is the item param of an enclosing\n// `.map()` over an INLINE array literal of string/number literals, e.g.:\n// {[\"TypeScript\", \"React\"].map((item) => <span>{item}</span>)}\n// In that case each rendered instance maps 1:1 (via the loop index) to a source\n// string literal we can edit directly. Returns null for anything not statically\n// resolvable (array from props/variable, transformed item, non-literal elements).\nfunction detectArrayLiteralItem(jsxElement: JSXElement, state: TransformState): ArrayLiteralItemInfo | null {\n // The element's only meaningful child must be a single {identifier} expression.\n let identName: string | null = null;\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n if (normalizeJSXText((child as any).value as string).length > 0) return null;\n continue;\n }\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n if (expr.type !== \"Identifier\") return null;\n if (identName !== null) return null; // more than one expression — not a plain item render\n identName = expr.name as string;\n continue;\n }\n // Any nested element/fragment/spread disqualifies.\n return null;\n }\n if (!identName) return null;\n\n // Find the enclosing loop whose item param matches the identifier (innermost first).\n for (let i = state.loopStack.length - 1; i >= 0; i--) {\n const loop = state.loopStack[i];\n if (loop.itemName !== identName) continue;\n // Need a stable per-instance index and an inline array of literals.\n if (!loop.indexName) return null;\n const arr = loop.arrayNode as any;\n if (!arr || arr.type !== \"ArrayExpression\") return null;\n\n const elements: ArrayLiteralItemInfo[\"elements\"] = [];\n for (const el of arr.elements) {\n // Only plain string/number literals can be rewritten in place.\n if (!el || el.type !== \"Literal\" || !hasRange(el)) return null;\n const value = (el as any).value;\n if (typeof value !== \"string\" && typeof value !== \"number\") return null;\n // For strings, the inner range excludes the surrounding quotes so we only\n // overwrite the content (mirrors how <img src> edits work).\n const isString = typeof value === \"string\";\n const startOffset = isString ? el.start + 1 : el.start;\n const endOffset = isString ? el.end - 1 : el.end;\n elements.push({\n id: `${state.filePath}:${el.start}`,\n startOffset,\n endOffset,\n originalContent: state.code.slice(startOffset, endOffset),\n // The opening quote sits at el.start for string literals.\n quote: isString ? state.code[el.start] : undefined,\n });\n }\n if (elements.length === 0) return null;\n return { indexName: loop.indexName, arrayStart: arr.start, elements };\n }\n return null;\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 fs from \"node:fs\";\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 /** The `.map()` callee object AST node — used to edit inline array-literal items in place. */\n arrayNode: Node | null;\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 /** Variable names from the `values` prop (e.g. [\"year\"] from values={{ year }}) */\n valueKeys?: string[];\n}\n\n// A segment of a mixed-text element: either static text or a JSX expression\nexport interface EditableSegment {\n type: \"text\" | \"expr\";\n // For \"text\": the raw JSX text value (with whitespace/newlines as in source)\n // For \"expr\": the full expression source including braces, e.g. \"{new Date().getFullYear()}\"\n raw: string;\n}\n\n// Registry entry for editable elements (text, rich-text, className, mixed-text)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"rich-text\" | \"className\" | \"mixed-text\" | \"image\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n // Only present for mixed-text entries\n segments?: EditableSegment[];\n // For text entries that map to a JS string literal (e.g. an inline array item),\n // the source quote character (\" ' or `). When set, the server escapes the new\n // content for that quote style before writing it back between the quotes.\n quote?: 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 /** Same as `constants`, but keeps the string Literal AST node so its source range can be edited */\n constantNodes: Map<string, StringLiteralNode>;\n /**\n * Stack of enclosing function scopes, innermost last. Each scope maps a destructured\n * parameter name to the string Literal used as its default value, e.g.\n * `function Hero({ heroImageSrc = \"/images/hero.webp\" })`.\n */\n paramDefaults: Map<string, StringLiteralNode>[];\n}\n\ntype StringLiteralNode = NodeWithRange & { type: \"Literal\"; value: string };\n\nfunction isStringLiteralNode(node: any): node is StringLiteralNode {\n return !!node && node.type === \"Literal\" && typeof node.value === \"string\" && hasRange(node);\n}\n\n/**\n * Collect `{ name = \"literal\" }` defaults from a function's parameters. Only object-pattern\n * params with a plain string default are tracked — that is the common \"props with default\"\n * shape emitted for site components.\n */\nfunction collectParamStringDefaults(params: any[]): Map<string, StringLiteralNode> {\n const defaults = new Map<string, StringLiteralNode>();\n for (const param of params) {\n // `({ a = \"x\" }: Props)` — TS annotations keep the ObjectPattern as the param itself.\n const pattern = param?.type === \"AssignmentPattern\" ? param.left : param;\n if (pattern?.type !== \"ObjectPattern\") continue;\n for (const prop of pattern.properties) {\n if (prop.type !== \"Property\" || prop.key?.type !== \"Identifier\") continue;\n const value = prop.value;\n if (value?.type !== \"AssignmentPattern\" || value.left?.type !== \"Identifier\") continue;\n if (isStringLiteralNode(value.right)) {\n defaults.set(value.left.name, value.right);\n }\n }\n }\n return defaults;\n}\n\n/**\n * Resolve the string Literal backing an `<img src>` attribute, so the editor can rewrite it.\n * Handles `src=\"...\"`, `src={\"...\"}`, `src={prop}` where `prop` has a string default in an\n * enclosing function's parameters, and `src={CONST}` where `const CONST = \"...\"`.\n * Returns the identifier name when the value was resolved through a variable, so the caller\n * can guard the injected attribute at runtime (a parent may pass a different value).\n */\nfunction resolveImageSrcLiteral(\n attr: JSXAttribute,\n state: TransformState,\n): { literal: StringLiteralNode; via: string | null } | null {\n const value = attr.value as any;\n if (isStringLiteralNode(value)) return { literal: value, via: null };\n if (value?.type !== \"JSXExpressionContainer\") return null;\n const expr = value.expression;\n if (isStringLiteralNode(expr)) return { literal: expr, via: null };\n if (expr?.type !== \"Identifier\") return null;\n for (let i = state.paramDefaults.length - 1; i >= 0; i--) {\n const literal = state.paramDefaults[i].get(expr.name);\n if (literal) return { literal, via: expr.name };\n }\n const constant = state.constantNodes.get(expr.name);\n return constant ? { literal: constant, via: expr.name } : null;\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 let isDevMode = false;\n let devWriteTimer: ReturnType<typeof setTimeout> | null = null;\n\n const flushDevRegistry = () => {\n if (!isDevMode || editableRegistry.size === 0) return;\n const registryDir = path.join(root, \"build\", \"server\");\n const registryPath = path.join(registryDir, \"upstart-registry.json\");\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n fs.mkdirSync(registryDir, { recursive: true });\n fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2));\n };\n\n const scheduleDevRegistryWrite = () => {\n if (!isDevMode) return;\n if (devWriteTimer) clearTimeout(devWriteTimer);\n devWriteTimer = setTimeout(flushDevRegistry, 150);\n };\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n isDevMode = config.command === \"serve\";\n },\n configureServer(_server) {\n isDevMode = true;\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 scheduleDevRegistryWrite();\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 constantNodes: new Map(),\n paramDefaults: [],\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 if (isStringLiteralNode(decl.init)) {\n state.constantNodes.set(decl.id.name, decl.init);\n }\n }\n }\n }\n\n // Track destructured parameter defaults of the enclosing function\n // (e.g. `function Hero({ heroImageSrc = \"/images/hero.webp\" })`) so `<img src={heroImageSrc}>`\n // can be resolved to an editable literal.\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\"\n ) {\n state.paramDefaults.push(collectParamStringDefaults((node as any).params ?? []));\n next();\n state.paramDefaults.pop();\n return;\n }\n\n // Handle .map() calls to track loops (must be before JSXElement)\n if (node.type === \"CallExpression\") {\n const mapCallInfo = detectAndPatchMapCall(node as CallExpression, state);\n\n if (mapCallInfo) {\n // Push loop context\n state.loopStack.push(mapCallInfo);\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 suffix expressions from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n // detectAndPatchMapCall() attempts to auto-inject an index param when missing.\n // If injection is not possible (no stable source range), fall back to \"0\".\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n\n if (loopSuffixes.length > 0) {\n const suffix = loopSuffixes.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 const isMixed = hasI18n && hasMixedNonI18nContent(jsxNode, state.code, state, state.constants);\n\n // Mixed content is normally disqualifying, but the very common\n // `<label><Trans i18nKey=\"…\" /> *</label>` shape is recoverable: the static\n // text around the single i18n node is exported as prefix/suffix so the editor\n // can keep it out of the saved translation.\n const i18nAffixes = isMixed\n ? detectI18nStaticAffixes(jsxNode, state.code, state, state.constants)\n : null;\n\n // The element must resolve to exactly ONE translation key at runtime, otherwise\n // an inline edit could not be routed to the right key.\n const i18nAttrValue = hasI18n\n ? buildI18nAttributeValue(jsxNode, allI18nKeys, state.code, state, state.constants)\n : null;\n\n // A single <Trans> next to element siblings without text (an <Icon>, an <svg>…): the\n // plain editor replaces the whole content of the editable element, so making the\n // container editable would destroy the icon, and React would later fight over it\n // (Iconify swaps its placeholder <span> for the <svg> once the icon data is loaded).\n // Wrap the <Trans> in its own editable <span> instead; the container stays untouched.\n const directTransNodes = jsxNode.children.filter(\n (c) =>\n c.type === \"JSXElement\" && detectTransComponent(c as JSXElement, state.code, state.constants),\n ) as JSXElement[];\n const elementSiblings = jsxNode.children.filter(\n (c) =>\n c.type === \"JSXElement\" && !detectTransComponent(c as JSXElement, state.code, state.constants),\n );\n const transToWrap =\n hasI18n &&\n i18nAttrValue &&\n !isMixed &&\n transChildren.length === 1 &&\n tCallChildren.length === 0 &&\n directTransNodes.length === 1 &&\n elementSiblings.length > 0 &&\n hasRange(directTransNodes[0])\n ? directTransNodes[0]\n : null;\n\n if (transToWrap) {\n const wrapperHash = hashContent(state.code.slice(transToWrap.start, transToWrap.end));\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n const wrapperAttrs = [\n loopSuffixes.length > 0\n ? `data-upstart-hash={\\`${wrapperHash}-${loopSuffixes.join(\"-\")}\\`}`\n : `data-upstart-hash=\"${wrapperHash}\"`,\n 'data-upstart-editable-text=\"true\"',\n 'data-upstart-editable-text-mode=\"plain\"',\n `data-upstart-i18n=${i18nAttrValue}`,\n ];\n const wrapperValueKeys = [...new Set(allI18nKeys.flatMap((t) => t.valueKeys ?? []))];\n if (wrapperValueKeys.length > 0) {\n wrapperAttrs.push(`data-i18n-values=\"${wrapperValueKeys.join(\",\")}\"`);\n }\n state.s.appendLeft(transToWrap.start, `<span ${wrapperAttrs.join(\" \")}>`);\n state.s.appendRight(transToWrap.end, \"</span>\");\n modified = true;\n } else if (hasI18n && i18nAttrValue && (!isMixed || i18nAffixes)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n if (i18nAffixes?.prefix) {\n attributes.push(`data-upstart-i18n-prefix=\"${escapeProp(i18nAffixes.prefix)}\"`);\n }\n if (i18nAffixes?.suffix) {\n attributes.push(`data-upstart-i18n-suffix=\"${escapeProp(i18nAffixes.suffix)}\"`);\n }\n\n attributes.push(`data-upstart-i18n=${i18nAttrValue}`);\n\n const allValueKeys = [...new Set(allI18nKeys.flatMap((t) => t.valueKeys ?? []))];\n if (allValueKeys.length > 0) {\n attributes.push(`data-i18n-values=\"${allValueKeys.join(\",\")}\"`);\n }\n }\n\n // Step 2a: Text leaf elements — plain text, editable directly in the TSX source\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"direct\"');\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 2b: Almost-leaf elements — inline formatting tags only, editable via InspectorPanel\n else if (!hasI18n && isAlmostLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"rich-panel\"');\n\n // Track the full children range so the server can overwrite them in the TSX source\n const children = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = children[0];\n const lastChild = children[children.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"rich-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 2c: Mixed-text leaf — JSXText + JSXExpressionContainer children only.\n // Expressions become non-editable chips; surrounding text is editable.\n else if (!hasI18n && isMixedTextLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const rangedChildren = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = rangedChildren[0];\n const lastChild = rangedChildren[rangedChildren.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const segments: EditableSegment[] = [];\n let exprIndex = 0;\n let templateStr = \"\";\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n const normalized = normalizeJSXText((child as any).value as string);\n segments.push({ type: \"text\", raw: (child as any).value as string });\n templateStr += normalized;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n const raw = hasRange(child) ? state.code.slice(child.start, child.end) : \"\";\n segments.push({ type: \"expr\", raw });\n templateStr += `{{${exprIndex++}}}`;\n }\n }\n\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"mixed-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n segments,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n attributes.push(`data-upstart-mixed-template=\"${escapeProp(templateStr.trim())}\"`);\n }\n }\n // Step 2d: Inline array-literal map item — <span>{item}</span> where `item` is the\n // param of a .map() over an inline array of literals. Each instance edits the\n // matching source string literal directly, addressed via the loop index.\n else if (!hasI18n) {\n const arrayItem = detectArrayLiteralItem(jsxNode, state);\n if (arrayItem) {\n attributes.push('data-upstart-editable-text=\"true\"');\n // \"plain\" mode + a data-upstart-id routes saves through editTextDirect while\n // keeping the editor plain-text (no rich HTML written into the string literal).\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n for (const el of arrayItem.elements) {\n editableRegistry.set(el.id, {\n file: state.filePath,\n type: \"text\",\n startOffset: el.startOffset,\n endOffset: el.endOffset,\n originalContent: el.originalContent,\n quote: el.quote,\n context: { parentTag: tagName || \"unknown\" },\n });\n }\n\n // Each rendered instance resolves its own id via the loop index, so the\n // edit targets the correct array element in source.\n const idsArray = `[${arrayItem.elements.map((e) => JSON.stringify(e.id)).join(\",\")}]`;\n attributes.push(`data-upstart-id={${idsArray}[${arrayItem.indexName}]}`);\n\n // Array identity + per-instance index drive the editor's add/delete (×/+)\n // controls, which add or remove elements of the inline array literal.\n const arrayId = `${state.filePath}:${arrayItem.arrayStart}`;\n attributes.push(`data-upstart-array-id=\"${arrayId}\"`);\n attributes.push(`data-upstart-array-index={${arrayItem.indexName}}`);\n } else if (hasVisibleTextContent(jsxNode)) {\n // Step 3: Non-leaf / dynamic text we cannot edit in place.\n attributes.push('data-upstart-editable-text=\"false\"');\n }\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 // Track <img src> when it resolves to a string literal → makes the image\n // swappable from the editor (mirrors the className tracking above).\n // See resolveImageSrcLiteral for the supported shapes.\n if (tagName === \"img\") {\n const srcAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" && attr.name.type === \"JSXIdentifier\" && attr.name.name === \"src\",\n );\n const resolved = srcAttr ? resolveImageSrcLiteral(srcAttr, state) : null;\n\n if (resolved) {\n const { literal, via } = resolved;\n const id = generateId(state.filePath, literal);\n\n // +1 / -1 to exclude the surrounding quotes, like className above\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"image\",\n startOffset: literal.start + 1,\n endOffset: literal.end - 1,\n originalContent: literal.value,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n if (via) {\n // The literal is a default/constant reached through a variable: a parent may\n // pass another value, in which case editing the literal would not change what\n // is rendered. Only mark the image editable when the rendered value is the one\n // the editor would rewrite.\n attributes.push(\n `data-upstart-image-id={${via} === ${JSON.stringify(literal.value)} ? \"${id}\" : undefined}`,\n );\n } else {\n attributes.push(`data-upstart-image-id=\"${id}\"`);\n }\n }\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 auto-inject index parameter if missing\nfunction detectAndPatchMapCall(node: CallExpression, state: TransformState): 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 let indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // If no index parameter exists in arrow function, auto-inject it\n if (!indexName && callback.type === \"ArrowFunctionExpression\") {\n indexName = \"__i\";\n\n // Find the closing paren of the parameter list safely\n const lastParam = params[params.length - 1];\n\n // ESTree `Pattern` type doesn't guarantee source range fields in typings.\n // Guard before reading offsets.\n if (!hasRange(lastParam)) {\n // Can't patch callback params without a stable source offset.\n // Keep indexName as null so hash suffix generation can handle this case.\n indexName = null;\n return {\n itemName,\n indexName,\n arrayExpr: exprToString(node.callee.object as Expression, state.code),\n arrayNode: node.callee.object as Node,\n };\n }\n\n let searchPos = lastParam.end;\n\n // Find the next ) after the last parameter\n while (searchPos < state.code.length && state.code[searchPos] !== \")\") {\n searchPos++;\n }\n\n if (searchPos < state.code.length && state.code[searchPos] === \")\") {\n // Insert the parameter before the closing paren\n state.s.appendLeft(searchPos, \", __i\");\n }\n }\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, state.code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n arrayNode: node.callee.object as Node,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\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 let valueKeys: string[] | undefined;\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 if (attrName === \"values\") {\n const container = attr.value as any;\n if (container?.type === \"JSXExpressionContainer\") {\n const expr = container.expression;\n if (expr?.type === \"ObjectExpression\") {\n const keys: string[] = [];\n for (const prop of expr.properties) {\n // oxc-parser uses estree \"Property\"; Babel uses \"ObjectProperty\"\n if ((prop.type === \"Property\" || prop.type === \"ObjectProperty\") && prop.key) {\n if (prop.key.type === \"Identifier\") keys.push(prop.key.name);\n else if (prop.key.type === \"Literal\" || prop.key.type === \"StringLiteral\")\n keys.push(prop.key.value);\n }\n }\n if (keys.length > 0) valueKeys = keys;\n }\n }\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 valueKeys,\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 // A fragment renders all of its children at once — collect every key it holds\n if (expr.type === \"JSXFragment\") {\n for (const child of expr.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n } else if (child.type === \"JSXExpressionContainer\") {\n const inner = (child as any).expression;\n if (inner && inner.type !== \"JSXEmptyExpression\") {\n findTransInExpression(inner, code, results, constants);\n }\n }\n }\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// Source of a JS expression resolving to \"namespace:key\" for one i18n reference.\nfunction i18nKeyExpressionSource(info: I18nKeyInfo): string {\n if (info.keyExpr || info.nsExpr) {\n const nsPart = info.nsExpr ? `\\${${info.nsExpr}}` : info.namespace;\n const keyPart = info.keyExpr ? `\\${${info.keyExpr}}` : info.key;\n return `\\`${nsPart}:${keyPart}\\``;\n }\n return JSON.stringify(info.fullKey);\n}\n\ninterface I18nTernary {\n /** Source of the ternary condition, e.g. \"isSubmitting\" */\n test: string;\n consequent: I18nKeyInfo;\n alternate: I18nKeyInfo;\n}\n\n// Detects an element whose only i18n content is a ternary between two translations,\n// e.g. `<button>{isSubmitting ? <Trans i18nKey=\"a\" /> : <Trans i18nKey=\"b\" />}</button>`.\n// Only one branch is ever rendered, so the key can be resolved at runtime instead of\n// being ambiguous. Returns null as soon as anything else produces i18n content.\nfunction detectI18nTernary(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): I18nTernary | null {\n const keysOf = (expr: unknown): I18nKeyInfo[] => {\n const found: I18nKeyInfo[] = [];\n findTransInExpression(expr, code, found, constants);\n findTCallInExpression(expr, code, found, state);\n return found;\n };\n\n let ternary: I18nTernary | null = null;\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXElement\") {\n // A <Trans> sibling renders at the same time as the ternary → ambiguous\n if (detectTransComponent(child as JSXElement, code, constants)) return null;\n continue;\n }\n\n if (child.type !== \"JSXExpressionContainer\") continue;\n\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n if (keysOf(expr).length === 0) continue;\n\n // A second i18n-producing child renders alongside the first → ambiguous\n if (ternary) return null;\n if (expr.type !== \"ConditionalExpression\" || !hasRange(expr.test)) return null;\n\n const consequentKeys = keysOf(expr.consequent);\n const alternateKeys = keysOf(expr.alternate);\n if (consequentKeys.length !== 1 || alternateKeys.length !== 1) return null;\n\n ternary = {\n test: code.slice(expr.test.start, expr.test.end),\n consequent: consequentKeys[0],\n alternate: alternateKeys[0],\n };\n }\n\n return ternary;\n}\n\n// Builds the source of the `data-upstart-i18n` attribute, which must resolve to a\n// single \"namespace:key\" at runtime so an inline edit can be routed to the right\n// translation:\n// \"translation:home.title\" — single static key\n// {`${ns}:${keyVar}`} — dynamic key and/or namespace\n// {isSubmitting ? \"translation:a\" : \"translation:b\"} — ternary between two translations\n// Returns null when the element renders several translations at once — the element\n// then stays non-editable rather than saving edits to a bogus key.\nfunction buildI18nAttributeValue(\n jsxNode: JSXElement,\n keys: I18nKeyInfo[],\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): string | null {\n if (keys.length === 1) {\n const [info] = keys;\n if (info.keyExpr || info.nsExpr) {\n return `{${i18nKeyExpressionSource(info)}}`;\n }\n return `\"${escapeProp(info.fullKey)}\"`;\n }\n\n const ternary = detectI18nTernary(jsxNode, code, state, constants);\n if (!ternary) return null;\n\n const consequent = i18nKeyExpressionSource(ternary.consequent);\n const alternate = i18nKeyExpressionSource(ternary.alternate);\n return `{${ternary.test} ? ${consequent} : ${alternate}}`;\n}\n\n// Returns the visible text of an element whose children are exclusively JSXText\n// (e.g. the \"*\" of `<span aria-hidden=\"true\">*</span>`), or null when the element\n// contains anything dynamic. Such an element renders static text, so next to an\n// i18n node it must be treated as a static affix, never as editable content.\nfunction getStaticElementText(jsxElement: JSXElement): string | null {\n let text = \"\";\n for (const child of jsxElement.children) {\n if (child.type !== \"JSXText\") return null;\n text += normalizeJSXText((child as any).value as string);\n }\n return text.trim().length > 0 ? text : null;\n}\n\ninterface I18nStaticAffixes {\n /** Static text rendered before the i18n node, e.g. \"\" */\n prefix: string;\n /** Static text rendered after the i18n node, e.g. \" *\" */\n suffix: string;\n}\n\n// Detects the \"single i18n source surrounded by static text\" shape, e.g.\n// <label><Trans i18nKey=\"contact.form.phone\" /> *</label>\n// Returns the static text rendered before/after the i18n node (normalised the same\n// way React normalises JSXText), or null when the element does not match: several\n// i18n sources, non-i18n elements, or dynamic expressions among the children.\n// The affixes let the runtime editor strip the static parts from the editable\n// content so they never leak into the saved translation.\nfunction detectI18nStaticAffixes(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): I18nStaticAffixes | null {\n let prefix = \"\";\n let suffix = \"\";\n let seenI18n = false;\n\n const append = (text: string): void => {\n if (seenI18n) suffix += text;\n else prefix += text;\n };\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n // React only trims JSXText around newlines; a single-line text (e.g. the \" \"\n // or \" *\" between two elements) is rendered verbatim.\n const raw = (child as any).value as string;\n const text = /[\\r\\n]/.test(raw) ? normalizeJSXText(raw) : raw;\n if (text) append(text);\n continue;\n }\n\n if (child.type === \"JSXElement\") {\n if (detectTransComponent(child as JSXElement, code, constants)) {\n if (seenI18n) return null;\n seenI18n = true;\n continue;\n }\n // A purely static element (e.g. <span aria-hidden=\"true\">*</span>) is an affix\n const staticText = getStaticElementText(child as JSXElement);\n if (staticText === null) return null;\n append(staticText);\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n\n // Whitespace-only literal (e.g. {\" \"}) renders as static text\n if (expr.type === \"Literal\" && typeof expr.value === \"string\" && expr.value.trim() === \"\") {\n append(expr.value);\n continue;\n }\n\n const results: I18nKeyInfo[] = [];\n findTCallInExpression(expr, code, results, state);\n findTransInExpression(expr, code, results, constants);\n if (results.length === 0) return null;\n if (seenI18n) return null;\n seenI18n = true;\n continue;\n }\n\n // Fragments, spread children… — too complex to reason about\n return null;\n }\n\n if (!seenI18n || (!prefix && !suffix)) return null;\n\n return { prefix, suffix };\n}\n\n// Returns true if the element mixes i18n content with non-i18n text/expressions.\n// In that case, inline editing would incorrectly overwrite the translation with\n// the full rendered text (including static parts like \"© 2026 Company.\").\n//\n// Safe patterns (NOT mixed):\n// {showWelcome && <Trans>} — boolean guard around Trans\n// {cond ? <Trans a> : <Trans b>} — ternary between two Trans\n// {\" \"} — whitespace-only literal\n//\n// Mixed patterns (flagged):\n// \"© John Doe.\" + <Trans> — non-whitespace JSXText alongside i18n\n// {year} + <Trans> — text-producing Identifier/MemberExpression alongside i18n\n// <span>*</span> + <Trans> — static sibling element rendering its own text\n//\n// Flagged elements can still be recovered by detectI18nStaticAffixes() when the\n// static parts merely surround a single i18n node.\nfunction hasMixedNonI18nContent(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): boolean {\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n if (((child as any).value as string).trim().length > 0) return true;\n } else if (child.type === \"JSXElement\") {\n // A sibling element that renders its own text (e.g. <span aria-hidden=\"true\">*</span>)\n // is not part of the translation — same hazard as raw JSXText.\n if (detectTransComponent(child as JSXElement, code, constants)) continue;\n if (hasVisibleTextContent(child as JSXElement)) return true;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n // Whitespace-only string literal (e.g. {\" \"}) is harmless\n if (expr.type === \"Literal\" && typeof expr.value === \"string\" && expr.value.trim() === \"\") continue;\n // If expression contains i18n content (t() or Trans), it is not non-i18n text\n const tResults: I18nKeyInfo[] = [];\n findTCallInExpression(expr, code, tResults, state);\n if (tResults.length > 0) continue;\n const transResults: I18nKeyInfo[] = [];\n findTransInExpression(expr, code, transResults, constants);\n if (transResults.length > 0) continue;\n // Flag only expressions that produce visible text (same heuristic as hasVisibleTextContent)\n const type = expr.type;\n if (\n type === \"Identifier\" ||\n type === \"MemberExpression\" ||\n type === \"Literal\" ||\n type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n return false;\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 // A fragment renders all of its children at once — collect every key it holds\n if (expr.type === \"JSXFragment\") {\n for (const child of expr.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const inner = (child as any).expression;\n if (inner && inner.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(inner, code, results, state);\n }\n }\n }\n return;\n }\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// Inline HTML tags that purely style text without introducing block structure.\n// An element whose children are only JSXText + these tags is an \"almost-leaf\"\n// that can be edited as rich text via the InspectorPanel.\nconst INLINE_FORMATTING_TAGS = new Set([\n \"b\",\n \"i\",\n \"em\",\n \"strong\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"mark\",\n \"small\",\n \"sub\",\n \"sup\",\n \"code\",\n \"kbd\",\n \"abbr\",\n \"cite\",\n \"time\",\n \"span\",\n \"a\",\n]);\n\n// Returns true if the element's children are exclusively JSXText nodes and\n// known inline formatting tags (no expressions, no block elements, no fragments).\n// Must have at least one inline tag (otherwise it would be a plain text leaf).\n// The inline child tags must themselves be \"simple\" — only JSXText children,\n// no expressions, no attributes — so that <span className=\"...\">{expr}</span>\n// siblings are not mistaken for plain inline formatting wrappers.\nfunction isAlmostLeafElement(jsxElement: JSXElement): boolean {\n let hasInlineElement = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n continue;\n }\n if (child.type === \"JSXElement\") {\n const childEl = child as JSXElement;\n const tagName = getJSXElementName(childEl.openingElement);\n if (!tagName || !INLINE_FORMATTING_TAGS.has(tagName.toLowerCase())) return false;\n // The inline child must be truly simple: no attributes and only JSXText children.\n // A <span className=\"...\"> or <span>{expr}</span> is not a simple inline wrapper.\n if (childEl.openingElement.attributes.length > 0) return false;\n for (const grandchild of childEl.children) {\n if (grandchild.type !== \"JSXText\") return false;\n }\n hasInlineElement = true;\n } else {\n return false;\n }\n }\n\n return hasInlineElement;\n}\n\n// Normalise a raw JSX text node value the same way React/Babel does at compile time:\n// - Split by newlines\n// - For non-first lines, strip leading whitespace; for non-last lines, strip trailing whitespace\n// - Remove lines that are entirely whitespace\n// - Join surviving lines, appending a space after each non-last non-empty line\nfunction normalizeJSXText(raw: string): string {\n const lines = raw.split(/\\r\\n|\\n|\\r/);\n const lastNonEmptyIdx = lines.reduce((acc, line, i) => (/[^ \\t]/.test(line) ? i : acc), -1);\n let result = \"\";\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i].replace(/\\t/g, \" \");\n if (i > 0) line = line.replace(/^[ ]+/, \"\");\n if (i < lines.length - 1) line = line.replace(/[ ]+$/, \"\");\n if (!line) continue;\n if (i !== lastNonEmptyIdx) line += \" \";\n result += line;\n }\n return result;\n}\n\n// Returns true if the element's children are exclusively JSXText + JSXExpressionContainer nodes\n// (no nested elements), AND it has at least one of each — making the static text parts editable\n// while expressions are rendered as non-editable chips.\nfunction isMixedTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n let hasExpr = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n if (normalizeJSXText((child as any).value as string).length > 0) hasText = true;\n continue;\n }\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n hasExpr = true;\n continue;\n }\n // JSXElement, JSXFragment, JSXSpreadChild → not a pure mixed-text leaf\n return false;\n }\n\n return hasText && hasExpr;\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\n// Per-element data needed to make an inline array-literal map item editable.\ninterface ArrayLiteralItemInfo {\n /** Loop index variable name used to pick the matching id at runtime, e.g. \"__i\". */\n indexName: string;\n /** Source start offset of the `[` — identifies the array for add/delete operations. */\n arrayStart: number;\n elements: {\n id: string;\n startOffset: number;\n endOffset: number;\n originalContent: string;\n /** Source quote char for string literals (\" ' or `); undefined for numbers. */\n quote?: string;\n }[];\n}\n\n// Detect `<span>{item}</span>` where `item` is the item param of an enclosing\n// `.map()` over an INLINE array literal of string/number literals, e.g.:\n// {[\"TypeScript\", \"React\"].map((item) => <span>{item}</span>)}\n// In that case each rendered instance maps 1:1 (via the loop index) to a source\n// string literal we can edit directly. Returns null for anything not statically\n// resolvable (array from props/variable, transformed item, non-literal elements).\nfunction detectArrayLiteralItem(jsxElement: JSXElement, state: TransformState): ArrayLiteralItemInfo | null {\n // The element's only meaningful child must be a single {identifier} expression.\n let identName: string | null = null;\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n if (normalizeJSXText((child as any).value as string).length > 0) return null;\n continue;\n }\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n if (expr.type !== \"Identifier\") return null;\n if (identName !== null) return null; // more than one expression — not a plain item render\n identName = expr.name as string;\n continue;\n }\n // Any nested element/fragment/spread disqualifies.\n return null;\n }\n if (!identName) return null;\n\n // Find the enclosing loop whose item param matches the identifier (innermost first).\n for (let i = state.loopStack.length - 1; i >= 0; i--) {\n const loop = state.loopStack[i];\n if (loop.itemName !== identName) continue;\n // Need a stable per-instance index and an inline array of literals.\n if (!loop.indexName) return null;\n const arr = loop.arrayNode as any;\n if (!arr || arr.type !== \"ArrayExpression\") return null;\n\n const elements: ArrayLiteralItemInfo[\"elements\"] = [];\n for (const el of arr.elements) {\n // Only plain string/number literals can be rewritten in place.\n if (!el || el.type !== \"Literal\" || !hasRange(el)) return null;\n const value = (el as any).value;\n if (typeof value !== \"string\" && typeof value !== \"number\") return null;\n // For strings, the inner range excludes the surrounding quotes so we only\n // overwrite the content (mirrors how <img src> edits work).\n const isString = typeof value === \"string\";\n const startOffset = isString ? el.start + 1 : el.start;\n const endOffset = isString ? el.end - 1 : el.end;\n elements.push({\n id: `${state.filePath}:${el.start}`,\n startOffset,\n endOffset,\n originalContent: state.code.slice(startOffset, endOffset),\n // The opening quote sits at el.start for string literals.\n quote: isString ? state.code[el.start] : undefined,\n });\n }\n if (elements.length === 0) return null;\n return { indexName: loop.indexName, arrayStart: arr.start, elements };\n }\n return null;\n}\n\nexport default upstartEditor.vite;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstart.gg/vite-plugins",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.75",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"oxc-parser": "0.101.0",
|
|
24
24
|
"unplugin": "^2.3.11",
|
|
25
25
|
"zimmerframe": "^1.1.4",
|
|
26
|
-
"@upstart.gg/sdk": "^0.1.
|
|
26
|
+
"@upstart.gg/sdk": "^0.1.75"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@rolldown/binding-linux-arm64-gnu": "1.0.0",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"peerDependencies": {
|
|
120
120
|
"zod": "4.3.6",
|
|
121
|
-
"@upstart.gg/sdk": "^0.1.
|
|
121
|
+
"@upstart.gg/sdk": "^0.1.75"
|
|
122
122
|
},
|
|
123
123
|
"author": "Upstart",
|
|
124
124
|
"publishConfig": {
|
|
@@ -1363,7 +1363,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1363
1363
|
);
|
|
1364
1364
|
});
|
|
1365
1365
|
|
|
1366
|
-
test("should NOT add image-id when src is
|
|
1366
|
+
test("should NOT add image-id when src is a prop without a literal default", () => {
|
|
1367
1367
|
const code = `
|
|
1368
1368
|
export default function App({ url }) {
|
|
1369
1369
|
return <img className="w-full" src={url} alt="" />;
|
|
@@ -1374,6 +1374,100 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1374
1374
|
expect(result).not.toContain("data-upstart-image-id");
|
|
1375
1375
|
});
|
|
1376
1376
|
|
|
1377
|
+
test("should add image-id when src is a literal inside braces", () => {
|
|
1378
|
+
const code = `
|
|
1379
|
+
export default function App() {
|
|
1380
|
+
return <img src={"/images/hero.webp"} alt="" />;
|
|
1381
|
+
}
|
|
1382
|
+
`;
|
|
1383
|
+
const result = transform(code);
|
|
1384
|
+
const imageId = result?.match(/data-upstart-image-id="([^"]+)"/)?.[1] as string;
|
|
1385
|
+
expect(imageId).toBeDefined();
|
|
1386
|
+
const entry = getRegistry()[imageId];
|
|
1387
|
+
expect(entry).toMatchObject({ type: "image", originalContent: "/images/hero.webp" });
|
|
1388
|
+
expect(code.slice(entry.startOffset, entry.endOffset)).toBe("/images/hero.webp");
|
|
1389
|
+
});
|
|
1390
|
+
|
|
1391
|
+
test("should resolve src={prop} to the prop's string default and guard it at runtime", () => {
|
|
1392
|
+
const code = `
|
|
1393
|
+
export default function HeroSection({
|
|
1394
|
+
heroImageSrc = "/images/home-hero.webp",
|
|
1395
|
+
}: HeroSectionProps) {
|
|
1396
|
+
return (
|
|
1397
|
+
<section>
|
|
1398
|
+
<img src={heroImageSrc} alt="Hero" className="w-full" />
|
|
1399
|
+
</section>
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
`;
|
|
1403
|
+
const result = transform(code);
|
|
1404
|
+
// The attribute is conditional: only present when the rendered value is the default.
|
|
1405
|
+
const m = result?.match(
|
|
1406
|
+
/data-upstart-image-id=\{heroImageSrc === "\/images\/home-hero\.webp" \? "([^"]+)" : undefined\}/,
|
|
1407
|
+
);
|
|
1408
|
+
expect(m).not.toBeNull();
|
|
1409
|
+
const imageId = m![1];
|
|
1410
|
+
const entry = getRegistry()[imageId];
|
|
1411
|
+
expect(entry).toMatchObject({ type: "image", originalContent: "/images/home-hero.webp" });
|
|
1412
|
+
// Offsets point at the default value in the function signature.
|
|
1413
|
+
expect(code.slice(entry.startOffset, entry.endOffset)).toBe("/images/home-hero.webp");
|
|
1414
|
+
expect(code.slice(entry.startOffset - 1, entry.startOffset)).toBe('"');
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
test("should resolve src={prop} defaults on arrow function components", () => {
|
|
1418
|
+
const code = `
|
|
1419
|
+
const Card = ({ image = "/images/card.webp" }) => <img src={image} alt="" />;
|
|
1420
|
+
`;
|
|
1421
|
+
const result = transform(code);
|
|
1422
|
+
expect(result).toMatch(
|
|
1423
|
+
/data-upstart-image-id=\{image === "\/images\/card\.webp" \? "test\.tsx:\d+" : undefined\}/,
|
|
1424
|
+
);
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
test("should not leak a param default from a sibling function", () => {
|
|
1428
|
+
const code = `
|
|
1429
|
+
function Other({ image = "/images/other.webp" }) { return <img src={image} alt="" />; }
|
|
1430
|
+
export default function App({ image }) {
|
|
1431
|
+
return <img src={image} alt="" />;
|
|
1432
|
+
}
|
|
1433
|
+
`;
|
|
1434
|
+
const result = transform(code);
|
|
1435
|
+
const matches = result?.match(/data-upstart-image-id=/g) ?? [];
|
|
1436
|
+
expect(matches).toHaveLength(1);
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
test("should resolve src={CONST} to a string constant", () => {
|
|
1440
|
+
const code = `
|
|
1441
|
+
const HERO = "/images/hero.webp";
|
|
1442
|
+
export default function App() {
|
|
1443
|
+
return <img src={HERO} alt="" />;
|
|
1444
|
+
}
|
|
1445
|
+
`;
|
|
1446
|
+
const result = transform(code);
|
|
1447
|
+
const m = result?.match(
|
|
1448
|
+
/data-upstart-image-id=\{HERO === "\/images\/hero\.webp" \? "([^"]+)" : undefined\}/,
|
|
1449
|
+
);
|
|
1450
|
+
expect(m).not.toBeNull();
|
|
1451
|
+
const entry = getRegistry()[m![1]];
|
|
1452
|
+
expect(code.slice(entry.startOffset, entry.endOffset)).toBe("/images/hero.webp");
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
test("editImage rewrites the prop default resolved from src={prop}", async () => {
|
|
1456
|
+
const code = `export default function Hero({ heroImageSrc = "/images/home-hero.webp" }) {
|
|
1457
|
+
return <img src={heroImageSrc} alt="" />;
|
|
1458
|
+
}
|
|
1459
|
+
`;
|
|
1460
|
+
const file = "app/components/Hero.tsx";
|
|
1461
|
+
const result = transform(code, file);
|
|
1462
|
+
const imageId = result?.match(/\? "([^"]+)" : undefined/)?.[1] as string;
|
|
1463
|
+
const { api, filePath } = await setupEditorApi(code, file);
|
|
1464
|
+
const res = await api.editImage({ action: "editImage", id: imageId, src: "/images/new-hero.webp" });
|
|
1465
|
+
expect(res.success).toBe(true);
|
|
1466
|
+
const updated = await fs.readFile(filePath, "utf-8");
|
|
1467
|
+
expect(updated).toContain('heroImageSrc = "/images/new-hero.webp"');
|
|
1468
|
+
expect(updated).not.toContain("home-hero");
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1377
1471
|
test("should NOT add image-id to non-img elements with a literal src", () => {
|
|
1378
1472
|
const code = `
|
|
1379
1473
|
export default function App() {
|
|
@@ -112,6 +112,68 @@ interface TransformState {
|
|
|
112
112
|
constants: Map<string, string>;
|
|
113
113
|
/** Maps t-function variable names to their default namespace, e.g. { "t": "dashboard" } */
|
|
114
114
|
tFunctions: Map<string, string>;
|
|
115
|
+
/** Same as `constants`, but keeps the string Literal AST node so its source range can be edited */
|
|
116
|
+
constantNodes: Map<string, StringLiteralNode>;
|
|
117
|
+
/**
|
|
118
|
+
* Stack of enclosing function scopes, innermost last. Each scope maps a destructured
|
|
119
|
+
* parameter name to the string Literal used as its default value, e.g.
|
|
120
|
+
* `function Hero({ heroImageSrc = "/images/hero.webp" })`.
|
|
121
|
+
*/
|
|
122
|
+
paramDefaults: Map<string, StringLiteralNode>[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type StringLiteralNode = NodeWithRange & { type: "Literal"; value: string };
|
|
126
|
+
|
|
127
|
+
function isStringLiteralNode(node: any): node is StringLiteralNode {
|
|
128
|
+
return !!node && node.type === "Literal" && typeof node.value === "string" && hasRange(node);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Collect `{ name = "literal" }` defaults from a function's parameters. Only object-pattern
|
|
133
|
+
* params with a plain string default are tracked — that is the common "props with default"
|
|
134
|
+
* shape emitted for site components.
|
|
135
|
+
*/
|
|
136
|
+
function collectParamStringDefaults(params: any[]): Map<string, StringLiteralNode> {
|
|
137
|
+
const defaults = new Map<string, StringLiteralNode>();
|
|
138
|
+
for (const param of params) {
|
|
139
|
+
// `({ a = "x" }: Props)` — TS annotations keep the ObjectPattern as the param itself.
|
|
140
|
+
const pattern = param?.type === "AssignmentPattern" ? param.left : param;
|
|
141
|
+
if (pattern?.type !== "ObjectPattern") continue;
|
|
142
|
+
for (const prop of pattern.properties) {
|
|
143
|
+
if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
|
|
144
|
+
const value = prop.value;
|
|
145
|
+
if (value?.type !== "AssignmentPattern" || value.left?.type !== "Identifier") continue;
|
|
146
|
+
if (isStringLiteralNode(value.right)) {
|
|
147
|
+
defaults.set(value.left.name, value.right);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return defaults;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the string Literal backing an `<img src>` attribute, so the editor can rewrite it.
|
|
156
|
+
* Handles `src="..."`, `src={"..."}`, `src={prop}` where `prop` has a string default in an
|
|
157
|
+
* enclosing function's parameters, and `src={CONST}` where `const CONST = "..."`.
|
|
158
|
+
* Returns the identifier name when the value was resolved through a variable, so the caller
|
|
159
|
+
* can guard the injected attribute at runtime (a parent may pass a different value).
|
|
160
|
+
*/
|
|
161
|
+
function resolveImageSrcLiteral(
|
|
162
|
+
attr: JSXAttribute,
|
|
163
|
+
state: TransformState,
|
|
164
|
+
): { literal: StringLiteralNode; via: string | null } | null {
|
|
165
|
+
const value = attr.value as any;
|
|
166
|
+
if (isStringLiteralNode(value)) return { literal: value, via: null };
|
|
167
|
+
if (value?.type !== "JSXExpressionContainer") return null;
|
|
168
|
+
const expr = value.expression;
|
|
169
|
+
if (isStringLiteralNode(expr)) return { literal: expr, via: null };
|
|
170
|
+
if (expr?.type !== "Identifier") return null;
|
|
171
|
+
for (let i = state.paramDefaults.length - 1; i >= 0; i--) {
|
|
172
|
+
const literal = state.paramDefaults[i].get(expr.name);
|
|
173
|
+
if (literal) return { literal, via: expr.name };
|
|
174
|
+
}
|
|
175
|
+
const constant = state.constantNodes.get(expr.name);
|
|
176
|
+
return constant ? { literal: constant, via: expr.name } : null;
|
|
115
177
|
}
|
|
116
178
|
|
|
117
179
|
export const upstartEditor = createUnplugin<Options>((options) => {
|
|
@@ -226,6 +288,8 @@ export function transformWithOxc(code: string, filePath: string) {
|
|
|
226
288
|
loopStack: [],
|
|
227
289
|
constants: new Map(),
|
|
228
290
|
tFunctions: new Map(),
|
|
291
|
+
constantNodes: new Map(),
|
|
292
|
+
paramDefaults: [],
|
|
229
293
|
};
|
|
230
294
|
|
|
231
295
|
let modified = false;
|
|
@@ -246,10 +310,27 @@ export function transformWithOxc(code: string, filePath: string) {
|
|
|
246
310
|
typeof decl.init.value === "string"
|
|
247
311
|
) {
|
|
248
312
|
state.constants.set(decl.id.name, decl.init.value);
|
|
313
|
+
if (isStringLiteralNode(decl.init)) {
|
|
314
|
+
state.constantNodes.set(decl.id.name, decl.init);
|
|
315
|
+
}
|
|
249
316
|
}
|
|
250
317
|
}
|
|
251
318
|
}
|
|
252
319
|
|
|
320
|
+
// Track destructured parameter defaults of the enclosing function
|
|
321
|
+
// (e.g. `function Hero({ heroImageSrc = "/images/hero.webp" })`) so `<img src={heroImageSrc}>`
|
|
322
|
+
// can be resolved to an editable literal.
|
|
323
|
+
if (
|
|
324
|
+
node.type === "FunctionDeclaration" ||
|
|
325
|
+
node.type === "FunctionExpression" ||
|
|
326
|
+
node.type === "ArrowFunctionExpression"
|
|
327
|
+
) {
|
|
328
|
+
state.paramDefaults.push(collectParamStringDefaults((node as any).params ?? []));
|
|
329
|
+
next();
|
|
330
|
+
state.paramDefaults.pop();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
253
334
|
// Handle .map() calls to track loops (must be before JSXElement)
|
|
254
335
|
if (node.type === "CallExpression") {
|
|
255
336
|
const mapCallInfo = detectAndPatchMapCall(node as CallExpression, state);
|
|
@@ -560,33 +641,41 @@ export function transformWithOxc(code: string, filePath: string) {
|
|
|
560
641
|
attributes.push(`data-upstart-classname-id="${id}"`);
|
|
561
642
|
}
|
|
562
643
|
|
|
563
|
-
// Track <img src
|
|
644
|
+
// Track <img src> when it resolves to a string literal → makes the image
|
|
564
645
|
// swappable from the editor (mirrors the className tracking above).
|
|
646
|
+
// See resolveImageSrcLiteral for the supported shapes.
|
|
565
647
|
if (tagName === "img") {
|
|
566
648
|
const srcAttr = opening.attributes.find(
|
|
567
649
|
(attr): attr is JSXAttribute =>
|
|
568
|
-
attr.type === "JSXAttribute" &&
|
|
569
|
-
attr.name.type === "JSXIdentifier" &&
|
|
570
|
-
attr.name.name === "src" &&
|
|
571
|
-
attr.value?.type === "Literal" &&
|
|
572
|
-
typeof (attr.value as any).value === "string",
|
|
650
|
+
attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "src",
|
|
573
651
|
);
|
|
652
|
+
const resolved = srcAttr ? resolveImageSrcLiteral(srcAttr, state) : null;
|
|
574
653
|
|
|
575
|
-
if (
|
|
576
|
-
const
|
|
577
|
-
const
|
|
654
|
+
if (resolved) {
|
|
655
|
+
const { literal, via } = resolved;
|
|
656
|
+
const id = generateId(state.filePath, literal);
|
|
578
657
|
|
|
579
658
|
// +1 / -1 to exclude the surrounding quotes, like className above
|
|
580
659
|
editableRegistry.set(id, {
|
|
581
660
|
file: state.filePath,
|
|
582
661
|
type: "image",
|
|
583
|
-
startOffset:
|
|
584
|
-
endOffset:
|
|
585
|
-
originalContent:
|
|
662
|
+
startOffset: literal.start + 1,
|
|
663
|
+
endOffset: literal.end - 1,
|
|
664
|
+
originalContent: literal.value,
|
|
586
665
|
context: { parentTag: tagName || "unknown" },
|
|
587
666
|
});
|
|
588
667
|
|
|
589
|
-
|
|
668
|
+
if (via) {
|
|
669
|
+
// The literal is a default/constant reached through a variable: a parent may
|
|
670
|
+
// pass another value, in which case editing the literal would not change what
|
|
671
|
+
// is rendered. Only mark the image editable when the rendered value is the one
|
|
672
|
+
// the editor would rewrite.
|
|
673
|
+
attributes.push(
|
|
674
|
+
`data-upstart-image-id={${via} === ${JSON.stringify(literal.value)} ? "${id}" : undefined}`,
|
|
675
|
+
);
|
|
676
|
+
} else {
|
|
677
|
+
attributes.push(`data-upstart-image-id="${id}"`);
|
|
678
|
+
}
|
|
590
679
|
}
|
|
591
680
|
}
|
|
592
681
|
|