fuma-translate 0.0.2 → 0.0.3
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/{compiler.d.mts → index.d.mts} +1 -1
- package/dist/index.d.mts.map +1 -0
- package/dist/{compiler.mjs → index.mjs} +4 -2
- package/dist/index.mjs.map +1 -0
- package/package.json +3 -23
- package/dist/compiler.d.mts.map +0 -1
- package/dist/compiler.mjs.map +0 -1
- package/dist/react.d.mts +0 -29
- package/dist/react.d.mts.map +0 -1
- package/dist/react.mjs +0 -33
- package/dist/react.mjs.map +0 -1
- package/dist/shared-CAg_QhcN.mjs +0 -8
- package/dist/shared-CAg_QhcN.mjs.map +0 -1
|
@@ -18,4 +18,4 @@ declare function compile(options: CompileOptions): Promise<CompileOutput>;
|
|
|
18
18
|
declare function typegen(output: CompileOutput): string;
|
|
19
19
|
//#endregion
|
|
20
20
|
export { CompileOptions, CompileOutput, StaticAnalysisError, compile, typegen };
|
|
21
|
-
//# sourceMappingURL=
|
|
21
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/compiler.ts"],"mappings":";;;UAgBiB,cAAA;;EAEf,KAAK;AAAA;AAAA,UAGU,aAAA;EAHV;EAKL,eAAe;AAAA;AAAA,cAGJ,mBAAA,SAA4B,KAAA;EAAA,SAG5B,IAAA;EAAA,SACA,IAAA,GAAO,IAAA;cAFhB,OAAA,UACS,IAAA,UACA,IAAA,GAAO,IAAA;AAAA;AAAA,iBAyWE,OAAA,CAAQ,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,aAAA;AAAA,iBAkBhD,OAAA,CAAQ,MAAqB,EAAb,aAAa"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as encodeKey } from "./shared-CAg_QhcN.mjs";
|
|
2
1
|
import fs from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { Visitor, parseSync } from "oxc-parser";
|
|
@@ -210,7 +209,10 @@ function typegen(output) {
|
|
|
210
209
|
if (output.translationKeys.length === 0) return "export type Translations = {};\n";
|
|
211
210
|
return `export type Translations = {\n${output.translationKeys.map((key) => ` ${JSON.stringify(key)}: string;`).join("\n")}\n};\n`;
|
|
212
211
|
}
|
|
212
|
+
function encodeKey(text, notes) {
|
|
213
|
+
return text + notes.map((n) => `(${n})`).join("");
|
|
214
|
+
}
|
|
213
215
|
//#endregion
|
|
214
216
|
export { StaticAnalysisError, compile, typegen };
|
|
215
217
|
|
|
216
|
-
//# sourceMappingURL=
|
|
218
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/compiler.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type {\n BindingPattern,\n CallExpression,\n Expression,\n ObjectProperty,\n ObjectPropertyKind,\n ParamPattern,\n Span,\n} from \"oxc-parser\";\nimport { parseSync, Visitor } from \"oxc-parser\";\nimport { glob } from \"tinyglobby\";\n\ntype SupportedLang = \"js\" | \"jsx\" | \"ts\" | \"tsx\";\n\nexport interface CompileOptions {\n /** glob patterns */\n input: string[];\n}\n\nexport interface CompileOutput {\n /** All encoded keys */\n translationKeys: string[];\n}\n\nexport class StaticAnalysisError extends Error {\n constructor(\n message: string,\n readonly file: string,\n readonly span?: Span,\n ) {\n super(message);\n this.name = \"StaticAnalysisError\";\n }\n}\n\nfunction formatLocation(source: string, offset: number): string {\n let line = 1;\n let column = 1;\n\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source[i] === \"\\n\") {\n line++;\n column = 1;\n } else {\n column++;\n }\n }\n\n return `${line}:${column}`;\n}\n\ntype HookNoteBranches = (string | undefined)[];\n\nfunction parseUseTranslationsCall(\n expr: Expression,\n source: string,\n file: string,\n): HookNoteBranches | null {\n if (expr.type !== \"CallExpression\") return null;\n if (expr.callee.type !== \"Identifier\" || expr.callee.name !== \"useTranslations\") {\n return null;\n }\n\n if (expr.arguments.length === 0) return [undefined];\n\n if (expr.arguments.length > 1) {\n fail(source, file, expr, \"useTranslations accepts at most one options argument\");\n }\n\n const arg = expr.arguments[0];\n if (!arg || arg.type === \"SpreadElement\") {\n fail(source, file, arg ?? expr, \"useTranslations options must be a static object\");\n }\n\n return collectNotes(arg, source, file);\n}\n\nfunction unwrapExpression(expr: Expression): Expression {\n while (\n expr.type === \"ParenthesizedExpression\" ||\n expr.type === \"TSAsExpression\" ||\n expr.type === \"TSSatisfiesExpression\" ||\n expr.type === \"TSTypeAssertion\"\n ) {\n expr = expr.expression;\n }\n return expr;\n}\n\nfunction fail(source: string, file: string, span: Span, message: string): never {\n throw new StaticAnalysisError(\n `${file}:${formatLocation(source, span.start)}: ${message}`,\n file,\n span,\n );\n}\n\nfunction collectStaticStrings(expr: Expression, source: string, file: string): string[] {\n expr = unwrapExpression(expr);\n\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n return [expr.value];\n }\n\n if (expr.type === \"TemplateLiteral\") {\n if (expr.expressions.length > 0) {\n fail(source, file, expr, \"translation key must be a static string\");\n }\n return [expr.quasis.map((q) => q.value.cooked ?? q.value.raw).join(\"\")];\n }\n\n if (expr.type === \"ConditionalExpression\") {\n return [\n ...collectStaticStrings(expr.consequent, source, file),\n ...collectStaticStrings(expr.alternate, source, file),\n ];\n }\n\n fail(source, file, expr, \"translation key must be a static string\");\n}\n\nfunction getNoteProperty(properties: ObjectPropertyKind[]): ObjectProperty | undefined {\n for (const prop of properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.kind !== \"init\") continue;\n\n if (prop.shorthand && prop.key.type === \"Identifier\") {\n if (prop.key.name === \"note\") return prop;\n continue;\n }\n\n if (prop.key.type === \"Identifier\" && prop.key.name === \"note\") {\n return prop;\n }\n\n if (\n prop.key.type === \"Literal\" &&\n typeof prop.key.value === \"string\" &&\n prop.key.value === \"note\"\n ) {\n return prop;\n }\n }\n return undefined;\n}\n\nfunction collectNotes(\n expr: Expression | undefined,\n source: string,\n file: string,\n): (string | undefined)[] {\n if (!expr) return [undefined];\n\n expr = unwrapExpression(expr);\n\n if (expr.type === \"ConditionalExpression\") {\n return [\n ...collectNotes(expr.consequent, source, file),\n ...collectNotes(expr.alternate, source, file),\n ];\n }\n\n if (expr.type !== \"ObjectExpression\") {\n fail(source, file, expr, \"translation options must be a static object\");\n }\n\n for (const prop of expr.properties) {\n if (prop.type === \"SpreadElement\") {\n fail(source, file, prop, \"translation options cannot use spread properties\");\n }\n }\n\n const noteProp = getNoteProperty(expr.properties);\n if (!noteProp) return [undefined];\n\n if (noteProp.shorthand) {\n fail(source, file, noteProp, \"translation note must be a static string\");\n }\n\n const noteValues = collectStaticStrings(noteProp.value, source, file);\n return noteValues.map((note) => note);\n}\n\nfunction currentScope(\n scopes: Map<string, HookNoteBranches | false>[],\n): Map<string, HookNoteBranches | false> {\n const scope = scopes.at(-1);\n if (!scope) throw new Error(\"scope stack is empty\");\n return scope;\n}\n\nfunction registerBinding(\n pattern: BindingPattern,\n hookNotes: HookNoteBranches | false,\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n switch (pattern.type) {\n case \"Identifier\":\n currentScope(scopes).set(pattern.name, hookNotes);\n return;\n case \"ObjectPattern\":\n for (const prop of pattern.properties) {\n if (prop.type === \"RestElement\") {\n registerBinding(prop.argument, false, scopes);\n } else {\n registerBinding(prop.value, false, scopes);\n }\n }\n return;\n case \"ArrayPattern\":\n for (const element of pattern.elements) {\n if (!element) continue;\n if (element.type === \"RestElement\") {\n registerBinding(element.argument, false, scopes);\n } else {\n registerBinding(element, false, scopes);\n }\n }\n return;\n case \"AssignmentPattern\":\n registerBinding(pattern.left, hookNotes, scopes);\n return;\n }\n}\n\nfunction registerParams(\n params: ParamPattern[],\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n for (const param of params) {\n if (param.type === \"TSParameterProperty\") {\n registerBinding(param.parameter, false, scopes);\n } else if (param.type === \"RestElement\") {\n registerBinding(param.argument, false, scopes);\n } else {\n registerBinding(param, false, scopes);\n }\n }\n}\n\nfunction getTranslationHookNotes(\n name: string,\n scopes: Map<string, HookNoteBranches | false>[],\n): HookNoteBranches | false {\n for (let i = scopes.length - 1; i >= 0; i--) {\n const scope = scopes[i];\n if (!scope) continue;\n if (scope.has(name)) return scope.get(name)!;\n }\n return false;\n}\n\nfunction encodeTranslationKey(\n text: string,\n hookNotes: HookNoteBranches,\n callNotes: HookNoteBranches,\n): string[] {\n const keys: string[] = [];\n\n for (const hookNote of hookNotes) {\n for (const callNote of callNotes) {\n const notes: string[] = [];\n if (hookNote) notes.push(hookNote);\n if (callNote) notes.push(callNote);\n keys.push(encodeKey(text, notes));\n }\n }\n\n return keys;\n}\n\nfunction analyzeCall(\n call: CallExpression,\n source: string,\n file: string,\n keys: Set<string>,\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n const callee = unwrapExpression(call.callee);\n if (callee.type !== \"Identifier\") return;\n\n const hookNotes = getTranslationHookNotes(callee.name, scopes);\n if (hookNotes === false) return;\n\n if (call.arguments.length === 0) {\n fail(source, file, call, \"translation call requires a static string argument\");\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg || firstArg.type === \"SpreadElement\") {\n fail(source, file, firstArg ?? call, \"translation key must be a static string\");\n }\n\n const texts = collectStaticStrings(firstArg, source, file);\n\n let callNotes: HookNoteBranches = [undefined];\n if (call.arguments.length > 1) {\n const secondArg = call.arguments[1];\n if (!secondArg || secondArg.type === \"SpreadElement\") {\n fail(source, file, secondArg ?? call, \"translation options must be a static object\");\n }\n callNotes = collectNotes(secondArg, source, file);\n }\n\n for (const text of texts) {\n for (const key of encodeTranslationKey(text, hookNotes, callNotes)) {\n keys.add(key);\n }\n }\n}\n\nfunction analyzeSource(file: string, lang: SupportedLang, source: string): string[] {\n const result = parseSync(file, source, { lang, sourceType: \"module\" });\n\n if (result.errors.length > 0) {\n const message = result.errors.map((error) => error.message).join(\"\\n\");\n throw new StaticAnalysisError(message, file);\n }\n\n const keys = new Set<string>();\n const scopes: Map<string, HookNoteBranches | false>[] = [new Map()];\n\n const pushScope = () => {\n scopes.push(new Map());\n };\n\n const popScope = () => {\n scopes.pop();\n };\n\n const visitor = new Visitor({\n BlockStatement: pushScope,\n \"BlockStatement:exit\": popScope,\n\n CatchClause: pushScope,\n \"CatchClause:exit\": popScope,\n\n FunctionDeclaration(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"FunctionDeclaration:exit\": popScope,\n\n FunctionExpression(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"FunctionExpression:exit\": popScope,\n\n ArrowFunctionExpression(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"ArrowFunctionExpression:exit\": popScope,\n\n VariableDeclarator(decl) {\n if (!decl.init) return;\n\n const init = unwrapExpression(decl.init);\n const hookNotes = parseUseTranslationsCall(init, source, file);\n registerBinding(decl.id, hookNotes ?? false, scopes);\n },\n\n CallExpression(call) {\n analyzeCall(call, source, file, keys, scopes);\n },\n });\n\n visitor.visit(result.program);\n return [...keys];\n}\n\nfunction getLang(file: string): SupportedLang | undefined {\n switch (path.extname(file)) {\n case \".tsx\":\n return \"tsx\";\n case \".ts\":\n case \".cts\":\n case \".mts\":\n return \"ts\";\n case \".jsx\":\n return \"jsx\";\n case \".cjs\":\n case \".mjs\":\n case \".js\":\n return \"js\";\n }\n}\n\nexport async function compile(options: CompileOptions): Promise<CompileOutput> {\n const files = await glob(options.input, { absolute: true });\n const keys = new Set<string>();\n\n for (const file of files) {\n const lang = getLang(file);\n if (!lang) continue;\n\n const source = await fs.readFile(file, \"utf8\");\n for (const key of analyzeSource(file, lang, source)) {\n keys.add(key);\n }\n }\n\n const translationKeys = [...keys].sort();\n return { translationKeys };\n}\n\nexport function typegen(output: CompileOutput): string {\n if (output.translationKeys.length === 0) {\n return \"export type Translations = {};\\n\";\n }\n\n const entries = output.translationKeys\n .map((key) => ` ${JSON.stringify(key)}: string;`)\n .join(\"\\n\");\n\n return `export type Translations = {\\n${entries}\\n};\\n`;\n}\n\nfunction encodeKey(text: string, notes: string[]): string {\n return text + notes.map((n) => `(${n})`).join(\"\");\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuma-translate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "The build-time package to implement multilingual in your JavaScript app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fuma Nama",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
12
|
"exports": {
|
|
13
|
-
"
|
|
14
|
-
"./react": "./dist/react.mjs",
|
|
13
|
+
".": "./dist/index.mjs",
|
|
15
14
|
"./package.json": "./package.json"
|
|
16
15
|
},
|
|
17
16
|
"publishConfig": {
|
|
@@ -23,30 +22,11 @@
|
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
24
|
"@types/node": "^25.9.2",
|
|
26
|
-
"@types/react": "19.2.17",
|
|
27
|
-
"@types/react-dom": "19.2.3",
|
|
28
|
-
"react": "^19.2.7",
|
|
29
|
-
"react-dom": "^19.2.7",
|
|
25
|
+
"@types/react": "^19.2.17",
|
|
30
26
|
"tsdown": "^0.22.2",
|
|
31
27
|
"typescript": "6.0.3",
|
|
32
28
|
"@repo/typescript-config": "0.0.0"
|
|
33
29
|
},
|
|
34
|
-
"peerDependencies": {
|
|
35
|
-
"@types/react": "*",
|
|
36
|
-
"react": "^19.2.0",
|
|
37
|
-
"react-dom": "^19.2.0"
|
|
38
|
-
},
|
|
39
|
-
"peerDependenciesMeta": {
|
|
40
|
-
"react": {
|
|
41
|
-
"optional": true
|
|
42
|
-
},
|
|
43
|
-
"react-dom": {
|
|
44
|
-
"optional": true
|
|
45
|
-
},
|
|
46
|
-
"@types/react": {
|
|
47
|
-
"optional": true
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
30
|
"scripts": {
|
|
51
31
|
"types:check": "tsc --noEmit",
|
|
52
32
|
"build": "tsdown",
|
package/dist/compiler.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.d.mts","names":[],"sources":["../src/compiler.ts"],"mappings":";;;UAiBiB,cAAA;;EAEf,KAAK;AAAA;AAAA,UAGU,aAAA;EAHV;EAKL,eAAe;AAAA;AAAA,cAGJ,mBAAA,SAA4B,KAAA;EAAA,SAG5B,IAAA;EAAA,SACA,IAAA,GAAO,IAAA;cAFhB,OAAA,UACS,IAAA,UACA,IAAA,GAAO,IAAA;AAAA;AAAA,iBAyWE,OAAA,CAAQ,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,aAAA;AAAA,iBAkBhD,OAAA,CAAQ,MAAqB,EAAb,aAAa"}
|
package/dist/compiler.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.mjs","names":[],"sources":["../src/compiler.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type {\n BindingPattern,\n CallExpression,\n Expression,\n ObjectProperty,\n ObjectPropertyKind,\n ParamPattern,\n Span,\n} from \"oxc-parser\";\nimport { parseSync, Visitor } from \"oxc-parser\";\nimport { glob } from \"tinyglobby\";\nimport { encodeKey } from \"./shared\";\n\ntype SupportedLang = \"js\" | \"jsx\" | \"ts\" | \"tsx\";\n\nexport interface CompileOptions {\n /** glob patterns */\n input: string[];\n}\n\nexport interface CompileOutput {\n /** All encoded keys */\n translationKeys: string[];\n}\n\nexport class StaticAnalysisError extends Error {\n constructor(\n message: string,\n readonly file: string,\n readonly span?: Span,\n ) {\n super(message);\n this.name = \"StaticAnalysisError\";\n }\n}\n\nfunction formatLocation(source: string, offset: number): string {\n let line = 1;\n let column = 1;\n\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source[i] === \"\\n\") {\n line++;\n column = 1;\n } else {\n column++;\n }\n }\n\n return `${line}:${column}`;\n}\n\ntype HookNoteBranches = (string | undefined)[];\n\nfunction parseUseTranslationsCall(\n expr: Expression,\n source: string,\n file: string,\n): HookNoteBranches | null {\n if (expr.type !== \"CallExpression\") return null;\n if (expr.callee.type !== \"Identifier\" || expr.callee.name !== \"useTranslations\") {\n return null;\n }\n\n if (expr.arguments.length === 0) return [undefined];\n\n if (expr.arguments.length > 1) {\n fail(source, file, expr, \"useTranslations accepts at most one options argument\");\n }\n\n const arg = expr.arguments[0];\n if (!arg || arg.type === \"SpreadElement\") {\n fail(source, file, arg ?? expr, \"useTranslations options must be a static object\");\n }\n\n return collectNotes(arg, source, file);\n}\n\nfunction unwrapExpression(expr: Expression): Expression {\n while (\n expr.type === \"ParenthesizedExpression\" ||\n expr.type === \"TSAsExpression\" ||\n expr.type === \"TSSatisfiesExpression\" ||\n expr.type === \"TSTypeAssertion\"\n ) {\n expr = expr.expression;\n }\n return expr;\n}\n\nfunction fail(source: string, file: string, span: Span, message: string): never {\n throw new StaticAnalysisError(\n `${file}:${formatLocation(source, span.start)}: ${message}`,\n file,\n span,\n );\n}\n\nfunction collectStaticStrings(expr: Expression, source: string, file: string): string[] {\n expr = unwrapExpression(expr);\n\n if (expr.type === \"Literal\" && typeof expr.value === \"string\") {\n return [expr.value];\n }\n\n if (expr.type === \"TemplateLiteral\") {\n if (expr.expressions.length > 0) {\n fail(source, file, expr, \"translation key must be a static string\");\n }\n return [expr.quasis.map((q) => q.value.cooked ?? q.value.raw).join(\"\")];\n }\n\n if (expr.type === \"ConditionalExpression\") {\n return [\n ...collectStaticStrings(expr.consequent, source, file),\n ...collectStaticStrings(expr.alternate, source, file),\n ];\n }\n\n fail(source, file, expr, \"translation key must be a static string\");\n}\n\nfunction getNoteProperty(properties: ObjectPropertyKind[]): ObjectProperty | undefined {\n for (const prop of properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.kind !== \"init\") continue;\n\n if (prop.shorthand && prop.key.type === \"Identifier\") {\n if (prop.key.name === \"note\") return prop;\n continue;\n }\n\n if (prop.key.type === \"Identifier\" && prop.key.name === \"note\") {\n return prop;\n }\n\n if (\n prop.key.type === \"Literal\" &&\n typeof prop.key.value === \"string\" &&\n prop.key.value === \"note\"\n ) {\n return prop;\n }\n }\n return undefined;\n}\n\nfunction collectNotes(\n expr: Expression | undefined,\n source: string,\n file: string,\n): (string | undefined)[] {\n if (!expr) return [undefined];\n\n expr = unwrapExpression(expr);\n\n if (expr.type === \"ConditionalExpression\") {\n return [\n ...collectNotes(expr.consequent, source, file),\n ...collectNotes(expr.alternate, source, file),\n ];\n }\n\n if (expr.type !== \"ObjectExpression\") {\n fail(source, file, expr, \"translation options must be a static object\");\n }\n\n for (const prop of expr.properties) {\n if (prop.type === \"SpreadElement\") {\n fail(source, file, prop, \"translation options cannot use spread properties\");\n }\n }\n\n const noteProp = getNoteProperty(expr.properties);\n if (!noteProp) return [undefined];\n\n if (noteProp.shorthand) {\n fail(source, file, noteProp, \"translation note must be a static string\");\n }\n\n const noteValues = collectStaticStrings(noteProp.value, source, file);\n return noteValues.map((note) => note);\n}\n\nfunction currentScope(\n scopes: Map<string, HookNoteBranches | false>[],\n): Map<string, HookNoteBranches | false> {\n const scope = scopes.at(-1);\n if (!scope) throw new Error(\"scope stack is empty\");\n return scope;\n}\n\nfunction registerBinding(\n pattern: BindingPattern,\n hookNotes: HookNoteBranches | false,\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n switch (pattern.type) {\n case \"Identifier\":\n currentScope(scopes).set(pattern.name, hookNotes);\n return;\n case \"ObjectPattern\":\n for (const prop of pattern.properties) {\n if (prop.type === \"RestElement\") {\n registerBinding(prop.argument, false, scopes);\n } else {\n registerBinding(prop.value, false, scopes);\n }\n }\n return;\n case \"ArrayPattern\":\n for (const element of pattern.elements) {\n if (!element) continue;\n if (element.type === \"RestElement\") {\n registerBinding(element.argument, false, scopes);\n } else {\n registerBinding(element, false, scopes);\n }\n }\n return;\n case \"AssignmentPattern\":\n registerBinding(pattern.left, hookNotes, scopes);\n return;\n }\n}\n\nfunction registerParams(\n params: ParamPattern[],\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n for (const param of params) {\n if (param.type === \"TSParameterProperty\") {\n registerBinding(param.parameter, false, scopes);\n } else if (param.type === \"RestElement\") {\n registerBinding(param.argument, false, scopes);\n } else {\n registerBinding(param, false, scopes);\n }\n }\n}\n\nfunction getTranslationHookNotes(\n name: string,\n scopes: Map<string, HookNoteBranches | false>[],\n): HookNoteBranches | false {\n for (let i = scopes.length - 1; i >= 0; i--) {\n const scope = scopes[i];\n if (!scope) continue;\n if (scope.has(name)) return scope.get(name)!;\n }\n return false;\n}\n\nfunction encodeTranslationKey(\n text: string,\n hookNotes: HookNoteBranches,\n callNotes: HookNoteBranches,\n): string[] {\n const keys: string[] = [];\n\n for (const hookNote of hookNotes) {\n for (const callNote of callNotes) {\n const notes: string[] = [];\n if (hookNote) notes.push(hookNote);\n if (callNote) notes.push(callNote);\n keys.push(encodeKey(text, notes));\n }\n }\n\n return keys;\n}\n\nfunction analyzeCall(\n call: CallExpression,\n source: string,\n file: string,\n keys: Set<string>,\n scopes: Map<string, HookNoteBranches | false>[],\n): void {\n const callee = unwrapExpression(call.callee);\n if (callee.type !== \"Identifier\") return;\n\n const hookNotes = getTranslationHookNotes(callee.name, scopes);\n if (hookNotes === false) return;\n\n if (call.arguments.length === 0) {\n fail(source, file, call, \"translation call requires a static string argument\");\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg || firstArg.type === \"SpreadElement\") {\n fail(source, file, firstArg ?? call, \"translation key must be a static string\");\n }\n\n const texts = collectStaticStrings(firstArg, source, file);\n\n let callNotes: HookNoteBranches = [undefined];\n if (call.arguments.length > 1) {\n const secondArg = call.arguments[1];\n if (!secondArg || secondArg.type === \"SpreadElement\") {\n fail(source, file, secondArg ?? call, \"translation options must be a static object\");\n }\n callNotes = collectNotes(secondArg, source, file);\n }\n\n for (const text of texts) {\n for (const key of encodeTranslationKey(text, hookNotes, callNotes)) {\n keys.add(key);\n }\n }\n}\n\nfunction analyzeSource(file: string, lang: SupportedLang, source: string): string[] {\n const result = parseSync(file, source, { lang, sourceType: \"module\" });\n\n if (result.errors.length > 0) {\n const message = result.errors.map((error) => error.message).join(\"\\n\");\n throw new StaticAnalysisError(message, file);\n }\n\n const keys = new Set<string>();\n const scopes: Map<string, HookNoteBranches | false>[] = [new Map()];\n\n const pushScope = () => {\n scopes.push(new Map());\n };\n\n const popScope = () => {\n scopes.pop();\n };\n\n const visitor = new Visitor({\n BlockStatement: pushScope,\n \"BlockStatement:exit\": popScope,\n\n CatchClause: pushScope,\n \"CatchClause:exit\": popScope,\n\n FunctionDeclaration(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"FunctionDeclaration:exit\": popScope,\n\n FunctionExpression(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"FunctionExpression:exit\": popScope,\n\n ArrowFunctionExpression(node) {\n pushScope();\n registerParams(node.params, scopes);\n },\n \"ArrowFunctionExpression:exit\": popScope,\n\n VariableDeclarator(decl) {\n if (!decl.init) return;\n\n const init = unwrapExpression(decl.init);\n const hookNotes = parseUseTranslationsCall(init, source, file);\n registerBinding(decl.id, hookNotes ?? false, scopes);\n },\n\n CallExpression(call) {\n analyzeCall(call, source, file, keys, scopes);\n },\n });\n\n visitor.visit(result.program);\n return [...keys];\n}\n\nfunction getLang(file: string): SupportedLang | undefined {\n switch (path.extname(file)) {\n case \".tsx\":\n return \"tsx\";\n case \".ts\":\n case \".cts\":\n case \".mts\":\n return \"ts\";\n case \".jsx\":\n return \"jsx\";\n case \".cjs\":\n case \".mjs\":\n case \".js\":\n return \"js\";\n }\n}\n\nexport async function compile(options: CompileOptions): Promise<CompileOutput> {\n const files = await glob(options.input, { absolute: true });\n const keys = new Set<string>();\n\n for (const file of files) {\n const lang = getLang(file);\n if (!lang) continue;\n\n const source = await fs.readFile(file, \"utf8\");\n for (const key of analyzeSource(file, lang, source)) {\n keys.add(key);\n }\n }\n\n const translationKeys = [...keys].sort();\n return { translationKeys };\n}\n\nexport function typegen(output: CompileOutput): string {\n if (output.translationKeys.length === 0) {\n return \"export type Translations = {};\\n\";\n }\n\n const entries = output.translationKeys\n .map((key) => ` ${JSON.stringify(key)}: string;`)\n .join(\"\\n\");\n\n return `export type Translations = {\\n${entries}\\n};\\n`;\n}\n"],"mappings":"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"}
|
package/dist/react.d.mts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
//#region src/react.d.ts
|
|
4
|
-
type Translations = Record<string, string>;
|
|
5
|
-
/** add translations, you can stack multiple <TranslationProvider /> to override/extend translations */
|
|
6
|
-
declare function TranslationProvider({
|
|
7
|
-
translations,
|
|
8
|
-
children
|
|
9
|
-
}: {
|
|
10
|
-
translations: Translations;
|
|
11
|
-
children: ReactNode;
|
|
12
|
-
}): import("react").JSX.Element;
|
|
13
|
-
type GetVariables<T extends string> = T extends `${string}{${infer K}}${infer After}` ? K | GetVariables<After> : never;
|
|
14
|
-
interface TranslationsHook {
|
|
15
|
-
<Text extends string>(text: Text, opts?: {
|
|
16
|
-
/**
|
|
17
|
-
* add more context to `text`.
|
|
18
|
-
* @example "The aria-label of close dialog button"
|
|
19
|
-
*/
|
|
20
|
-
note?: string;
|
|
21
|
-
variables?: Record<GetVariables<Text>, string>;
|
|
22
|
-
}): string;
|
|
23
|
-
}
|
|
24
|
-
declare function useTranslations(hookOptions?: {
|
|
25
|
-
/** provide additional context to all t() calls */note?: string;
|
|
26
|
-
}): TranslationsHook;
|
|
27
|
-
//#endregion
|
|
28
|
-
export { TranslationProvider, TranslationsHook, useTranslations };
|
|
29
|
-
//# sourceMappingURL=react.d.mts.map
|
package/dist/react.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"mappings":";;;KAIK,YAAA,GAAe,MAAM;;iBAKV,mBAAA;EACd,YAAA;EACA;AAAA;EAEA,YAAA,EAAc,YAAA;EACd,QAAA,EAAU,SAAA;AAAA,oBACX,GAAA,CAAA,OAAA;AAAA,KASI,YAAA,qBAAiC,CAAA,iDAClC,CAAA,GAAI,YAAA,CAAa,KAAA;AAAA,UAGJ,gBAAA;EAAA,sBAEb,IAAA,EAAM,IAAA,EACN,IAAA;IAtB+B;;;;IA2B7B,IAAA;IACA,SAAA,GAAY,MAAA,CAAO,YAAA,CAAa,IAAA;EAAA;AAAA;AAAA,iBAKtB,eAAA,CAAgB,WAAA;EA/B9B,kDAiCA,IAAA;AAAA,IACE,gBAAgB"}
|
package/dist/react.mjs
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { t as encodeKey } from "./shared-CAg_QhcN.mjs";
|
|
3
|
-
import { createContext, use, useMemo } from "react";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
-
//#region src/react.tsx
|
|
6
|
-
const Context = createContext({});
|
|
7
|
-
/** add translations, you can stack multiple <TranslationProvider /> to override/extend translations */
|
|
8
|
-
function TranslationProvider({ translations, children }) {
|
|
9
|
-
const parent = use(Context);
|
|
10
|
-
return /* @__PURE__ */ jsx(Context, {
|
|
11
|
-
value: useMemo(() => ({
|
|
12
|
-
...parent,
|
|
13
|
-
...translations
|
|
14
|
-
}), [parent, translations]),
|
|
15
|
-
children
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
function useTranslations(hookOptions) {
|
|
19
|
-
const translations = use(Context);
|
|
20
|
-
return (rawText, opts = {}) => {
|
|
21
|
-
const { note, variables } = opts;
|
|
22
|
-
const notes = [];
|
|
23
|
-
if (hookOptions?.note) notes.push(hookOptions.note);
|
|
24
|
-
if (note) notes.push(note);
|
|
25
|
-
let text = translations[encodeKey(rawText, notes)] ?? rawText;
|
|
26
|
-
if (variables) for (const k in variables) text = text.replaceAll(`{${k}}`, variables[k]);
|
|
27
|
-
return text;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
export { TranslationProvider, useTranslations };
|
|
32
|
-
|
|
33
|
-
//# sourceMappingURL=react.mjs.map
|
package/dist/react.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["\"use client\";\nimport { createContext, use, useMemo, type ReactNode } from \"react\";\nimport { encodeKey } from \"./shared\";\n\ntype Translations = Record<string, string>;\n\nconst Context = createContext<Translations>({});\n\n/** add translations, you can stack multiple <TranslationProvider /> to override/extend translations */\nexport function TranslationProvider({\n translations,\n children,\n}: {\n translations: Translations;\n children: ReactNode;\n}) {\n const parent = use(Context);\n return (\n <Context value={useMemo(() => ({ ...parent, ...translations }), [parent, translations])}>\n {children}\n </Context>\n );\n}\n\ntype GetVariables<T extends string> = T extends `${string}{${infer K}}${infer After}`\n ? K | GetVariables<After>\n : never;\n\nexport interface TranslationsHook {\n <Text extends string>(\n text: Text,\n opts?: {\n /**\n * add more context to `text`.\n * @example \"The aria-label of close dialog button\"\n */\n note?: string;\n variables?: Record<GetVariables<Text>, string>;\n },\n ): string;\n}\n\nexport function useTranslations(hookOptions?: {\n /** provide additional context to all t() calls */\n note?: string;\n}): TranslationsHook {\n const translations = use(Context);\n\n return (rawText, opts = {}) => {\n const { note, variables } = opts;\n const notes: string[] = [];\n if (hookOptions?.note) notes.push(hookOptions.note);\n if (note) notes.push(note);\n const k = encodeKey(rawText, notes);\n let text = translations[k] ?? rawText;\n\n if (variables) {\n for (const k in variables) text = text.replaceAll(`{${k}}`, variables[k as never]);\n }\n\n return text;\n };\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,cAA4B,CAAC,CAAC;;AAG9C,SAAgB,oBAAoB,EAClC,cACA,YAIC;CACD,MAAM,SAAS,IAAI,OAAO;CAC1B,OACE,oBAAC,SAAD;EAAS,OAAO,eAAe;GAAE,GAAG;GAAQ,GAAG;EAAa,IAAI,CAAC,QAAQ,YAAY,CAAC;EACnF;CACM,CAAA;AAEb;AAoBA,SAAgB,gBAAgB,aAGX;CACnB,MAAM,eAAe,IAAI,OAAO;CAEhC,QAAQ,SAAS,OAAO,CAAC,MAAM;EAC7B,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,QAAkB,CAAC;EACzB,IAAI,aAAa,MAAM,MAAM,KAAK,YAAY,IAAI;EAClD,IAAI,MAAM,MAAM,KAAK,IAAI;EAEzB,IAAI,OAAO,aADD,UAAU,SAAS,KACL,MAAM;EAE9B,IAAI,WACF,KAAK,MAAM,KAAK,WAAW,OAAO,KAAK,WAAW,IAAI,EAAE,IAAI,UAAU,EAAW;EAGnF,OAAO;CACT;AACF"}
|
package/dist/shared-CAg_QhcN.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shared-CAg_QhcN.mjs","names":[],"sources":["../src/shared.ts"],"sourcesContent":["export function encodeKey(text: string, notes: string[]): string {\n return text + notes.map((n) => `(${n})`).join(\"\");\n}\n"],"mappings":";AAAA,SAAgB,UAAU,MAAc,OAAyB;CAC/D,OAAO,OAAO,MAAM,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE;AAClD"}
|