translate-kit 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1186 -244
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,108 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/scanner/filters.ts
|
|
13
|
+
function isLikelyKebabIdentifier(text2) {
|
|
14
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)+$/.test(text2)) return false;
|
|
15
|
+
return text2.split("-").length >= 3;
|
|
16
|
+
}
|
|
17
|
+
function isLikelyConstantIdentifier(text2) {
|
|
18
|
+
return /^(?=.*[_\d])[A-Z0-9_]+$/.test(text2);
|
|
19
|
+
}
|
|
20
|
+
function isContentProperty(propName) {
|
|
21
|
+
return CONTENT_PROPERTY_NAMES.includes(propName);
|
|
22
|
+
}
|
|
23
|
+
function isTranslatableProp(propName, customProps) {
|
|
24
|
+
if (NEVER_TRANSLATE_PROPS.includes(propName)) return false;
|
|
25
|
+
const allowed = customProps ?? DEFAULT_TRANSLATABLE_PROPS;
|
|
26
|
+
return allowed.includes(propName);
|
|
27
|
+
}
|
|
28
|
+
function isIgnoredTag(tagName) {
|
|
29
|
+
return IGNORE_TAGS.includes(tagName.toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
function shouldIgnore(text2) {
|
|
32
|
+
const trimmed = text2.trim();
|
|
33
|
+
if (trimmed.length === 0) return true;
|
|
34
|
+
if (isLikelyKebabIdentifier(trimmed)) return true;
|
|
35
|
+
if (isLikelyConstantIdentifier(trimmed)) return true;
|
|
36
|
+
return IGNORE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
37
|
+
}
|
|
38
|
+
var DEFAULT_TRANSLATABLE_PROPS, NEVER_TRANSLATE_PROPS, IGNORE_TAGS, IGNORE_PATTERNS, CONTENT_PROPERTY_NAMES;
|
|
39
|
+
var init_filters = __esm({
|
|
40
|
+
"src/scanner/filters.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
DEFAULT_TRANSLATABLE_PROPS = [
|
|
43
|
+
"placeholder",
|
|
44
|
+
"title",
|
|
45
|
+
"alt",
|
|
46
|
+
"aria-label",
|
|
47
|
+
"aria-description",
|
|
48
|
+
"aria-placeholder",
|
|
49
|
+
"label"
|
|
50
|
+
];
|
|
51
|
+
NEVER_TRANSLATE_PROPS = [
|
|
52
|
+
"className",
|
|
53
|
+
"class",
|
|
54
|
+
"id",
|
|
55
|
+
"key",
|
|
56
|
+
"ref",
|
|
57
|
+
"href",
|
|
58
|
+
"src",
|
|
59
|
+
"type",
|
|
60
|
+
"name",
|
|
61
|
+
"value",
|
|
62
|
+
"htmlFor",
|
|
63
|
+
"for",
|
|
64
|
+
"role",
|
|
65
|
+
"style",
|
|
66
|
+
"data-testid",
|
|
67
|
+
"data-cy",
|
|
68
|
+
"onClick",
|
|
69
|
+
"onChange",
|
|
70
|
+
"onSubmit",
|
|
71
|
+
"onFocus",
|
|
72
|
+
"onBlur"
|
|
73
|
+
];
|
|
74
|
+
IGNORE_TAGS = [
|
|
75
|
+
"script",
|
|
76
|
+
"style",
|
|
77
|
+
"code",
|
|
78
|
+
"pre",
|
|
79
|
+
"svg",
|
|
80
|
+
"path",
|
|
81
|
+
"circle",
|
|
82
|
+
"rect",
|
|
83
|
+
"line",
|
|
84
|
+
"polyline",
|
|
85
|
+
"polygon"
|
|
86
|
+
];
|
|
87
|
+
IGNORE_PATTERNS = [
|
|
88
|
+
/^\s*$/,
|
|
89
|
+
// Whitespace only
|
|
90
|
+
/^https?:\/\//,
|
|
91
|
+
// URLs
|
|
92
|
+
/^[\d.,%$€£¥]+$/,
|
|
93
|
+
// Numbers, currency
|
|
94
|
+
/^[^\p{L}]*$/u
|
|
95
|
+
// No letters at all (Unicode-aware)
|
|
96
|
+
];
|
|
97
|
+
CONTENT_PROPERTY_NAMES = [
|
|
98
|
+
"title",
|
|
99
|
+
"description",
|
|
100
|
+
"label",
|
|
101
|
+
"text",
|
|
102
|
+
"content",
|
|
103
|
+
"heading",
|
|
104
|
+
"subtitle",
|
|
105
|
+
"caption",
|
|
106
|
+
"summary",
|
|
107
|
+
"message",
|
|
108
|
+
"placeholder",
|
|
109
|
+
"alt"
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
12
114
|
// src/config.ts
|
|
13
115
|
import { loadConfig } from "c12";
|
|
14
116
|
import { z } from "zod";
|
|
@@ -33,6 +135,7 @@ var configSchema;
|
|
|
33
135
|
var init_config = __esm({
|
|
34
136
|
"src/config.ts"() {
|
|
35
137
|
"use strict";
|
|
138
|
+
init_filters();
|
|
36
139
|
configSchema = z.object({
|
|
37
140
|
model: z.custom(
|
|
38
141
|
(val) => val != null && typeof val === "object",
|
|
@@ -56,7 +159,7 @@ var init_config = __esm({
|
|
|
56
159
|
scan: z.object({
|
|
57
160
|
include: z.array(z.string()),
|
|
58
161
|
exclude: z.array(z.string()).optional(),
|
|
59
|
-
translatableProps: z.array(z.string()).default([
|
|
162
|
+
translatableProps: z.array(z.string()).default([...DEFAULT_TRANSLATABLE_PROPS]),
|
|
60
163
|
i18nImport: z.string().default("next-intl")
|
|
61
164
|
}).optional(),
|
|
62
165
|
inline: z.object({
|
|
@@ -188,108 +291,6 @@ var init_parser = __esm({
|
|
|
188
291
|
}
|
|
189
292
|
});
|
|
190
293
|
|
|
191
|
-
// src/scanner/filters.ts
|
|
192
|
-
function isLikelyKebabIdentifier(text2) {
|
|
193
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)+$/.test(text2)) return false;
|
|
194
|
-
return text2.split("-").length >= 3;
|
|
195
|
-
}
|
|
196
|
-
function isLikelyConstantIdentifier(text2) {
|
|
197
|
-
return /^(?=.*[_\d])[A-Z0-9_]+$/.test(text2);
|
|
198
|
-
}
|
|
199
|
-
function isContentProperty(propName) {
|
|
200
|
-
return CONTENT_PROPERTY_NAMES.includes(propName);
|
|
201
|
-
}
|
|
202
|
-
function isTranslatableProp(propName, customProps) {
|
|
203
|
-
if (NEVER_TRANSLATE_PROPS.includes(propName)) return false;
|
|
204
|
-
const allowed = customProps ?? DEFAULT_TRANSLATABLE_PROPS;
|
|
205
|
-
return allowed.includes(propName);
|
|
206
|
-
}
|
|
207
|
-
function isIgnoredTag(tagName) {
|
|
208
|
-
return IGNORE_TAGS.includes(tagName.toLowerCase());
|
|
209
|
-
}
|
|
210
|
-
function shouldIgnore(text2) {
|
|
211
|
-
const trimmed = text2.trim();
|
|
212
|
-
if (trimmed.length === 0) return true;
|
|
213
|
-
if (isLikelyKebabIdentifier(trimmed)) return true;
|
|
214
|
-
if (isLikelyConstantIdentifier(trimmed)) return true;
|
|
215
|
-
return IGNORE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
216
|
-
}
|
|
217
|
-
var DEFAULT_TRANSLATABLE_PROPS, NEVER_TRANSLATE_PROPS, IGNORE_TAGS, IGNORE_PATTERNS, CONTENT_PROPERTY_NAMES;
|
|
218
|
-
var init_filters = __esm({
|
|
219
|
-
"src/scanner/filters.ts"() {
|
|
220
|
-
"use strict";
|
|
221
|
-
DEFAULT_TRANSLATABLE_PROPS = [
|
|
222
|
-
"placeholder",
|
|
223
|
-
"title",
|
|
224
|
-
"alt",
|
|
225
|
-
"aria-label",
|
|
226
|
-
"aria-description",
|
|
227
|
-
"aria-placeholder",
|
|
228
|
-
"label"
|
|
229
|
-
];
|
|
230
|
-
NEVER_TRANSLATE_PROPS = [
|
|
231
|
-
"className",
|
|
232
|
-
"class",
|
|
233
|
-
"id",
|
|
234
|
-
"key",
|
|
235
|
-
"ref",
|
|
236
|
-
"href",
|
|
237
|
-
"src",
|
|
238
|
-
"type",
|
|
239
|
-
"name",
|
|
240
|
-
"value",
|
|
241
|
-
"htmlFor",
|
|
242
|
-
"for",
|
|
243
|
-
"role",
|
|
244
|
-
"style",
|
|
245
|
-
"data-testid",
|
|
246
|
-
"data-cy",
|
|
247
|
-
"onClick",
|
|
248
|
-
"onChange",
|
|
249
|
-
"onSubmit",
|
|
250
|
-
"onFocus",
|
|
251
|
-
"onBlur"
|
|
252
|
-
];
|
|
253
|
-
IGNORE_TAGS = [
|
|
254
|
-
"script",
|
|
255
|
-
"style",
|
|
256
|
-
"code",
|
|
257
|
-
"pre",
|
|
258
|
-
"svg",
|
|
259
|
-
"path",
|
|
260
|
-
"circle",
|
|
261
|
-
"rect",
|
|
262
|
-
"line",
|
|
263
|
-
"polyline",
|
|
264
|
-
"polygon"
|
|
265
|
-
];
|
|
266
|
-
IGNORE_PATTERNS = [
|
|
267
|
-
/^\s*$/,
|
|
268
|
-
// Whitespace only
|
|
269
|
-
/^https?:\/\//,
|
|
270
|
-
// URLs
|
|
271
|
-
/^[\d.,%$€£¥]+$/,
|
|
272
|
-
// Numbers, currency
|
|
273
|
-
/^[^\p{L}]*$/u
|
|
274
|
-
// No letters at all (Unicode-aware)
|
|
275
|
-
];
|
|
276
|
-
CONTENT_PROPERTY_NAMES = [
|
|
277
|
-
"title",
|
|
278
|
-
"description",
|
|
279
|
-
"label",
|
|
280
|
-
"text",
|
|
281
|
-
"content",
|
|
282
|
-
"heading",
|
|
283
|
-
"subtitle",
|
|
284
|
-
"caption",
|
|
285
|
-
"summary",
|
|
286
|
-
"message",
|
|
287
|
-
"placeholder",
|
|
288
|
-
"alt"
|
|
289
|
-
];
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
|
|
293
294
|
// src/utils/ast-helpers.ts
|
|
294
295
|
function resolveDefault(mod) {
|
|
295
296
|
if (typeof mod === "function") return mod;
|
|
@@ -374,6 +375,31 @@ function getComponentName(path) {
|
|
|
374
375
|
}
|
|
375
376
|
return void 0;
|
|
376
377
|
}
|
|
378
|
+
function isPascalCase(name) {
|
|
379
|
+
if (name === "__default__") return true;
|
|
380
|
+
return /^[A-Z]/.test(name);
|
|
381
|
+
}
|
|
382
|
+
function getTopLevelConstName(path) {
|
|
383
|
+
let current = path.parentPath;
|
|
384
|
+
while (current) {
|
|
385
|
+
if (current.isFunctionDeclaration() || current.isFunctionExpression() || current.isArrowFunctionExpression() || current.isClassDeclaration() || current.isClassExpression() || current.isClassMethod() || current.isClassPrivateMethod()) {
|
|
386
|
+
return void 0;
|
|
387
|
+
}
|
|
388
|
+
if (current.isVariableDeclarator()) {
|
|
389
|
+
const declParent = current.parentPath;
|
|
390
|
+
if (declParent?.isVariableDeclaration() && declParent.node.kind === "const") {
|
|
391
|
+
const grandParent = declParent.parentPath;
|
|
392
|
+
if (grandParent?.isProgram() || grandParent?.isExportNamedDeclaration()) {
|
|
393
|
+
const id = current.node.id;
|
|
394
|
+
if (id.type === "Identifier") return id.name;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
current = current.parentPath;
|
|
400
|
+
}
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
377
403
|
function getParentTagName(path) {
|
|
378
404
|
let current = path.parentPath;
|
|
379
405
|
while (current) {
|
|
@@ -617,11 +643,59 @@ function extractStrings(ast, filePath, translatableProps) {
|
|
|
617
643
|
});
|
|
618
644
|
},
|
|
619
645
|
ObjectProperty(path) {
|
|
620
|
-
|
|
646
|
+
const inFunction = isInsideFunction(path);
|
|
647
|
+
if (!inFunction) {
|
|
648
|
+
const constName = getTopLevelConstName(path);
|
|
649
|
+
if (!constName) return;
|
|
650
|
+
const keyNode2 = path.node.key;
|
|
651
|
+
if (keyNode2.type !== "Identifier" && keyNode2.type !== "StringLiteral")
|
|
652
|
+
return;
|
|
653
|
+
const propName2 = keyNode2.type === "Identifier" ? keyNode2.name : keyNode2.value;
|
|
654
|
+
if (!isContentProperty(propName2)) return;
|
|
655
|
+
const valueNode2 = path.node.value;
|
|
656
|
+
if (valueNode2.type === "ConditionalExpression") {
|
|
657
|
+
const texts = collectConditionalTexts(valueNode2);
|
|
658
|
+
for (const t3 of texts) {
|
|
659
|
+
results.push({
|
|
660
|
+
text: t3,
|
|
661
|
+
type: "module-object-property",
|
|
662
|
+
file: filePath,
|
|
663
|
+
line: valueNode2.loc?.start.line ?? 0,
|
|
664
|
+
column: valueNode2.loc?.start.column ?? 0,
|
|
665
|
+
propName: propName2,
|
|
666
|
+
parentConstName: constName
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
let text3;
|
|
672
|
+
if (valueNode2.type === "StringLiteral") {
|
|
673
|
+
text3 = valueNode2.value.trim();
|
|
674
|
+
} else if (valueNode2.type === "TemplateLiteral") {
|
|
675
|
+
const info = buildTemplateLiteralText(
|
|
676
|
+
valueNode2.quasis,
|
|
677
|
+
valueNode2.expressions
|
|
678
|
+
);
|
|
679
|
+
if (info) text3 = info.text;
|
|
680
|
+
}
|
|
681
|
+
if (!text3 || shouldIgnore(text3)) return;
|
|
682
|
+
results.push({
|
|
683
|
+
text: text3,
|
|
684
|
+
type: "module-object-property",
|
|
685
|
+
file: filePath,
|
|
686
|
+
line: valueNode2.loc?.start.line ?? 0,
|
|
687
|
+
column: valueNode2.loc?.start.column ?? 0,
|
|
688
|
+
propName: propName2,
|
|
689
|
+
parentConstName: constName
|
|
690
|
+
});
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
621
693
|
const ownerFn = getNearestFunctionPath(path);
|
|
622
694
|
if (!ownerFn) return;
|
|
623
695
|
if (!functionContainsJSX(ownerFn)) return;
|
|
624
|
-
const componentName = getComponentName(
|
|
696
|
+
const componentName = getComponentName(
|
|
697
|
+
ownerFn
|
|
698
|
+
);
|
|
625
699
|
if (!componentName) return;
|
|
626
700
|
const keyNode = path.node.key;
|
|
627
701
|
if (keyNode.type !== "Identifier" && keyNode.type !== "StringLiteral")
|
|
@@ -740,14 +814,14 @@ var init_extractor = __esm({
|
|
|
740
814
|
|
|
741
815
|
// src/scanner/context-enricher.ts
|
|
742
816
|
function deriveRoutePath(filePath) {
|
|
743
|
-
const appMatch = filePath.match(/src\/app\/(.+?)\/page\.[jt]sx?$/);
|
|
817
|
+
const appMatch = filePath.match(/(?:src\/)?app\/(.+?)\/page\.[jt]sx?$/);
|
|
744
818
|
if (appMatch) return appMatch[1].replace(/\//g, ".");
|
|
745
819
|
const pagesMatch = filePath.match(/(?:src\/)?pages\/(.+?)\.[jt]sx?$/);
|
|
746
820
|
if (pagesMatch) {
|
|
747
821
|
const route = pagesMatch[1].replace(/\/index$/, "").replace(/\//g, ".");
|
|
748
822
|
return route || void 0;
|
|
749
823
|
}
|
|
750
|
-
const compMatch = filePath.match(/src\/components\/(.+?)\//);
|
|
824
|
+
const compMatch = filePath.match(/(?:src\/)?components\/(.+?)\//);
|
|
751
825
|
if (compMatch) return compMatch[1];
|
|
752
826
|
return void 0;
|
|
753
827
|
}
|
|
@@ -981,6 +1055,7 @@ function buildPrompt(strings, existingMap) {
|
|
|
981
1055
|
if (str.componentName) parts.push(`component: ${str.componentName}`);
|
|
982
1056
|
if (str.parentTag) parts.push(`tag: ${str.parentTag}`);
|
|
983
1057
|
if (str.propName) parts.push(`prop: ${str.propName}`);
|
|
1058
|
+
if (str.parentConstName) parts.push(`const: ${str.parentConstName}`);
|
|
984
1059
|
if (str.sectionHeading) parts.push(`section: "${str.sectionHeading}"`);
|
|
985
1060
|
if (str.siblingTexts?.length) {
|
|
986
1061
|
parts.push(
|
|
@@ -1368,7 +1443,11 @@ function resolveNamedImportLocal(ast, importSource, importedName) {
|
|
|
1368
1443
|
return null;
|
|
1369
1444
|
}
|
|
1370
1445
|
function ensureNamedImportLocal(ast, importSource, importedName) {
|
|
1371
|
-
const existingLocal = resolveNamedImportLocal(
|
|
1446
|
+
const existingLocal = resolveNamedImportLocal(
|
|
1447
|
+
ast,
|
|
1448
|
+
importSource,
|
|
1449
|
+
importedName
|
|
1450
|
+
);
|
|
1372
1451
|
if (existingLocal) return existingLocal;
|
|
1373
1452
|
for (const node of ast.program.body) {
|
|
1374
1453
|
if (node.type !== "ImportDeclaration" || node.source.value !== importSource) {
|
|
@@ -1380,12 +1459,7 @@ function ensureNamedImportLocal(ast, importSource, importedName) {
|
|
|
1380
1459
|
return importedName;
|
|
1381
1460
|
}
|
|
1382
1461
|
const importDecl = t2.importDeclaration(
|
|
1383
|
-
[
|
|
1384
|
-
t2.importSpecifier(
|
|
1385
|
-
t2.identifier(importedName),
|
|
1386
|
-
t2.identifier(importedName)
|
|
1387
|
-
)
|
|
1388
|
-
],
|
|
1462
|
+
[t2.importSpecifier(t2.identifier(importedName), t2.identifier(importedName))],
|
|
1389
1463
|
t2.stringLiteral(importSource)
|
|
1390
1464
|
);
|
|
1391
1465
|
const lastImportIndex = findLastImportIndex(ast);
|
|
@@ -1562,6 +1636,142 @@ function transformConditionalBranchInline(node, textToKey) {
|
|
|
1562
1636
|
}
|
|
1563
1637
|
return { node, count: 0 };
|
|
1564
1638
|
}
|
|
1639
|
+
function preDiscoverFactoryRefComponents(ast, importedNames) {
|
|
1640
|
+
if (importedNames.length === 0) return /* @__PURE__ */ new Set();
|
|
1641
|
+
const nameSet = new Set(importedNames);
|
|
1642
|
+
const comps = /* @__PURE__ */ new Set();
|
|
1643
|
+
traverse2(ast, {
|
|
1644
|
+
Identifier(path) {
|
|
1645
|
+
if (!nameSet.has(path.node.name)) return;
|
|
1646
|
+
const parent = path.parent;
|
|
1647
|
+
if (parent.type === "ImportSpecifier" || parent.type === "ImportDefaultSpecifier" || parent.type === "ImportNamespaceSpecifier" || parent.type === "ExportSpecifier") return;
|
|
1648
|
+
if (parent.type === "VariableDeclarator" && parent.id === path.node) return;
|
|
1649
|
+
if (parent.type === "CallExpression" && parent.callee === path.node) return;
|
|
1650
|
+
const binding = path.scope?.getBinding(path.node.name);
|
|
1651
|
+
if (binding) {
|
|
1652
|
+
const bPath = binding.path;
|
|
1653
|
+
const isImportBinding = bPath.isImportSpecifier() || bPath.isImportDefaultSpecifier();
|
|
1654
|
+
const isTopLevelConst = bPath.isVariableDeclarator() && bPath.parentPath?.isVariableDeclaration() && (bPath.parentPath.parentPath?.isProgram() || bPath.parentPath.parentPath?.isExportNamedDeclaration());
|
|
1655
|
+
if (!isImportBinding && !isTopLevelConst) return;
|
|
1656
|
+
}
|
|
1657
|
+
if (!isInsideFunction(path)) return;
|
|
1658
|
+
const comp = getComponentName(path);
|
|
1659
|
+
if (comp && isPascalCase(comp)) comps.add(comp);
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
return comps;
|
|
1663
|
+
}
|
|
1664
|
+
function wrapModuleFactoryDeclarations(ast, constNames) {
|
|
1665
|
+
if (constNames.length === 0) return false;
|
|
1666
|
+
const nameSet = new Set(constNames);
|
|
1667
|
+
let wrapped = false;
|
|
1668
|
+
for (const node of ast.program.body) {
|
|
1669
|
+
let decl;
|
|
1670
|
+
if (node.type === "VariableDeclaration") {
|
|
1671
|
+
decl = node;
|
|
1672
|
+
} else if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
|
|
1673
|
+
decl = node.declaration;
|
|
1674
|
+
}
|
|
1675
|
+
if (!decl || decl.kind !== "const") continue;
|
|
1676
|
+
for (const declarator of decl.declarations) {
|
|
1677
|
+
if (declarator.id.type !== "Identifier") continue;
|
|
1678
|
+
if (!nameSet.has(declarator.id.name)) continue;
|
|
1679
|
+
if (!declarator.init) continue;
|
|
1680
|
+
const typeAnnotation = declarator.id.typeAnnotation;
|
|
1681
|
+
if (typeAnnotation) {
|
|
1682
|
+
declarator.id.typeAnnotation = null;
|
|
1683
|
+
}
|
|
1684
|
+
if (declarator.init.type === "ArrowFunctionExpression" && declarator.init.params.length === 1 && declarator.init.params[0].type === "Identifier" && declarator.init.params[0].name === "t") {
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
const originalInit = declarator.init;
|
|
1688
|
+
const tParam = t2.identifier("t");
|
|
1689
|
+
tParam.typeAnnotation = t2.tsTypeAnnotation(t2.tsAnyKeyword());
|
|
1690
|
+
const arrowFn = t2.arrowFunctionExpression(
|
|
1691
|
+
[tParam],
|
|
1692
|
+
t2.parenthesizedExpression(originalInit)
|
|
1693
|
+
);
|
|
1694
|
+
if (typeAnnotation) {
|
|
1695
|
+
arrowFn.returnType = typeAnnotation;
|
|
1696
|
+
}
|
|
1697
|
+
declarator.init = arrowFn;
|
|
1698
|
+
wrapped = true;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return wrapped;
|
|
1702
|
+
}
|
|
1703
|
+
function isInsideFunctionParam(path) {
|
|
1704
|
+
let current = path;
|
|
1705
|
+
while (current.parentPath) {
|
|
1706
|
+
if (current.parentPath.isFunction()) {
|
|
1707
|
+
const fn = current.parentPath.node;
|
|
1708
|
+
return fn.params.includes(current.node);
|
|
1709
|
+
}
|
|
1710
|
+
current = current.parentPath;
|
|
1711
|
+
}
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
function rewriteModuleFactoryReferences(ast, importedNames, componentTranslatorIds) {
|
|
1715
|
+
if (importedNames.length === 0) return { componentsNeedingT: /* @__PURE__ */ new Set(), rewrote: false };
|
|
1716
|
+
const nameSet = new Set(importedNames);
|
|
1717
|
+
const componentsNeedingT = /* @__PURE__ */ new Set();
|
|
1718
|
+
let rewrote = false;
|
|
1719
|
+
const rewrites = [];
|
|
1720
|
+
traverse2(ast, {
|
|
1721
|
+
Identifier(path) {
|
|
1722
|
+
if (!nameSet.has(path.node.name)) return;
|
|
1723
|
+
const parent = path.parent;
|
|
1724
|
+
if (parent.type === "ImportSpecifier" || parent.type === "ImportDefaultSpecifier" || parent.type === "ImportNamespaceSpecifier" || parent.type === "ExportSpecifier") {
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
if (parent.type.startsWith("TS") && parent.type !== "TSNonNullExpression" && parent.type !== "TSAsExpression" && parent.type !== "TSSatisfiesExpression") {
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
if (parent.type === "CallExpression" && parent.callee === path.node) {
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
if (parent.type === "ObjectProperty" && parent.key === path.node) {
|
|
1734
|
+
if (parent.shorthand) {
|
|
1735
|
+
if (path.key === "key") return;
|
|
1736
|
+
} else {
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
const binding = path.scope?.getBinding(path.node.name);
|
|
1741
|
+
if (binding) {
|
|
1742
|
+
const bPath = binding.path;
|
|
1743
|
+
const isImportBinding = bPath.isImportSpecifier() || bPath.isImportDefaultSpecifier();
|
|
1744
|
+
const isTopLevelConst = bPath.isVariableDeclarator() && bPath.parentPath?.isVariableDeclaration() && (bPath.parentPath.parentPath?.isProgram() || bPath.parentPath.parentPath?.isExportNamedDeclaration());
|
|
1745
|
+
if (!isImportBinding && !isTopLevelConst) return;
|
|
1746
|
+
}
|
|
1747
|
+
if (isInsideFunctionParam(path)) return;
|
|
1748
|
+
if (!isInsideFunction(path)) return;
|
|
1749
|
+
const compName = getComponentName(path);
|
|
1750
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
1751
|
+
const translatorId = componentTranslatorIds.get(compName) ?? "t";
|
|
1752
|
+
if (parent.type === "ObjectProperty" && parent.shorthand) {
|
|
1753
|
+
rewrites.push({ path, translatorId, compName, shorthandProp: parent });
|
|
1754
|
+
} else {
|
|
1755
|
+
rewrites.push({ path, translatorId, compName });
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
});
|
|
1759
|
+
for (const { path, translatorId, compName, shorthandProp } of rewrites) {
|
|
1760
|
+
const callExpr = t2.callExpression(
|
|
1761
|
+
t2.identifier(path.node.name),
|
|
1762
|
+
[t2.identifier(translatorId)]
|
|
1763
|
+
);
|
|
1764
|
+
if (shorthandProp) {
|
|
1765
|
+
shorthandProp.shorthand = false;
|
|
1766
|
+
shorthandProp.value = callExpr;
|
|
1767
|
+
} else {
|
|
1768
|
+
path.replaceWith(callExpr);
|
|
1769
|
+
}
|
|
1770
|
+
componentsNeedingT.add(compName);
|
|
1771
|
+
rewrote = true;
|
|
1772
|
+
}
|
|
1773
|
+
return { componentsNeedingT, rewrote };
|
|
1774
|
+
}
|
|
1565
1775
|
function transform(ast, textToKey, options = {}) {
|
|
1566
1776
|
if (options.mode === "inline") {
|
|
1567
1777
|
return transformInline(ast, textToKey, options);
|
|
@@ -1592,6 +1802,8 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1592
1802
|
if (isInsideClass(path)) return;
|
|
1593
1803
|
const text2 = path.node.value.trim();
|
|
1594
1804
|
if (!text2 || !(text2 in textToKey)) return;
|
|
1805
|
+
const compName = getComponentName(path);
|
|
1806
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
1595
1807
|
const parent = path.parentPath;
|
|
1596
1808
|
if (!parent?.isJSXElement()) return;
|
|
1597
1809
|
const key = textToKey[text2];
|
|
@@ -1623,6 +1835,8 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1623
1835
|
if (isInsideClass(path)) return;
|
|
1624
1836
|
const expr = path.node.expression;
|
|
1625
1837
|
if (path.parent.type === "JSXAttribute") return;
|
|
1838
|
+
const compName = getComponentName(path);
|
|
1839
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
1626
1840
|
if (expr.type === "ConditionalExpression") {
|
|
1627
1841
|
const result = transformConditionalBranch(expr, textToKey);
|
|
1628
1842
|
if (result.count > 0) {
|
|
@@ -1654,6 +1868,11 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1654
1868
|
if (isInsideClass(path)) return;
|
|
1655
1869
|
const value = path.node.value;
|
|
1656
1870
|
if (!value) return;
|
|
1871
|
+
const attrName = path.node.name;
|
|
1872
|
+
const propName = attrName.type === "JSXIdentifier" ? attrName.name : attrName.name.name;
|
|
1873
|
+
if (!isTranslatableProp(propName, options.translatableProps)) return;
|
|
1874
|
+
const compName = getComponentName(path);
|
|
1875
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
1657
1876
|
if (value.type === "JSXExpressionContainer" && value.expression.type === "ConditionalExpression") {
|
|
1658
1877
|
const result = transformConditionalBranch(value.expression, textToKey);
|
|
1659
1878
|
if (result.count > 0) {
|
|
@@ -1708,8 +1927,66 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1708
1927
|
},
|
|
1709
1928
|
ObjectProperty(path) {
|
|
1710
1929
|
if (isInsideClass(path)) return;
|
|
1711
|
-
|
|
1712
|
-
const
|
|
1930
|
+
const factoryConstNames = options.moduleFactoryConstNames ?? [];
|
|
1931
|
+
const inFunction = isInsideFunction(path);
|
|
1932
|
+
if (!inFunction) {
|
|
1933
|
+
const constName = getTopLevelConstName(path);
|
|
1934
|
+
if (!constName || !factoryConstNames.includes(constName)) return;
|
|
1935
|
+
const keyNode2 = path.node.key;
|
|
1936
|
+
if (keyNode2.type !== "Identifier" && keyNode2.type !== "StringLiteral")
|
|
1937
|
+
return;
|
|
1938
|
+
const propName2 = keyNode2.type === "Identifier" ? keyNode2.name : keyNode2.value;
|
|
1939
|
+
if (!isContentProperty(propName2)) return;
|
|
1940
|
+
const valueNode2 = path.node.value;
|
|
1941
|
+
if (valueNode2.type === "ConditionalExpression") {
|
|
1942
|
+
const result = transformConditionalBranch(valueNode2, textToKey);
|
|
1943
|
+
if (result.count > 0) {
|
|
1944
|
+
path.node.value = result.node;
|
|
1945
|
+
stringsWrapped += result.count;
|
|
1946
|
+
collectConditionalKeys(valueNode2, textToKey).forEach(
|
|
1947
|
+
(k) => allUsedKeys.push(k)
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
let text3;
|
|
1953
|
+
let templateInfo2;
|
|
1954
|
+
if (valueNode2.type === "StringLiteral") {
|
|
1955
|
+
text3 = valueNode2.value;
|
|
1956
|
+
} else if (valueNode2.type === "TemplateLiteral") {
|
|
1957
|
+
const info = buildTemplateLiteralText(
|
|
1958
|
+
valueNode2.quasis,
|
|
1959
|
+
valueNode2.expressions
|
|
1960
|
+
);
|
|
1961
|
+
if (info) {
|
|
1962
|
+
text3 = info.text;
|
|
1963
|
+
templateInfo2 = {
|
|
1964
|
+
placeholders: info.placeholders,
|
|
1965
|
+
expressions: valueNode2.expressions
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
if (!text3 || !(text3 in textToKey)) return;
|
|
1970
|
+
const key2 = textToKey[text3];
|
|
1971
|
+
const args2 = [t2.stringLiteral(key2)];
|
|
1972
|
+
if (templateInfo2 && templateInfo2.placeholders.length > 0) {
|
|
1973
|
+
args2.push(
|
|
1974
|
+
buildValuesObject(
|
|
1975
|
+
templateInfo2.expressions,
|
|
1976
|
+
templateInfo2.placeholders
|
|
1977
|
+
)
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
path.node.value = markGeneratedCall(
|
|
1981
|
+
t2.callExpression(t2.identifier("t"), args2)
|
|
1982
|
+
);
|
|
1983
|
+
stringsWrapped++;
|
|
1984
|
+
allUsedKeys.push(key2);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
const ownerFn = getNearestFunctionPath2(
|
|
1988
|
+
path
|
|
1989
|
+
);
|
|
1713
1990
|
if (!ownerFn) return;
|
|
1714
1991
|
if (!functionContainsJSX2(ownerFn.node)) return;
|
|
1715
1992
|
const compName = getComponentName(ownerFn);
|
|
@@ -1766,7 +2043,8 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1766
2043
|
trackKey(path, key);
|
|
1767
2044
|
}
|
|
1768
2045
|
});
|
|
1769
|
-
|
|
2046
|
+
const hasModuleFactoryWork = (options.moduleFactoryConstNames?.length ?? 0) > 0 || (options.moduleFactoryImportedNames?.length ?? 0) > 0;
|
|
2047
|
+
if (stringsWrapped === 0 && !hasModuleFactoryWork) {
|
|
1770
2048
|
return {
|
|
1771
2049
|
code: generate(ast).code,
|
|
1772
2050
|
stringsWrapped: 0,
|
|
@@ -1774,18 +2052,28 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1774
2052
|
usedKeys: []
|
|
1775
2053
|
};
|
|
1776
2054
|
}
|
|
2055
|
+
const moduleFactoryImportedNames = options.moduleFactoryImportedNames ?? [];
|
|
2056
|
+
const factoryRefComponents = preDiscoverFactoryRefComponents(ast, moduleFactoryImportedNames);
|
|
2057
|
+
for (const comp of factoryRefComponents) {
|
|
2058
|
+
componentsNeedingT.add(comp);
|
|
2059
|
+
}
|
|
1777
2060
|
const componentNamespaces = /* @__PURE__ */ new Map();
|
|
1778
2061
|
for (const [comp, keys] of componentKeys) {
|
|
1779
|
-
|
|
2062
|
+
if (factoryRefComponents.has(comp)) {
|
|
2063
|
+
componentNamespaces.set(comp, null);
|
|
2064
|
+
} else {
|
|
2065
|
+
componentNamespaces.set(comp, detectNamespace([...keys]));
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
for (const comp of factoryRefComponents) {
|
|
2069
|
+
if (!componentNamespaces.has(comp)) {
|
|
2070
|
+
componentNamespaces.set(comp, null);
|
|
2071
|
+
}
|
|
1780
2072
|
}
|
|
1781
2073
|
const namespacedComponents = /* @__PURE__ */ new Set();
|
|
1782
2074
|
const componentTranslatorIds = /* @__PURE__ */ new Map();
|
|
1783
2075
|
if (isClient) {
|
|
1784
|
-
const useTranslationsLocal = ensureNamedImportLocal(
|
|
1785
|
-
ast,
|
|
1786
|
-
importSource,
|
|
1787
|
-
"useTranslations"
|
|
1788
|
-
);
|
|
2076
|
+
const useTranslationsLocal = componentsNeedingT.size > 0 ? ensureNamedImportLocal(ast, importSource, "useTranslations") : "useTranslations";
|
|
1789
2077
|
traverse2(ast, {
|
|
1790
2078
|
FunctionDeclaration(path) {
|
|
1791
2079
|
const name = getComponentName(path);
|
|
@@ -1881,11 +2169,7 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1881
2169
|
});
|
|
1882
2170
|
} else {
|
|
1883
2171
|
const serverSource = `${importSource}/server`;
|
|
1884
|
-
const getTranslationsLocal = ensureNamedImportLocal(
|
|
1885
|
-
ast,
|
|
1886
|
-
serverSource,
|
|
1887
|
-
"getTranslations"
|
|
1888
|
-
);
|
|
2172
|
+
const getTranslationsLocal = componentsNeedingT.size > 0 ? ensureNamedImportLocal(ast, serverSource, "getTranslations") : "getTranslations";
|
|
1889
2173
|
traverse2(ast, {
|
|
1890
2174
|
FunctionDeclaration(path) {
|
|
1891
2175
|
const name = getComponentName(path);
|
|
@@ -1991,20 +2275,31 @@ function transform(ast, textToKey, options = {}) {
|
|
|
1991
2275
|
noScope: true
|
|
1992
2276
|
});
|
|
1993
2277
|
}
|
|
2278
|
+
const moduleFactoryConstNames = options.moduleFactoryConstNames ?? [];
|
|
2279
|
+
const didWrapFactory = wrapModuleFactoryDeclarations(ast, moduleFactoryConstNames);
|
|
1994
2280
|
const effectiveNamespaces = /* @__PURE__ */ new Map();
|
|
1995
2281
|
for (const [comp, ns] of componentNamespaces) {
|
|
1996
2282
|
effectiveNamespaces.set(comp, namespacedComponents.has(comp) ? ns : null);
|
|
1997
2283
|
}
|
|
1998
2284
|
rewriteKeysForNamespaces(ast, effectiveNamespaces);
|
|
1999
2285
|
rewriteGeneratedCallsForTranslator(ast, componentTranslatorIds);
|
|
2286
|
+
const { componentsNeedingT: factoryComponentsNeedingT, rewrote: didRewriteRefs } = rewriteModuleFactoryReferences(
|
|
2287
|
+
ast,
|
|
2288
|
+
moduleFactoryImportedNames,
|
|
2289
|
+
componentTranslatorIds
|
|
2290
|
+
);
|
|
2291
|
+
for (const comp of factoryComponentsNeedingT) {
|
|
2292
|
+
componentsNeedingT.add(comp);
|
|
2293
|
+
}
|
|
2000
2294
|
if (needsClientDirective && componentsNeedingT.size > 0) {
|
|
2001
2295
|
addUseClientDirective(ast);
|
|
2002
2296
|
}
|
|
2297
|
+
const didModify = stringsWrapped > 0 || didWrapFactory || didRewriteRefs;
|
|
2003
2298
|
const output = generate(ast, { retainLines: false });
|
|
2004
2299
|
return {
|
|
2005
2300
|
code: output.code,
|
|
2006
2301
|
stringsWrapped,
|
|
2007
|
-
modified:
|
|
2302
|
+
modified: didModify,
|
|
2008
2303
|
usedKeys: allUsedKeys
|
|
2009
2304
|
};
|
|
2010
2305
|
}
|
|
@@ -2044,7 +2339,10 @@ function injectTIntoBlock(block, useTranslationsLocal, namespace, componentName,
|
|
|
2044
2339
|
if (d.id.type !== "Identifier") continue;
|
|
2045
2340
|
if (isTranslationFactoryCall(d.init, useTranslationsLocal)) {
|
|
2046
2341
|
return {
|
|
2047
|
-
namespaced: updateCallNamespace(
|
|
2342
|
+
namespaced: updateCallNamespace(
|
|
2343
|
+
d.init,
|
|
2344
|
+
namespace
|
|
2345
|
+
),
|
|
2048
2346
|
translatorId: d.id.name
|
|
2049
2347
|
};
|
|
2050
2348
|
}
|
|
@@ -2088,7 +2386,10 @@ function injectAsyncTIntoBlock(block, getTranslationsLocal, namespace, component
|
|
|
2088
2386
|
}
|
|
2089
2387
|
if (isGetTranslationsCall(d.init, getTranslationsLocal)) {
|
|
2090
2388
|
return {
|
|
2091
|
-
namespaced: updateCallNamespace(
|
|
2389
|
+
namespaced: updateCallNamespace(
|
|
2390
|
+
d.init,
|
|
2391
|
+
namespace
|
|
2392
|
+
),
|
|
2092
2393
|
translatorId: d.id.name
|
|
2093
2394
|
};
|
|
2094
2395
|
}
|
|
@@ -2291,9 +2592,7 @@ function addUseClientDirective(ast) {
|
|
|
2291
2592
|
if (!ast.program.directives) {
|
|
2292
2593
|
ast.program.directives = [];
|
|
2293
2594
|
}
|
|
2294
|
-
ast.program.directives.unshift(
|
|
2295
|
-
t2.directive(t2.directiveLiteral("use client"))
|
|
2296
|
-
);
|
|
2595
|
+
ast.program.directives.unshift(t2.directive(t2.directiveLiteral("use client")));
|
|
2297
2596
|
}
|
|
2298
2597
|
function transformInline(ast, textToKey, options) {
|
|
2299
2598
|
const componentPath = options.componentPath ?? "@/components/t";
|
|
@@ -2327,6 +2626,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2327
2626
|
if (isInsideClass(path)) return;
|
|
2328
2627
|
const text2 = path.node.value.trim();
|
|
2329
2628
|
if (!text2 || !(text2 in textToKey)) return;
|
|
2629
|
+
const compName = getComponentName(path);
|
|
2630
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
2330
2631
|
const parent = path.parentPath;
|
|
2331
2632
|
if (!parent?.isJSXElement()) return;
|
|
2332
2633
|
const parentOpening = parent.node.openingElement;
|
|
@@ -2365,13 +2666,14 @@ function transformInline(ast, textToKey, options) {
|
|
|
2365
2666
|
if (isInsideClass(path)) return;
|
|
2366
2667
|
const expr = path.node.expression;
|
|
2367
2668
|
if (path.parent.type === "JSXAttribute") return;
|
|
2669
|
+
const compName = getComponentName(path);
|
|
2670
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
2368
2671
|
if (expr.type === "ConditionalExpression") {
|
|
2369
2672
|
const result = transformConditionalBranchInline(expr, textToKey);
|
|
2370
2673
|
if (result.count > 0) {
|
|
2371
2674
|
path.node.expression = result.node;
|
|
2372
2675
|
stringsWrapped += result.count;
|
|
2373
|
-
|
|
2374
|
-
if (compName2) componentsNeedingT.add(compName2);
|
|
2676
|
+
componentsNeedingT.add(compName);
|
|
2375
2677
|
}
|
|
2376
2678
|
return;
|
|
2377
2679
|
}
|
|
@@ -2390,13 +2692,17 @@ function transformInline(ast, textToKey, options) {
|
|
|
2390
2692
|
t2.callExpression(t2.identifier("t"), args)
|
|
2391
2693
|
);
|
|
2392
2694
|
stringsWrapped++;
|
|
2393
|
-
|
|
2394
|
-
if (compName) componentsNeedingT.add(compName);
|
|
2695
|
+
componentsNeedingT.add(compName);
|
|
2395
2696
|
},
|
|
2396
2697
|
JSXAttribute(path) {
|
|
2397
2698
|
if (isInsideClass(path)) return;
|
|
2398
2699
|
const value = path.node.value;
|
|
2399
2700
|
if (!value) return;
|
|
2701
|
+
const attrName = path.node.name;
|
|
2702
|
+
const propName = attrName.type === "JSXIdentifier" ? attrName.name : attrName.name.name;
|
|
2703
|
+
if (!isTranslatableProp(propName, options.translatableProps)) return;
|
|
2704
|
+
const compName = getComponentName(path);
|
|
2705
|
+
if (!compName || !isPascalCase(compName)) return;
|
|
2400
2706
|
if (value.type === "JSXExpressionContainer" && value.expression.type === "ConditionalExpression") {
|
|
2401
2707
|
const result = transformConditionalBranchInline(
|
|
2402
2708
|
value.expression,
|
|
@@ -2405,8 +2711,7 @@ function transformInline(ast, textToKey, options) {
|
|
|
2405
2711
|
if (result.count > 0) {
|
|
2406
2712
|
path.node.value = t2.jsxExpressionContainer(result.node);
|
|
2407
2713
|
stringsWrapped += result.count;
|
|
2408
|
-
|
|
2409
|
-
if (compName2) componentsNeedingT.add(compName2);
|
|
2714
|
+
componentsNeedingT.add(compName);
|
|
2410
2715
|
}
|
|
2411
2716
|
return;
|
|
2412
2717
|
}
|
|
@@ -2452,13 +2757,69 @@ function transformInline(ast, textToKey, options) {
|
|
|
2452
2757
|
markGeneratedCall(t2.callExpression(t2.identifier("t"), args))
|
|
2453
2758
|
);
|
|
2454
2759
|
stringsWrapped++;
|
|
2455
|
-
|
|
2456
|
-
if (compName) componentsNeedingT.add(compName);
|
|
2760
|
+
componentsNeedingT.add(compName);
|
|
2457
2761
|
},
|
|
2458
2762
|
ObjectProperty(path) {
|
|
2459
2763
|
if (isInsideClass(path)) return;
|
|
2460
|
-
|
|
2461
|
-
const
|
|
2764
|
+
const factoryConstNames = options.moduleFactoryConstNames ?? [];
|
|
2765
|
+
const inFunction = isInsideFunction(path);
|
|
2766
|
+
if (!inFunction) {
|
|
2767
|
+
const constName = getTopLevelConstName(path);
|
|
2768
|
+
if (!constName || !factoryConstNames.includes(constName)) return;
|
|
2769
|
+
const keyNode2 = path.node.key;
|
|
2770
|
+
if (keyNode2.type !== "Identifier" && keyNode2.type !== "StringLiteral")
|
|
2771
|
+
return;
|
|
2772
|
+
const propName2 = keyNode2.type === "Identifier" ? keyNode2.name : keyNode2.value;
|
|
2773
|
+
if (!isContentProperty(propName2)) return;
|
|
2774
|
+
const valueNode2 = path.node.value;
|
|
2775
|
+
if (valueNode2.type === "ConditionalExpression") {
|
|
2776
|
+
const result = transformConditionalBranchInline(valueNode2, textToKey);
|
|
2777
|
+
if (result.count > 0) {
|
|
2778
|
+
path.node.value = result.node;
|
|
2779
|
+
stringsWrapped += result.count;
|
|
2780
|
+
}
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
let text3;
|
|
2784
|
+
let templateInfo2;
|
|
2785
|
+
if (valueNode2.type === "StringLiteral") {
|
|
2786
|
+
text3 = valueNode2.value;
|
|
2787
|
+
} else if (valueNode2.type === "TemplateLiteral") {
|
|
2788
|
+
const info = buildTemplateLiteralText(
|
|
2789
|
+
valueNode2.quasis,
|
|
2790
|
+
valueNode2.expressions
|
|
2791
|
+
);
|
|
2792
|
+
if (info) {
|
|
2793
|
+
text3 = info.text;
|
|
2794
|
+
templateInfo2 = {
|
|
2795
|
+
placeholders: info.placeholders,
|
|
2796
|
+
expressions: valueNode2.expressions
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if (!text3 || !(text3 in textToKey)) return;
|
|
2801
|
+
const key2 = textToKey[text3];
|
|
2802
|
+
const args2 = [
|
|
2803
|
+
t2.stringLiteral(text3),
|
|
2804
|
+
t2.stringLiteral(key2)
|
|
2805
|
+
];
|
|
2806
|
+
if (templateInfo2 && templateInfo2.placeholders.length > 0) {
|
|
2807
|
+
args2.push(
|
|
2808
|
+
buildValuesObject(
|
|
2809
|
+
templateInfo2.expressions,
|
|
2810
|
+
templateInfo2.placeholders
|
|
2811
|
+
)
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
path.node.value = markGeneratedCall(
|
|
2815
|
+
t2.callExpression(t2.identifier("t"), args2)
|
|
2816
|
+
);
|
|
2817
|
+
stringsWrapped++;
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
const ownerFn = getNearestFunctionPath2(
|
|
2821
|
+
path
|
|
2822
|
+
);
|
|
2462
2823
|
if (!ownerFn) return;
|
|
2463
2824
|
if (!functionContainsJSX2(ownerFn.node)) return;
|
|
2464
2825
|
const compName = getComponentName(ownerFn);
|
|
@@ -2516,7 +2877,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2516
2877
|
componentsNeedingT.add(compName);
|
|
2517
2878
|
}
|
|
2518
2879
|
});
|
|
2519
|
-
|
|
2880
|
+
const hasModuleFactoryWorkInline = (options.moduleFactoryConstNames?.length ?? 0) > 0 || (options.moduleFactoryImportedNames?.length ?? 0) > 0;
|
|
2881
|
+
if (stringsWrapped === 0 && !repaired && !boundaryRepaired && !hasModuleFactoryWorkInline) {
|
|
2520
2882
|
return {
|
|
2521
2883
|
code: generate(ast).code,
|
|
2522
2884
|
stringsWrapped: 0,
|
|
@@ -2524,7 +2886,7 @@ function transformInline(ast, textToKey, options) {
|
|
|
2524
2886
|
usedKeys: []
|
|
2525
2887
|
};
|
|
2526
2888
|
}
|
|
2527
|
-
if (stringsWrapped === 0 && (repaired || boundaryRepaired)) {
|
|
2889
|
+
if (stringsWrapped === 0 && !hasModuleFactoryWorkInline && (repaired || boundaryRepaired)) {
|
|
2528
2890
|
if (needsClientDirective && boundaryRepaired) {
|
|
2529
2891
|
addUseClientDirective(ast);
|
|
2530
2892
|
}
|
|
@@ -2536,6 +2898,11 @@ function transformInline(ast, textToKey, options) {
|
|
|
2536
2898
|
usedKeys: []
|
|
2537
2899
|
};
|
|
2538
2900
|
}
|
|
2901
|
+
const moduleFactoryImportedNamesInline = options.moduleFactoryImportedNames ?? [];
|
|
2902
|
+
const factoryRefComponentsInline = preDiscoverFactoryRefComponents(ast, moduleFactoryImportedNamesInline);
|
|
2903
|
+
for (const comp of factoryRefComponentsInline) {
|
|
2904
|
+
componentsNeedingT.add(comp);
|
|
2905
|
+
}
|
|
2539
2906
|
const needsHook = componentsNeedingT.size > 0;
|
|
2540
2907
|
const hookName = isClient ? "useT" : "createT";
|
|
2541
2908
|
const importPath = isClient ? componentPath : `${componentPath}-server`;
|
|
@@ -2585,6 +2952,7 @@ function transformInline(ast, textToKey, options) {
|
|
|
2585
2952
|
const importedHookName = isClient ? "useT" : "createT";
|
|
2586
2953
|
const hookLocalName = resolveImportedLocalName(ast, hookSources, importedHookName) ?? importedHookName;
|
|
2587
2954
|
const hookCall = isClient ? t2.callExpression(t2.identifier(hookLocalName), []) : t2.callExpression(t2.identifier(hookLocalName), []);
|
|
2955
|
+
const serverAwait = !isClient;
|
|
2588
2956
|
traverse2(ast, {
|
|
2589
2957
|
FunctionDeclaration(path) {
|
|
2590
2958
|
const name = getComponentName(path);
|
|
@@ -2595,7 +2963,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2595
2963
|
body,
|
|
2596
2964
|
hookCall,
|
|
2597
2965
|
name,
|
|
2598
|
-
path.node
|
|
2966
|
+
path.node,
|
|
2967
|
+
serverAwait
|
|
2599
2968
|
);
|
|
2600
2969
|
componentTranslatorIds.set(name, translatorId);
|
|
2601
2970
|
},
|
|
@@ -2611,7 +2980,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2611
2980
|
block,
|
|
2612
2981
|
hookCall,
|
|
2613
2982
|
name,
|
|
2614
|
-
init
|
|
2983
|
+
init,
|
|
2984
|
+
serverAwait
|
|
2615
2985
|
);
|
|
2616
2986
|
componentTranslatorIds.set(name, translatorId);
|
|
2617
2987
|
return;
|
|
@@ -2624,7 +2994,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2624
2994
|
block,
|
|
2625
2995
|
hookCall,
|
|
2626
2996
|
name,
|
|
2627
|
-
wrappedFn
|
|
2997
|
+
wrappedFn,
|
|
2998
|
+
serverAwait
|
|
2628
2999
|
);
|
|
2629
3000
|
componentTranslatorIds.set(name, translatorId);
|
|
2630
3001
|
}
|
|
@@ -2638,7 +3009,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2638
3009
|
decl.body,
|
|
2639
3010
|
hookCall,
|
|
2640
3011
|
name,
|
|
2641
|
-
decl
|
|
3012
|
+
decl,
|
|
3013
|
+
serverAwait
|
|
2642
3014
|
);
|
|
2643
3015
|
componentTranslatorIds.set(name, translatorId);
|
|
2644
3016
|
return;
|
|
@@ -2649,7 +3021,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2649
3021
|
block,
|
|
2650
3022
|
hookCall,
|
|
2651
3023
|
name,
|
|
2652
|
-
decl
|
|
3024
|
+
decl,
|
|
3025
|
+
serverAwait
|
|
2653
3026
|
);
|
|
2654
3027
|
componentTranslatorIds.set(name, translatorId);
|
|
2655
3028
|
return;
|
|
@@ -2662,7 +3035,8 @@ function transformInline(ast, textToKey, options) {
|
|
|
2662
3035
|
block,
|
|
2663
3036
|
hookCall,
|
|
2664
3037
|
name,
|
|
2665
|
-
wrappedFn
|
|
3038
|
+
wrappedFn,
|
|
3039
|
+
serverAwait
|
|
2666
3040
|
);
|
|
2667
3041
|
componentTranslatorIds.set(name, translatorId);
|
|
2668
3042
|
}
|
|
@@ -2670,24 +3044,44 @@ function transformInline(ast, textToKey, options) {
|
|
|
2670
3044
|
noScope: true
|
|
2671
3045
|
});
|
|
2672
3046
|
}
|
|
3047
|
+
const moduleFactoryConstNames = options.moduleFactoryConstNames ?? [];
|
|
3048
|
+
const didWrapFactoryInline = wrapModuleFactoryDeclarations(ast, moduleFactoryConstNames);
|
|
2673
3049
|
rewriteGeneratedCallsForTranslator(ast, componentTranslatorIds);
|
|
3050
|
+
const moduleFactoryImportedNames = options.moduleFactoryImportedNames ?? [];
|
|
3051
|
+
const { componentsNeedingT: factoryComponentsNeedingT, rewrote: didRewriteRefsInline } = rewriteModuleFactoryReferences(
|
|
3052
|
+
ast,
|
|
3053
|
+
moduleFactoryImportedNames,
|
|
3054
|
+
componentTranslatorIds
|
|
3055
|
+
);
|
|
3056
|
+
for (const comp of factoryComponentsNeedingT) {
|
|
3057
|
+
componentsNeedingT.add(comp);
|
|
3058
|
+
}
|
|
2674
3059
|
if (needsClientDirective && (needsHook || boundaryRepaired)) {
|
|
2675
3060
|
addUseClientDirective(ast);
|
|
2676
3061
|
}
|
|
3062
|
+
const didModifyInline = stringsWrapped > 0 || repaired || boundaryRepaired || didWrapFactoryInline || didRewriteRefsInline;
|
|
2677
3063
|
const output = generate(ast, { retainLines: false });
|
|
2678
|
-
return { code: output.code, stringsWrapped, modified:
|
|
3064
|
+
return { code: output.code, stringsWrapped, modified: didModifyInline, usedKeys: [] };
|
|
2679
3065
|
}
|
|
2680
|
-
function injectInlineHookIntoBlock(block, hookCall, componentName, ownerFn) {
|
|
3066
|
+
function injectInlineHookIntoBlock(block, hookCall, componentName, ownerFn, useAwait) {
|
|
2681
3067
|
const targetName = hookCall.callee.type === "Identifier" ? hookCall.callee.name : void 0;
|
|
2682
3068
|
let sawConflictingT = false;
|
|
2683
3069
|
for (const stmt of block.body) {
|
|
2684
3070
|
if (stmt.type !== "VariableDeclaration") continue;
|
|
2685
3071
|
for (const d of stmt.declarations) {
|
|
2686
3072
|
if (d.id.type !== "Identifier") continue;
|
|
2687
|
-
|
|
3073
|
+
const callExpr = d.init?.type === "AwaitExpression" && d.init.argument.type === "CallExpression" ? d.init.argument : d.init?.type === "CallExpression" ? d.init : null;
|
|
3074
|
+
if (callExpr && callExpr.callee.type === "Identifier" && (callExpr.callee.name === "useT" || callExpr.callee.name === "createT" || (targetName ? callExpr.callee.name === targetName : false))) {
|
|
2688
3075
|
const translatorId2 = d.id.name;
|
|
2689
|
-
if (targetName &&
|
|
2690
|
-
|
|
3076
|
+
if (targetName && callExpr.callee.name !== targetName) {
|
|
3077
|
+
callExpr.callee = t2.identifier(targetName);
|
|
3078
|
+
}
|
|
3079
|
+
if (useAwait && d.init?.type !== "AwaitExpression") {
|
|
3080
|
+
d.init = t2.awaitExpression(d.init);
|
|
3081
|
+
if (ownerFn && !ownerFn.async) {
|
|
3082
|
+
ownerFn.async = true;
|
|
3083
|
+
ownerFn.returnType = null;
|
|
3084
|
+
}
|
|
2691
3085
|
}
|
|
2692
3086
|
return translatorId2;
|
|
2693
3087
|
}
|
|
@@ -2701,13 +3095,15 @@ function injectInlineHookIntoBlock(block, hookCall, componentName, ownerFn) {
|
|
|
2701
3095
|
Injected fallback translator "${translatorId}" to avoid conflicts.` : `Detected "const t = ..." conflict. Injected fallback translator "${translatorId}".`
|
|
2702
3096
|
);
|
|
2703
3097
|
}
|
|
3098
|
+
const initExpr = useAwait ? t2.awaitExpression(t2.cloneNode(hookCall, true)) : t2.cloneNode(hookCall, true);
|
|
2704
3099
|
const tDecl = t2.variableDeclaration("const", [
|
|
2705
|
-
t2.variableDeclarator(
|
|
2706
|
-
t2.identifier(translatorId),
|
|
2707
|
-
t2.cloneNode(hookCall, true)
|
|
2708
|
-
)
|
|
3100
|
+
t2.variableDeclarator(t2.identifier(translatorId), initExpr)
|
|
2709
3101
|
]);
|
|
2710
3102
|
block.body.unshift(tDecl);
|
|
3103
|
+
if (useAwait && ownerFn && !ownerFn.async) {
|
|
3104
|
+
ownerFn.async = true;
|
|
3105
|
+
ownerFn.returnType = null;
|
|
3106
|
+
}
|
|
2711
3107
|
return translatorId;
|
|
2712
3108
|
}
|
|
2713
3109
|
var traverse2, generate;
|
|
@@ -2724,7 +3120,7 @@ var init_transform = __esm({
|
|
|
2724
3120
|
});
|
|
2725
3121
|
|
|
2726
3122
|
// src/codegen/index.ts
|
|
2727
|
-
import { dirname, extname, join as join2, resolve } from "path";
|
|
3123
|
+
import { basename, dirname, extname, join as join2, resolve } from "path";
|
|
2728
3124
|
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
2729
3125
|
import _traverse3 from "@babel/traverse";
|
|
2730
3126
|
import { glob as glob2 } from "tinyglobby";
|
|
@@ -2896,6 +3292,445 @@ function buildClientGraph(entries, cwd, pathAliases) {
|
|
|
2896
3292
|
}
|
|
2897
3293
|
return clientReachable;
|
|
2898
3294
|
}
|
|
3295
|
+
function hasTranslatableValue(node, textToKey) {
|
|
3296
|
+
if (node.type === "StringLiteral") {
|
|
3297
|
+
const text2 = node.value.trim();
|
|
3298
|
+
return !!text2 && text2 in textToKey;
|
|
3299
|
+
}
|
|
3300
|
+
if (node.type === "TemplateLiteral") {
|
|
3301
|
+
const info = buildTemplateLiteralText(node.quasis, node.expressions);
|
|
3302
|
+
return !!info && info.text in textToKey;
|
|
3303
|
+
}
|
|
3304
|
+
if (node.type === "ConditionalExpression") {
|
|
3305
|
+
return hasTranslatableValue(node.consequent, textToKey) || hasTranslatableValue(node.alternate, textToKey);
|
|
3306
|
+
}
|
|
3307
|
+
return false;
|
|
3308
|
+
}
|
|
3309
|
+
function collectModuleFactoryCandidates(entries, textToKey) {
|
|
3310
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
3311
|
+
for (const entry of entries) {
|
|
3312
|
+
if (!entry.ast) continue;
|
|
3313
|
+
const topLevelConsts = /* @__PURE__ */ new Map();
|
|
3314
|
+
for (const node of entry.ast.program.body) {
|
|
3315
|
+
let decl;
|
|
3316
|
+
let exported = false;
|
|
3317
|
+
if (node.type === "VariableDeclaration" && node.kind === "const") {
|
|
3318
|
+
decl = node;
|
|
3319
|
+
} else if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration" && node.declaration.kind === "const") {
|
|
3320
|
+
decl = node.declaration;
|
|
3321
|
+
exported = true;
|
|
3322
|
+
}
|
|
3323
|
+
if (!decl) continue;
|
|
3324
|
+
for (const declarator of decl.declarations) {
|
|
3325
|
+
if (declarator.id.type !== "Identifier") continue;
|
|
3326
|
+
const typed = !!declarator.id.typeAnnotation;
|
|
3327
|
+
topLevelConsts.set(declarator.id.name, { exported, typed });
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
traverse3(entry.ast, {
|
|
3331
|
+
ObjectProperty(path) {
|
|
3332
|
+
let current = path.parentPath;
|
|
3333
|
+
let insideFunction = false;
|
|
3334
|
+
let constName;
|
|
3335
|
+
while (current) {
|
|
3336
|
+
if (current.isFunctionDeclaration?.() || current.isFunctionExpression?.() || current.isArrowFunctionExpression?.()) {
|
|
3337
|
+
insideFunction = true;
|
|
3338
|
+
break;
|
|
3339
|
+
}
|
|
3340
|
+
if (current.isVariableDeclarator?.()) {
|
|
3341
|
+
const id = current.node.id;
|
|
3342
|
+
if (id.type === "Identifier" && topLevelConsts.has(id.name)) {
|
|
3343
|
+
constName = id.name;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
current = current.parentPath;
|
|
3347
|
+
}
|
|
3348
|
+
if (insideFunction || !constName) return;
|
|
3349
|
+
const info = topLevelConsts.get(constName);
|
|
3350
|
+
if (!info) return;
|
|
3351
|
+
if (info.exported && FRAMEWORK_RESERVED_EXPORTS.has(constName)) return;
|
|
3352
|
+
const keyNode = path.node.key;
|
|
3353
|
+
if (keyNode.type !== "Identifier" && keyNode.type !== "StringLiteral") return;
|
|
3354
|
+
const propName = keyNode.type === "Identifier" ? keyNode.name : keyNode.value;
|
|
3355
|
+
if (!isContentProperty(propName)) return;
|
|
3356
|
+
const valueNode = path.node.value;
|
|
3357
|
+
if (!hasTranslatableValue(valueNode, textToKey)) return;
|
|
3358
|
+
if (!candidates.has(entry.filePath)) {
|
|
3359
|
+
candidates.set(entry.filePath, /* @__PURE__ */ new Set());
|
|
3360
|
+
}
|
|
3361
|
+
candidates.get(entry.filePath).add(constName);
|
|
3362
|
+
},
|
|
3363
|
+
noScope: true
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
return candidates;
|
|
3367
|
+
}
|
|
3368
|
+
function collectModuleFactoryImportEdges(entries, candidates, cwd, knownFiles, pathAliases) {
|
|
3369
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
3370
|
+
const factoryExports = /* @__PURE__ */ new Map();
|
|
3371
|
+
for (const [file, names] of candidates) {
|
|
3372
|
+
factoryExports.set(file, names);
|
|
3373
|
+
}
|
|
3374
|
+
for (const entry of entries) {
|
|
3375
|
+
if (!entry.ast) continue;
|
|
3376
|
+
for (const node of entry.ast.program.body) {
|
|
3377
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
3378
|
+
if (node.specifiers.some((s) => s.type === "ImportNamespaceSpecifier")) continue;
|
|
3379
|
+
const source = node.source.value;
|
|
3380
|
+
const resolved = resolveLocalImport(
|
|
3381
|
+
entry.filePath,
|
|
3382
|
+
source,
|
|
3383
|
+
cwd,
|
|
3384
|
+
knownFiles,
|
|
3385
|
+
pathAliases
|
|
3386
|
+
);
|
|
3387
|
+
if (!resolved || !factoryExports.has(resolved)) continue;
|
|
3388
|
+
const exportedNames = factoryExports.get(resolved);
|
|
3389
|
+
for (const spec of node.specifiers) {
|
|
3390
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
3391
|
+
const importedName = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
3392
|
+
if (!exportedNames.has(importedName)) continue;
|
|
3393
|
+
const localName = spec.local.name;
|
|
3394
|
+
if (!importsByFile.has(entry.filePath)) {
|
|
3395
|
+
importsByFile.set(entry.filePath, []);
|
|
3396
|
+
}
|
|
3397
|
+
importsByFile.get(entry.filePath).push(localName);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
return importsByFile;
|
|
3402
|
+
}
|
|
3403
|
+
function collectLocalFactoryUsages(ast, constNames) {
|
|
3404
|
+
const used = /* @__PURE__ */ new Set();
|
|
3405
|
+
traverse3(ast, {
|
|
3406
|
+
Identifier(path) {
|
|
3407
|
+
if (!constNames.has(path.node.name)) return;
|
|
3408
|
+
const parent = path.parent;
|
|
3409
|
+
if (parent.type === "VariableDeclarator" && parent.id === path.node) return;
|
|
3410
|
+
if (parent.type === "ImportSpecifier" || parent.type === "ExportSpecifier" || parent.type === "ImportDefaultSpecifier") return;
|
|
3411
|
+
const compName = getComponentName(path);
|
|
3412
|
+
if (compName && isPascalCase(compName)) {
|
|
3413
|
+
used.add(path.node.name);
|
|
3414
|
+
}
|
|
3415
|
+
},
|
|
3416
|
+
noScope: true
|
|
3417
|
+
});
|
|
3418
|
+
return [...used];
|
|
3419
|
+
}
|
|
3420
|
+
function markUnsafeExport(importerAst, localName, importerFile, safeCandidates, entries, cwd, knownFiles, pathAliases, unsafeExportedNames) {
|
|
3421
|
+
for (const node of importerAst.program.body) {
|
|
3422
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
3423
|
+
for (const spec of node.specifiers) {
|
|
3424
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
3425
|
+
if (spec.local.name !== localName) continue;
|
|
3426
|
+
const exportedName = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
3427
|
+
const resolved = resolveLocalImport(
|
|
3428
|
+
importerFile,
|
|
3429
|
+
node.source.value,
|
|
3430
|
+
cwd,
|
|
3431
|
+
knownFiles,
|
|
3432
|
+
pathAliases
|
|
3433
|
+
);
|
|
3434
|
+
if (resolved && safeCandidates.has(resolved)) {
|
|
3435
|
+
if (!unsafeExportedNames.has(resolved)) {
|
|
3436
|
+
unsafeExportedNames.set(resolved, /* @__PURE__ */ new Set());
|
|
3437
|
+
}
|
|
3438
|
+
unsafeExportedNames.get(resolved).add(exportedName);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
if (safeCandidates.has(importerFile) && safeCandidates.get(importerFile).has(localName)) {
|
|
3443
|
+
if (!unsafeExportedNames.has(importerFile)) {
|
|
3444
|
+
unsafeExportedNames.set(importerFile, /* @__PURE__ */ new Set());
|
|
3445
|
+
}
|
|
3446
|
+
unsafeExportedNames.get(importerFile).add(localName);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
function resolveLocalToExportedName(importerAst, localName, importerFile, sourceFile, cwd, knownFiles, pathAliases) {
|
|
3450
|
+
if (!importerAst) return void 0;
|
|
3451
|
+
for (const node of importerAst.program.body) {
|
|
3452
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
3453
|
+
for (const spec of node.specifiers) {
|
|
3454
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
3455
|
+
if (spec.local.name !== localName) continue;
|
|
3456
|
+
const resolved = resolveLocalImport(
|
|
3457
|
+
importerFile,
|
|
3458
|
+
node.source.value,
|
|
3459
|
+
cwd,
|
|
3460
|
+
knownFiles,
|
|
3461
|
+
pathAliases
|
|
3462
|
+
);
|
|
3463
|
+
if (resolved !== sourceFile) continue;
|
|
3464
|
+
return spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
if (importerFile === sourceFile) return localName;
|
|
3468
|
+
return void 0;
|
|
3469
|
+
}
|
|
3470
|
+
function isInsideFunctionParam2(path) {
|
|
3471
|
+
let current = path;
|
|
3472
|
+
while (current.parentPath) {
|
|
3473
|
+
if (current.parentPath.isFunction()) {
|
|
3474
|
+
const fn = current.parentPath.node;
|
|
3475
|
+
return fn.params.includes(current.node);
|
|
3476
|
+
}
|
|
3477
|
+
current = current.parentPath;
|
|
3478
|
+
}
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
function getBindingSafety(ast, bindingName) {
|
|
3482
|
+
let unsafe = false;
|
|
3483
|
+
let reason;
|
|
3484
|
+
traverse3(ast, {
|
|
3485
|
+
Identifier(path) {
|
|
3486
|
+
if (unsafe) return;
|
|
3487
|
+
if (path.node.name !== bindingName) return;
|
|
3488
|
+
const binding = path.scope?.getBinding(bindingName);
|
|
3489
|
+
if (!binding) return;
|
|
3490
|
+
const bPath = binding.path;
|
|
3491
|
+
const isTopLevel = bPath.isVariableDeclarator?.() && bPath.parentPath?.isVariableDeclaration?.() && (bPath.parentPath.parentPath?.isProgram?.() || bPath.parentPath.parentPath?.isExportNamedDeclaration?.());
|
|
3492
|
+
const isImportBinding = bPath.isImportSpecifier?.() || bPath.isImportDefaultSpecifier?.();
|
|
3493
|
+
if (!isTopLevel && !isImportBinding) return;
|
|
3494
|
+
const parent = path.parent;
|
|
3495
|
+
if (parent.type === "ImportSpecifier" || parent.type === "ImportDefaultSpecifier" || parent.type === "ExportSpecifier") {
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
3498
|
+
if (parent.type === "TSTypeReference" || parent.type === "TSTypeQuery" || parent.type === "TSTypeAnnotation") {
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3501
|
+
if (parent.type === "VariableDeclarator" && parent.id === path.node) {
|
|
3502
|
+
return;
|
|
3503
|
+
}
|
|
3504
|
+
const compName = getComponentName(path);
|
|
3505
|
+
if (!compName || !isPascalCase(compName)) {
|
|
3506
|
+
unsafe = true;
|
|
3507
|
+
reason = `"${bindingName}" is referenced outside a React component`;
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
if (isInsideFunctionParam2(path)) {
|
|
3511
|
+
unsafe = true;
|
|
3512
|
+
reason = `"${bindingName}" is referenced in a function parameter default`;
|
|
3513
|
+
return;
|
|
3514
|
+
}
|
|
3515
|
+
if (parent.type === "AssignmentExpression" && parent.left === path.node) {
|
|
3516
|
+
unsafe = true;
|
|
3517
|
+
reason = `"${bindingName}" is mutated via assignment`;
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
if (parent.type === "MemberExpression" && parent.object === path.node) {
|
|
3521
|
+
const grandParent = path.parentPath?.parent;
|
|
3522
|
+
if (grandParent?.type === "CallExpression" && grandParent.callee === parent && parent.property.type === "Identifier" && ["push", "splice", "pop", "shift", "unshift", "sort", "reverse", "fill"].includes(parent.property.name)) {
|
|
3523
|
+
unsafe = true;
|
|
3524
|
+
reason = `"${bindingName}" is mutated via .${parent.property.name}()`;
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
if (grandParent?.type === "AssignmentExpression" && grandParent.left === parent) {
|
|
3528
|
+
unsafe = true;
|
|
3529
|
+
reason = `"${bindingName}" is mutated via property/indexed assignment`;
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
if (grandParent?.type === "UnaryExpression" && grandParent.operator === "delete") {
|
|
3533
|
+
unsafe = true;
|
|
3534
|
+
reason = `"${bindingName}" is mutated via delete`;
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
if (parent.type === "UpdateExpression") {
|
|
3539
|
+
unsafe = true;
|
|
3540
|
+
reason = `"${bindingName}" is mutated via ${parent.operator}`;
|
|
3541
|
+
return;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
});
|
|
3545
|
+
return unsafe ? { safe: false, reason } : { safe: true };
|
|
3546
|
+
}
|
|
3547
|
+
async function findExternalImportersOfCandidates(candidates, entries, cwd, pathAliases) {
|
|
3548
|
+
const blocked = /* @__PURE__ */ new Map();
|
|
3549
|
+
if (candidates.size === 0) return blocked;
|
|
3550
|
+
const allFiles = await glob2(["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"], {
|
|
3551
|
+
ignore: ["node_modules/**", "dist/**", "build/**", ".next/**", ".output/**"],
|
|
3552
|
+
cwd,
|
|
3553
|
+
absolute: true
|
|
3554
|
+
});
|
|
3555
|
+
const includedFiles = new Set(entries.map((e) => e.filePath));
|
|
3556
|
+
const externalFiles = allFiles.filter((f) => !includedFiles.has(f));
|
|
3557
|
+
if (externalFiles.length === 0) return blocked;
|
|
3558
|
+
const candidateBasenames = /* @__PURE__ */ new Set();
|
|
3559
|
+
for (const file of candidates.keys()) {
|
|
3560
|
+
candidateBasenames.add(basename(file).replace(/\.[^.]+$/, ""));
|
|
3561
|
+
}
|
|
3562
|
+
const allKnownFiles = new Set(allFiles);
|
|
3563
|
+
const parseLimit = pLimit3(10);
|
|
3564
|
+
await Promise.all(
|
|
3565
|
+
externalFiles.map(
|
|
3566
|
+
(extFile) => parseLimit(async () => {
|
|
3567
|
+
let content;
|
|
3568
|
+
try {
|
|
3569
|
+
content = await readFile3(extFile, "utf-8");
|
|
3570
|
+
} catch {
|
|
3571
|
+
return;
|
|
3572
|
+
}
|
|
3573
|
+
let mightImport = false;
|
|
3574
|
+
for (const base of candidateBasenames) {
|
|
3575
|
+
if (content.includes(base)) {
|
|
3576
|
+
mightImport = true;
|
|
3577
|
+
break;
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
if (!mightImport) return;
|
|
3581
|
+
let ast;
|
|
3582
|
+
try {
|
|
3583
|
+
ast = parseFile(content, extFile);
|
|
3584
|
+
} catch {
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
for (const node of ast.program.body) {
|
|
3588
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
3589
|
+
const resolved = resolveLocalImport(
|
|
3590
|
+
extFile,
|
|
3591
|
+
node.source.value,
|
|
3592
|
+
cwd,
|
|
3593
|
+
allKnownFiles,
|
|
3594
|
+
pathAliases
|
|
3595
|
+
);
|
|
3596
|
+
if (!resolved || !candidates.has(resolved)) continue;
|
|
3597
|
+
const exportedNames = candidates.get(resolved);
|
|
3598
|
+
if (node.specifiers.some((s) => s.type === "ImportNamespaceSpecifier")) {
|
|
3599
|
+
if (!blocked.has(resolved)) blocked.set(resolved, /* @__PURE__ */ new Set());
|
|
3600
|
+
for (const name of exportedNames) {
|
|
3601
|
+
blocked.get(resolved).add(name);
|
|
3602
|
+
}
|
|
3603
|
+
continue;
|
|
3604
|
+
}
|
|
3605
|
+
for (const spec of node.specifiers) {
|
|
3606
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
3607
|
+
const importedName = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
3608
|
+
if (exportedNames.has(importedName)) {
|
|
3609
|
+
if (!blocked.has(resolved)) blocked.set(resolved, /* @__PURE__ */ new Set());
|
|
3610
|
+
blocked.get(resolved).add(importedName);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
})
|
|
3615
|
+
)
|
|
3616
|
+
);
|
|
3617
|
+
return blocked;
|
|
3618
|
+
}
|
|
3619
|
+
async function buildModuleFactoryPlan(entries, textToKey, cwd, pathAliases) {
|
|
3620
|
+
const candidates = collectModuleFactoryCandidates(entries, textToKey);
|
|
3621
|
+
const knownFiles = new Set(entries.filter((e) => e.ast).map((e) => e.filePath));
|
|
3622
|
+
const externallyBlocked = await findExternalImportersOfCandidates(
|
|
3623
|
+
candidates,
|
|
3624
|
+
entries,
|
|
3625
|
+
cwd,
|
|
3626
|
+
pathAliases
|
|
3627
|
+
);
|
|
3628
|
+
for (const [file, names] of externallyBlocked) {
|
|
3629
|
+
const candidateNames = candidates.get(file);
|
|
3630
|
+
if (!candidateNames) continue;
|
|
3631
|
+
for (const name of names) {
|
|
3632
|
+
if (candidateNames.has(name)) {
|
|
3633
|
+
candidateNames.delete(name);
|
|
3634
|
+
logWarning(
|
|
3635
|
+
`Module factory: blocking "${name}" in ${file} \u2014 imported by a file outside the include scope`
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
if (candidateNames.size === 0) {
|
|
3640
|
+
candidates.delete(file);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
const safeCandidates = /* @__PURE__ */ new Map();
|
|
3644
|
+
for (const [file, names] of candidates) {
|
|
3645
|
+
const entry = entries.find((e) => e.filePath === file);
|
|
3646
|
+
if (!entry?.ast) continue;
|
|
3647
|
+
const safeNames = /* @__PURE__ */ new Set();
|
|
3648
|
+
for (const name of names) {
|
|
3649
|
+
const safety = getBindingSafety(entry.ast, name);
|
|
3650
|
+
if (safety.safe) {
|
|
3651
|
+
safeNames.add(name);
|
|
3652
|
+
} else {
|
|
3653
|
+
logWarning(`Module factory: skipping "${name}" in ${file}: ${safety.reason}`);
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
if (safeNames.size > 0) {
|
|
3657
|
+
safeCandidates.set(file, safeNames);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
const importEdges = collectModuleFactoryImportEdges(
|
|
3661
|
+
entries,
|
|
3662
|
+
safeCandidates,
|
|
3663
|
+
cwd,
|
|
3664
|
+
knownFiles,
|
|
3665
|
+
pathAliases
|
|
3666
|
+
);
|
|
3667
|
+
for (const [file, names] of safeCandidates) {
|
|
3668
|
+
const entry = entries.find((e) => e.filePath === file);
|
|
3669
|
+
if (!entry?.ast) continue;
|
|
3670
|
+
const localRefs = collectLocalFactoryUsages(entry.ast, names);
|
|
3671
|
+
if (localRefs.length > 0) {
|
|
3672
|
+
const existing = importEdges.get(file) ?? [];
|
|
3673
|
+
importEdges.set(file, [...existing, ...localRefs]);
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
const unsafeExportedNames = /* @__PURE__ */ new Map();
|
|
3677
|
+
const safeImports = /* @__PURE__ */ new Map();
|
|
3678
|
+
for (const [file, localNames] of importEdges) {
|
|
3679
|
+
const entry = entries.find((e) => e.filePath === file);
|
|
3680
|
+
if (!entry?.ast) continue;
|
|
3681
|
+
const safeLocalNames = [];
|
|
3682
|
+
for (const name of localNames) {
|
|
3683
|
+
const safety = getBindingSafety(entry.ast, name);
|
|
3684
|
+
if (safety.safe) {
|
|
3685
|
+
safeLocalNames.push(name);
|
|
3686
|
+
} else {
|
|
3687
|
+
logWarning(`Module factory: skipping import "${name}" in ${file}: ${safety.reason}`);
|
|
3688
|
+
markUnsafeExport(entry.ast, name, file, safeCandidates, entries, cwd, knownFiles, pathAliases, unsafeExportedNames);
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
if (safeLocalNames.length > 0) {
|
|
3692
|
+
safeImports.set(file, safeLocalNames);
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
for (const [sourceFile, unsafeNames] of unsafeExportedNames) {
|
|
3696
|
+
const safeNames = safeCandidates.get(sourceFile);
|
|
3697
|
+
if (!safeNames) continue;
|
|
3698
|
+
for (const name of unsafeNames) {
|
|
3699
|
+
safeNames.delete(name);
|
|
3700
|
+
logWarning(`Module factory: blocking "${name}" in ${sourceFile} because an importer cannot be safely rewritten`);
|
|
3701
|
+
}
|
|
3702
|
+
if (safeNames.size === 0) {
|
|
3703
|
+
safeCandidates.delete(sourceFile);
|
|
3704
|
+
}
|
|
3705
|
+
for (const [importFile, locals] of safeImports) {
|
|
3706
|
+
const filtered = locals.filter((l) => {
|
|
3707
|
+
const exportedName = resolveLocalToExportedName(
|
|
3708
|
+
entries.find((e) => e.filePath === importFile)?.ast,
|
|
3709
|
+
l,
|
|
3710
|
+
importFile,
|
|
3711
|
+
sourceFile,
|
|
3712
|
+
cwd,
|
|
3713
|
+
knownFiles,
|
|
3714
|
+
pathAliases
|
|
3715
|
+
);
|
|
3716
|
+
return !exportedName || !unsafeNames.has(exportedName);
|
|
3717
|
+
});
|
|
3718
|
+
if (filtered.length > 0) {
|
|
3719
|
+
safeImports.set(importFile, filtered);
|
|
3720
|
+
} else {
|
|
3721
|
+
safeImports.delete(importFile);
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
const factoryConstsByFile = /* @__PURE__ */ new Map();
|
|
3726
|
+
for (const [file, names] of safeCandidates) {
|
|
3727
|
+
factoryConstsByFile.set(file, [...names]);
|
|
3728
|
+
}
|
|
3729
|
+
return {
|
|
3730
|
+
factoryConstsByFile,
|
|
3731
|
+
factoryImportsByFile: safeImports
|
|
3732
|
+
};
|
|
3733
|
+
}
|
|
2899
3734
|
async function codegen(options, cwd = process.cwd()) {
|
|
2900
3735
|
const files = await glob2(options.include, {
|
|
2901
3736
|
ignore: options.exclude ?? [],
|
|
@@ -2905,7 +3740,8 @@ async function codegen(options, cwd = process.cwd()) {
|
|
|
2905
3740
|
const transformOpts = {
|
|
2906
3741
|
i18nImport: options.i18nImport,
|
|
2907
3742
|
mode: options.mode,
|
|
2908
|
-
componentPath: options.componentPath
|
|
3743
|
+
componentPath: options.componentPath,
|
|
3744
|
+
translatableProps: options.translatableProps
|
|
2909
3745
|
};
|
|
2910
3746
|
const parseLimit = pLimit3(10);
|
|
2911
3747
|
const parsedEntries = await Promise.all(
|
|
@@ -2933,6 +3769,15 @@ async function codegen(options, cwd = process.cwd()) {
|
|
|
2933
3769
|
);
|
|
2934
3770
|
const pathAliases = await loadPathAliasResolvers(cwd);
|
|
2935
3771
|
const forceClientSet = buildClientGraph(parsedEntries, cwd, pathAliases);
|
|
3772
|
+
let factoryPlan;
|
|
3773
|
+
if (options.moduleFactory) {
|
|
3774
|
+
factoryPlan = await buildModuleFactoryPlan(
|
|
3775
|
+
parsedEntries,
|
|
3776
|
+
options.textToKey,
|
|
3777
|
+
cwd,
|
|
3778
|
+
pathAliases
|
|
3779
|
+
);
|
|
3780
|
+
}
|
|
2936
3781
|
const limit = pLimit3(10);
|
|
2937
3782
|
let completed = 0;
|
|
2938
3783
|
const clientNamespacesSet = /* @__PURE__ */ new Set();
|
|
@@ -2950,7 +3795,9 @@ async function codegen(options, cwd = process.cwd()) {
|
|
|
2950
3795
|
const isClient = forceClientSet.has(entry.filePath) || entry.isClientRoot;
|
|
2951
3796
|
const fileTransformOpts = {
|
|
2952
3797
|
...transformOpts,
|
|
2953
|
-
forceClient: forceClientSet.has(entry.filePath)
|
|
3798
|
+
forceClient: forceClientSet.has(entry.filePath),
|
|
3799
|
+
moduleFactoryConstNames: factoryPlan?.factoryConstsByFile.get(entry.filePath),
|
|
3800
|
+
moduleFactoryImportedNames: factoryPlan?.factoryImportsByFile.get(entry.filePath)
|
|
2954
3801
|
};
|
|
2955
3802
|
const result = transform(
|
|
2956
3803
|
entry.ast,
|
|
@@ -3009,13 +3856,15 @@ async function codegen(options, cwd = process.cwd()) {
|
|
|
3009
3856
|
clientNamespaces: [...clientNamespacesSet].sort()
|
|
3010
3857
|
};
|
|
3011
3858
|
}
|
|
3012
|
-
var traverse3, SOURCE_EXTENSIONS;
|
|
3859
|
+
var traverse3, SOURCE_EXTENSIONS, FRAMEWORK_RESERVED_EXPORTS;
|
|
3013
3860
|
var init_codegen = __esm({
|
|
3014
3861
|
"src/codegen/index.ts"() {
|
|
3015
3862
|
"use strict";
|
|
3016
3863
|
init_parser();
|
|
3017
3864
|
init_ast_helpers();
|
|
3018
3865
|
init_transform();
|
|
3866
|
+
init_filters();
|
|
3867
|
+
init_template_literal();
|
|
3019
3868
|
init_logger();
|
|
3020
3869
|
traverse3 = resolveDefault(_traverse3);
|
|
3021
3870
|
SOURCE_EXTENSIONS = [
|
|
@@ -3028,6 +3877,20 @@ var init_codegen = __esm({
|
|
|
3028
3877
|
".mjs",
|
|
3029
3878
|
".cjs"
|
|
3030
3879
|
];
|
|
3880
|
+
FRAMEWORK_RESERVED_EXPORTS = /* @__PURE__ */ new Set([
|
|
3881
|
+
"metadata",
|
|
3882
|
+
"generateMetadata",
|
|
3883
|
+
"viewport",
|
|
3884
|
+
"generateViewport",
|
|
3885
|
+
"revalidate",
|
|
3886
|
+
"dynamic",
|
|
3887
|
+
"dynamicParams",
|
|
3888
|
+
"fetchCache",
|
|
3889
|
+
"runtime",
|
|
3890
|
+
"preferredRegion",
|
|
3891
|
+
"maxDuration",
|
|
3892
|
+
"generateStaticParams"
|
|
3893
|
+
]);
|
|
3031
3894
|
}
|
|
3032
3895
|
});
|
|
3033
3896
|
|
|
@@ -3224,7 +4087,10 @@ async function translateAll(input) {
|
|
|
3224
4087
|
)
|
|
3225
4088
|
);
|
|
3226
4089
|
if (totalInputTokens > 0 || totalOutputTokens > 0) {
|
|
3227
|
-
onUsage?.({
|
|
4090
|
+
onUsage?.({
|
|
4091
|
+
inputTokens: totalInputTokens,
|
|
4092
|
+
outputTokens: totalOutputTokens
|
|
4093
|
+
});
|
|
3228
4094
|
}
|
|
3229
4095
|
return results;
|
|
3230
4096
|
}
|
|
@@ -3266,7 +4132,9 @@ async function writeTranslationSplit(dir, flatEntries) {
|
|
|
3266
4132
|
const content = JSON.stringify(nested, null, 2) + "\n";
|
|
3267
4133
|
await writeFile2(filePath, content, "utf-8");
|
|
3268
4134
|
}
|
|
3269
|
-
const currentFiles = new Set(
|
|
4135
|
+
const currentFiles = new Set(
|
|
4136
|
+
[...byNamespace.keys()].map((ns) => `${ns}.json`)
|
|
4137
|
+
);
|
|
3270
4138
|
let existing;
|
|
3271
4139
|
try {
|
|
3272
4140
|
existing = await readdir(dir);
|
|
@@ -3483,7 +4351,11 @@ async function runScanStep(input) {
|
|
|
3483
4351
|
await writeFile4(sourceFile, content, "utf-8");
|
|
3484
4352
|
}
|
|
3485
4353
|
if (config.typeSafe) {
|
|
3486
|
-
await generateNextIntlTypes(
|
|
4354
|
+
await generateNextIntlTypes(
|
|
4355
|
+
config.messagesDir,
|
|
4356
|
+
config.sourceLocale,
|
|
4357
|
+
config.splitByNamespace
|
|
4358
|
+
);
|
|
3487
4359
|
}
|
|
3488
4360
|
}
|
|
3489
4361
|
return {
|
|
@@ -3513,6 +4385,8 @@ async function runCodegenStep(input) {
|
|
|
3513
4385
|
i18nImport: config.scan.i18nImport,
|
|
3514
4386
|
mode,
|
|
3515
4387
|
componentPath: config.inline?.componentPath,
|
|
4388
|
+
moduleFactory: input.moduleFactory,
|
|
4389
|
+
translatableProps: config.scan.translatableProps,
|
|
3516
4390
|
onProgress: callbacks?.onProgress
|
|
3517
4391
|
},
|
|
3518
4392
|
cwd
|
|
@@ -3659,7 +4533,11 @@ function createUsageTracker() {
|
|
|
3659
4533
|
outputTokens += usage.outputTokens ?? 0;
|
|
3660
4534
|
},
|
|
3661
4535
|
get() {
|
|
3662
|
-
return {
|
|
4536
|
+
return {
|
|
4537
|
+
inputTokens,
|
|
4538
|
+
outputTokens,
|
|
4539
|
+
totalTokens: inputTokens + outputTokens
|
|
4540
|
+
};
|
|
3663
4541
|
}
|
|
3664
4542
|
};
|
|
3665
4543
|
}
|
|
@@ -3679,7 +4557,11 @@ async function estimateCost(model, usage) {
|
|
|
3679
4557
|
providers
|
|
3680
4558
|
});
|
|
3681
4559
|
if (costs.totalUSD == null) return null;
|
|
3682
|
-
return {
|
|
4560
|
+
return {
|
|
4561
|
+
totalUSD: costs.totalUSD,
|
|
4562
|
+
inputUSD: costs.inputUSD ?? 0,
|
|
4563
|
+
outputUSD: costs.outputUSD ?? 0
|
|
4564
|
+
};
|
|
3683
4565
|
} catch {
|
|
3684
4566
|
return null;
|
|
3685
4567
|
}
|
|
@@ -3724,6 +4606,19 @@ var init_cli_utils = __esm({
|
|
|
3724
4606
|
});
|
|
3725
4607
|
|
|
3726
4608
|
// src/templates/t-component.ts
|
|
4609
|
+
function buildSingleFileLoadBody(targetLocales, relativeMessagesDir) {
|
|
4610
|
+
const cases = targetLocales.map(
|
|
4611
|
+
(locale) => ` case "${locale}": return (await import("${relativeMessagesDir}/${locale}.json")).default;`
|
|
4612
|
+
).join("\n");
|
|
4613
|
+
return ` try {
|
|
4614
|
+
switch (locale) {
|
|
4615
|
+
${cases}
|
|
4616
|
+
default: return {};
|
|
4617
|
+
}
|
|
4618
|
+
} catch {
|
|
4619
|
+
return {};
|
|
4620
|
+
}`;
|
|
4621
|
+
}
|
|
3727
4622
|
function serverTemplate(clientBasename, opts) {
|
|
3728
4623
|
if (!opts) {
|
|
3729
4624
|
return `import type { ReactNode } from "react";
|
|
@@ -3757,6 +4652,15 @@ export function createT(messages?: Messages) {
|
|
|
3757
4652
|
}
|
|
3758
4653
|
const allLocales = [opts.sourceLocale, ...opts.targetLocales];
|
|
3759
4654
|
const allLocalesStr = allLocales.map((l) => `"${l}"`).join(", ");
|
|
4655
|
+
const isSplit = !!opts.splitByNamespace;
|
|
4656
|
+
let messagesImportPath = opts.relativeMessagesDir ?? opts.messagesDir;
|
|
4657
|
+
if (!messagesImportPath.startsWith(".") && !messagesImportPath.startsWith("/")) {
|
|
4658
|
+
messagesImportPath = `./${messagesImportPath}`;
|
|
4659
|
+
}
|
|
4660
|
+
const loadBody = isSplit ? SPLIT_LOAD_BODY : buildSingleFileLoadBody(opts.targetLocales, messagesImportPath);
|
|
4661
|
+
const messagesDirConst = isSplit ? `
|
|
4662
|
+
const messagesDir = "${opts.messagesDir}";
|
|
4663
|
+
` : "\n";
|
|
3760
4664
|
return `import type { ReactNode } from "react";
|
|
3761
4665
|
import { cache } from "react";
|
|
3762
4666
|
export { I18nProvider } from "./${clientBasename}";
|
|
@@ -3765,9 +4669,7 @@ type Messages = Record<string, string>;
|
|
|
3765
4669
|
|
|
3766
4670
|
const supported = [${allLocalesStr}] as const;
|
|
3767
4671
|
type Locale = (typeof supported)[number];
|
|
3768
|
-
const defaultLocale: Locale = "${opts.sourceLocale}"
|
|
3769
|
-
const messagesDir = "${opts.messagesDir}";
|
|
3770
|
-
|
|
4672
|
+
const defaultLocale: Locale = "${opts.sourceLocale}";${messagesDirConst}
|
|
3771
4673
|
function parseAcceptLanguage(header: string): Locale {
|
|
3772
4674
|
const langs = header
|
|
3773
4675
|
.split(",")
|
|
@@ -3789,8 +4691,7 @@ export function setLocale(locale: string) {
|
|
|
3789
4691
|
getLocaleStore().current = locale;
|
|
3790
4692
|
}
|
|
3791
4693
|
|
|
3792
|
-
// Per-request cached message loading \u2014 works
|
|
3793
|
-
// Uses dynamic imports so this file can be safely imported from client components
|
|
4694
|
+
// Per-request cached message loading \u2014 works on all platforms via static imports
|
|
3794
4695
|
const getCachedMessages = cache(async (): Promise<Messages> => {
|
|
3795
4696
|
let locale: Locale | null = getLocaleStore().current as Locale | null;
|
|
3796
4697
|
|
|
@@ -3813,7 +4714,7 @@ const getCachedMessages = cache(async (): Promise<Messages> => {
|
|
|
3813
4714
|
}
|
|
3814
4715
|
|
|
3815
4716
|
if (locale === defaultLocale) return {};
|
|
3816
|
-
${
|
|
4717
|
+
${loadBody}
|
|
3817
4718
|
});
|
|
3818
4719
|
|
|
3819
4720
|
// Per-request message store (populated by setServerMessages in layout)
|
|
@@ -3942,7 +4843,7 @@ ${getMessagesBody}
|
|
|
3942
4843
|
}
|
|
3943
4844
|
`;
|
|
3944
4845
|
}
|
|
3945
|
-
var CLIENT_TEMPLATE,
|
|
4846
|
+
var CLIENT_TEMPLATE, SPLIT_LOAD_BODY;
|
|
3946
4847
|
var init_t_component = __esm({
|
|
3947
4848
|
"src/templates/t-component.ts"() {
|
|
3948
4849
|
"use strict";
|
|
@@ -3976,18 +4877,9 @@ export function useT() {
|
|
|
3976
4877
|
};
|
|
3977
4878
|
}
|
|
3978
4879
|
`;
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
const filePath = join(process.cwd(), messagesDir, \`\${locale}.json\`);
|
|
3983
|
-
const content = await readFile(filePath, "utf-8");
|
|
3984
|
-
return JSON.parse(content);
|
|
3985
|
-
} catch {
|
|
3986
|
-
return {};
|
|
3987
|
-
}`;
|
|
3988
|
-
SPLIT_LOAD_BODY = ` const { readFile, readdir } = await import("node:fs/promises");
|
|
3989
|
-
const { join } = await import("node:path");
|
|
3990
|
-
try {
|
|
4880
|
+
SPLIT_LOAD_BODY = ` try {
|
|
4881
|
+
const { readFile, readdir } = await import("node:fs/promises");
|
|
4882
|
+
const { join } = await import("node:path");
|
|
3991
4883
|
const dir = join(process.cwd(), messagesDir, locale);
|
|
3992
4884
|
let files: string[];
|
|
3993
4885
|
try { files = (await readdir(dir)).filter(f => f.endsWith(".json")); } catch { return {}; }
|
|
@@ -4013,24 +4905,32 @@ export function useT() {
|
|
|
4013
4905
|
// src/init.ts
|
|
4014
4906
|
var init_exports = {};
|
|
4015
4907
|
__export(init_exports, {
|
|
4908
|
+
detectIncludePatterns: () => detectIncludePatterns,
|
|
4016
4909
|
generateConfigFile: () => generateConfigFile,
|
|
4017
4910
|
runInitWizard: () => runInitWizard,
|
|
4018
4911
|
updateLayoutWithSelectiveMessages: () => updateLayoutWithSelectiveMessages
|
|
4019
4912
|
});
|
|
4020
4913
|
import * as p from "@clack/prompts";
|
|
4021
4914
|
import { existsSync } from "fs";
|
|
4022
|
-
import { basename, join as join6, relative } from "path";
|
|
4915
|
+
import { basename as basename2, join as join6, relative } from "path";
|
|
4023
4916
|
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
4024
4917
|
function detectIncludePatterns(cwd) {
|
|
4025
4918
|
const patterns = [];
|
|
4026
|
-
|
|
4919
|
+
const hasSrc = existsSync(join6(cwd, "src"));
|
|
4920
|
+
if (hasSrc && existsSync(join6(cwd, "src", "app"))) {
|
|
4921
|
+
patterns.push("src/app/**/*.tsx", "src/app/**/*.jsx");
|
|
4922
|
+
} else if (existsSync(join6(cwd, "app"))) {
|
|
4027
4923
|
patterns.push("app/**/*.tsx", "app/**/*.jsx");
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4924
|
+
}
|
|
4925
|
+
if (hasSrc && existsSync(join6(cwd, "src", "pages"))) {
|
|
4926
|
+
patterns.push("src/pages/**/*.tsx", "src/pages/**/*.jsx");
|
|
4927
|
+
} else if (existsSync(join6(cwd, "pages"))) {
|
|
4031
4928
|
patterns.push("pages/**/*.tsx", "pages/**/*.jsx");
|
|
4032
|
-
|
|
4033
|
-
|
|
4929
|
+
}
|
|
4930
|
+
if (hasSrc && existsSync(join6(cwd, "src", "components"))) {
|
|
4931
|
+
patterns.push("src/components/**/*.tsx", "src/components/**/*.jsx");
|
|
4932
|
+
} else if (existsSync(join6(cwd, "components"))) {
|
|
4933
|
+
patterns.push("components/**/*.tsx", "components/**/*.jsx");
|
|
4034
4934
|
}
|
|
4035
4935
|
return patterns.length > 0 ? patterns : ["**/*.tsx", "**/*.jsx"];
|
|
4036
4936
|
}
|
|
@@ -4097,6 +4997,13 @@ function generateConfigFile(opts) {
|
|
|
4097
4997
|
lines.push(
|
|
4098
4998
|
` include: [${opts.includePatterns.map((p2) => `"${p2}"`).join(", ")}],`
|
|
4099
4999
|
);
|
|
5000
|
+
lines.push(` // Add more directories as needed. Examples:`);
|
|
5001
|
+
lines.push(` // "config/**/*.ts" \u2014 for data/config files (needed with --module-factory)`);
|
|
5002
|
+
lines.push(` // "lib/**/*.ts" \u2014 for utility files with translatable strings`);
|
|
5003
|
+
lines.push(` // "layouts/**/*.tsx" \u2014 for layout components`);
|
|
5004
|
+
lines.push(` //`);
|
|
5005
|
+
lines.push(` // Note: When using codegen --module-factory, include any directories`);
|
|
5006
|
+
lines.push(` // that contain exported constants with translatable strings (e.g. config/).`);
|
|
4100
5007
|
lines.push(` exclude: ["**/*.test.*", "**/*.spec.*"],`);
|
|
4101
5008
|
if (opts.mode === "keys" && opts.i18nImport) {
|
|
4102
5009
|
lines.push(` i18nImport: "${opts.i18nImport}",`);
|
|
@@ -4259,11 +5166,7 @@ export default getRequestConfig(async () => {
|
|
|
4259
5166
|
"export default withNextIntl($1);"
|
|
4260
5167
|
);
|
|
4261
5168
|
const updated = importLine + "\n" + pluginLine + "\n" + wrapped;
|
|
4262
|
-
if (await safeWriteModifiedFile(
|
|
4263
|
-
nextConfigPath,
|
|
4264
|
-
updated,
|
|
4265
|
-
"next.config.ts"
|
|
4266
|
-
)) {
|
|
5169
|
+
if (await safeWriteModifiedFile(nextConfigPath, updated, "next.config.ts")) {
|
|
4267
5170
|
filesCreated.push("next.config.ts (updated)");
|
|
4268
5171
|
}
|
|
4269
5172
|
}
|
|
@@ -4287,16 +5190,16 @@ export default getRequestConfig(async () => {
|
|
|
4287
5190
|
/<\/body>/,
|
|
4288
5191
|
" </NextIntlClientProvider>\n </body>"
|
|
4289
5192
|
);
|
|
4290
|
-
if (await safeWriteModifiedFile(
|
|
4291
|
-
layoutPath,
|
|
4292
|
-
layoutContent,
|
|
4293
|
-
"root layout"
|
|
4294
|
-
)) {
|
|
5193
|
+
if (await safeWriteModifiedFile(layoutPath, layoutContent, "root layout")) {
|
|
4295
5194
|
filesCreated.push(relative(cwd, layoutPath) + " (updated)");
|
|
4296
5195
|
}
|
|
4297
5196
|
}
|
|
4298
5197
|
}
|
|
4299
|
-
await createEmptyMessageFiles(
|
|
5198
|
+
await createEmptyMessageFiles(
|
|
5199
|
+
join6(cwd, messagesDir),
|
|
5200
|
+
allLocales,
|
|
5201
|
+
splitByNamespace
|
|
5202
|
+
);
|
|
4300
5203
|
if (filesCreated.length > 0) {
|
|
4301
5204
|
p.log.success(`next-intl configured: ${filesCreated.join(", ")}`);
|
|
4302
5205
|
}
|
|
@@ -4307,9 +5210,19 @@ async function dropInlineComponents(cwd, componentPath, localeOpts) {
|
|
|
4307
5210
|
await mkdir4(dir, { recursive: true });
|
|
4308
5211
|
const clientFile = `${fsPath}.tsx`;
|
|
4309
5212
|
const serverFile = `${fsPath}-server.tsx`;
|
|
4310
|
-
const clientBasename =
|
|
5213
|
+
const clientBasename = basename2(fsPath);
|
|
5214
|
+
const serverDir = join6(serverFile, "..");
|
|
5215
|
+
const absoluteMessagesDir = join6(cwd, localeOpts.messagesDir);
|
|
5216
|
+
let relativeMessagesDir = relative(serverDir, absoluteMessagesDir);
|
|
5217
|
+
if (!relativeMessagesDir.startsWith(".")) {
|
|
5218
|
+
relativeMessagesDir = `./${relativeMessagesDir}`;
|
|
5219
|
+
}
|
|
4311
5220
|
await writeFile5(clientFile, CLIENT_TEMPLATE, "utf-8");
|
|
4312
|
-
await writeFile5(
|
|
5221
|
+
await writeFile5(
|
|
5222
|
+
serverFile,
|
|
5223
|
+
serverTemplate(clientBasename, { ...localeOpts, relativeMessagesDir }),
|
|
5224
|
+
"utf-8"
|
|
5225
|
+
);
|
|
4313
5226
|
const relClient = relative(cwd, clientFile);
|
|
4314
5227
|
const relServer = relative(cwd, serverFile);
|
|
4315
5228
|
p.log.success(`Created inline components: ${relClient}, ${relServer}`);
|
|
@@ -4353,19 +5266,16 @@ import { getLocale, getMessages } from "@/i18n";
|
|
|
4353
5266
|
/<\/body>/,
|
|
4354
5267
|
" </I18nProvider>\n </body>"
|
|
4355
5268
|
);
|
|
4356
|
-
if (await safeWriteModifiedFile(
|
|
4357
|
-
layoutPath,
|
|
4358
|
-
layoutContent,
|
|
4359
|
-
"root layout"
|
|
4360
|
-
)) {
|
|
5269
|
+
if (await safeWriteModifiedFile(layoutPath, layoutContent, "root layout")) {
|
|
4361
5270
|
filesCreated.push(relative(cwd, layoutPath) + " (updated)");
|
|
4362
5271
|
}
|
|
4363
5272
|
}
|
|
4364
5273
|
}
|
|
4365
|
-
await createEmptyMessageFiles(
|
|
4366
|
-
|
|
4367
|
-
...targetLocales
|
|
4368
|
-
|
|
5274
|
+
await createEmptyMessageFiles(
|
|
5275
|
+
join6(cwd, messagesDir),
|
|
5276
|
+
[sourceLocale, ...targetLocales],
|
|
5277
|
+
splitByNamespace
|
|
5278
|
+
);
|
|
4369
5279
|
if (filesCreated.length > 0) {
|
|
4370
5280
|
p.log.success(`Inline i18n configured: ${filesCreated.join(", ")}`);
|
|
4371
5281
|
}
|
|
@@ -4402,7 +5312,11 @@ async function updateLayoutWithSelectiveMessages(cwd, clientNamespaces) {
|
|
|
4402
5312
|
"messages={clientMessages}"
|
|
4403
5313
|
);
|
|
4404
5314
|
}
|
|
4405
|
-
if (await safeWriteModifiedFile(
|
|
5315
|
+
if (await safeWriteModifiedFile(
|
|
5316
|
+
layoutPath,
|
|
5317
|
+
content,
|
|
5318
|
+
"root layout (selective messages)"
|
|
5319
|
+
)) {
|
|
4406
5320
|
const rel = relative(cwd, layoutPath);
|
|
4407
5321
|
p.log.success(
|
|
4408
5322
|
`Updated ${rel} with selective message passing (${clientNamespaces.length} namespaces)`
|
|
@@ -4485,21 +5399,13 @@ async function runInitWizard() {
|
|
|
4485
5399
|
splitByNamespace = split;
|
|
4486
5400
|
}
|
|
4487
5401
|
const detected = detectIncludePatterns(cwd);
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
5402
|
+
const patternsInput = await p.text({
|
|
5403
|
+
message: "Include patterns (comma-separated):",
|
|
5404
|
+
initialValue: detected.join(", "),
|
|
5405
|
+
placeholder: "app/**/*.tsx, components/**/*.tsx"
|
|
4491
5406
|
});
|
|
4492
|
-
if (p.isCancel(
|
|
4493
|
-
|
|
4494
|
-
includePatterns = detected;
|
|
4495
|
-
} else {
|
|
4496
|
-
const customPatterns = await p.text({
|
|
4497
|
-
message: "Include patterns (comma-separated):",
|
|
4498
|
-
initialValue: "src/**/*.tsx, src/**/*.jsx"
|
|
4499
|
-
});
|
|
4500
|
-
if (p.isCancel(customPatterns)) cancel2();
|
|
4501
|
-
includePatterns = customPatterns.split(",").map((s) => s.trim());
|
|
4502
|
-
}
|
|
5407
|
+
if (p.isCancel(patternsInput)) cancel2();
|
|
5408
|
+
const includePatterns = patternsInput.split(",").map((s) => s.trim());
|
|
4503
5409
|
let i18nImport = "";
|
|
4504
5410
|
let componentPath;
|
|
4505
5411
|
let typeSafe = false;
|
|
@@ -4570,7 +5476,13 @@ async function runInitWizard() {
|
|
|
4570
5476
|
splitByNamespace
|
|
4571
5477
|
);
|
|
4572
5478
|
} else {
|
|
4573
|
-
await setupNextIntl(
|
|
5479
|
+
await setupNextIntl(
|
|
5480
|
+
cwd,
|
|
5481
|
+
sourceLocale,
|
|
5482
|
+
targetLocales,
|
|
5483
|
+
messagesDir,
|
|
5484
|
+
splitByNamespace
|
|
5485
|
+
);
|
|
4574
5486
|
}
|
|
4575
5487
|
const runPipeline = await p.confirm({
|
|
4576
5488
|
message: "Run the full pipeline now?"
|
|
@@ -4623,7 +5535,10 @@ async function runInitWizard() {
|
|
|
4623
5535
|
`Codegen... ${codegenResult.stringsWrapped} strings wrapped in ${codegenResult.filesModified} files`
|
|
4624
5536
|
);
|
|
4625
5537
|
if (codegenResult.clientNamespaces.length > 0) {
|
|
4626
|
-
await updateLayoutWithSelectiveMessages(
|
|
5538
|
+
await updateLayoutWithSelectiveMessages(
|
|
5539
|
+
cwd,
|
|
5540
|
+
codegenResult.clientNamespaces
|
|
5541
|
+
);
|
|
4627
5542
|
}
|
|
4628
5543
|
for (const locale of targetLocales) {
|
|
4629
5544
|
const st = p.spinner();
|
|
@@ -4685,6 +5600,7 @@ var init_init = __esm({
|
|
|
4685
5600
|
}
|
|
4686
5601
|
};
|
|
4687
5602
|
LOCALE_OPTIONS = [
|
|
5603
|
+
{ value: "en", label: "English (en)" },
|
|
4688
5604
|
{ value: "es", label: "Spanish (es)" },
|
|
4689
5605
|
{ value: "fr", label: "French (fr)" },
|
|
4690
5606
|
{ value: "de", label: "German (de)" },
|
|
@@ -4946,6 +5862,11 @@ var codegenCommand = defineCommand({
|
|
|
4946
5862
|
type: "boolean",
|
|
4947
5863
|
description: "Show what would be changed without modifying files",
|
|
4948
5864
|
default: false
|
|
5865
|
+
},
|
|
5866
|
+
"module-factory": {
|
|
5867
|
+
type: "boolean",
|
|
5868
|
+
description: "Convert module-level constants with translatable strings into factory functions",
|
|
5869
|
+
default: false
|
|
4949
5870
|
}
|
|
4950
5871
|
},
|
|
4951
5872
|
async run({ args }) {
|
|
@@ -4989,6 +5910,7 @@ var codegenCommand = defineCommand({
|
|
|
4989
5910
|
const result = await runCodegenStep({
|
|
4990
5911
|
config,
|
|
4991
5912
|
cwd: process.cwd(),
|
|
5913
|
+
moduleFactory: args["module-factory"],
|
|
4992
5914
|
callbacks: {
|
|
4993
5915
|
onProgress: (c, t3) => logProgress(c, t3, "Processing files...")
|
|
4994
5916
|
}
|
|
@@ -5015,7 +5937,11 @@ var typegenCommand = defineCommand({
|
|
|
5015
5937
|
logWarning("Type generation is only available in keys mode.");
|
|
5016
5938
|
return;
|
|
5017
5939
|
}
|
|
5018
|
-
await generateNextIntlTypes(
|
|
5940
|
+
await generateNextIntlTypes(
|
|
5941
|
+
config.messagesDir,
|
|
5942
|
+
config.sourceLocale,
|
|
5943
|
+
config.splitByNamespace
|
|
5944
|
+
);
|
|
5019
5945
|
logSuccess(`Generated ${join7(config.messagesDir, "next-intl.d.ts")}`);
|
|
5020
5946
|
}
|
|
5021
5947
|
});
|
|
@@ -5035,6 +5961,11 @@ var runCommand = defineCommand({
|
|
|
5035
5961
|
default: false,
|
|
5036
5962
|
description: "Ignore translation cache"
|
|
5037
5963
|
},
|
|
5964
|
+
"module-factory": {
|
|
5965
|
+
type: "boolean",
|
|
5966
|
+
description: "Convert module-level constants with translatable strings into factory functions",
|
|
5967
|
+
default: false
|
|
5968
|
+
},
|
|
5038
5969
|
verbose: {
|
|
5039
5970
|
type: "boolean",
|
|
5040
5971
|
default: false,
|
|
@@ -5044,7 +5975,9 @@ var runCommand = defineCommand({
|
|
|
5044
5975
|
async run({ args }) {
|
|
5045
5976
|
const config = await loadTranslateKitConfig();
|
|
5046
5977
|
if (!config.scan) {
|
|
5047
|
-
logError(
|
|
5978
|
+
logError(
|
|
5979
|
+
"No scan configuration found. Add a 'scan' section to your config."
|
|
5980
|
+
);
|
|
5048
5981
|
process.exit(1);
|
|
5049
5982
|
}
|
|
5050
5983
|
const usageTracker = createUsageTracker();
|
|
@@ -5059,7 +5992,9 @@ var runCommand = defineCommand({
|
|
|
5059
5992
|
}
|
|
5060
5993
|
});
|
|
5061
5994
|
logProgressClear();
|
|
5062
|
-
logSuccess(
|
|
5995
|
+
logSuccess(
|
|
5996
|
+
`Scan: ${scanResult.bareStringCount} strings from ${scanResult.fileCount} files`
|
|
5997
|
+
);
|
|
5063
5998
|
if (scanResult.bareStringCount === 0 && Object.keys(scanResult.textToKey).length === 0) {
|
|
5064
5999
|
logWarning("No translatable strings found.");
|
|
5065
6000
|
return;
|
|
@@ -5068,12 +6003,15 @@ var runCommand = defineCommand({
|
|
|
5068
6003
|
config,
|
|
5069
6004
|
cwd: process.cwd(),
|
|
5070
6005
|
textToKey: scanResult.textToKey,
|
|
6006
|
+
moduleFactory: args["module-factory"],
|
|
5071
6007
|
callbacks: {
|
|
5072
6008
|
onProgress: (c, t3) => logProgress(c, t3, "Codegen...")
|
|
5073
6009
|
}
|
|
5074
6010
|
});
|
|
5075
6011
|
logProgressClear();
|
|
5076
|
-
logSuccess(
|
|
6012
|
+
logSuccess(
|
|
6013
|
+
`Codegen: ${codegenResult.stringsWrapped} strings wrapped in ${codegenResult.filesModified} files`
|
|
6014
|
+
);
|
|
5077
6015
|
const locales = config.targetLocales;
|
|
5078
6016
|
logStart(config.sourceLocale, locales);
|
|
5079
6017
|
const translateResult = await runTranslateStep({
|
|
@@ -5095,7 +6033,10 @@ var runCommand = defineCommand({
|
|
|
5095
6033
|
const usage = usageTracker.get();
|
|
5096
6034
|
if (usage.totalTokens > 0) {
|
|
5097
6035
|
const cost = await estimateCost(config.model, usage);
|
|
5098
|
-
logUsage(
|
|
6036
|
+
logUsage(
|
|
6037
|
+
formatUsage(usage),
|
|
6038
|
+
cost ? formatCost(cost.totalUSD) : void 0
|
|
6039
|
+
);
|
|
5099
6040
|
}
|
|
5100
6041
|
}
|
|
5101
6042
|
});
|
|
@@ -5140,10 +6081,11 @@ var main = defineCommand({
|
|
|
5140
6081
|
typegen Generate TypeScript types for message keys
|
|
5141
6082
|
|
|
5142
6083
|
Flags:
|
|
5143
|
-
--dry-run
|
|
5144
|
-
--force
|
|
5145
|
-
--locale
|
|
5146
|
-
--
|
|
6084
|
+
--dry-run Preview without writing files
|
|
6085
|
+
--force Ignore translation cache
|
|
6086
|
+
--locale Only translate a specific locale
|
|
6087
|
+
--module-factory Convert module-level constants into factory functions
|
|
6088
|
+
--verbose Verbose output
|
|
5147
6089
|
|
|
5148
6090
|
Examples:
|
|
5149
6091
|
translate-kit init # Set up a new project
|