Support building account v3

This commit is contained in:
Joseph Garrone 2024-07-07 18:45:14 +02:00
parent e2f5eb79ad
commit c4638daf1b
10 changed files with 133 additions and 72 deletions

View File

@ -33,6 +33,7 @@ export async function buildJar(params: {
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
resourcesDirPath: string;
doesImplementAccountV1Theme: boolean;
buildContext: BuildContextLike;
}): Promise<void> {
const {
@ -40,6 +41,7 @@ export async function buildJar(params: {
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
doesImplementAccountV1Theme,
buildContext
} = params;
@ -61,7 +63,7 @@ export async function buildJar(params: {
srcDirPath: resourcesDirPath,
destDirPath: tmpResourcesDirPath,
transformSourceCode:
keycloakAccountV1Version !== null
!doesImplementAccountV1Theme || keycloakAccountV1Version !== null
? undefined
: (params: {
fileRelativePath: string;
@ -105,7 +107,17 @@ export async function buildJar(params: {
}
});
if (keycloakAccountV1Version === null) {
remove_account_v1_in_meta_inf: {
if (!doesImplementAccountV1Theme) {
// NOTE: We do not have account v1 anyway
break remove_account_v1_in_meta_inf;
}
if (keycloakAccountV1Version !== null) {
// NOTE: No, we need to keep account-v1 in meta-inf
break remove_account_v1_in_meta_inf;
}
writeMetaInfKeycloakThemes({
resourcesDirPath: tmpResourcesDirPath,
getNewMetaInfKeycloakTheme: ({ metaInfKeycloakTheme }) => {
@ -135,6 +147,7 @@ export async function buildJar(params: {
}
})();
// TODO: Remove this optimization, it's a bit hacky.
if (doBreak) {
break route_legacy_pages;
}

View File

@ -12,6 +12,7 @@ export type BuildContextLike = BuildContextLike_buildJar & {
keycloakifyBuildDirPath: string;
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
jarTargets: BuildContext["jarTargets"];
doUseAccountV3: boolean;
};
assert<BuildContext extends BuildContextLike ? true : false>();
@ -22,7 +23,9 @@ export async function buildJars(params: {
}): Promise<void> {
const { resourcesDirPath, buildContext } = params;
const doesImplementAccountTheme = buildContext.recordIsImplementedByThemeType.account;
const doesImplementAccountV1Theme =
buildContext.recordIsImplementedByThemeType.account &&
!buildContext.doUseAccountV3;
await Promise.all(
keycloakAccountV1Versions
@ -30,7 +33,7 @@ export async function buildJars(params: {
keycloakThemeAdditionalInfoExtensionVersions.map(
keycloakThemeAdditionalInfoExtensionVersion => {
const keycloakVersionRange = getKeycloakVersionRangeForJar({
doesImplementAccountTheme,
doesImplementAccountV1Theme,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion
});
@ -55,6 +58,7 @@ export async function buildJars(params: {
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
doesImplementAccountV1Theme,
buildContext
});
}

View File

@ -6,17 +6,17 @@ import type {
import type { KeycloakVersionRange } from "../../shared/KeycloakVersionRange";
export function getKeycloakVersionRangeForJar(params: {
doesImplementAccountTheme: boolean;
doesImplementAccountV1Theme: boolean;
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
}): KeycloakVersionRange | undefined {
const {
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
doesImplementAccountTheme
doesImplementAccountV1Theme
} = params;
if (doesImplementAccountTheme) {
if (doesImplementAccountV1Theme) {
const keycloakVersionRange = (() => {
switch (keycloakAccountV1Version) {
case null:
@ -63,7 +63,7 @@ export function getKeycloakVersionRangeForJar(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithAccountTheme | undefined
KeycloakVersionRange.WithAccountV1Theme | undefined
>
>();
@ -87,7 +87,7 @@ export function getKeycloakVersionRangeForJar(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithoutAccountTheme | undefined
KeycloakVersionRange.WithoutAccountV1Theme | undefined
>
>();

View File

@ -34,6 +34,7 @@ export function generateFtlFilesCodeFactory(params: {
keycloakifyVersion: string;
themeType: ThemeType;
fieldNames: string[];
isAccountV3: boolean;
}) {
const {
themeName,
@ -41,7 +42,8 @@ export function generateFtlFilesCodeFactory(params: {
buildContext,
keycloakifyVersion,
themeType,
fieldNames
fieldNames,
isAccountV3
} = params;
const $ = cheerio.load(indexHtmlCode);
@ -68,7 +70,8 @@ export function generateFtlFilesCodeFactory(params: {
const { fixedCssCode } = replaceImportsInCssCode({
cssCode,
cssFileRelativeDirPath: undefined,
buildContext
buildContext,
isAccountV3
});
$(element).text(fixedCssCode);
@ -93,7 +96,7 @@ export function generateFtlFilesCodeFactory(params: {
new RegExp(
`^${(buildContext.urlPathname ?? "/").replace(/\//g, "\\/")}`
),
`\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
`\${${!isAccountV3 ? "url.resourcesPath" : "resourceUrl"}}/${basenameOfTheKeycloakifyResourcesDir}/`
)
);
})

View File

@ -1,4 +1,5 @@
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
<#assign themeType="KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr">
const kcContext = ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
if( kcContext.messagesPerField ){
var existsError_singleFieldName = kcContext.messagesPerField.existsError;
@ -27,7 +28,7 @@ if( kcContext.messagesPerField ){
}
kcContext.keycloakifyVersion = "KEYCLOAKIFY_VERSION_xEdKd3xEdr";
kcContext.themeVersion = "KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx";
kcContext.themeType = "KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr";
kcContext.themeType = "${themeType}";
kcContext.themeName = "KEYCLOAKIFY_THEME_NAME_cXxKd3xEer";
kcContext.pageId = "${pageId}";
if( kcContext.url && kcContext.url.resourcesPath ){

View File

@ -54,6 +54,7 @@ export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
themeSrcDirPath: string;
bundler: { type: "vite" } | { type: "webpack" };
doUseAccountV3: boolean;
};
assert<BuildContext extends BuildContextLike ? true : false>();
@ -71,6 +72,7 @@ export async function generateResourcesForMainTheme(params: {
};
for (const themeType of ["login", "account"] as const) {
const isAccountV3 = themeType === "account" && buildContext.doUseAccountV3;
if (!buildContext.recordIsImplementedByThemeType[themeType]) {
continue;
}
@ -136,7 +138,8 @@ export async function generateResourcesForMainTheme(params: {
const { fixedCssCode } = replaceImportsInCssCode({
cssCode: sourceCode.toString("utf8"),
cssFileRelativeDirPath: pathDirname(fileRelativePath),
buildContext
buildContext,
isAccountV3
});
return {
@ -171,7 +174,8 @@ export async function generateResourcesForMainTheme(params: {
fieldNames: readFieldNameUsage({
themeSrcDirPath: buildContext.themeSrcDirPath,
themeType
})
}),
isAccountV3
});
[
@ -180,13 +184,15 @@ export async function generateResourcesForMainTheme(params: {
case "login":
return loginThemePageIds;
case "account":
return accountThemePageIds;
return isAccountV3 ? ["index.ftl"] : accountThemePageIds;
}
})(),
...readExtraPagesNames({
themeType,
themeSrcDirPath: buildContext.themeSrcDirPath
})
...(isAccountV3
? []
: readExtraPagesNames({
themeType,
themeSrcDirPath: buildContext.themeSrcDirPath
}))
].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
@ -196,40 +202,52 @@ export async function generateResourcesForMainTheme(params: {
);
});
generateMessageProperties({
themeSrcDirPath: buildContext.themeSrcDirPath,
themeType
}).forEach(({ languageTag, propertiesFileSource }) => {
const messagesDirPath = pathJoin(themeTypeDirPath, "messages");
i18n_messages_generation: {
if (isAccountV3) {
break i18n_messages_generation;
}
fs.mkdirSync(pathJoin(themeTypeDirPath, "messages"), {
recursive: true
generateMessageProperties({
themeSrcDirPath: buildContext.themeSrcDirPath,
themeType
}).forEach(({ languageTag, propertiesFileSource }) => {
const messagesDirPath = pathJoin(themeTypeDirPath, "messages");
fs.mkdirSync(pathJoin(themeTypeDirPath, "messages"), {
recursive: true
});
const propertiesFilePath = pathJoin(
messagesDirPath,
`messages_${languageTag}.properties`
);
fs.writeFileSync(
propertiesFilePath,
Buffer.from(propertiesFileSource, "utf8")
);
});
}
const propertiesFilePath = pathJoin(
messagesDirPath,
`messages_${languageTag}.properties`
);
keycloak_static_resources: {
if (isAccountV3) {
break keycloak_static_resources;
}
fs.writeFileSync(
propertiesFilePath,
Buffer.from(propertiesFileSource, "utf8")
);
});
await downloadKeycloakStaticResources({
keycloakVersion: (() => {
switch (themeType) {
case "account":
return lastKeycloakVersionWithAccountV1;
case "login":
return buildContext.loginThemeResourcesFromKeycloakVersion;
}
})(),
themeDirPath: pathResolve(pathJoin(themeTypeDirPath, "..")),
themeType,
buildContext
});
await downloadKeycloakStaticResources({
keycloakVersion: (() => {
switch (themeType) {
case "account":
return lastKeycloakVersionWithAccountV1;
case "login":
return buildContext.loginThemeResourcesFromKeycloakVersion;
}
})(),
themeDirPath: pathResolve(pathJoin(themeTypeDirPath, "..")),
themeType,
buildContext
});
}
fs.writeFileSync(
pathJoin(themeTypeDirPath, "theme.properties"),
@ -238,12 +256,13 @@ export async function generateResourcesForMainTheme(params: {
`parent=${(() => {
switch (themeType) {
case "account":
return accountV1ThemeName;
return isAccountV3 ? "base" : accountV1ThemeName;
case "login":
return "keycloak";
}
assert<Equals<typeof themeType, never>>(false);
})()}`,
...(isAccountV3 ? ["deprecatedMode=false"] : []),
...(buildContext.extraThemeProperties ?? []),
...buildContext.environmentVariables.map(
({ name, default: defaultValue }) =>
@ -268,7 +287,15 @@ export async function generateResourcesForMainTheme(params: {
});
}
if (buildContext.recordIsImplementedByThemeType.account) {
bring_in_account_v1: {
if (buildContext.doUseAccountV3) {
break bring_in_account_v1;
}
if (!buildContext.recordIsImplementedByThemeType.account) {
break bring_in_account_v1;
}
await bringInAccountV1({
resourcesDirPath,
buildContext

View File

@ -12,11 +12,12 @@ assert<BuildContext extends BuildContextLike ? true : false>();
export function replaceImportsInCssCode(params: {
cssCode: string;
cssFileRelativeDirPath: string | undefined;
isAccountV3: boolean;
buildContext: BuildContextLike;
}): {
fixedCssCode: string;
} {
const { cssCode, cssFileRelativeDirPath, buildContext } = params;
const { cssCode, cssFileRelativeDirPath, buildContext, isAccountV3 } = params;
const fixedCssCode = cssCode.replace(
/url\(["']?(\/[^/][^)"']+)["']?\)/g,
@ -37,7 +38,7 @@ export function replaceImportsInCssCode(params: {
break inline_style_in_html;
}
return `url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}${assetFileAbsoluteUrlPathname})`;
return `url(\${${!isAccountV3 ? "url.resourcesPath" : "resourceUrl"}}/${basenameOfTheKeycloakifyResourcesDir}${assetFileAbsoluteUrlPathname})`;
}
const assetFileRelativeUrlPathname = posix.relative(

View File

@ -1,9 +1,9 @@
export type KeycloakVersionRange =
| KeycloakVersionRange.WithAccountTheme
| KeycloakVersionRange.WithoutAccountTheme;
| KeycloakVersionRange.WithAccountV1Theme
| KeycloakVersionRange.WithoutAccountV1Theme;
export namespace KeycloakVersionRange {
export type WithoutAccountTheme = "21-and-below" | "22-and-above";
export type WithoutAccountV1Theme = "21-and-below" | "22-and-above";
export type WithAccountTheme = "21-and-below" | "23" | "24" | "25-and-above";
export type WithAccountV1Theme = "21-and-below" | "23" | "24" | "25-and-above";
}

View File

@ -63,6 +63,7 @@ export type BuildContext = {
packageJsonDirPath: string;
packageJsonScripts: Record<string, string>;
};
doUseAccountV3: boolean;
};
export type BuildOptions = {
@ -77,16 +78,17 @@ export type BuildOptions = {
kcContextExclusionsFtl?: string;
/** https://docs.keycloakify.dev/v/v10/targetting-specific-keycloak-versions */
keycloakVersionTargets?: BuildOptions.KeycloakVersionTargets;
doUseAccountV3?: boolean;
};
export namespace BuildOptions {
export type KeycloakVersionTargets =
| ({ hasAccountTheme: true } & Record<
KeycloakVersionRange.WithAccountTheme,
KeycloakVersionRange.WithAccountV1Theme,
string | boolean
>)
| ({ hasAccountTheme: false } & Record<
KeycloakVersionRange.WithoutAccountTheme,
KeycloakVersionRange.WithoutAccountV1Theme,
string | boolean
>);
}
@ -229,6 +231,7 @@ export function getBuildContext(params: {
projectBuildDirPath?: string;
staticDirPathInProjectBuildDirPath?: string;
publicDirPath?: string;
doUseAccountV3?: boolean;
};
type ParsedPackageJson = {
@ -297,7 +300,8 @@ export function getBuildContext(params: {
return zKeycloakVersionTargets;
})()
).optional()
).optional(),
doUseAccountV3: z.boolean().optional()
});
{
@ -386,6 +390,8 @@ export function getBuildContext(params: {
const bundler = resolvedViteConfig !== undefined ? "vite" : "webpack";
const doUseAccountV3 = buildOptions.doUseAccountV3 ?? false;
return {
bundler:
resolvedViteConfig !== undefined
@ -606,10 +612,10 @@ export function getBuildContext(params: {
}
const keycloakVersionRange: KeycloakVersionRange = (() => {
const doesImplementAccountTheme =
recordIsImplementedByThemeType.account;
const doesImplementAccountV1Theme =
!doUseAccountV3 && recordIsImplementedByThemeType.account;
if (doesImplementAccountTheme) {
if (doesImplementAccountV1Theme) {
const keycloakVersionRange = (() => {
if (buildForKeycloakMajorVersionNumber <= 21) {
return "21-and-below" as const;
@ -631,7 +637,7 @@ export function getBuildContext(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithAccountTheme
KeycloakVersionRange.WithAccountV1Theme
>
>();
@ -648,7 +654,7 @@ export function getBuildContext(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithoutAccountTheme
KeycloakVersionRange.WithoutAccountV1Theme
>
>();
@ -696,7 +702,7 @@ export function getBuildContext(params: {
const jarTargets_default = (() => {
const jarTargets: BuildContext["jarTargets"] = [];
if (recordIsImplementedByThemeType.account) {
if (!doUseAccountV3 && recordIsImplementedByThemeType.account) {
for (const keycloakVersionRange of [
"21-and-below",
"23",
@ -706,7 +712,7 @@ export function getBuildContext(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithAccountTheme
KeycloakVersionRange.WithAccountV1Theme
>
>(true);
jarTargets.push({
@ -723,7 +729,7 @@ export function getBuildContext(params: {
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithoutAccountTheme
KeycloakVersionRange.WithoutAccountV1Theme
>
>(true);
jarTargets.push({
@ -742,8 +748,9 @@ export function getBuildContext(params: {
}
if (
buildOptions.keycloakVersionTargets.hasAccountTheme !==
recordIsImplementedByThemeType.account
buildOptions.keycloakVersionTargets.hasAccountTheme !== doUseAccountV3
? false
: recordIsImplementedByThemeType.account
) {
console.log(
chalk.red(
@ -863,6 +870,7 @@ export function getBuildContext(params: {
}
return jarTargets;
})()
})(),
doUseAccountV3
};
}

View File

@ -396,6 +396,7 @@ describe("css replacer", () => {
}
`,
cssFileRelativeDirPath: "assets/",
isAccountV3: false,
buildContext: {
urlPathname: undefined
}
@ -434,6 +435,7 @@ describe("css replacer", () => {
}
`,
cssFileRelativeDirPath: "assets/",
isAccountV3: false,
buildContext: {
urlPathname: "/a/b/"
}
@ -472,6 +474,7 @@ describe("css replacer", () => {
}
`,
cssFileRelativeDirPath: undefined,
isAccountV3: false,
buildContext: {
urlPathname: "/a/b/"
}
@ -510,6 +513,7 @@ describe("css replacer", () => {
}
`,
cssFileRelativeDirPath: undefined,
isAccountV3: false,
buildContext: {
urlPathname: undefined
}