@lingo.dev/compiler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/LICENSE.md +201 -0
  2. package/README.md +192 -0
  3. package/build/_virtual/rolldown_runtime.cjs +29 -0
  4. package/build/_virtual/rolldown_runtime.mjs +7 -0
  5. package/build/index.cjs +0 -0
  6. package/build/index.d.cts +2 -0
  7. package/build/index.d.mts +2 -0
  8. package/build/index.mjs +1 -0
  9. package/build/metadata/manager.cjs +131 -0
  10. package/build/metadata/manager.mjs +123 -0
  11. package/build/metadata/manager.mjs.map +1 -0
  12. package/build/plugin/build-translator.cjs +198 -0
  13. package/build/plugin/build-translator.mjs +196 -0
  14. package/build/plugin/build-translator.mjs.map +1 -0
  15. package/build/plugin/cleanup.cjs +20 -0
  16. package/build/plugin/cleanup.mjs +20 -0
  17. package/build/plugin/cleanup.mjs.map +1 -0
  18. package/build/plugin/next-compiler-loader.cjs +41 -0
  19. package/build/plugin/next-compiler-loader.d.cts +12 -0
  20. package/build/plugin/next-compiler-loader.d.cts.map +1 -0
  21. package/build/plugin/next-compiler-loader.d.mts +13 -0
  22. package/build/plugin/next-compiler-loader.d.mts.map +1 -0
  23. package/build/plugin/next-compiler-loader.mjs +42 -0
  24. package/build/plugin/next-compiler-loader.mjs.map +1 -0
  25. package/build/plugin/next-config-loader.cjs +13 -0
  26. package/build/plugin/next-config-loader.d.cts +8 -0
  27. package/build/plugin/next-config-loader.d.cts.map +1 -0
  28. package/build/plugin/next-config-loader.d.mts +9 -0
  29. package/build/plugin/next-config-loader.d.mts.map +1 -0
  30. package/build/plugin/next-config-loader.mjs +14 -0
  31. package/build/plugin/next-config-loader.mjs.map +1 -0
  32. package/build/plugin/next-locale-client-loader.cjs +9 -0
  33. package/build/plugin/next-locale-client-loader.d.cts +8 -0
  34. package/build/plugin/next-locale-client-loader.d.cts.map +1 -0
  35. package/build/plugin/next-locale-client-loader.d.mts +9 -0
  36. package/build/plugin/next-locale-client-loader.d.mts.map +1 -0
  37. package/build/plugin/next-locale-client-loader.mjs +10 -0
  38. package/build/plugin/next-locale-client-loader.mjs.map +1 -0
  39. package/build/plugin/next-locale-server-loader.cjs +9 -0
  40. package/build/plugin/next-locale-server-loader.d.cts +8 -0
  41. package/build/plugin/next-locale-server-loader.d.cts.map +1 -0
  42. package/build/plugin/next-locale-server-loader.d.mts +9 -0
  43. package/build/plugin/next-locale-server-loader.d.mts.map +1 -0
  44. package/build/plugin/next-locale-server-loader.mjs +10 -0
  45. package/build/plugin/next-locale-server-loader.mjs.map +1 -0
  46. package/build/plugin/next.cjs +220 -0
  47. package/build/plugin/next.d.cts +9 -0
  48. package/build/plugin/next.d.cts.map +1 -0
  49. package/build/plugin/next.d.mts +9 -0
  50. package/build/plugin/next.d.mts.map +1 -0
  51. package/build/plugin/next.mjs +222 -0
  52. package/build/plugin/next.mjs.map +1 -0
  53. package/build/plugin/transform/babel-compat.cjs +13 -0
  54. package/build/plugin/transform/babel-compat.mjs +10 -0
  55. package/build/plugin/transform/babel-compat.mjs.map +1 -0
  56. package/build/plugin/transform/index.cjs +44 -0
  57. package/build/plugin/transform/index.mjs +42 -0
  58. package/build/plugin/transform/index.mjs.map +1 -0
  59. package/build/plugin/transform/metadata.cjs +142 -0
  60. package/build/plugin/transform/metadata.mjs +141 -0
  61. package/build/plugin/transform/metadata.mjs.map +1 -0
  62. package/build/plugin/transform/parse-override.cjs +145 -0
  63. package/build/plugin/transform/parse-override.mjs +144 -0
  64. package/build/plugin/transform/parse-override.mjs.map +1 -0
  65. package/build/plugin/transform/process-file.cjs +391 -0
  66. package/build/plugin/transform/process-file.mjs +390 -0
  67. package/build/plugin/transform/process-file.mjs.map +1 -0
  68. package/build/plugin/transform/use-i18n.cjs +8 -0
  69. package/build/plugin/transform/use-i18n.mjs +7 -0
  70. package/build/plugin/transform/use-i18n.mjs.map +1 -0
  71. package/build/plugin/transform/utils.cjs +205 -0
  72. package/build/plugin/transform/utils.mjs +192 -0
  73. package/build/plugin/transform/utils.mjs.map +1 -0
  74. package/build/plugin/unplugin.cjs +188 -0
  75. package/build/plugin/unplugin.d.cts +8 -0
  76. package/build/plugin/unplugin.d.cts.map +1 -0
  77. package/build/plugin/unplugin.d.mts +8 -0
  78. package/build/plugin/unplugin.d.mts.map +1 -0
  79. package/build/plugin/unplugin.mjs +186 -0
  80. package/build/plugin/unplugin.mjs.map +1 -0
  81. package/build/plugin/vite.cjs +28 -0
  82. package/build/plugin/vite.d.cts +9 -0
  83. package/build/plugin/vite.d.cts.map +1 -0
  84. package/build/plugin/vite.d.mts +9 -0
  85. package/build/plugin/vite.d.mts.map +1 -0
  86. package/build/plugin/vite.mjs +29 -0
  87. package/build/plugin/vite.mjs.map +1 -0
  88. package/build/plugin/webpack.cjs +27 -0
  89. package/build/plugin/webpack.d.cts +8 -0
  90. package/build/plugin/webpack.d.cts.map +1 -0
  91. package/build/plugin/webpack.d.mts +8 -0
  92. package/build/plugin/webpack.d.mts.map +1 -0
  93. package/build/plugin/webpack.mjs +28 -0
  94. package/build/plugin/webpack.mjs.map +1 -0
  95. package/build/react/client/index.cjs +9 -0
  96. package/build/react/client/index.d.cts +5 -0
  97. package/build/react/client/index.d.mts +5 -0
  98. package/build/react/client/index.mjs +6 -0
  99. package/build/react/client/useTranslation.cjs +71 -0
  100. package/build/react/client/useTranslation.d.cts +42 -0
  101. package/build/react/client/useTranslation.d.cts.map +1 -0
  102. package/build/react/client/useTranslation.d.mts +42 -0
  103. package/build/react/client/useTranslation.d.mts.map +1 -0
  104. package/build/react/client/useTranslation.mjs +71 -0
  105. package/build/react/client/useTranslation.mjs.map +1 -0
  106. package/build/react/next/client.cjs +25 -0
  107. package/build/react/next/client.d.cts +9 -0
  108. package/build/react/next/client.d.cts.map +1 -0
  109. package/build/react/next/client.d.mts +9 -0
  110. package/build/react/next/client.d.mts.map +1 -0
  111. package/build/react/next/client.mjs +24 -0
  112. package/build/react/next/client.mjs.map +1 -0
  113. package/build/react/next/cookie-locale-resolver.cjs +29 -0
  114. package/build/react/next/cookie-locale-resolver.d.cts +33 -0
  115. package/build/react/next/cookie-locale-resolver.d.cts.map +1 -0
  116. package/build/react/next/cookie-locale-resolver.d.mts +33 -0
  117. package/build/react/next/cookie-locale-resolver.d.mts.map +1 -0
  118. package/build/react/next/cookie-locale-resolver.mjs +29 -0
  119. package/build/react/next/cookie-locale-resolver.mjs.map +1 -0
  120. package/build/react/next/server.cjs +21 -0
  121. package/build/react/next/server.d.cts +13 -0
  122. package/build/react/next/server.d.cts.map +1 -0
  123. package/build/react/next/server.d.mts +14 -0
  124. package/build/react/next/server.d.mts.map +1 -0
  125. package/build/react/next/server.mjs +20 -0
  126. package/build/react/next/server.mjs.map +1 -0
  127. package/build/react/server/ServerLingoProvider.cjs +19 -0
  128. package/build/react/server/ServerLingoProvider.d.cts +12 -0
  129. package/build/react/server/ServerLingoProvider.d.cts.map +1 -0
  130. package/build/react/server/ServerLingoProvider.d.mts +12 -0
  131. package/build/react/server/ServerLingoProvider.d.mts.map +1 -0
  132. package/build/react/server/ServerLingoProvider.mjs +19 -0
  133. package/build/react/server/ServerLingoProvider.mjs.map +1 -0
  134. package/build/react/server/index.cjs +7 -0
  135. package/build/react/server/index.d.cts +4 -0
  136. package/build/react/server/index.d.mts +4 -0
  137. package/build/react/server/index.mjs +5 -0
  138. package/build/react/server/useTranslation.cjs +60 -0
  139. package/build/react/server/useTranslation.d.cts +36 -0
  140. package/build/react/server/useTranslation.d.cts.map +1 -0
  141. package/build/react/server/useTranslation.d.mts +36 -0
  142. package/build/react/server/useTranslation.d.mts.map +1 -0
  143. package/build/react/server/useTranslation.mjs +60 -0
  144. package/build/react/server/useTranslation.mjs.map +1 -0
  145. package/build/react/server-only/index.cjs +42 -0
  146. package/build/react/server-only/index.d.cts +38 -0
  147. package/build/react/server-only/index.d.cts.map +1 -0
  148. package/build/react/server-only/index.d.mts +38 -0
  149. package/build/react/server-only/index.d.mts.map +1 -0
  150. package/build/react/server-only/index.mjs +42 -0
  151. package/build/react/server-only/index.mjs.map +1 -0
  152. package/build/react/server-only/translations.cjs +85 -0
  153. package/build/react/server-only/translations.mjs +85 -0
  154. package/build/react/server-only/translations.mjs.map +1 -0
  155. package/build/react/shared/LingoContext.cjs +14 -0
  156. package/build/react/shared/LingoContext.d.cts +41 -0
  157. package/build/react/shared/LingoContext.d.cts.map +1 -0
  158. package/build/react/shared/LingoContext.d.mts +41 -0
  159. package/build/react/shared/LingoContext.d.mts.map +1 -0
  160. package/build/react/shared/LingoContext.mjs +13 -0
  161. package/build/react/shared/LingoContext.mjs.map +1 -0
  162. package/build/react/shared/LingoProvider.cjs +274 -0
  163. package/build/react/shared/LingoProvider.d.cts +76 -0
  164. package/build/react/shared/LingoProvider.d.cts.map +1 -0
  165. package/build/react/shared/LingoProvider.d.mts +76 -0
  166. package/build/react/shared/LingoProvider.d.mts.map +1 -0
  167. package/build/react/shared/LingoProvider.mjs +274 -0
  168. package/build/react/shared/LingoProvider.mjs.map +1 -0
  169. package/build/react/shared/LocaleSwitcher.cjs +61 -0
  170. package/build/react/shared/LocaleSwitcher.d.cts +71 -0
  171. package/build/react/shared/LocaleSwitcher.d.cts.map +1 -0
  172. package/build/react/shared/LocaleSwitcher.d.mts +71 -0
  173. package/build/react/shared/LocaleSwitcher.d.mts.map +1 -0
  174. package/build/react/shared/LocaleSwitcher.mjs +61 -0
  175. package/build/react/shared/LocaleSwitcher.mjs.map +1 -0
  176. package/build/react/shared/render-rich-text.cjs +55 -0
  177. package/build/react/shared/render-rich-text.d.cts +17 -0
  178. package/build/react/shared/render-rich-text.d.cts.map +1 -0
  179. package/build/react/shared/render-rich-text.d.mts +17 -0
  180. package/build/react/shared/render-rich-text.d.mts.map +1 -0
  181. package/build/react/shared/render-rich-text.mjs +54 -0
  182. package/build/react/shared/render-rich-text.mjs.map +1 -0
  183. package/build/react/shared/utils.cjs +34 -0
  184. package/build/react/shared/utils.mjs +35 -0
  185. package/build/react/shared/utils.mjs.map +1 -0
  186. package/build/react/types.d.cts +16 -0
  187. package/build/react/types.d.cts.map +1 -0
  188. package/build/react/types.d.mts +16 -0
  189. package/build/react/types.d.mts.map +1 -0
  190. package/build/translation-server/logger.cjs +37 -0
  191. package/build/translation-server/logger.mjs +37 -0
  192. package/build/translation-server/logger.mjs.map +1 -0
  193. package/build/translation-server/translation-server.cjs +547 -0
  194. package/build/translation-server/translation-server.mjs +544 -0
  195. package/build/translation-server/translation-server.mjs.map +1 -0
  196. package/build/translation-server/ws-events.cjs +15 -0
  197. package/build/translation-server/ws-events.mjs +15 -0
  198. package/build/translation-server/ws-events.mjs.map +1 -0
  199. package/build/translators/api.cjs +12 -0
  200. package/build/translators/api.mjs +12 -0
  201. package/build/translators/api.mjs.map +1 -0
  202. package/build/translators/cache-factory.cjs +26 -0
  203. package/build/translators/cache-factory.mjs +27 -0
  204. package/build/translators/cache-factory.mjs.map +1 -0
  205. package/build/translators/lingo/model-factory.cjs +179 -0
  206. package/build/translators/lingo/model-factory.mjs +174 -0
  207. package/build/translators/lingo/model-factory.mjs.map +1 -0
  208. package/build/translators/lingo/prompt.cjs +43 -0
  209. package/build/translators/lingo/prompt.mjs +43 -0
  210. package/build/translators/lingo/prompt.mjs.map +1 -0
  211. package/build/translators/lingo/service.cjs +152 -0
  212. package/build/translators/lingo/service.mjs +152 -0
  213. package/build/translators/lingo/service.mjs.map +1 -0
  214. package/build/translators/lingo/shots.cjs +28 -0
  215. package/build/translators/lingo/shots.mjs +28 -0
  216. package/build/translators/lingo/shots.mjs.map +1 -0
  217. package/build/translators/local-cache.cjs +115 -0
  218. package/build/translators/local-cache.mjs +113 -0
  219. package/build/translators/local-cache.mjs.map +1 -0
  220. package/build/translators/parse-xml.cjs +109 -0
  221. package/build/translators/parse-xml.mjs +108 -0
  222. package/build/translators/parse-xml.mjs.map +1 -0
  223. package/build/translators/pluralization/icu-validator.cjs +36 -0
  224. package/build/translators/pluralization/icu-validator.mjs +36 -0
  225. package/build/translators/pluralization/icu-validator.mjs.map +1 -0
  226. package/build/translators/pluralization/pattern-detector.cjs +25 -0
  227. package/build/translators/pluralization/pattern-detector.mjs +25 -0
  228. package/build/translators/pluralization/pattern-detector.mjs.map +1 -0
  229. package/build/translators/pluralization/prompt.cjs +98 -0
  230. package/build/translators/pluralization/prompt.mjs +98 -0
  231. package/build/translators/pluralization/prompt.mjs.map +1 -0
  232. package/build/translators/pluralization/service.cjs +247 -0
  233. package/build/translators/pluralization/service.mjs +247 -0
  234. package/build/translators/pluralization/service.mjs.map +1 -0
  235. package/build/translators/pluralization/shots.cjs +53 -0
  236. package/build/translators/pluralization/shots.mjs +53 -0
  237. package/build/translators/pluralization/shots.mjs.map +1 -0
  238. package/build/translators/pluralization/types.d.cts +17 -0
  239. package/build/translators/pluralization/types.d.cts.map +1 -0
  240. package/build/translators/pluralization/types.d.mts +17 -0
  241. package/build/translators/pluralization/types.d.mts.map +1 -0
  242. package/build/translators/pseudotranslator/index.cjs +129 -0
  243. package/build/translators/pseudotranslator/index.mjs +129 -0
  244. package/build/translators/pseudotranslator/index.mjs.map +1 -0
  245. package/build/translators/translation-service.cjs +182 -0
  246. package/build/translators/translation-service.mjs +183 -0
  247. package/build/translators/translation-service.mjs.map +1 -0
  248. package/build/translators/translator-factory.cjs +49 -0
  249. package/build/translators/translator-factory.mjs +50 -0
  250. package/build/translators/translator-factory.mjs.map +1 -0
  251. package/build/types.d.cts +161 -0
  252. package/build/types.d.cts.map +1 -0
  253. package/build/types.d.mts +161 -0
  254. package/build/types.d.mts.map +1 -0
  255. package/build/utils/config-factory.cjs +58 -0
  256. package/build/utils/config-factory.mjs +58 -0
  257. package/build/utils/config-factory.mjs.map +1 -0
  258. package/build/utils/hash.cjs +17 -0
  259. package/build/utils/hash.mjs +16 -0
  260. package/build/utils/hash.mjs.map +1 -0
  261. package/build/utils/is-valid-locale.cjs +14 -0
  262. package/build/utils/is-valid-locale.mjs +14 -0
  263. package/build/utils/is-valid-locale.mjs.map +1 -0
  264. package/build/utils/logger.cjs +51 -0
  265. package/build/utils/logger.mjs +50 -0
  266. package/build/utils/logger.mjs.map +1 -0
  267. package/build/utils/path-helpers.cjs +49 -0
  268. package/build/utils/path-helpers.mjs +47 -0
  269. package/build/utils/path-helpers.mjs.map +1 -0
  270. package/build/utils/timeout.cjs +42 -0
  271. package/build/utils/timeout.mjs +41 -0
  272. package/build/utils/timeout.mjs.map +1 -0
  273. package/build/virtual/code-generator.cjs +54 -0
  274. package/build/virtual/code-generator.mjs +53 -0
  275. package/build/virtual/code-generator.mjs.map +1 -0
  276. package/build/virtual/config.cjs +10 -0
  277. package/build/virtual/config.d.cts +9 -0
  278. package/build/virtual/config.d.cts.map +1 -0
  279. package/build/virtual/config.d.mts +9 -0
  280. package/build/virtual/config.d.mts.map +1 -0
  281. package/build/virtual/config.mjs +8 -0
  282. package/build/virtual/config.mjs.map +1 -0
  283. package/build/virtual/locale/client.cjs +23 -0
  284. package/build/virtual/locale/client.d.cts +19 -0
  285. package/build/virtual/locale/client.d.cts.map +1 -0
  286. package/build/virtual/locale/client.d.mts +19 -0
  287. package/build/virtual/locale/client.d.mts.map +1 -0
  288. package/build/virtual/locale/client.mjs +22 -0
  289. package/build/virtual/locale/client.mjs.map +1 -0
  290. package/build/virtual/locale/server.cjs +13 -0
  291. package/build/virtual/locale/server.d.cts +13 -0
  292. package/build/virtual/locale/server.d.cts.map +1 -0
  293. package/build/virtual/locale/server.d.mts +13 -0
  294. package/build/virtual/locale/server.d.mts.map +1 -0
  295. package/build/virtual/locale/server.mjs +13 -0
  296. package/build/virtual/locale/server.mjs.map +1 -0
  297. package/build/widget/lingo-dev-widget.cjs +228 -0
  298. package/build/widget/lingo-dev-widget.mjs +229 -0
  299. package/build/widget/lingo-dev-widget.mjs.map +1 -0
  300. package/package.json +189 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.mjs","names":["keyName: string | null","entries: TranslationEntry[]"],"sources":["../../../src/plugin/transform/metadata.ts"],"sourcesContent":["/**\n * Transforms Next.js metadata object to use t() calls. See https://nextjs.org/docs/app/getting-started/metadata-and-og-images for docs on next metadata.\n */\nimport * as t from \"@babel/types\";\nimport {\n isObjectExpression,\n isReturnStatement,\n type VariableDeclarator,\n} from \"@babel/types\";\nimport {\n constructServerTranslationHookCall,\n createTranslationEntry,\n} from \"./utils\";\nimport type { NodePath } from \"@babel/traverse\";\nimport type { TranslationEntry } from \"../../types\";\nimport type { VisitorsInternalState } from \"./process-file\";\n\n/**\n * Whitelist of metadata fields that should be translated.\n * Other fields (like technical configurations, URLs, etc.) are left unchanged.\n */\nconst TRANSLATABLE_METADATA_FIELDS = new Set([\n // Top-level fields\n \"title\",\n \"description\",\n\n // Title object fields (template and default)\n \"title.template\",\n \"title.default\",\n\n // OpenGraph fields\n \"openGraph.title\",\n \"openGraph.description\",\n \"openGraph.images[*].alt\",\n\n // Twitter fields\n \"twitter.title\",\n \"twitter.description\",\n \"twitter.images[*].alt\",\n\n // Apple Web App\n \"appleWebApp.title\",\n]);\n\nconst NEXT_METADATA_EXPORT_NAME = \"metadata\";\nconst NEXT_GENERATE_METADATA_FUNCTION_NAME = \"generateMetadata\";\n\ntype MetadataInputState = Pick<VisitorsInternalState, \"filePath\">;\nexport type MetadataOutputState = Pick<\n VisitorsInternalState,\n \"newEntries\" | \"needsAsyncImport\"\n>;\n\n/**\n * Check if a field path matches a pattern with array indices.\n * For example: \"openGraph.images[0].alt\" matches \"openGraph.images[*].alt\"\n */\nfunction matchesArrayPattern(fieldPath: string, pattern: string): boolean {\n // Replace [number] with [*] for comparison\n const normalizedPath = fieldPath.replace(/\\[\\d+]/g, \"[*]\");\n return normalizedPath === pattern;\n}\n\n/**\n * Check if a metadata field path should be translated\n */\nfunction isTranslatableMetadataField(fieldPath: string): boolean {\n // Direct match\n if (TRANSLATABLE_METADATA_FIELDS.has(fieldPath)) {\n return true;\n }\n\n // Check for array pattern matches (e.g., images[0].alt matches images[*].alt)\n for (const pattern of TRANSLATABLE_METADATA_FIELDS) {\n if (pattern.includes(\"[*]\") && matchesArrayPattern(fieldPath, pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction isNextGenerateMetadataFunction(path: NodePath<t.FunctionDeclaration>) {\n if (path.node.id?.name === NEXT_GENERATE_METADATA_FUNCTION_NAME) {\n // Check if it's exported\n const parent = path.parent;\n if (parent.type === \"ExportNamedDeclaration\" || parent.type === \"Program\") {\n return true;\n }\n }\n}\n\nfunction getNextMetadataObjectOrNull(\n path: NodePath<t.ExportNamedDeclaration>,\n): VariableDeclarator[\"init\"] | null {\n const declaration = path.node.declaration;\n if (!declaration || declaration.type !== \"VariableDeclaration\") return null;\n\n const declarator = declaration.declarations[0];\n if (!declarator || declarator.id.type !== \"Identifier\") return null;\n if (declarator.id.name === NEXT_METADATA_EXPORT_NAME && declarator.init) {\n return declarator.init;\n }\n}\n\n/**\n * Recursively transforms metadata object properties to use t() calls\n */\nfunction transformMetadataObject(\n objectExpression: t.ObjectExpression,\n state: MetadataInputState,\n entries: TranslationEntry[],\n parentPath: string = \"\",\n): void {\n for (const prop of objectExpression.properties) {\n if (prop.type !== \"ObjectProperty\") continue;\n\n // Get the key name\n let keyName: string | null = null;\n if (prop.key.type === \"Identifier\") {\n keyName = prop.key.name;\n } else if (prop.key.type === \"StringLiteral\") {\n keyName = prop.key.value;\n }\n\n if (!keyName) continue;\n\n const fieldPath = parentPath ? `${parentPath}.${keyName}` : keyName;\n\n // Check if this field should be translated\n const shouldTranslate = isTranslatableMetadataField(fieldPath);\n // Transform string values (only if whitelisted)\n if (prop.value.type === \"StringLiteral\" && shouldTranslate) {\n const text = prop.value.value;\n const entry = createTranslationEntry(\n \"metadata\",\n text,\n {\n fieldPath: fieldPath,\n },\n state.filePath,\n prop.value.loc?.start.line,\n prop.value.loc?.start.column,\n );\n entries.push(entry);\n\n // Replace with t(hash, fallback) call\n prop.value = t.callExpression(t.identifier(\"t\"), [\n t.stringLiteral(entry.hash),\n t.stringLiteral(text),\n ]);\n // Only static template literals like `content`\n } else if (prop.value.type === \"TemplateLiteral\" && shouldTranslate) {\n if (\n prop.value.expressions.length === 0 &&\n prop.value.quasis.length === 1\n ) {\n const staticValue = prop.value.quasis[0].value.cooked;\n if (staticValue) {\n const entry = createTranslationEntry(\n \"metadata\",\n staticValue,\n {\n fieldPath: fieldPath,\n },\n state.filePath,\n prop.value.loc?.start.line,\n prop.value.loc?.start.column,\n );\n entries.push(entry);\n\n // Replace with t(hash, fallback) call\n prop.value = t.callExpression(t.identifier(\"t\"), [\n t.stringLiteral(entry.hash),\n t.stringLiteral(staticValue),\n ]);\n }\n }\n }\n // Transform nested objects (always recurse to check nested fields)\n else if (prop.value.type === \"ObjectExpression\") {\n transformMetadataObject(prop.value, state, entries, fieldPath);\n }\n // Handle arrays (e.g., images: [{url: '...', alt: '...'}])\n else if (prop.value.type === \"ArrayExpression\") {\n prop.value.elements.forEach((element, index) => {\n if (element && element.type === \"ObjectExpression\") {\n transformMetadataObject(\n element,\n state,\n entries,\n `${fieldPath}[${index}]`,\n );\n }\n });\n }\n }\n}\n\n/**\n * Transform existing generateMetadata function to add translations\n */\nfunction transformMetadataFunction(\n path: NodePath<t.FunctionDeclaration>,\n state: MetadataInputState,\n): MetadataOutputState | null {\n const body = path.get(\"body\");\n if (!body.isBlockStatement()) return null;\n\n // Find the return statement with metadata object\n let metadataReturn = undefined as NodePath<t.ReturnStatement> | undefined;\n body.traverse({\n ReturnStatement(returnPath) {\n if (!metadataReturn) {\n metadataReturn = returnPath;\n }\n },\n });\n\n if (!isReturnStatement(metadataReturn) || !metadataReturn.node.argument)\n return null;\n if (!isObjectExpression(metadataReturn.node.argument)) return null;\n\n const entries: TranslationEntry[] = [];\n const metadataObject = metadataReturn.node.argument;\n transformMetadataObject(metadataObject, state, entries);\n\n if (entries.length === 0) {\n return null;\n }\n\n // Server translations require function to be async.\n if (!path.node.async) {\n path.node.async = true;\n }\n\n const serverCall = constructServerTranslationHookCall({\n hashes: entries.map((e) => e.hash),\n });\n body.node.body.unshift(serverCall);\n\n return {\n needsAsyncImport: true,\n newEntries: entries,\n };\n}\n\n/**\n * Process static metadata export\n * Only converts to function if there are translatable strings\n */\nexport function processNextStaticMetadata(\n path: NodePath<t.ExportNamedDeclaration>,\n state: MetadataInputState,\n): MetadataOutputState | null {\n // Get the metadata object\n const metadataObject = getNextMetadataObjectOrNull(path);\n if (!metadataObject || metadataObject.type !== \"ObjectExpression\")\n return null;\n\n // Clone and try transforming (transformMetadataObject modifies in place)\n const clonedObject = t.cloneNode(metadataObject, true);\n const entries: TranslationEntry[] = [];\n transformMetadataObject(clonedObject, state, entries);\n\n // If nothing to translate, leave as-is\n if (entries.length === 0) {\n return null;\n }\n\n // Create async generateMetadata function with transformed object\n const serverCall = constructServerTranslationHookCall({\n hashes: entries.map((e) => e.hash),\n });\n\n const generateMetadataFunction = t.functionDeclaration(\n t.identifier(NEXT_GENERATE_METADATA_FUNCTION_NAME),\n [],\n t.blockStatement([serverCall, t.returnStatement(clonedObject)]),\n false,\n true, // async - required for server translations\n );\n\n // Replace static export with function export\n path.replaceWith(t.exportNamedDeclaration(generateMetadataFunction, []));\n path.skip();\n\n return {\n needsAsyncImport: true,\n newEntries: entries,\n };\n}\n\nexport function processNextDynamicMetadata(\n path: NodePath<t.FunctionDeclaration>,\n state: MetadataInputState,\n): MetadataOutputState | null {\n if (!isNextGenerateMetadataFunction(path)) return null;\n const newState = transformMetadataFunction(path, state);\n path.skip();\n return newState;\n}\n"],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
