@babelize/engine 0.0.0-stage → 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 (246) hide show
  1. package/LICENSE +202 -0
  2. package/dist/BabelizeEngine.d.ts +12 -0
  3. package/dist/BabelizeEngine.js +47 -0
  4. package/dist/analysis/analyze.d.ts +106 -0
  5. package/dist/analysis/analyze.js +323 -0
  6. package/dist/config/env.d.ts +9 -0
  7. package/dist/config/env.js +16 -0
  8. package/dist/config.d.ts +74 -0
  9. package/dist/config.js +47 -0
  10. package/dist/index.d.ts +54 -0
  11. package/dist/index.js +126 -0
  12. package/dist/injection/InjectionStrategy.d.ts +62 -0
  13. package/dist/injection/InjectionStrategy.js +2 -0
  14. package/dist/injection/StrategyFactory.d.ts +4 -0
  15. package/dist/injection/StrategyFactory.js +29 -0
  16. package/dist/injection/django/django-template-injector.d.ts +29 -0
  17. package/dist/injection/django/django-template-injector.js +216 -0
  18. package/dist/injection/html/html-injector.d.ts +11 -0
  19. package/dist/injection/html/html-injector.js +173 -0
  20. package/dist/injection/strategies/NextIntlStrategy.d.ts +25 -0
  21. package/dist/injection/strategies/NextIntlStrategy.js +141 -0
  22. package/dist/injection/strategies/ReactI18nextStrategy.d.ts +19 -0
  23. package/dist/injection/strategies/ReactI18nextStrategy.js +116 -0
  24. package/dist/injection/strategies/VanillaStrategy.d.ts +11 -0
  25. package/dist/injection/strategies/VanillaStrategy.js +73 -0
  26. package/dist/injection/strategies/VueI18nStrategy.d.ts +11 -0
  27. package/dist/injection/strategies/VueI18nStrategy.js +84 -0
  28. package/dist/local/formatter.d.ts +26 -0
  29. package/dist/local/formatter.js +105 -0
  30. package/dist/local/git.d.ts +23 -0
  31. package/dist/local/git.js +136 -0
  32. package/dist/local/hosted-provider.d.ts +68 -0
  33. package/dist/local/hosted-provider.js +215 -0
  34. package/dist/local/index.d.ts +23 -0
  35. package/dist/local/index.js +75 -0
  36. package/dist/local/provider.d.ts +56 -0
  37. package/dist/local/provider.js +119 -0
  38. package/dist/local/pseudo-provider.d.ts +18 -0
  39. package/dist/local/pseudo-provider.js +207 -0
  40. package/dist/local/report.d.ts +177 -0
  41. package/dist/local/report.js +186 -0
  42. package/dist/local/translate-core.d.ts +215 -0
  43. package/dist/local/translate-core.js +461 -0
  44. package/dist/local/translate-messages.d.ts +49 -0
  45. package/dist/local/translate-messages.js +91 -0
  46. package/dist/local/workspace-copy.d.ts +91 -0
  47. package/dist/local/workspace-copy.js +355 -0
  48. package/dist/modules/extractor/extract.d.ts +20 -0
  49. package/dist/modules/extractor/extract.js +701 -0
  50. package/dist/modules/extractor/index.d.ts +2 -0
  51. package/dist/modules/extractor/index.js +63 -0
  52. package/dist/modules/finalization/django-finalize.d.ts +13 -0
  53. package/dist/modules/finalization/django-finalize.js +275 -0
  54. package/dist/modules/finalization/finalize.d.ts +36 -0
  55. package/dist/modules/finalization/finalize.js +717 -0
  56. package/dist/modules/finalization/index.d.ts +1 -0
  57. package/dist/modules/finalization/index.js +5 -0
  58. package/dist/modules/finalization/nextjs-finalize.d.ts +37 -0
  59. package/dist/modules/finalization/nextjs-finalize.js +329 -0
  60. package/dist/modules/finalization/static-runtime.d.ts +9 -0
  61. package/dist/modules/finalization/static-runtime.js +261 -0
  62. package/dist/modules/format-injection/index.d.ts +19 -0
  63. package/dist/modules/format-injection/index.js +272 -0
  64. package/dist/modules/format-injection/inject-formats.d.ts +31 -0
  65. package/dist/modules/format-injection/inject-formats.js +208 -0
  66. package/dist/modules/glossary/glossary.d.ts +9 -0
  67. package/dist/modules/glossary/glossary.js +27 -0
  68. package/dist/modules/glossary/index.d.ts +3 -0
  69. package/dist/modules/glossary/index.js +8 -0
  70. package/dist/modules/glossary/masker.d.ts +23 -0
  71. package/dist/modules/glossary/masker.js +56 -0
  72. package/dist/modules/glossary/types.d.ts +22 -0
  73. package/dist/modules/glossary/types.js +2 -0
  74. package/dist/modules/injection/index.d.ts +1 -0
  75. package/dist/modules/injection/index.js +6 -0
  76. package/dist/modules/injection/inject.d.ts +29 -0
  77. package/dist/modules/injection/inject.js +1066 -0
  78. package/dist/modules/keygen/index.d.ts +1 -0
  79. package/dist/modules/keygen/index.js +11 -0
  80. package/dist/modules/keygen/keygen.d.ts +4 -0
  81. package/dist/modules/keygen/keygen.js +116 -0
  82. package/dist/modules/keygen/locale-data.d.ts +7 -0
  83. package/dist/modules/keygen/locale-data.js +73 -0
  84. package/dist/modules/keygen/public.d.ts +2 -0
  85. package/dist/modules/keygen/public.js +12 -0
  86. package/dist/modules/locale-builder/index.d.ts +1 -0
  87. package/dist/modules/locale-builder/index.js +7 -0
  88. package/dist/modules/locale-builder/locales.d.ts +13 -0
  89. package/dist/modules/locale-builder/locales.js +107 -0
  90. package/dist/modules/locale-builder/po.d.ts +33 -0
  91. package/dist/modules/locale-builder/po.js +131 -0
  92. package/dist/modules/lockfile/hasher.d.ts +7 -0
  93. package/dist/modules/lockfile/hasher.js +13 -0
  94. package/dist/modules/lockfile/index.d.ts +3 -0
  95. package/dist/modules/lockfile/index.js +21 -0
  96. package/dist/modules/lockfile/lockfile-manager.d.ts +90 -0
  97. package/dist/modules/lockfile/lockfile-manager.js +482 -0
  98. package/dist/modules/lockfile/schema.d.ts +154 -0
  99. package/dist/modules/lockfile/schema.js +45 -0
  100. package/dist/modules/parser/index.d.ts +2 -0
  101. package/dist/modules/parser/index.js +9 -0
  102. package/dist/modules/summary/index.d.ts +1 -0
  103. package/dist/modules/summary/index.js +7 -0
  104. package/dist/modules/summary/summary.d.ts +9 -0
  105. package/dist/modules/summary/summary.js +195 -0
  106. package/dist/modules/sync-detector/index.d.ts +2 -0
  107. package/dist/modules/sync-detector/index.js +7 -0
  108. package/dist/modules/sync-detector/reporter.d.ts +3 -0
  109. package/dist/modules/sync-detector/reporter.js +16 -0
  110. package/dist/modules/sync-detector/sync-detector.d.ts +39 -0
  111. package/dist/modules/sync-detector/sync-detector.js +374 -0
  112. package/dist/modules/translator/batch-optimizer.d.ts +12 -0
  113. package/dist/modules/translator/batch-optimizer.js +30 -0
  114. package/dist/modules/translator/client/byok.provider.d.ts +64 -0
  115. package/dist/modules/translator/client/byok.provider.js +319 -0
  116. package/dist/modules/translator/client/direct-translator.client.d.ts +12 -0
  117. package/dist/modules/translator/client/direct-translator.client.js +71 -0
  118. package/dist/modules/translator/client/gemini.provider.d.ts +1 -0
  119. package/dist/modules/translator/client/gemini.provider.js +110 -0
  120. package/dist/modules/translator/client/groq.provider.d.ts +1 -0
  121. package/dist/modules/translator/client/groq.provider.js +111 -0
  122. package/dist/modules/translator/client/request.types.d.ts +1 -0
  123. package/dist/modules/translator/client/request.types.js +2 -0
  124. package/dist/modules/translator/client/retry.d.ts +29 -0
  125. package/dist/modules/translator/client/retry.js +94 -0
  126. package/dist/modules/translator/client/test-utils/mock.client.d.ts +31 -0
  127. package/dist/modules/translator/client/test-utils/mock.client.js +59 -0
  128. package/dist/modules/translator/client/translator.interface.d.ts +1 -0
  129. package/dist/modules/translator/client/translator.interface.js +2 -0
  130. package/dist/modules/translator/language-codes/errors.d.ts +10 -0
  131. package/dist/modules/translator/language-codes/errors.js +18 -0
  132. package/dist/modules/translator/language-codes/flores-map.d.ts +1 -0
  133. package/dist/modules/translator/language-codes/flores-map.js +107 -0
  134. package/dist/modules/translator/language-codes/index.d.ts +11 -0
  135. package/dist/modules/translator/language-codes/index.js +24 -0
  136. package/dist/modules/translator/language-codes/language-mapper.d.ts +25 -0
  137. package/dist/modules/translator/language-codes/language-mapper.js +132 -0
  138. package/dist/modules/translator/language-codes/rtl-languages.d.ts +2 -0
  139. package/dist/modules/translator/language-codes/rtl-languages.js +9 -0
  140. package/dist/modules/translator/memory/memory-matcher.d.ts +8 -0
  141. package/dist/modules/translator/memory/memory-matcher.js +218 -0
  142. package/dist/modules/translator/memory/types.d.ts +22 -0
  143. package/dist/modules/translator/memory/types.js +2 -0
  144. package/dist/modules/translator/translate.d.ts +38 -0
  145. package/dist/modules/translator/translate.js +932 -0
  146. package/dist/modules/translator/translator.d.ts +5 -0
  147. package/dist/modules/translator/translator.js +46 -0
  148. package/dist/modules/vue/migrate.d.ts +13 -0
  149. package/dist/modules/vue/migrate.js +40 -0
  150. package/dist/modules/vue/script-inject.d.ts +16 -0
  151. package/dist/modules/vue/script-inject.js +115 -0
  152. package/dist/modules/vue/sfc.d.ts +11 -0
  153. package/dist/modules/vue/sfc.js +51 -0
  154. package/dist/modules/vue/template-rewrite.d.ts +25 -0
  155. package/dist/modules/vue/template-rewrite.js +76 -0
  156. package/dist/modules/vue/template-scan.d.ts +29 -0
  157. package/dist/modules/vue/template-scan.js +264 -0
  158. package/dist/modules/workspace/index.d.ts +1 -0
  159. package/dist/modules/workspace/index.js +5 -0
  160. package/dist/modules/workspace/validate.d.ts +13 -0
  161. package/dist/modules/workspace/validate.js +298 -0
  162. package/dist/orchestrator.d.ts +8 -0
  163. package/dist/orchestrator.js +616 -0
  164. package/dist/providers/default-translation-provider.d.ts +20 -0
  165. package/dist/providers/default-translation-provider.js +30 -0
  166. package/dist/providers/index.d.ts +2 -0
  167. package/dist/providers/index.js +21 -0
  168. package/dist/providers/types.d.ts +145 -0
  169. package/dist/providers/types.js +2 -0
  170. package/dist/qa/check-project.d.ts +49 -0
  171. package/dist/qa/check-project.js +150 -0
  172. package/dist/qa/locale-files.d.ts +44 -0
  173. package/dist/qa/locale-files.js +159 -0
  174. package/dist/qa/locale-qa.d.ts +61 -0
  175. package/dist/qa/locale-qa.js +325 -0
  176. package/dist/qa/review.d.ts +43 -0
  177. package/dist/qa/review.js +175 -0
  178. package/dist/runtime/config.d.ts +8 -0
  179. package/dist/runtime/config.js +36 -0
  180. package/dist/runtime/index.d.ts +3 -0
  181. package/dist/runtime/index.js +8 -0
  182. package/dist/runtime/types.d.ts +363 -0
  183. package/dist/runtime/types.js +38 -0
  184. package/dist/types.d.ts +134 -0
  185. package/dist/types.js +12 -0
  186. package/dist/utils/ast-helpers.d.ts +57 -0
  187. package/dist/utils/ast-helpers.js +608 -0
  188. package/dist/utils/cache-keys.d.ts +8 -0
  189. package/dist/utils/cache-keys.js +18 -0
  190. package/dist/utils/circuit-breaker.d.ts +43 -0
  191. package/dist/utils/circuit-breaker.js +119 -0
  192. package/dist/utils/concurrent-map.d.ts +4 -0
  193. package/dist/utils/concurrent-map.js +21 -0
  194. package/dist/utils/detect-format.d.ts +5 -0
  195. package/dist/utils/detect-format.js +125 -0
  196. package/dist/utils/dtl-mask.d.ts +25 -0
  197. package/dist/utils/dtl-mask.js +58 -0
  198. package/dist/utils/extraction-options.d.ts +23 -0
  199. package/dist/utils/extraction-options.js +23 -0
  200. package/dist/utils/file-utils.d.ts +33 -0
  201. package/dist/utils/file-utils.js +262 -0
  202. package/dist/utils/glossary-match.d.ts +9 -0
  203. package/dist/utils/glossary-match.js +25 -0
  204. package/dist/utils/html-extractor.d.ts +8 -0
  205. package/dist/utils/html-extractor.js +317 -0
  206. package/dist/utils/i18next-icu.d.ts +38 -0
  207. package/dist/utils/i18next-icu.js +159 -0
  208. package/dist/utils/icu-message.d.ts +65 -0
  209. package/dist/utils/icu-message.js +346 -0
  210. package/dist/utils/icu-spans.d.ts +10 -0
  211. package/dist/utils/icu-spans.js +126 -0
  212. package/dist/utils/jsx-message.d.ts +93 -0
  213. package/dist/utils/jsx-message.js +621 -0
  214. package/dist/utils/key-usage.d.ts +35 -0
  215. package/dist/utils/key-usage.js +247 -0
  216. package/dist/utils/logger.d.ts +24 -0
  217. package/dist/utils/logger.js +33 -0
  218. package/dist/utils/metadata.d.ts +80 -0
  219. package/dist/utils/metadata.js +255 -0
  220. package/dist/utils/module-constants.d.ts +30 -0
  221. package/dist/utils/module-constants.js +265 -0
  222. package/dist/utils/not-injected.d.ts +6 -0
  223. package/dist/utils/not-injected.js +36 -0
  224. package/dist/utils/placeholder-protection.d.ts +57 -0
  225. package/dist/utils/placeholder-protection.js +136 -0
  226. package/dist/utils/pseudo-markers.d.ts +9 -0
  227. package/dist/utils/pseudo-markers.js +16 -0
  228. package/dist/utils/py-extractor.d.ts +14 -0
  229. package/dist/utils/py-extractor.js +285 -0
  230. package/dist/utils/string-utils.d.ts +52 -0
  231. package/dist/utils/string-utils.js +270 -0
  232. package/dist/utils/translation-quality.d.ts +34 -0
  233. package/dist/utils/translation-quality.js +156 -0
  234. package/dist/utils/translation-sanitizer.d.ts +10 -0
  235. package/dist/utils/translation-sanitizer.js +57 -0
  236. package/dist/utils/translator-caller.d.ts +32 -0
  237. package/dist/utils/translator-caller.js +217 -0
  238. package/dist/utils/translator-load-balancer.d.ts +73 -0
  239. package/dist/utils/translator-load-balancer.js +293 -0
  240. package/dist/utils/ui-copy.d.ts +32 -0
  241. package/dist/utils/ui-copy.js +126 -0
  242. package/dist/utils/workspace.d.ts +14 -0
  243. package/dist/utils/workspace.js +89 -0
  244. package/package.json +64 -4
  245. package/schemas/translate-report.schema.json +297 -0
  246. package/README.md +0 -3
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.scanKeyReferences = scanKeyReferences;
37
+ exports.findKeyUsage = findKeyUsage;
38
+ exports.reportKeyUsage = reportKeyUsage;
39
+ const path = __importStar(require("path"));
40
+ const types_1 = require("../runtime/types");
41
+ const logger_1 = require("./logger");
42
+ const file_utils_1 = require("./file-utils");
43
+ const ast_helpers_1 = require("./ast-helpers");
44
+ const CALL_RE = /(?:\$t|\bt(?:\.(?:rich|markup|raw|has))?)\(\s*/g;
45
+ /**
46
+ * Names the translator is bound to in a file besides `t` — the injector uses
47
+ * an alias (`tr`, `tr2`) where `t` is taken: `const tr = useTranslations()`,
48
+ * `const tr = await getTranslations()`, `const { t: tr } = useTranslation()`.
49
+ */
50
+ const HOOK_ALIAS_RE = /\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*(?:await\s+)?(?:useTranslations|getTranslations)\(/g;
51
+ const DESTRUCTURED_ALIAS_RE = /\bconst\s*\{[^}]*?\bt\s*:\s*([A-Za-z_$][\w$]*)[^}]*\}\s*=\s*useTranslation\(/g;
52
+ function translatorNames(content) {
53
+ const names = new Set(['t']);
54
+ for (const m of content.matchAll(HOOK_ALIAS_RE))
55
+ names.add(m[1]);
56
+ for (const m of content.matchAll(DESTRUCTURED_ALIAS_RE))
57
+ names.add(m[1]);
58
+ return [...names];
59
+ }
60
+ function callPattern(names) {
61
+ const escaped = names.map((n) => n.replace(/[$]/g, '\\$')).join('|');
62
+ // Same shape as CALL_RE (`i18n.t(` still counts), with the aliases added.
63
+ return new RegExp(`(?:\\$t|(?<![\\w$])(?:${escaped})(?:\\.(?:rich|markup|raw|has))?)\\(\\s*`, 'g');
64
+ }
65
+ const LITERAL_ARG_RE = /^(['"`])((?:(?!\1)[^\\$\n]|\\.)*)\1/;
66
+ const ATTR_RE = /(?:\bi18nKey|\bdata-i18n(?:-[\w-]+)?)\s*=\s*(?:\{\s*)?(['"`])([^'"`$]+)\1/g;
67
+ const NAMESPACE_RE = /\b(?:useTranslations|getTranslations)\(\s*(?:(['"`])([^'"`$]+)\1|\{[^}]*?\bnamespace\s*:\s*(['"`])([^'"`$]+)\3)/g;
68
+ /** Data attributes that are configuration, not keys. */
69
+ const NON_KEY_ATTRS = /^data-i18n-(?:args|html)$/;
70
+ function scanKeyReferences(content) {
71
+ const keys = [];
72
+ let dynamic = false;
73
+ const namespaces = [];
74
+ for (const m of content.matchAll(NAMESPACE_RE))
75
+ namespaces.push(m[2] ?? m[4]);
76
+ const names = translatorNames(content);
77
+ let nonLiteral = false;
78
+ for (const m of content.matchAll(names.length > 1 ? callPattern(names) : CALL_RE)) {
79
+ const rest = content.slice((m.index ?? 0) + m[0].length);
80
+ if (rest.startsWith(')'))
81
+ continue; // t() — not a lookup
82
+ const literal = LITERAL_ARG_RE.exec(rest);
83
+ if (literal) {
84
+ keys.push(literal[2]);
85
+ }
86
+ else {
87
+ nonLiteral = true;
88
+ }
89
+ }
90
+ if (nonLiteral) {
91
+ // `t(tab)` over a module constant the injector rewrote to keys is not
92
+ // dynamic: its keys are the constant's string literals.
93
+ const resolved = resolveConstantKeys(content, names);
94
+ if (resolved)
95
+ keys.push(...resolved);
96
+ else
97
+ dynamic = true;
98
+ }
99
+ for (const m of content.matchAll(ATTR_RE)) {
100
+ const attr = m[0].split(/\s*=/)[0];
101
+ if (NON_KEY_ATTRS.test(attr))
102
+ continue;
103
+ keys.push(m[2]);
104
+ }
105
+ const withNamespaces = [...keys];
106
+ for (const ns of namespaces)
107
+ for (const k of keys)
108
+ withNamespaces.push(`${ns}.${k}`);
109
+ return { keys: withNamespaces, dynamic };
110
+ }
111
+ async function findKeyUsage(workspace) {
112
+ const referenced = new Set();
113
+ const dynamicFiles = [];
114
+ const seen = new Set();
115
+ for (const dir of workspace.sourceDirs) {
116
+ const files = await (0, file_utils_1.findFiles)(path.join(workspace.path, dir), types_1.SUPPORTED_EXTENSIONS, types_1.EXCLUDED_PATTERNS);
117
+ for (const file of files) {
118
+ if (seen.has(file))
119
+ continue;
120
+ seen.add(file);
121
+ const content = await (0, file_utils_1.readFile)(file).catch(() => '');
122
+ const { keys, dynamic } = scanKeyReferences(content);
123
+ for (const k of keys)
124
+ referenced.add(k);
125
+ if (dynamic)
126
+ dynamicFiles.push(path.relative(workspace.path, file).split(path.sep).join('/'));
127
+ }
128
+ }
129
+ return { referenced, dynamicFiles };
130
+ }
131
+ /**
132
+ * Keys of translator calls whose argument comes from a module-level constant
133
+ * of string literals (`CONST.map((x) => t(x))`, `t(x.label)`, `t(CONST.prop)`,
134
+ * `t(CONST[0])`). Null when any non-literal call can't be resolved that way
135
+ * (or the file doesn't parse): the file is dynamic.
136
+ */
137
+ function resolveConstantKeys(content, names) {
138
+ let ast;
139
+ try {
140
+ ast = (0, ast_helpers_1.parseCode)(content);
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ const keys = [];
146
+ let unresolved = false;
147
+ const literalsOf = (init) => {
148
+ if (!init)
149
+ return null;
150
+ const out = [];
151
+ const take = (node) => {
152
+ if (ast_helpers_1.t.isStringLiteral(node)) {
153
+ out.push(node.value);
154
+ return true;
155
+ }
156
+ if (ast_helpers_1.t.isObjectExpression(node)) {
157
+ for (const p of node.properties)
158
+ if (ast_helpers_1.t.isObjectProperty(p) && ast_helpers_1.t.isStringLiteral(p.value))
159
+ out.push(p.value.value);
160
+ return true;
161
+ }
162
+ return false;
163
+ };
164
+ if (ast_helpers_1.t.isArrayExpression(init)) {
165
+ for (const e of init.elements)
166
+ if (!e || !take(e))
167
+ return null;
168
+ return out;
169
+ }
170
+ return ast_helpers_1.t.isObjectExpression(init) && take(init) ? out : null;
171
+ };
172
+ /** The module-level `const` an identifier refers to, if any. */
173
+ const moduleConst = (p, name) => {
174
+ const binding = p.scope.getBinding(name);
175
+ if (!binding || binding.kind !== 'const' || binding.scope.block.type !== 'Program' || !binding.path.isVariableDeclarator())
176
+ return null;
177
+ return binding.path.node.init ?? null;
178
+ };
179
+ /** `x` where x is the item parameter of `CONST.map(...)`. */
180
+ const mapItemSource = (p, name) => {
181
+ const binding = p.scope.getBinding(name);
182
+ if (!binding || binding.kind !== 'param')
183
+ return null;
184
+ const fn = binding.path.parentPath;
185
+ const call = fn?.parentPath;
186
+ if (!fn || !call?.isCallExpression() || call.node.arguments[0] !== fn.node)
187
+ return null;
188
+ const callee = call.node.callee;
189
+ if (!ast_helpers_1.t.isMemberExpression(callee) || !ast_helpers_1.t.isIdentifier(callee.property, { name: 'map' }) || !ast_helpers_1.t.isIdentifier(callee.object))
190
+ return null;
191
+ return moduleConst(call, callee.object.name);
192
+ };
193
+ (0, ast_helpers_1.traverse)(ast, {
194
+ CallExpression(p) {
195
+ const callee = p.node.callee;
196
+ const isTranslator = (ast_helpers_1.t.isIdentifier(callee) && (names.includes(callee.name) || callee.name === '$t')) ||
197
+ (ast_helpers_1.t.isMemberExpression(callee) && !callee.computed && ast_helpers_1.t.isIdentifier(callee.property) &&
198
+ (callee.property.name === 't' ||
199
+ (ast_helpers_1.t.isIdentifier(callee.object) && names.includes(callee.object.name) && ['rich', 'markup', 'raw', 'has'].includes(callee.property.name))));
200
+ if (!isTranslator)
201
+ return;
202
+ const arg = p.node.arguments[0];
203
+ if (!arg || ast_helpers_1.t.isStringLiteral(arg) || (ast_helpers_1.t.isTemplateLiteral(arg) && arg.expressions.length === 0))
204
+ return;
205
+ let source = null;
206
+ if (ast_helpers_1.t.isIdentifier(arg))
207
+ source = mapItemSource(p, arg.name);
208
+ else if (ast_helpers_1.t.isMemberExpression(arg) && ast_helpers_1.t.isIdentifier(arg.object))
209
+ source = mapItemSource(p, arg.object.name) ?? moduleConst(p, arg.object.name);
210
+ const literals = literalsOf(source);
211
+ if (literals)
212
+ keys.push(...literals);
213
+ else
214
+ unresolved = true;
215
+ },
216
+ });
217
+ return unresolved ? null : keys;
218
+ }
219
+ const PLURAL_SUFFIX = /_(?:zero|one|two|few|many|other)$/;
220
+ /** A source key looks used if referenced as-is, by its plural base, or under a referenced subtree. */
221
+ function isKeyReferenced(key, referenced) {
222
+ if (referenced.has(key))
223
+ return true;
224
+ const base = key.replace(PLURAL_SUFFIX, '');
225
+ if (base !== key && referenced.has(base))
226
+ return true;
227
+ const parts = key.split('.');
228
+ for (let i = parts.length - 1; i > 0; i--) {
229
+ if (referenced.has(parts.slice(0, i).join('.')))
230
+ return true;
231
+ }
232
+ return false;
233
+ }
234
+ /** Report-only: source keys with no t() reference found. Never used to skip or drop anything. */
235
+ async function reportKeyUsage(workspace, sourceKeys, extractedNow) {
236
+ try {
237
+ const usage = await findKeyUsage(workspace);
238
+ if (usage.dynamicFiles.length > 0)
239
+ return { unused: [], dynamicFiles: usage.dynamicFiles };
240
+ const unused = Object.keys(sourceKeys).filter((k) => !extractedNow.has(k) && !isKeyReferenced(k, usage.referenced));
241
+ return { unused: unused.sort(), dynamicFiles: [] };
242
+ }
243
+ catch (err) {
244
+ logger_1.engineLogger.warn('Key usage scan failed', { error: err instanceof Error ? err.message : String(err) });
245
+ return { unused: [], dynamicFiles: [] };
246
+ }
247
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Engine logging. The engine has no logging backend of its own: hosts call
3
+ * setEngineLogger() to route engine logs into theirs (the backend passes its
4
+ * structured logger). Until then, warnings and errors go to the console and
5
+ * info/debug are dropped, so library users (CLI, tests) aren't flooded.
6
+ */
7
+ export interface LogContext {
8
+ [key: string]: any;
9
+ }
10
+ export interface EngineLogger {
11
+ debug(message: string, context?: LogContext): void;
12
+ info(message: string, context?: LogContext): void;
13
+ warn(message: string, context?: LogContext): void;
14
+ error(message: string, context?: LogContext): void;
15
+ }
16
+ /** Process-wide default logger (the backend's structured logger). */
17
+ export declare function setEngineLogger(logger: EngineLogger): void;
18
+ /**
19
+ * Run `fn` with `logger` receiving every engine log made inside it (including
20
+ * across awaits), without touching the process-wide logger. Concurrent runs
21
+ * in one process (a long-lived MCP server) each keep their own logs.
22
+ */
23
+ export declare function runWithEngineLogger<T>(logger: EngineLogger, fn: () => T): T;
24
+ export declare const engineLogger: EngineLogger;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.engineLogger = void 0;
4
+ exports.setEngineLogger = setEngineLogger;
5
+ exports.runWithEngineLogger = runWithEngineLogger;
6
+ const async_hooks_1 = require("async_hooks");
7
+ const consoleLogger = {
8
+ debug: () => { },
9
+ info: () => { },
10
+ warn: (message, context) => console.warn(`[engine] ${message}`, context ?? ''),
11
+ error: (message, context) => console.error(`[engine] ${message}`, context ?? ''),
12
+ };
13
+ let impl = consoleLogger;
14
+ const scoped = new async_hooks_1.AsyncLocalStorage();
15
+ /** Process-wide default logger (the backend's structured logger). */
16
+ function setEngineLogger(logger) {
17
+ impl = logger;
18
+ }
19
+ /**
20
+ * Run `fn` with `logger` receiving every engine log made inside it (including
21
+ * across awaits), without touching the process-wide logger. Concurrent runs
22
+ * in one process (a long-lived MCP server) each keep their own logs.
23
+ */
24
+ function runWithEngineLogger(logger, fn) {
25
+ return scoped.run(logger, fn);
26
+ }
27
+ const current = () => scoped.getStore() ?? impl;
28
+ exports.engineLogger = {
29
+ debug: (message, context) => current().debug(message, context),
30
+ info: (message, context) => current().info(message, context),
31
+ warn: (message, context) => current().warn(message, context),
32
+ error: (message, context) => current().error(message, context),
33
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * .babelize Metadata Utilities (engine v2).
3
+ *
4
+ * Moved from v1 engine utils so the v1 engine can be deleted cleanly while
5
+ * workers/controllers keep using the same metadata behaviors.
6
+ */
7
+ export interface BabelHistoryEntry {
8
+ id: string;
9
+ timestamp: string;
10
+ duration: number;
11
+ status: 'success' | 'failed' | 'partial';
12
+ targetLocales: string[];
13
+ stats: {
14
+ filesProcessed: number;
15
+ stringsExtracted: number;
16
+ stringsTranslated: number;
17
+ errors: number;
18
+ };
19
+ error?: string;
20
+ }
21
+ export interface ExtractedStringEntry {
22
+ key: string;
23
+ originalText: string;
24
+ sourceFile: string;
25
+ line: number;
26
+ context?: string;
27
+ lastUpdated: string;
28
+ }
29
+ export interface BabelizeConfig {
30
+ sourceLocale: string;
31
+ targetLocales: string[];
32
+ confidenceThreshold: number;
33
+ excludePatterns: string[];
34
+ customTranslations: Record<string, Record<string, string>>;
35
+ }
36
+ export type FrameworkType = 'react' | 'nextjs' | 'vue' | 'angular' | 'svelte' | 'astro' | 'remix' | 'gatsby' | 'nuxt' | 'static' | 'unknown';
37
+ export type PackageManagerType = 'npm' | 'yarn' | 'pnpm' | 'bun';
38
+ export interface ProjectInfo {
39
+ framework: FrameworkType;
40
+ frameworks?: FrameworkType[];
41
+ frameworkVersion?: string;
42
+ packageManager: PackageManagerType | 'unknown';
43
+ packageManagers: PackageManagerType[];
44
+ nodeVersion?: string;
45
+ projectVersion?: string;
46
+ projectName?: string;
47
+ dependencies: {
48
+ react?: string;
49
+ next?: string;
50
+ vue?: string;
51
+ angular?: string;
52
+ svelte?: string;
53
+ astro?: string;
54
+ remix?: string;
55
+ gatsby?: string;
56
+ nuxt?: string;
57
+ 'react-i18next'?: string;
58
+ };
59
+ detectedAt: string;
60
+ }
61
+ export declare function getBabelizeDir(workspacePath: string): string;
62
+ export declare function ensureBabelizeDir(workspacePath: string): Promise<string>;
63
+ export declare function getBabelHistory(workspacePath: string): Promise<BabelHistoryEntry[]>;
64
+ export declare function addBabelToHistory(workspacePath: string, babel: BabelHistoryEntry): Promise<void>;
65
+ export declare function getLastSuccessfulBabel(workspacePath: string): Promise<BabelHistoryEntry | null>;
66
+ export declare function getCachedStrings(workspacePath: string): Promise<ExtractedStringEntry[]>;
67
+ export declare function saveCachedStrings(workspacePath: string, strings: ExtractedStringEntry[]): Promise<void>;
68
+ export declare function clearCachedStrings(workspacePath: string): Promise<void>;
69
+ export declare function getProjectConfig(workspacePath: string): Promise<BabelizeConfig>;
70
+ export declare function saveProjectConfig(workspacePath: string, config: Partial<BabelizeConfig>): Promise<void>;
71
+ export declare function getBabelizeSummary(workspacePath: string): Promise<{
72
+ hasHistory: boolean;
73
+ babelCount: number;
74
+ lastBabel: BabelHistoryEntry | null;
75
+ lastSuccessfulBabel: BabelHistoryEntry | null;
76
+ cachedStringsCount: number;
77
+ config: BabelizeConfig;
78
+ }>;
79
+ export declare function detectProjectInfo(workspacePath: string): Promise<ProjectInfo>;
80
+ export declare function getProjectInfo(workspacePath: string): Promise<ProjectInfo | null>;
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ /**
3
+ * .babelize Metadata Utilities (engine v2).
4
+ *
5
+ * Moved from v1 engine utils so the v1 engine can be deleted cleanly while
6
+ * workers/controllers keep using the same metadata behaviors.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.getBabelizeDir = getBabelizeDir;
46
+ exports.ensureBabelizeDir = ensureBabelizeDir;
47
+ exports.getBabelHistory = getBabelHistory;
48
+ exports.addBabelToHistory = addBabelToHistory;
49
+ exports.getLastSuccessfulBabel = getLastSuccessfulBabel;
50
+ exports.getCachedStrings = getCachedStrings;
51
+ exports.saveCachedStrings = saveCachedStrings;
52
+ exports.clearCachedStrings = clearCachedStrings;
53
+ exports.getProjectConfig = getProjectConfig;
54
+ exports.saveProjectConfig = saveProjectConfig;
55
+ exports.getBabelizeSummary = getBabelizeSummary;
56
+ exports.detectProjectInfo = detectProjectInfo;
57
+ exports.getProjectInfo = getProjectInfo;
58
+ const fs = __importStar(require("fs-extra"));
59
+ const path = __importStar(require("path"));
60
+ const fast_glob_1 = __importDefault(require("fast-glob"));
61
+ const logger_1 = require("./logger");
62
+ const DEFAULT_CONFIG = {
63
+ sourceLocale: 'en',
64
+ targetLocales: [],
65
+ confidenceThreshold: 80,
66
+ excludePatterns: ['node_modules/**', '.git/**', 'dist/**', 'build/**'],
67
+ customTranslations: {},
68
+ };
69
+ function getBabelizeDir(workspacePath) {
70
+ return path.join(workspacePath, '.babelize');
71
+ }
72
+ async function ensureBabelizeDir(workspacePath) {
73
+ const babelizeDir = getBabelizeDir(workspacePath);
74
+ await fs.ensureDir(babelizeDir);
75
+ return babelizeDir;
76
+ }
77
+ async function getBabelHistory(workspacePath) {
78
+ const historyPath = path.join(getBabelizeDir(workspacePath), 'babel-history.json');
79
+ if (await fs.pathExists(historyPath)) {
80
+ try {
81
+ return await fs.readJson(historyPath);
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ }
87
+ return [];
88
+ }
89
+ async function addBabelToHistory(workspacePath, babel) {
90
+ await ensureBabelizeDir(workspacePath);
91
+ const historyPath = path.join(getBabelizeDir(workspacePath), 'babel-history.json');
92
+ const history = await getBabelHistory(workspacePath);
93
+ history.unshift(babel);
94
+ const trimmedHistory = history.slice(0, 50);
95
+ await fs.writeJson(historyPath, trimmedHistory, { spaces: 2 });
96
+ }
97
+ async function getLastSuccessfulBabel(workspacePath) {
98
+ const history = await getBabelHistory(workspacePath);
99
+ return history.find((r) => r.status === 'success') || null;
100
+ }
101
+ async function getCachedStrings(workspacePath) {
102
+ const cachePath = path.join(getBabelizeDir(workspacePath), 'extracted-strings.json');
103
+ if (await fs.pathExists(cachePath)) {
104
+ try {
105
+ return await fs.readJson(cachePath);
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ }
111
+ return [];
112
+ }
113
+ async function saveCachedStrings(workspacePath, strings) {
114
+ await ensureBabelizeDir(workspacePath);
115
+ const cachePath = path.join(getBabelizeDir(workspacePath), 'extracted-strings.json');
116
+ await fs.writeJson(cachePath, strings, { spaces: 2 });
117
+ }
118
+ async function clearCachedStrings(workspacePath) {
119
+ const cachePath = path.join(getBabelizeDir(workspacePath), 'extracted-strings.json');
120
+ if (await fs.pathExists(cachePath))
121
+ await fs.remove(cachePath);
122
+ }
123
+ async function getProjectConfig(workspacePath) {
124
+ const configPath = path.join(getBabelizeDir(workspacePath), 'config.json');
125
+ if (await fs.pathExists(configPath)) {
126
+ try {
127
+ const config = await fs.readJson(configPath);
128
+ return { ...DEFAULT_CONFIG, ...config };
129
+ }
130
+ catch {
131
+ return DEFAULT_CONFIG;
132
+ }
133
+ }
134
+ return DEFAULT_CONFIG;
135
+ }
136
+ async function saveProjectConfig(workspacePath, config) {
137
+ await ensureBabelizeDir(workspacePath);
138
+ const configPath = path.join(getBabelizeDir(workspacePath), 'config.json');
139
+ const existingConfig = await getProjectConfig(workspacePath);
140
+ const mergedConfig = { ...existingConfig, ...config };
141
+ await fs.writeJson(configPath, mergedConfig, { spaces: 2 });
142
+ }
143
+ async function getBabelizeSummary(workspacePath) {
144
+ const history = await getBabelHistory(workspacePath);
145
+ const cachedStrings = await getCachedStrings(workspacePath);
146
+ const config = await getProjectConfig(workspacePath);
147
+ return {
148
+ hasHistory: history.length > 0,
149
+ babelCount: history.length,
150
+ lastBabel: history[0] || null,
151
+ lastSuccessfulBabel: history.find((r) => r.status === 'success') || null,
152
+ cachedStringsCount: cachedStrings.length,
153
+ config,
154
+ };
155
+ }
156
+ function detectPackageManagers(workspacePath) {
157
+ const detected = [];
158
+ if (fs.existsSync(path.join(workspacePath, 'bun.lockb')) || fs.existsSync(path.join(workspacePath, 'bun.lock'))) {
159
+ detected.push('bun');
160
+ }
161
+ if (fs.existsSync(path.join(workspacePath, 'pnpm-lock.yaml')))
162
+ detected.push('pnpm');
163
+ if (fs.existsSync(path.join(workspacePath, 'yarn.lock')))
164
+ detected.push('yarn');
165
+ if (fs.existsSync(path.join(workspacePath, 'package-lock.json')))
166
+ detected.push('npm');
167
+ return detected;
168
+ }
169
+ async function detectProjectInfo(workspacePath) {
170
+ await ensureBabelizeDir(workspacePath);
171
+ const packageJsonPath = path.join(workspacePath, 'package.json');
172
+ let packageJson = {};
173
+ if (await fs.pathExists(packageJsonPath)) {
174
+ try {
175
+ packageJson = await fs.readJson(packageJsonPath);
176
+ }
177
+ catch (err) {
178
+ logger_1.engineLogger.warn('Failed to read package.json', {
179
+ path: packageJsonPath,
180
+ error: err instanceof Error ? err.message : String(err),
181
+ });
182
+ }
183
+ }
184
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
185
+ const frameworks = [];
186
+ let primaryFramework = 'unknown';
187
+ let frameworkVersion;
188
+ const frameworkChecks = [
189
+ { name: 'nextjs', dep: 'next' },
190
+ { name: 'nuxt', dep: 'nuxt' },
191
+ { name: 'gatsby', dep: 'gatsby' },
192
+ { name: 'remix', dep: '@remix-run/react' },
193
+ { name: 'astro', dep: 'astro' },
194
+ { name: 'vue', dep: 'vue' },
195
+ { name: 'svelte', dep: 'svelte' },
196
+ { name: 'angular', dep: '@angular/core' },
197
+ { name: 'react', dep: 'react' },
198
+ ];
199
+ for (const check of frameworkChecks) {
200
+ if (deps[check.dep]) {
201
+ frameworks.push(check.name);
202
+ if (primaryFramework === 'unknown') {
203
+ primaryFramework = check.name;
204
+ frameworkVersion = deps[check.dep];
205
+ }
206
+ }
207
+ }
208
+ if (primaryFramework === 'unknown' && frameworks.length === 0) {
209
+ const htmlFiles = await (0, fast_glob_1.default)('**/*.html', { cwd: workspacePath, onlyFiles: true, deep: 3 });
210
+ if (htmlFiles.length > 0) {
211
+ primaryFramework = 'static';
212
+ frameworks.push('static');
213
+ logger_1.engineLogger.info('Detected static HTML/CSS/JS project', { workspacePath });
214
+ }
215
+ }
216
+ const packageManagers = detectPackageManagers(workspacePath);
217
+ const primaryPackageManager = packageManagers.length > 0 ? packageManagers[0] : 'unknown';
218
+ const projectInfo = {
219
+ framework: primaryFramework,
220
+ frameworks: frameworks.length > 0 ? frameworks : undefined,
221
+ frameworkVersion,
222
+ packageManager: primaryPackageManager,
223
+ packageManagers,
224
+ projectVersion: packageJson.version,
225
+ projectName: packageJson.name,
226
+ dependencies: {
227
+ react: deps['react'],
228
+ next: deps['next'],
229
+ vue: deps['vue'],
230
+ angular: deps['@angular/core'],
231
+ svelte: deps['svelte'] || deps['@sveltejs/kit'],
232
+ astro: deps['astro'],
233
+ remix: deps['@remix-run/react'] || deps['remix'],
234
+ gatsby: deps['gatsby'],
235
+ nuxt: deps['nuxt'],
236
+ 'react-i18next': deps['react-i18next'],
237
+ },
238
+ detectedAt: new Date().toISOString(),
239
+ };
240
+ const infoPath = path.join(getBabelizeDir(workspacePath), 'project-info.json');
241
+ await fs.writeJson(infoPath, projectInfo, { spaces: 2 });
242
+ return projectInfo;
243
+ }
244
+ async function getProjectInfo(workspacePath) {
245
+ const infoPath = path.join(getBabelizeDir(workspacePath), 'project-info.json');
246
+ if (await fs.pathExists(infoPath)) {
247
+ try {
248
+ return await fs.readJson(infoPath);
249
+ }
250
+ catch {
251
+ return null;
252
+ }
253
+ }
254
+ return null;
255
+ }
@@ -0,0 +1,30 @@
1
+ import type { NodePath } from '@babel/traverse';
2
+ import * as t from '@babel/types';
3
+ /**
4
+ * UI copy in module-level constants, rendered inside components:
5
+ *
6
+ * const TABS = ["Overview", "Settings"];
7
+ * export default function Page() {
8
+ * return <nav>{TABS.map((tab) => <a key={tab}>{tab}</a>)}</nav>;
9
+ * }
10
+ *
11
+ * There's no hook at module level, so the strings can't be replaced in
12
+ * place. When every use of the copy is provably a render (a JSX child, a
13
+ * translatable attribute, or a React `key`), the constant's strings become
14
+ * translation keys and each render becomes `t(tab)`. Otherwise — the values
15
+ * are also data (compared, sent, used in URLs) — nothing is rewritten and the
16
+ * strings are reported, so a run doesn't claim everything was translated.
17
+ */
18
+ export interface ModuleConstantPlan {
19
+ name: string;
20
+ /** Every use of the copy is a render: safe to rewrite. */
21
+ provable: boolean;
22
+ /** The UI strings that are rendered (rewritten to keys when provable). */
23
+ literals: Array<NodePath<t.StringLiteral>>;
24
+ /** Expressions that render a string (`tab`, `item.label`), wrapped in `t()` when provable. */
25
+ renderSites: Array<NodePath<t.Expression>>;
26
+ /** Why it isn't provable, when it isn't. */
27
+ reason?: string;
28
+ }
29
+ /** Plans for a file's module-level UI constants that are rendered in components. */
30
+ export declare function analyzeModuleConstants(ast: t.File): ModuleConstantPlan[];