Skip to content

Commit cc66535

Browse files
committed
front: extract keys with TypeScript
Depends on i18next/react-i18next#1842 Signed-off-by: Simon Ser <[email protected]>
1 parent e4fac7f commit cc66535

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

front/scripts/foo.ts

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import * as ts from 'typescript';
2+
import path from 'node:path';
3+
4+
const tsconfigPath = ts.findConfigFile(process.cwd(), ts.sys.fileExists, 'tsconfig.json');
5+
const tsconfigFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
6+
const tsconfig = ts.parseJsonConfigFileContent(
7+
tsconfigFile.config,
8+
ts.sys,
9+
path.dirname(tsconfigPath)
10+
);
11+
12+
const program = ts.createProgram({
13+
options: tsconfig.options,
14+
rootNames: tsconfig.fileNames,
15+
projectReferences: tsconfig.projectReferences,
16+
});
17+
const checker = program.getTypeChecker();
18+
//console.log(checker);
19+
20+
const files = program.getSourceFiles();
21+
22+
const extractedKeys = new Set();
23+
24+
function visitCallExpression(file, node: ts.Node) {
25+
const symbol = checker.getSymbolAtLocation(node.expression);
26+
if (!symbol) {
27+
return;
28+
}
29+
30+
const type = checker.getTypeOfSymbolAtLocation(symbol, node.expression);
31+
if (type.symbol?.escapedName !== 'TFunction') {
32+
return;
33+
}
34+
35+
//console.log(node.expression.escapedText);
36+
//console.log(checker.typeToString(type));
37+
//console.log(type.symbol?.escapedName);
38+
39+
//if (node.expression.escapedText !== 't') {
40+
// console.log('A', file.fileName, file.getLineAndCharacterOfPosition(node.pos));
41+
//}
42+
43+
if (type.resolvedTypeArguments.length !== 2) {
44+
throw new Error('Expected two generic type arguments for TFunction');
45+
}
46+
if (node.arguments.length < 1) {
47+
throw new Error('Expected at least one argument for TFunction');
48+
}
49+
50+
const namespaceType = type.resolvedTypeArguments[0];
51+
const prefixType = type.resolvedTypeArguments[1];
52+
53+
let namespaces;
54+
if (typeof namespaceType.value === 'string') {
55+
namespaces = [namespaceType.value];
56+
} else if (checker.isTupleType(namespaceType)) {
57+
namespaces = namespaceType.resolvedTypeArguments.map((type) => {
58+
return type.value;
59+
}).filter((value) => typeof value === 'string');
60+
if (namespaceType.resolvedTypeArguments.length !== namespaces.length) {
61+
//console.log('C', file.fileName, file.getLineAndCharacterOfPosition(node.pos));
62+
//console.log(namespaceType.resolvedTypeArguments.map(checker.typeToString));
63+
//console.log(ts.Debug.formatSyntaxKind(symbol.valueDeclaration.kind));
64+
//return;
65+
}
66+
if (namespaces.length === 0) {
67+
return;
68+
}
69+
} else {
70+
//console.log('B', file.fileName, file.getLineAndCharacterOfPosition(node.pos));
71+
//console.log(checker.typeToString(namespaceType));
72+
return;
73+
}
74+
75+
if (typeof prefixType.value !== 'string' && prefixType !== checker.getUndefinedType()) {
76+
//console.log('D', file.fileName, file.getLineAndCharacterOfPosition(node.pos));
77+
//console.log(type.resolvedTypeArguments.map(checker.typeToString));
78+
//console.log(type.resolvedTypeArguments.map((t) => t.value));
79+
//console.log(namespaceType);
80+
return;
81+
}
82+
83+
const prefix = prefixType.value;
84+
85+
const keyNode = node.arguments[0];
86+
let key;
87+
if (ts.isStringLiteral(keyNode)) {
88+
key = keyNode.text;
89+
} else {
90+
const symbol = checker.getSymbolAtLocation(keyNode);
91+
const type = symbol ? checker.getTypeOfSymbol(symbol) : null;
92+
if (!ts.isTemplateExpression(keyNode)) {
93+
//console.log('E', file.fileName, file.getLineAndCharacterOfPosition(node.pos));
94+
//console.log(ts.Debug.formatSyntaxKind(keyNode.kind));
95+
//console.log(type);
96+
}
97+
return;
98+
}
99+
100+
// TODO: extract ns from TFunction options, if any
101+
102+
//console.log(namespaces, prefix, key);
103+
104+
if (!key.includes(':')) {
105+
if (prefix) {
106+
key = prefix + '.' + key;
107+
}
108+
key = namespaces[0] + ':' + key;
109+
}
110+
111+
extractedKeys.add(key);
112+
}
113+
114+
function visitNode(file, node: ts.Node) {
115+
if (ts.isCallExpression(node)) {
116+
visitCallExpression(file, node);
117+
}
118+
119+
//if (ts.isCallExpression(node) && node.expression?.name?.escapedText === 't') {
120+
//console.log(file.fileName, file.getLineAndCharacterOfPosition(node.pos));
121+
//console.log(node);
122+
//console.log(checker.getSymbolAtLocation(node.expression));
123+
//console.log('');
124+
//}
125+
126+
node.forEachChild((node) => visitNode(file, node));
127+
}
128+
129+
for (const file of files) {
130+
//console.log(file);
131+
132+
visitNode(file, file);
133+
}
134+
135+
console.log(extractedKeys.size);

0 commit comments

Comments
 (0)