2
+ const require_logger = require('../../utils/logger.cjs');
3
+ let _babel_types = require("@babel/types");
4
+ _babel_types = require_rolldown_runtime.__toESM(_babel_types);
5
+
6
+ //#region src/plugin/transform/parse-override.ts
7
+ const OVERRIDE_ATTRIBUTE = "data-lingo-override";
8
+ /**
9
+ * Parse the data-lingo-override attribute value
10
+ * Supports object expression: data-lingo-override={{ de: "text", fr: "text" }}
11
+ *
12
+ * @param attr - The JSX attribute to parse
13
+ * @returns Parsed overrides object or null if invalid
14
+ */
15
+ function parseOverrideAttribute(attr) {
16
+ if (!attr.value) {
17
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} attribute has no value`);
18
+ return null;
19
+ }
20
+ try {
21
+ if (_babel_types.isJSXExpressionContainer(attr.value)) {
22
+ const result = parseObjectExpression(attr.value.expression);
23
+ require_logger.logger.debug(`[PARSE_OVERRIDE] Parsed JSXExpressionContainer:`, result);
24
+ return result;
25
+ }
26
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} must be an object expression`);
27
+ return null;
28
+ } catch (error) {
29
+ require_logger.logger.warn(`Failed to parse ${OVERRIDE_ATTRIBUTE}:`, error);
30
+ return null;
31
+ }
32
+ }
33
+ /**
34
+ * Parse object expression from JSX: {{ de: "text", fr: "text" }}
35
+ */
36
+ function parseObjectExpression(expression) {
37
+ if (_babel_types.isJSXEmptyExpression(expression)) {
38
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} cannot be empty`);
39
+ return null;
40
+ }
41
+ if (!_babel_types.isObjectExpression(expression)) {
42
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} must be an object expression, got: ${expression.type}`);
43
+ return null;
44
+ }
45
+ const overrides = {};
46
+ for (const prop of expression.properties) {
47
+ if (_babel_types.isSpreadElement(prop)) {
48
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} does not support spread properties`);
49
+ continue;
50
+ }
51
+ if (!_babel_types.isObjectProperty(prop)) {
52
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} only supports simple properties`);
53
+ continue;
54
+ }
55
+ let key;
56
+ if (_babel_types.isIdentifier(prop.key)) key = prop.key.name;
57
+ else if (_babel_types.isStringLiteral(prop.key)) key = prop.key.value;
58
+ else {
59
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} keys must be identifiers or strings, got: ${prop.key.type}`);
60
+ continue;
61
+ }
62
+ let value;
63
+ if (_babel_types.isStringLiteral(prop.value)) {
64
+ value = prop.value.value;
65
+ require_logger.logger.debug(`[PARSE_OVERRIDE] Parsed string literal for ${key}: "${value}"`);
66
+ } else if (_babel_types.isTemplateLiteral(prop.value) && prop.value.quasis.length === 1 && prop.value.expressions.length === 0) {
67
+ value = prop.value.quasis[0].value.cooked || "";
68
+ require_logger.logger.debug(`[PARSE_OVERRIDE] Parsed template literal for ${key}: "${value}"`);
69
+ } else {
70
+ require_logger.logger.warn(`${OVERRIDE_ATTRIBUTE} value for "${key}" must be a string literal, got: ${prop.value.type}`);
71
+ continue;
72
+ }
73
+ overrides[key] = value;
74
+ }
75
+ require_logger.logger.debug(`[PARSE_OVERRIDE] Final overrides object:`, overrides);
76
+ return Object.keys(overrides).length > 0 ? overrides : null;
77
+ }
78
+ /**
79
+ * Validate override object
80
+ * Checks:
81
+ * - Is a plain object
82
+ * - All keys are valid locale codes (basic check)
83
+ * - All values are non-empty strings
84
+ *
85
+ * @param overrides - The overrides to validate
86
+ * @returns true if valid, false otherwise
87
+ */
88
+ function validateOverrides(overrides) {
89
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
90
+ require_logger.logger.warn("Overrides must be a plain object");
91
+ return false;
92
+ }
93
+ const entries = Object.entries(overrides);
94
+ if (entries.length === 0) {
95
+ require_logger.logger.warn("Overrides object is empty");
96
+ return false;
97
+ }
98
+ for (const [locale, text] of entries) {
99
+ if (!/^[a-z]{2}(-[A-Z]{2})?$/.test(locale)) {
100
+ require_logger.logger.warn(`Invalid locale code "${locale}" in overrides. Expected format: "en" or "en-US"`);
101
+ return false;
102
+ }
103
+ if (typeof text !== "string" || text.trim().length === 0) {
104
+ require_logger.logger.warn(`Override for locale "${locale}" must be a non-empty string`);
105
+ return false;
106
+ }
107
+ }
108
+ return true;
109
+ }
110
+ /**
111
+ * Find the ${OVERRIDE_ATTRIBUTE} attribute in a JSX element
112
+ *
113
+ * @param attributes - Array of JSX attributes
114
+ * @returns The override attribute or undefined
115
+ */
116
+ function findOverrideAttribute(attributes) {
117
+ return attributes.find((attr) => _babel_types.isJSXAttribute(attr) && _babel_types.isJSXIdentifier(attr.name) && attr.name.name === OVERRIDE_ATTRIBUTE);
118
+ }
119
+ /**
120
+ * Remove the ${OVERRIDE_ATTRIBUTE} attribute from a JSX element
121
+ * (We don't want this to appear in the final transformed output)
122
+ *
123
+ * @param attributes - Array of JSX attributes
124
+ * @returns Array with override attribute removed
125
+ */
126
+ function removeOverrideAttribute(attributes) {
127
+ return attributes.filter((attr) => !(_babel_types.isJSXAttribute(attr) && _babel_types.isJSXIdentifier(attr.name) && attr.name.name === `${OVERRIDE_ATTRIBUTE}`));
128
+ }
129
+ function processOverrideAttributes(path) {
130
+ let overrides;
131
+ if (path.node.type === "JSXElement") {
132
+ const overrideAttr = findOverrideAttribute(path.node.openingElement.attributes);
133
+ if (overrideAttr) {
134
+ const parsedOverrides = parseOverrideAttribute(overrideAttr);
135
+ if (parsedOverrides && validateOverrides(parsedOverrides)) {
136
+ overrides = parsedOverrides;
137
+ path.node.openingElement.attributes = removeOverrideAttribute(path.node.openingElement.attributes);
138
+ }
139
+ }
140
+ }
141
+ return overrides;
142
+ }
143
+
144
+ //#endregion
145
+ exports.processOverrideAttributes = processOverrideAttributes;
@@ -0,0 +1,144 @@
1
+ import { logger } from "../../utils/logger.mjs";
2
+ import * as t from "@babel/types";
3
+
4
+ //#region src/plugin/transform/parse-override.ts
5
+ const OVERRIDE_ATTRIBUTE = "data-lingo-override";
6
+ /**
7
+ * Parse the data-lingo-override attribute value
8
+ * Supports object expression: data-lingo-override={{ de: "text", fr: "text" }}
9
+ *
10
+ * @param attr - The JSX attribute to parse
11
+ * @returns Parsed overrides object or null if invalid
12
+ */
13
+ function parseOverrideAttribute(attr) {
14
+ if (!attr.value) {
15
+ logger.warn(`${OVERRIDE_ATTRIBUTE} attribute has no value`);
16
+ return null;
17
+ }
18
+ try {
19
+ if (t.isJSXExpressionContainer(attr.value)) {
20
+ const result = parseObjectExpression(attr.value.expression);
21
+ logger.debug(`[PARSE_OVERRIDE] Parsed JSXExpressionContainer:`, result);
22
+ return result;
23
+ }
24
+ logger.warn(`${OVERRIDE_ATTRIBUTE} must be an object expression`);
25
+ return null;
26
+ } catch (error) {
27
+ logger.warn(`Failed to parse ${OVERRIDE_ATTRIBUTE}:`, error);
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * Parse object expression from JSX: {{ de: "text", fr: "text" }}
33
+ */
34
+ function parseObjectExpression(expression) {
35
+ if (t.isJSXEmptyExpression(expression)) {
36
+ logger.warn(`${OVERRIDE_ATTRIBUTE} cannot be empty`);
37
+ return null;
38
+ }
39
+ if (!t.isObjectExpression(expression)) {
40
+ logger.warn(`${OVERRIDE_ATTRIBUTE} must be an object expression, got: ${expression.type}`);
41
+ return null;
42
+ }
43
+ const overrides = {};
44
+ for (const prop of expression.properties) {
45
+ if (t.isSpreadElement(prop)) {
46
+ logger.warn(`${OVERRIDE_ATTRIBUTE} does not support spread properties`);
47
+ continue;
48
+ }
49
+ if (!t.isObjectProperty(prop)) {
50
+ logger.warn(`${OVERRIDE_ATTRIBUTE} only supports simple properties`);
51
+ continue;
52
+ }
53
+ let key;
54
+ if (t.isIdentifier(prop.key)) key = prop.key.name;
55
+ else if (t.isStringLiteral(prop.key)) key = prop.key.value;
56
+ else {
57
+ logger.warn(`${OVERRIDE_ATTRIBUTE} keys must be identifiers or strings, got: ${prop.key.type}`);
58
+ continue;
59
+ }
60
+ let value;
61
+ if (t.isStringLiteral(prop.value)) {
62
+ value = prop.value.value;
63
+ logger.debug(`[PARSE_OVERRIDE] Parsed string literal for ${key}: "${value}"`);
64
+ } else if (t.isTemplateLiteral(prop.value) && prop.value.quasis.length === 1 && prop.value.expressions.length === 0) {
65
+ value = prop.value.quasis[0].value.cooked || "";
66
+ logger.debug(`[PARSE_OVERRIDE] Parsed template literal for ${key}: "${value}"`);
67
+ } else {
68
+ logger.warn(`${OVERRIDE_ATTRIBUTE} value for "${key}" must be a string literal, got: ${prop.value.type}`);
69
+ continue;
70
+ }
71
+ overrides[key] = value;
72
+ }
73
+ logger.debug(`[PARSE_OVERRIDE] Final overrides object:`, overrides);
74
+ return Object.keys(overrides).length > 0 ? overrides : null;
75
+ }
76
+ /**
77
+ * Validate override object
78
+ * Checks:
79
+ * - Is a plain object
80
+ * - All keys are valid locale codes (basic check)
81
+ * - All values are non-empty strings
82
+ *
83
+ * @param overrides - The overrides to validate
84
+ * @returns true if valid, false otherwise
85
+ */
86
+ function validateOverrides(overrides) {
87
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
88
+ logger.warn("Overrides must be a plain object");
89
+ return false;
90
+ }
91
+ const entries = Object.entries(overrides);
92
+ if (entries.length === 0) {
93
+ logger.warn("Overrides object is empty");
94
+ return false;
95
+ }
96
+ for (const [locale, text] of entries) {
97
+ if (!/^[a-z]{2}(-[A-Z]{2})?$/.test(locale)) {
98
+ logger.warn(`Invalid locale code "${locale}" in overrides. Expected format: "en" or "en-US"`);
99
+ return false;
100
+ }
101
+ if (typeof text !== "string" || text.trim().length === 0) {
102
+ logger.warn(`Override for locale "${locale}" must be a non-empty string`);
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ /**
109
+ * Find the ${OVERRIDE_ATTRIBUTE} attribute in a JSX element
110
+ *
111
+ * @param attributes - Array of JSX attributes
112
+ * @returns The override attribute or undefined
113
+ */
114
+ function findOverrideAttribute(attributes) {
115
+ return attributes.find((attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === OVERRIDE_ATTRIBUTE);
116
+ }
117
+ /**
118
+ * Remove the ${OVERRIDE_ATTRIBUTE} attribute from a JSX element
119
+ * (We don't want this to appear in the final transformed output)
120
+ *
121
+ * @param attributes - Array of JSX attributes
122
+ * @returns Array with override attribute removed
123
+ */
124
+ function removeOverrideAttribute(attributes) {
125
+ return attributes.filter((attr) => !(t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === `${OVERRIDE_ATTRIBUTE}`));
126
+ }
127
+ function processOverrideAttributes(path) {
128
+ let overrides;
129
+ if (path.node.type === "JSXElement") {
130
+ const overrideAttr = findOverrideAttribute(path.node.openingElement.attributes);
131
+ if (overrideAttr) {
132
+ const parsedOverrides = parseOverrideAttribute(overrideAttr);
133
+ if (parsedOverrides && validateOverrides(parsedOverrides)) {
134
+ overrides = parsedOverrides;
135
+ path.node.openingElement.attributes = removeOverrideAttribute(path.node.openingElement.attributes);
136
+ }
137
+ }
138
+ }
139
+ return overrides;
140
+ }
141
+
142
+ //#endregion
143
+ export { processOverrideAttributes };
144
+ //# sourceMappingURL=parse-override.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-override.mjs","names":["overrides: Record<string, string>","key: string","value: string","overrides: Record<string, string> | undefined"],"sources":["../../../src/plugin/transform/parse-override.ts"],"sourcesContent":["/**\n * Utilities for parsing and validating translation override attributes\n */\n\nimport type { JSXAttribute } from \"@babel/types\";\nimport * as t from \"@babel/types\";\nimport { logger } from \"../../utils/logger.js\";\nimport type { NodePath } from \"@babel/traverse\";\n\nconst OVERRIDE_ATTRIBUTE = \"data-lingo-override\";\n\n/**\n * Parse the data-lingo-override attribute value\n * Supports object expression: data-lingo-override={{ de: \"text\", fr: \"text\" }}\n *\n * @param attr - The JSX attribute to parse\n * @returns Parsed overrides object or null if invalid\n */\nexport function parseOverrideAttribute(\n attr: JSXAttribute,\n): Record<string, string> | null {\n if (!attr.value) {\n logger.warn(`${OVERRIDE_ATTRIBUTE} attribute has no value`);\n return null;\n }\n\n try {\n // Handle JSXExpressionContainer: {{ de: \"...\", fr: \"...\" }}\n if (t.isJSXExpressionContainer(attr.value)) {\n const result = parseObjectExpression(attr.value.expression);\n logger.debug(`[PARSE_OVERRIDE] Parsed JSXExpressionContainer:`, result);\n return result;\n }\n\n logger.warn(`${OVERRIDE_ATTRIBUTE} must be an object expression`);\n return null;\n } catch (error) {\n logger.warn(`Failed to parse ${OVERRIDE_ATTRIBUTE}:`, error);\n return null;\n }\n}\n\n/**\n * Parse object expression from JSX: {{ de: \"text\", fr: \"text\" }}\n */\nfunction parseObjectExpression(\n expression: t.Expression | t.JSXEmptyExpression,\n): Record<string, string> | null {\n if (t.isJSXEmptyExpression(expression)) {\n logger.warn(`${OVERRIDE_ATTRIBUTE} cannot be empty`);\n return null;\n }\n\n if (!t.isObjectExpression(expression)) {\n logger.warn(\n `${OVERRIDE_ATTRIBUTE} must be an object expression, got: ${expression.type}`,\n );\n return null;\n }\n\n const overrides: Record<string, string> = {};\n\n for (const prop of expression.properties) {\n // Skip spread properties\n if (t.isSpreadElement(prop)) {\n logger.warn(`${OVERRIDE_ATTRIBUTE} does not support spread properties`);\n continue;\n }\n\n // Only support ObjectProperty (not methods or getters/setters)\n if (!t.isObjectProperty(prop)) {\n logger.warn(`${OVERRIDE_ATTRIBUTE} only supports simple properties`);\n continue;\n }\n\n // Get the key (locale code)\n let key: string;\n if (t.isIdentifier(prop.key)) {\n key = prop.key.name;\n } else if (t.isStringLiteral(prop.key)) {\n key = prop.key.value;\n } else {\n logger.warn(\n `${OVERRIDE_ATTRIBUTE} keys must be identifiers or strings, got: ${prop.key.type}`,\n );\n continue;\n }\n\n // Get the value (translated text)\n let value: string;\n if (t.isStringLiteral(prop.value)) {\n value = prop.value.value;\n logger.debug(\n `[PARSE_OVERRIDE] Parsed string literal for ${key}: \"${value}\"`,\n );\n } else if (\n t.isTemplateLiteral(prop.value) &&\n prop.value.quasis.length === 1 &&\n prop.value.expressions.length === 0\n ) {\n // Support template literals with no expressions: `text`\n value = prop.value.quasis[0].value.cooked || \"\";\n logger.debug(\n `[PARSE_OVERRIDE] Parsed template literal for ${key}: \"${value}\"`,\n );\n } else {\n logger.warn(\n `${OVERRIDE_ATTRIBUTE} value for \"${key}\" must be a string literal, got: ${prop.value.type}`,\n );\n continue;\n }\n\n overrides[key] = value;\n }\n\n logger.debug(`[PARSE_OVERRIDE] Final overrides object:`, overrides);\n return Object.keys(overrides).length > 0 ? overrides : null;\n}\n\n/**\n * Validate override object\n * Checks:\n * - Is a plain object\n * - All keys are valid locale codes (basic check)\n * - All values are non-empty strings\n *\n * @param overrides - The overrides to validate\n * @returns true if valid, false otherwise\n */\nexport function validateOverrides(overrides: Record<string, string>): boolean {\n if (\n typeof overrides !== \"object\" ||\n overrides === null ||\n Array.isArray(overrides)\n ) {\n logger.warn(\"Overrides must be a plain object\");\n return false;\n }\n\n const entries = Object.entries(overrides);\n if (entries.length === 0) {\n logger.warn(\"Overrides object is empty\");\n return false;\n }\n\n for (const [locale, text] of entries) {\n // Basic locale code validation (2-letter code or with region)\n if (!/^[a-z]{2}(-[A-Z]{2})?$/.test(locale)) {\n logger.warn(\n `Invalid locale code \"${locale}\" in overrides. Expected format: \"en\" or \"en-US\"`,\n );\n return false;\n }\n\n // Check value is non-empty string\n if (typeof text !== \"string\" || text.trim().length === 0) {\n logger.warn(`Override for locale \"${locale}\" must be a non-empty string`);\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Find the ${OVERRIDE_ATTRIBUTE} attribute in a JSX element\n *\n * @param attributes - Array of JSX attributes\n * @returns The override attribute or undefined\n */\nexport function findOverrideAttribute(\n attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[],\n): JSXAttribute | undefined {\n const found = attributes.find(\n (attr): attr is JSXAttribute =>\n t.isJSXAttribute(attr) &&\n t.isJSXIdentifier(attr.name) &&\n attr.name.name === OVERRIDE_ATTRIBUTE,\n );\n return found;\n}\n\n/**\n * Remove the ${OVERRIDE_ATTRIBUTE} attribute from a JSX element\n * (We don't want this to appear in the final transformed output)\n *\n * @param attributes - Array of JSX attributes\n * @returns Array with override attribute removed\n */\nexport function removeOverrideAttribute(\n attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[],\n): (t.JSXAttribute | t.JSXSpreadAttribute)[] {\n return attributes.filter(\n (attr) =>\n !(\n t.isJSXAttribute(attr) &&\n t.isJSXIdentifier(attr.name) &&\n attr.name.name === `${OVERRIDE_ATTRIBUTE}`\n ),\n );\n}\n\nexport function processOverrideAttributes(\n path: NodePath<t.JSXElement | t.JSXFragment>,\n): Record<string, string> | undefined {\n // Check for override attribute (only on JSXElement, not Fragment)\n let overrides: Record<string, string> | undefined;\n if (path.node.type === \"JSXElement\") {\n const overrideAttr = findOverrideAttribute(\n path.node.openingElement.attributes,\n );\n if (overrideAttr) {\n const parsedOverrides = parseOverrideAttribute(overrideAttr);\n if (parsedOverrides && validateOverrides(parsedOverrides)) {\n overrides = parsedOverrides;\n // Remove the override attribute from the output\n path.node.openingElement.attributes = removeOverrideAttribute(\n path.node.openingElement.attributes,\n );\n }\n }\n }\n return overrides;\n}\n"],"mappings":"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"}