@lexibase/rdx-next 6.0.0 → 6.0.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/bin/homepage-translations.mjs +76 -16
- package/package.json +1 -1
|
@@ -21,7 +21,12 @@ async function sourcePhrases(rootDir) {
|
|
|
21
21
|
if (source === undefined)
|
|
22
22
|
throw new Error(`Create ${sourceFile} to use homepage.translations.`)
|
|
23
23
|
const { default: ts } = await import('typescript')
|
|
24
|
-
const file = ts.createSourceFile(
|
|
24
|
+
const file = ts.createSourceFile(
|
|
25
|
+
sourceFile,
|
|
26
|
+
source,
|
|
27
|
+
ts.ScriptTarget.Latest,
|
|
28
|
+
true
|
|
29
|
+
)
|
|
25
30
|
for (const statement of file.statements) {
|
|
26
31
|
if (
|
|
27
32
|
!ts.isExpressionStatement(statement) ||
|
|
@@ -34,19 +39,67 @@ async function sourcePhrases(rootDir) {
|
|
|
34
39
|
)
|
|
35
40
|
}
|
|
36
41
|
const phrases = new Set()
|
|
37
|
-
|
|
42
|
+
const warnings = []
|
|
43
|
+
const passedOn =
|
|
44
|
+
't is passed on or renamed; phrases translated elsewhere are left out of the translation report'
|
|
45
|
+
const needsLiteral =
|
|
46
|
+
't() needs a literal phrase; this call is left out of the translation report'
|
|
47
|
+
function warn(node, message) {
|
|
48
|
+
const { line } = file.getLineAndCharacterOfPosition(node.getStart(file))
|
|
49
|
+
warnings.push({ line: line + 1, message })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Bind identifiers within this file so an unrelated or shadowed t is ignored.
|
|
53
|
+
const options = { noLib: true, noResolve: true, types: [] }
|
|
54
|
+
const program = ts.createProgram([sourceFile], options, {
|
|
55
|
+
...ts.createCompilerHost(options),
|
|
56
|
+
getSourceFile: (name) => (name === sourceFile ? file : undefined),
|
|
57
|
+
})
|
|
58
|
+
const checker = program.getTypeChecker()
|
|
59
|
+
const bindings = new Set()
|
|
60
|
+
function findBindings(node) {
|
|
38
61
|
if (
|
|
39
|
-
ts.
|
|
40
|
-
ts.
|
|
41
|
-
node.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
ts.isBindingElement(node) &&
|
|
63
|
+
ts.isObjectBindingPattern(node.parent) &&
|
|
64
|
+
ts.isIdentifier(node.name)
|
|
65
|
+
) {
|
|
66
|
+
const key = node.propertyName ?? node.name
|
|
67
|
+
if (ts.isStringLiteralLike(key) || ts.isIdentifier(key)) {
|
|
68
|
+
if (key.text === 't') {
|
|
69
|
+
if (node.name.text === 't')
|
|
70
|
+
bindings.add(checker.getSymbolAtLocation(node.name))
|
|
71
|
+
else warn(node, passedOn)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
ts.forEachChild(node, findBindings)
|
|
76
|
+
}
|
|
77
|
+
findBindings(file)
|
|
78
|
+
|
|
79
|
+
function visit(node) {
|
|
80
|
+
if (ts.isIdentifier(node) && node.text === 't') {
|
|
81
|
+
const parent = node.parent
|
|
82
|
+
const symbol = ts.isShorthandPropertyAssignment(parent)
|
|
83
|
+
? checker.getShorthandAssignmentValueSymbol(parent)
|
|
84
|
+
: checker.getSymbolAtLocation(node)
|
|
85
|
+
if (
|
|
86
|
+
symbol &&
|
|
87
|
+
bindings.has(symbol) &&
|
|
88
|
+
!ts.isBindingElement(parent) &&
|
|
89
|
+
!ts.isTypeQueryNode(parent)
|
|
90
|
+
) {
|
|
91
|
+
if (ts.isCallExpression(parent) && parent.expression === node) {
|
|
92
|
+
const [argument] = parent.arguments
|
|
93
|
+
if (parent.arguments.length === 1 && ts.isStringLiteralLike(argument))
|
|
94
|
+
phrases.add(argument.text)
|
|
95
|
+
else warn(node, needsLiteral)
|
|
96
|
+
} else warn(node, passedOn)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
46
99
|
ts.forEachChild(node, visit)
|
|
47
100
|
}
|
|
48
101
|
visit(file)
|
|
49
|
-
return phrases
|
|
102
|
+
return { phrases, warnings }
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
export async function readHomepageTranslations(
|
|
@@ -54,7 +107,11 @@ export async function readHomepageTranslations(
|
|
|
54
107
|
{ report = false } = {}
|
|
55
108
|
) {
|
|
56
109
|
const { rootDir, localization } = project
|
|
57
|
-
const phrases = await sourcePhrases(rootDir)
|
|
110
|
+
const { phrases, warnings } = await sourcePhrases(rootDir)
|
|
111
|
+
if (report) {
|
|
112
|
+
for (const { line, message } of warnings.sort((a, b) => a.line - b.line))
|
|
113
|
+
console.warn(`[RDX] ${sourceFile}:${line}: ${message}`)
|
|
114
|
+
}
|
|
58
115
|
phrases.delete(metadataKey)
|
|
59
116
|
const catalogs = []
|
|
60
117
|
for (const locale of localization?.locales ?? []) {
|
|
@@ -86,11 +143,14 @@ export async function readHomepageTranslations(
|
|
|
86
143
|
`[RDX] ${file}: missing translation for ${JSON.stringify(phrase)}`
|
|
87
144
|
)
|
|
88
145
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
146
|
+
// An unrecognized call may use any of the remaining keys.
|
|
147
|
+
if (!warnings.length) {
|
|
148
|
+
for (const phrase of Object.keys(messages)) {
|
|
149
|
+
if (phrase !== metadataKey && !phrases.has(phrase))
|
|
150
|
+
console.warn(
|
|
151
|
+
`[RDX] ${file}: orphaned translation for ${JSON.stringify(phrase)}`
|
|
152
|
+
)
|
|
153
|
+
}
|
|
94
154
|
}
|
|
95
155
|
}
|
|
96
156
|
return { catalogs, phrases }
|