Almost done, left to extract the extra language resources
This commit is contained in:
parent
bb163132fe
commit
b0b6b994ed
@ -10,12 +10,26 @@ import { escapeStringForPropertiesFile } from "../../tools/escapeStringForProper
|
|||||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { BuildContext } from "../../shared/buildContext";
|
||||||
|
|
||||||
|
export type BuildContextLike = {
|
||||||
|
themeNames: string[];
|
||||||
|
themeSrcDirPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export function generateMessageProperties(params: {
|
export function generateMessageProperties(params: {
|
||||||
themeSrcDirPath: string;
|
buildContext: BuildContextLike;
|
||||||
themeType: ThemeType;
|
themeType: ThemeType;
|
||||||
}): { languageTag: string; propertiesFileSource: string }[] {
|
}): {
|
||||||
const { themeSrcDirPath, themeType } = params;
|
languageTags: string[];
|
||||||
|
writeMessagePropertiesFiles: (params: {
|
||||||
|
messageDirPath: string;
|
||||||
|
themeName: string;
|
||||||
|
}) => void;
|
||||||
|
} {
|
||||||
|
const { buildContext, themeType } = params;
|
||||||
|
|
||||||
const baseMessagesDirPath = pathJoin(
|
const baseMessagesDirPath = pathJoin(
|
||||||
getThisCodebaseRootDirPath(),
|
getThisCodebaseRootDirPath(),
|
||||||
@ -25,51 +39,49 @@ export function generateMessageProperties(params: {
|
|||||||
"messages_defaultSet"
|
"messages_defaultSet"
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseMessageBundle: { [languageTag: string]: Record<string, string> } =
|
const messages_defaultSet_by_languageTag_defaultSet: {
|
||||||
Object.fromEntries(
|
[languageTag_defaultSet: string]: Record<string, string>;
|
||||||
fs
|
} = Object.fromEntries(
|
||||||
.readdirSync(baseMessagesDirPath)
|
fs
|
||||||
.filter(basename => basename !== "index.ts" && basename !== "types.ts")
|
.readdirSync(baseMessagesDirPath)
|
||||||
.map(basename => ({
|
.filter(basename => basename !== "index.ts" && basename !== "types.ts")
|
||||||
languageTag: basename.replace(/\.ts$/, ""),
|
.map(basename => ({
|
||||||
filePath: pathJoin(baseMessagesDirPath, basename)
|
languageTag: basename.replace(/\.ts$/, ""),
|
||||||
}))
|
filePath: pathJoin(baseMessagesDirPath, basename)
|
||||||
.map(({ languageTag, filePath }) => {
|
}))
|
||||||
const lines = fs
|
.map(({ languageTag, filePath }) => {
|
||||||
.readFileSync(filePath)
|
const lines = fs.readFileSync(filePath).toString("utf8").split(/\r?\n/);
|
||||||
.toString("utf8")
|
|
||||||
.split(/\r?\n/);
|
|
||||||
|
|
||||||
let messagesJson = "{";
|
let messagesJson = "{";
|
||||||
|
|
||||||
let isInDeclaration = false;
|
let isInDeclaration = false;
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!isInDeclaration) {
|
if (!isInDeclaration) {
|
||||||
if (line.startsWith("const messages")) {
|
if (line.startsWith("const messages")) {
|
||||||
isInDeclaration = true;
|
isInDeclaration = true;
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.startsWith("}")) {
|
continue;
|
||||||
messagesJson += "}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesJson += line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = JSON.parse(messagesJson) as Record<string, string>;
|
if (line.startsWith("}")) {
|
||||||
|
messagesJson += "}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return [languageTag, messages];
|
messagesJson += line;
|
||||||
})
|
}
|
||||||
);
|
|
||||||
|
const messages = JSON.parse(messagesJson) as Record<string, string>;
|
||||||
|
|
||||||
|
return [languageTag, messages];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const { i18nTsFilePath } = (() => {
|
const { i18nTsFilePath } = (() => {
|
||||||
let files = crawl({
|
let files = crawl({
|
||||||
dirPath: pathJoin(themeSrcDirPath, themeType),
|
dirPath: pathJoin(buildContext.themeSrcDirPath, themeType),
|
||||||
returnedPathsType: "absolute"
|
returnedPathsType: "absolute"
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,7 +100,7 @@ export function generateMessageProperties(params: {
|
|||||||
files = files.sort((a, b) => a.length - b.length);
|
files = files.sort((a, b) => a.length - b.length);
|
||||||
|
|
||||||
files = files.filter(file =>
|
files = files.filter(file =>
|
||||||
fs.readFileSync(file).toString("utf8").includes("createUseI18n(")
|
fs.readFileSync(file).toString("utf8").includes("i18nBuilder")
|
||||||
);
|
);
|
||||||
|
|
||||||
const i18nTsFilePath: string | undefined = files[0];
|
const i18nTsFilePath: string | undefined = files[0];
|
||||||
@ -96,13 +108,13 @@ export function generateMessageProperties(params: {
|
|||||||
return { i18nTsFilePath };
|
return { i18nTsFilePath };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const messageBundle: { [languageTag: string]: Record<string, string> } | undefined =
|
const i18nTsRoot = (() => {
|
||||||
(() => {
|
if (i18nTsFilePath === undefined) {
|
||||||
if (i18nTsFilePath === undefined) {
|
return undefined;
|
||||||
return undefined;
|
}
|
||||||
}
|
const root: recast.types.ASTNode = recast.parse(
|
||||||
|
fs.readFileSync(i18nTsFilePath).toString("utf8"),
|
||||||
const root = recast.parse(fs.readFileSync(i18nTsFilePath).toString("utf8"), {
|
{
|
||||||
parser: {
|
parser: {
|
||||||
parse: (code: string) =>
|
parse: (code: string) =>
|
||||||
babelParser.parse(code, {
|
babelParser.parse(code, {
|
||||||
@ -112,81 +124,176 @@ export function generateMessageProperties(params: {
|
|||||||
generator: babelGenerate,
|
generator: babelGenerate,
|
||||||
types: babelTypes
|
types: babelTypes
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
return root;
|
||||||
|
})();
|
||||||
|
|
||||||
let messageBundleDeclarationTsCode: string | undefined = undefined;
|
const messages_defaultSet_by_languageTag_notInDefaultSet:
|
||||||
|
| { [languageTag_notInDefaultSet: string]: Record<string, string> }
|
||||||
|
| undefined = (() => {
|
||||||
|
if (i18nTsRoot === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
recast.visit(root, {
|
let firstArgumentCode: string | undefined = undefined;
|
||||||
visitCallExpression: function (path) {
|
|
||||||
if (
|
recast.visit(i18nTsRoot, {
|
||||||
path.node.callee.type === "Identifier" &&
|
visitCallExpression: function (path) {
|
||||||
path.node.callee.name === "createUseI18n"
|
const node = path.node;
|
||||||
) {
|
|
||||||
messageBundleDeclarationTsCode = babelGenerate(
|
if (
|
||||||
path.node.arguments[0] as any
|
node.callee.type === "MemberExpression" &&
|
||||||
).code;
|
node.callee.property.type === "Identifier" &&
|
||||||
return false;
|
node.callee.property.name === "withExtraLanguages"
|
||||||
|
) {
|
||||||
|
firstArgumentCode = babelGenerate(node.arguments[0] as any).code;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.traverse(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (firstArgumentCode === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
return {};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const messages_defaultSet_by_languageTag = {
|
||||||
|
...messages_defaultSet_by_languageTag_defaultSet,
|
||||||
|
...messages_defaultSet_by_languageTag_notInDefaultSet
|
||||||
|
};
|
||||||
|
|
||||||
|
const messages_themeDefined_by_languageTag:
|
||||||
|
| {
|
||||||
|
[languageTag: string]:
|
||||||
|
| Record<string, string | Record<string, string>>
|
||||||
|
| undefined;
|
||||||
|
}
|
||||||
|
| undefined = (() => {
|
||||||
|
if (i18nTsRoot === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let firstArgumentCode: string | undefined = undefined;
|
||||||
|
|
||||||
|
recast.visit(i18nTsRoot, {
|
||||||
|
visitCallExpression: function (path) {
|
||||||
|
const node = path.node;
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.callee.type === "MemberExpression" &&
|
||||||
|
node.callee.property.type === "Identifier" &&
|
||||||
|
node.callee.property.name === "withCustomTranslations"
|
||||||
|
) {
|
||||||
|
firstArgumentCode = babelGenerate(node.arguments[0] as any).code;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.traverse(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (firstArgumentCode === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages_themeDefined_by_languageTag: {
|
||||||
|
[languageTag: string]: Record<string, string | Record<string, string>>;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
eval(
|
||||||
|
`${symToStr({ messages_themeDefined_by_languageTag })} = ${firstArgumentCode}`
|
||||||
|
);
|
||||||
|
} 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",
|
||||||
|
firstArgumentCode
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages_themeDefined_by_languageTag;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const languageTags = Object.keys(messages_defaultSet_by_languageTag);
|
||||||
|
|
||||||
|
return {
|
||||||
|
languageTags,
|
||||||
|
writeMessagePropertiesFiles: ({ messageDirPath, themeName }) => {
|
||||||
|
for (const languageTag of languageTags) {
|
||||||
|
const messages = {
|
||||||
|
...messages_defaultSet_by_languageTag[languageTag]
|
||||||
|
};
|
||||||
|
|
||||||
|
add_theme_defined_messages: {
|
||||||
|
if (messages_themeDefined_by_languageTag === undefined) {
|
||||||
|
break add_theme_defined_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.traverse(path);
|
let messages_themeDefined =
|
||||||
|
messages_themeDefined_by_languageTag[languageTag];
|
||||||
|
|
||||||
|
if (messages_themeDefined === undefined) {
|
||||||
|
messages_themeDefined =
|
||||||
|
messages_themeDefined_by_languageTag[FALLBACK_LANGUAGE_TAG];
|
||||||
|
}
|
||||||
|
if (messages_themeDefined === undefined) {
|
||||||
|
messages_themeDefined =
|
||||||
|
messages_themeDefined_by_languageTag[
|
||||||
|
Object.keys(messages_themeDefined_by_languageTag)[0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (messages_themeDefined === undefined) {
|
||||||
|
break add_theme_defined_messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, messageOrMessageByThemeName] of Object.entries(
|
||||||
|
messages_themeDefined
|
||||||
|
)) {
|
||||||
|
const message = (() => {
|
||||||
|
if (typeof messageOrMessageByThemeName === "string") {
|
||||||
|
return messageOrMessageByThemeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = messageOrMessageByThemeName[themeName];
|
||||||
|
|
||||||
|
assert(message !== undefined);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
})();
|
||||||
|
|
||||||
|
messages[key] = message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
assert(messageBundleDeclarationTsCode !== undefined);
|
const propertiesFileSource = [
|
||||||
|
"",
|
||||||
|
...Object.entries(messages).map(
|
||||||
|
([key, value]) => `${key}=${escapeStringForPropertiesFile(value)}`
|
||||||
|
),
|
||||||
|
""
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
let messageBundle: {
|
fs.writeFileSync(
|
||||||
[languageTag: string]: Record<string, string>;
|
pathJoin(messageDirPath, `messages_${languageTag}.properties`),
|
||||||
} = {};
|
Buffer.from(propertiesFileSource, "utf8")
|
||||||
|
|
||||||
try {
|
|
||||||
eval(
|
|
||||||
`${symToStr({ messageBundle })} = ${messageBundleDeclarationTsCode}`
|
|
||||||
);
|
|
||||||
} 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",
|
|
||||||
messageBundleDeclarationTsCode
|
|
||||||
].join(" ")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return messageBundle;
|
};
|
||||||
})();
|
|
||||||
|
|
||||||
const mergedMessageBundle: { [languageTag: string]: Record<string, string> } =
|
|
||||||
Object.fromEntries(
|
|
||||||
Object.entries(baseMessageBundle).map(([languageTag, messages]) => [
|
|
||||||
languageTag,
|
|
||||||
{
|
|
||||||
...messages,
|
|
||||||
...(messageBundle === undefined
|
|
||||||
? {}
|
|
||||||
: messageBundle[languageTag] ??
|
|
||||||
messageBundle[FALLBACK_LANGUAGE_TAG] ??
|
|
||||||
messageBundle[Object.keys(messageBundle)[0]] ??
|
|
||||||
{})
|
|
||||||
}
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageProperties: { languageTag: string; propertiesFileSource: string }[] =
|
|
||||||
Object.entries(mergedMessageBundle).map(([languageTag, messages]) => ({
|
|
||||||
languageTag,
|
|
||||||
propertiesFileSource: [
|
|
||||||
"",
|
|
||||||
...(themeType !== "account" ? ["parent=base"] : []),
|
|
||||||
...Object.entries(messages).map(
|
|
||||||
([key, value]) => `${key}=${escapeStringForPropertiesFile(value)}`
|
|
||||||
),
|
|
||||||
""
|
|
||||||
].join("\n")
|
|
||||||
}));
|
|
||||||
|
|
||||||
return messageProperties;
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ export async function generateResources(params: {
|
|||||||
rmSync(resourcesDirPath, { recursive: true });
|
rmSync(resourcesDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
await generateResourcesForMainTheme({
|
const { writeMessagePropertiesFiles } = await generateResourcesForMainTheme({
|
||||||
resourcesDirPath,
|
resourcesDirPath,
|
||||||
themeName,
|
themeName,
|
||||||
buildContext
|
buildContext
|
||||||
@ -36,7 +36,8 @@ export async function generateResources(params: {
|
|||||||
generateResourcesForThemeVariant({
|
generateResourcesForThemeVariant({
|
||||||
resourcesDirPath,
|
resourcesDirPath,
|
||||||
themeName,
|
themeName,
|
||||||
themeVariantName
|
themeVariantName,
|
||||||
|
writeMessagePropertiesFiles
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@ import type { BuildContext } from "../../shared/buildContext";
|
|||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import { readFieldNameUsage } from "./readFieldNameUsage";
|
import { readFieldNameUsage } from "./readFieldNameUsage";
|
||||||
import { readExtraPagesNames } from "./readExtraPageNames";
|
import { readExtraPagesNames } from "./readExtraPageNames";
|
||||||
import { generateMessageProperties } from "./generateMessageProperties";
|
import {
|
||||||
|
generateMessageProperties,
|
||||||
|
type BuildContextLike as BuildContextLike_generateMessageProperties
|
||||||
|
} from "./generateMessageProperties";
|
||||||
import { rmSync } from "../../tools/fs.rmSync";
|
import { rmSync } from "../../tools/fs.rmSync";
|
||||||
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
|
||||||
import {
|
import {
|
||||||
@ -29,24 +32,30 @@ import { escapeStringForPropertiesFile } from "../../tools/escapeStringForProper
|
|||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||||
|
|
||||||
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode & {
|
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
||||||
extraThemeProperties: string[] | undefined;
|
BuildContextLike_generateMessageProperties & {
|
||||||
projectDirPath: string;
|
extraThemeProperties: string[] | undefined;
|
||||||
projectBuildDirPath: string;
|
projectDirPath: string;
|
||||||
environmentVariables: { name: string; default: string }[];
|
projectBuildDirPath: string;
|
||||||
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
environmentVariables: { name: string; default: string }[];
|
||||||
themeSrcDirPath: string;
|
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||||
bundler: "vite" | "webpack";
|
themeSrcDirPath: string;
|
||||||
packageJsonFilePath: string;
|
bundler: "vite" | "webpack";
|
||||||
};
|
packageJsonFilePath: string;
|
||||||
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export async function generateResourcesForMainTheme(params: {
|
export async function generateResourcesForMainTheme(params: {
|
||||||
|
buildContext: BuildContextLike;
|
||||||
themeName: string;
|
themeName: string;
|
||||||
resourcesDirPath: string;
|
resourcesDirPath: string;
|
||||||
buildContext: BuildContextLike;
|
}): Promise<{
|
||||||
}): Promise<void> {
|
writeMessagePropertiesFiles: (params: {
|
||||||
|
getMessageDirPath: (params: { themeType: ThemeType }) => string;
|
||||||
|
themeName: string;
|
||||||
|
}) => void;
|
||||||
|
}> {
|
||||||
const { themeName, resourcesDirPath, buildContext } = params;
|
const { themeName, resourcesDirPath, buildContext } = params;
|
||||||
|
|
||||||
const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
|
const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
|
||||||
@ -54,6 +63,10 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
return pathJoin(resourcesDirPath, "theme", themeName, themeType);
|
return pathJoin(resourcesDirPath, "theme", themeName, themeType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const writeMessagePropertiesFilesByThemeType: Partial<
|
||||||
|
Record<ThemeType, (params: { messageDirPath: string; themeName: string }) => void>
|
||||||
|
> = {};
|
||||||
|
|
||||||
for (const themeType of ["login", "account"] as const) {
|
for (const themeType of ["login", "account"] as const) {
|
||||||
if (!buildContext.implementedThemeTypes[themeType].isImplemented) {
|
if (!buildContext.implementedThemeTypes[themeType].isImplemented) {
|
||||||
continue;
|
continue;
|
||||||
@ -187,30 +200,27 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let languageTags: string[] | undefined = undefined;
|
||||||
|
|
||||||
i18n_messages_generation: {
|
i18n_messages_generation: {
|
||||||
if (isForAccountSpa) {
|
if (isForAccountSpa) {
|
||||||
break i18n_messages_generation;
|
break i18n_messages_generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateMessageProperties({
|
const wrap = generateMessageProperties({
|
||||||
themeSrcDirPath: buildContext.themeSrcDirPath,
|
buildContext,
|
||||||
themeType
|
themeType
|
||||||
}).forEach(({ languageTag, propertiesFileSource }) => {
|
});
|
||||||
const messagesDirPath = pathJoin(themeTypeDirPath, "messages");
|
|
||||||
|
|
||||||
fs.mkdirSync(pathJoin(themeTypeDirPath, "messages"), {
|
languageTags = wrap.languageTags;
|
||||||
recursive: true
|
const { writeMessagePropertiesFiles } = wrap;
|
||||||
});
|
|
||||||
|
|
||||||
const propertiesFilePath = pathJoin(
|
writeMessagePropertiesFilesByThemeType[themeType] =
|
||||||
messagesDirPath,
|
writeMessagePropertiesFiles;
|
||||||
`messages_${languageTag}.properties`
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
writeMessagePropertiesFiles({
|
||||||
propertiesFilePath,
|
messageDirPath: pathJoin(themeTypeDirPath, "messages"),
|
||||||
Buffer.from(propertiesFileSource, "utf8")
|
themeName
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +290,10 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
...(buildContext.extraThemeProperties ?? []),
|
...(buildContext.extraThemeProperties ?? []),
|
||||||
...buildContext.environmentVariables.map(
|
...buildContext.environmentVariables.map(
|
||||||
({ name, default: defaultValue }) =>
|
({ name, default: defaultValue }) =>
|
||||||
`${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`
|
`${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`,
|
||||||
|
...(languageTags === undefined
|
||||||
|
? []
|
||||||
|
: `locales=${languageTags.join(",")}`)
|
||||||
)
|
)
|
||||||
].join("\n\n"),
|
].join("\n\n"),
|
||||||
"utf8"
|
"utf8"
|
||||||
@ -338,4 +351,20 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
getNewMetaInfKeycloakTheme: () => metaInfKeycloakThemes
|
getNewMetaInfKeycloakTheme: () => metaInfKeycloakThemes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
writeMessagePropertiesFiles: ({ getMessageDirPath, themeName }) => {
|
||||||
|
objectEntries(writeMessagePropertiesFilesByThemeType).forEach(
|
||||||
|
([themeType, writeMessagePropertiesFiles]) => {
|
||||||
|
if (writeMessagePropertiesFiles === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeMessagePropertiesFiles({
|
||||||
|
messageDirPath: getMessageDirPath({ themeType }),
|
||||||
|
themeName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
|
import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
|
||||||
import { transformCodebase } from "../../tools/transformCodebase";
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
import type { BuildContext } from "../../shared/buildContext";
|
|
||||||
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { ThemeType } from "../../shared/constants";
|
||||||
export type BuildContextLike = {
|
|
||||||
keycloakifyBuildDirPath: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
|
||||||
|
|
||||||
export function generateResourcesForThemeVariant(params: {
|
export function generateResourcesForThemeVariant(params: {
|
||||||
resourcesDirPath: string;
|
resourcesDirPath: string;
|
||||||
themeName: string;
|
themeName: string;
|
||||||
themeVariantName: string;
|
themeVariantName: string;
|
||||||
|
writeMessagePropertiesFiles: (params: {
|
||||||
|
getMessageDirPath: (params: { themeType: ThemeType }) => string;
|
||||||
|
themeName: string;
|
||||||
|
}) => void;
|
||||||
}) {
|
}) {
|
||||||
const { resourcesDirPath, themeName, themeVariantName } = params;
|
const { resourcesDirPath, themeName, themeVariantName, writeMessagePropertiesFiles } =
|
||||||
|
params;
|
||||||
|
|
||||||
const mainThemeDirPath = pathJoin(resourcesDirPath, "theme", themeName);
|
const mainThemeDirPath = pathJoin(resourcesDirPath, "theme", themeName);
|
||||||
|
const themeVariantDirPath = pathJoin(mainThemeDirPath, "..", themeVariantName);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
srcDirPath: mainThemeDirPath,
|
srcDirPath: mainThemeDirPath,
|
||||||
destDirPath: pathJoin(mainThemeDirPath, "..", themeVariantName),
|
destDirPath: themeVariantDirPath,
|
||||||
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||||
if (
|
if (
|
||||||
pathExtname(fileRelativePath) === ".ftl" &&
|
pathExtname(fileRelativePath) === ".ftl" &&
|
||||||
@ -67,4 +67,10 @@ export function generateResourcesForThemeVariant(params: {
|
|||||||
return newMetaInfKeycloakTheme;
|
return newMetaInfKeycloakTheme;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
writeMessagePropertiesFiles({
|
||||||
|
getMessageDirPath: ({ themeType }) =>
|
||||||
|
pathJoin(themeVariantDirPath, themeType, "messages"),
|
||||||
|
themeName: themeVariantName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ export function createGetI18n<
|
|||||||
|
|
||||||
let label = labelBySupportedLanguageTag[languageTag];
|
let label = labelBySupportedLanguageTag[languageTag];
|
||||||
|
|
||||||
if (label === undefined) {
|
if (label === undefined || label === "" || label === languageTag) {
|
||||||
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(languageTag));
|
assert(is<Exclude<LanguageTag, LanguageTag_defaultSet>>(languageTag));
|
||||||
|
|
||||||
const entry = extraLanguageTranslations[languageTag];
|
const entry = extraLanguageTranslations[languageTag];
|
||||||
|
94
stories/login/gpt.md
Normal file
94
stories/login/gpt.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
Hello GPT,
|
||||||
|
|
||||||
|
So, I'm using recast in a node script to parse a typescript source file and extract the part that I'm intrested in.
|
||||||
|
|
||||||
|
Example of the source file:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createUseI18n } from "keycloakify/login";
|
||||||
|
|
||||||
|
export const { useI18n, ofTypeI18n } = createUseI18n({
|
||||||
|
en: {
|
||||||
|
myCustomMessage: "My custom message"
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
myCustomMessage: "Mon message personnalisé"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export type I18n = typeof ofTypeI18n;
|
||||||
|
```
|
||||||
|
|
||||||
|
The string that I want to extract from this source file is:
|
||||||
|
|
||||||
|
```raw
|
||||||
|
{
|
||||||
|
en: {
|
||||||
|
myCustomMessage: "My custom message"
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
myCustomMessage: "Mon message personnalisé"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is my script:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const root = recast.parse(fs.readFileSync(i18nTsFilePath).toString("utf8"), {
|
||||||
|
parser: {
|
||||||
|
parse: (code: string) =>
|
||||||
|
babelParser.parse(code, {
|
||||||
|
sourceType: "module",
|
||||||
|
plugins: ["typescript"]
|
||||||
|
}),
|
||||||
|
generator: babelGenerate,
|
||||||
|
types: babelTypes
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let messageBundleDeclarationTsCode: string | undefined = undefined;
|
||||||
|
|
||||||
|
recast.visit(root, {
|
||||||
|
visitCallExpression: function (path) {
|
||||||
|
if (
|
||||||
|
path.node.callee.type === "Identifier" &&
|
||||||
|
path.node.callee.name === "createUseI18n"
|
||||||
|
) {
|
||||||
|
messageBundleDeclarationTsCode = babelGenerate(
|
||||||
|
path.node.arguments[0] as any
|
||||||
|
).code;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.traverse(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Here messageBundleDeclarationTsCode contains the string I want
|
||||||
|
```
|
||||||
|
|
||||||
|
It works, but now, the API has changed. The source file looks like this:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { i18nBuilder } from "keycloakify/login/i18n";
|
||||||
|
|
||||||
|
const { useI18n, ofTypeI18n } = i18nBuilder
|
||||||
|
.withThemeName<"my-theme-1" | "my-theme-2">()
|
||||||
|
.withCustomTranslations({
|
||||||
|
en: {
|
||||||
|
myCustomMessage: "My custom message"
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
myCustomMessage: "Mon message personnalisé"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type I18n = typeof ofTypeI18n;
|
||||||
|
|
||||||
|
export { useI18n, type I18n };
|
||||||
|
```
|
||||||
|
|
||||||
|
Can you modify the script to extract the string taking into account the change that have been made to the source file?
|
||||||
|
(I need to extract the argument that is passed to the `withCustomTranslations` method)
|
Loading…
x
Reference in New Issue
Block a user