Complete statical parsing of withExtraLanguages

This commit is contained in:
Joseph Garrone 2024-09-22 22:46:56 +02:00
parent 10d4da9fbf
commit c07af8491c

View File

@ -1,6 +1,6 @@
import { type ThemeType, FALLBACK_LANGUAGE_TAG } from "../../shared/constants";
import { crawl } from "../../tools/crawl";
import { join as pathJoin } from "path";
import { join as pathJoin, dirname as pathDirname } from "path";
import { symToStr } from "tsafe/symToStr";
import * as recast from "recast";
import * as babelParser from "@babel/parser";
@ -11,6 +11,7 @@ import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPa
import * as fs from "fs";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../../shared/buildContext";
import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
export type BuildContextLike = {
themeNames: string[];
@ -112,20 +113,7 @@ export function generateMessageProperties(params: {
if (i18nTsFilePath === undefined) {
return undefined;
}
const root: recast.types.ASTNode = recast.parse(
fs.readFileSync(i18nTsFilePath).toString("utf8"),
{
parser: {
parse: (code: string) =>
babelParser.parse(code, {
sourceType: "module",
plugins: ["typescript"]
}),
generator: babelGenerate,
types: babelTypes
}
}
);
const root = recastParseTs(i18nTsFilePath);
return root;
})();
@ -136,7 +124,10 @@ export function generateMessageProperties(params: {
return undefined;
}
let out: Record<string, { label: string; path: string }> = {};
let extraLanguageEntryByLanguageTag: Record<
string,
{ label: string; path: string }
> = {};
recast.visit(i18nTsRoot, {
visitCallExpression: function (path) {
@ -225,7 +216,10 @@ export function generateMessageProperties(params: {
});
if (label && pathStr) {
out[lang] = { label, path: pathStr };
extraLanguageEntryByLanguageTag[lang] = {
label,
path: pathStr
};
}
}
}
@ -239,9 +233,69 @@ export function generateMessageProperties(params: {
}
});
console.log(out);
const messages_defaultSet_by_languageTag_notInDefaultSet = Object.fromEntries(
Object.entries(extraLanguageEntryByLanguageTag).map(
([languageTag, { path: relativePathWithoutExt }]) => [
languageTag,
(() => {
const filePath = getAbsoluteAndInOsFormatPath({
pathIsh: relativePathWithoutExt.endsWith(".ts")
? relativePathWithoutExt
: `${relativePathWithoutExt}.ts`,
cwd: pathDirname(i18nTsFilePath)
});
return {};
const root = recastParseTs(filePath);
let declarationCode: string | undefined = "";
recast.visit(root, {
visitVariableDeclarator: function (path) {
const node = path.node;
// Check if the variable name is 'messages'
if (
node.id.type === "Identifier" &&
node.id.name === "messages"
) {
// Ensure there is an initializer
if (node.init) {
// Generate code from the initializer, preserving comments
declarationCode = recast
.print(node.init)
.code.replace(/}.*$/, "}");
}
return false; // Stop traversing this path
}
this.traverse(path); // Continue traversing other paths
}
});
assert(
declarationCode !== undefined,
`${filePath} does not contain a 'messages' variable declaration`
);
let messages: Record<string, string> = {};
try {
eval(`${symToStr({ messages })} = ${declarationCode};`);
} catch {
throw new Error(
`The declaration of 'message' in ${filePath} cannot be statically evaluated: ${declarationCode}`
);
}
return messages;
})()
]
)
);
console.log(messages_defaultSet_by_languageTag_notInDefaultSet);
return messages_defaultSet_by_languageTag_notInDefaultSet;
})();
const messages_defaultSet_by_languageTag = {
@ -294,12 +348,8 @@ export function generateMessageProperties(params: {
} catch {
console.warn(
[
"WARNING: Make sure the messageBundle your provided as argument of createUseI18n can be statically evaluated.",
"This is important because we need to put your i18n messages in messages_*.properties files",
"or they won't be available server side.",
"\n",
"The following code could not be evaluated:",
"\n",
"WARNING: The argument of withCustomTranslations can't be statically evaluated!",
"This needs to be fixed refer to the documentation: https://docs.keycloakify.dev/i18n",
firstArgumentCode
].join(" ")
);
@ -376,3 +426,17 @@ export function generateMessageProperties(params: {
}
};
}
function recastParseTs(filePath: string): recast.types.ASTNode {
return recast.parse(fs.readFileSync(filePath).toString("utf8"), {
parser: {
parse: (code: string) =>
babelParser.parse(code, {
sourceType: "module",
plugins: ["typescript"]
}),
generator: babelGenerate,
types: babelTypes
}
});
}