Introduce build option: accountThemeImplementation
This commit is contained in:
parent
13dc47533c
commit
db23ab0bc2
@ -24,7 +24,7 @@ export type BuildContextLike = BuildContextLike_generatePom & {
|
|||||||
artifactId: string;
|
artifactId: string;
|
||||||
themeVersion: string;
|
themeVersion: string;
|
||||||
cacheDirPath: string;
|
cacheDirPath: string;
|
||||||
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
|
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
@ -135,7 +135,7 @@ export async function buildJar(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
route_legacy_pages: {
|
route_legacy_pages: {
|
||||||
if (!buildContext.recordIsImplementedByThemeType.login) {
|
if (!buildContext.implementedThemeTypes.login.isImplemented) {
|
||||||
break route_legacy_pages;
|
break route_legacy_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,8 @@ import type { BuildContext } from "../../shared/buildContext";
|
|||||||
export type BuildContextLike = BuildContextLike_buildJar & {
|
export type BuildContextLike = BuildContextLike_buildJar & {
|
||||||
projectDirPath: string;
|
projectDirPath: string;
|
||||||
keycloakifyBuildDirPath: string;
|
keycloakifyBuildDirPath: string;
|
||||||
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
|
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||||
jarTargets: BuildContext["jarTargets"];
|
jarTargets: BuildContext["jarTargets"];
|
||||||
doUseAccountV3: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
@ -24,8 +23,8 @@ export async function buildJars(params: {
|
|||||||
const { resourcesDirPath, buildContext } = params;
|
const { resourcesDirPath, buildContext } = params;
|
||||||
|
|
||||||
const doesImplementAccountV1Theme =
|
const doesImplementAccountV1Theme =
|
||||||
buildContext.recordIsImplementedByThemeType.account &&
|
buildContext.implementedThemeTypes.account.isImplemented &&
|
||||||
!buildContext.doUseAccountV3;
|
buildContext.implementedThemeTypes.account.type === "Multi-Page";
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
keycloakAccountV1Versions
|
keycloakAccountV1Versions
|
||||||
|
@ -53,10 +53,9 @@ export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
|||||||
projectDirPath: string;
|
projectDirPath: string;
|
||||||
projectBuildDirPath: string;
|
projectBuildDirPath: string;
|
||||||
environmentVariables: { name: string; default: string }[];
|
environmentVariables: { name: string; default: string }[];
|
||||||
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
|
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||||
themeSrcDirPath: string;
|
themeSrcDirPath: string;
|
||||||
bundler: { type: "vite" } | { type: "webpack" };
|
bundler: { type: "vite" } | { type: "webpack" };
|
||||||
doUseAccountV3: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
@ -74,11 +73,15 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const themeType of ["login", "account"] as const) {
|
for (const themeType of ["login", "account"] as const) {
|
||||||
const isAccountV3 = themeType === "account" && buildContext.doUseAccountV3;
|
if (!buildContext.implementedThemeTypes[themeType].isImplemented) {
|
||||||
if (!buildContext.recordIsImplementedByThemeType[themeType]) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isForAccountSpa =
|
||||||
|
themeType === "account" &&
|
||||||
|
(assert(buildContext.implementedThemeTypes.account.isImplemented),
|
||||||
|
buildContext.implementedThemeTypes.account.type === "Single-Page");
|
||||||
|
|
||||||
const themeTypeDirPath = getThemeTypeDirPath({ themeType });
|
const themeTypeDirPath = getThemeTypeDirPath({ themeType });
|
||||||
|
|
||||||
apply_replacers_and_move_to_theme_resources: {
|
apply_replacers_and_move_to_theme_resources: {
|
||||||
@ -93,7 +96,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
themeType === "account" &&
|
themeType === "account" &&
|
||||||
buildContext.recordIsImplementedByThemeType.login
|
buildContext.implementedThemeTypes.login.isImplemented
|
||||||
) {
|
) {
|
||||||
// NOTE: We prevent doing it twice, it has been done for the login theme.
|
// NOTE: We prevent doing it twice, it has been done for the login theme.
|
||||||
|
|
||||||
@ -184,10 +187,10 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
case "login":
|
case "login":
|
||||||
return LOGIN_THEME_PAGE_IDS;
|
return LOGIN_THEME_PAGE_IDS;
|
||||||
case "account":
|
case "account":
|
||||||
return isAccountV3 ? ["index.ftl"] : ACCOUNT_THEME_PAGE_IDS;
|
return isForAccountSpa ? ["index.ftl"] : ACCOUNT_THEME_PAGE_IDS;
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
...(isAccountV3
|
...(isForAccountSpa
|
||||||
? []
|
? []
|
||||||
: readExtraPagesNames({
|
: readExtraPagesNames({
|
||||||
themeType,
|
themeType,
|
||||||
@ -203,7 +206,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
i18n_messages_generation: {
|
i18n_messages_generation: {
|
||||||
if (isAccountV3) {
|
if (isForAccountSpa) {
|
||||||
break i18n_messages_generation;
|
break i18n_messages_generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +233,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keycloak_static_resources: {
|
keycloak_static_resources: {
|
||||||
if (isAccountV3) {
|
if (isForAccountSpa) {
|
||||||
break keycloak_static_resources;
|
break keycloak_static_resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,13 +259,13 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
`parent=${(() => {
|
`parent=${(() => {
|
||||||
switch (themeType) {
|
switch (themeType) {
|
||||||
case "account":
|
case "account":
|
||||||
return isAccountV3 ? "base" : ACCOUNT_V1_THEME_NAME;
|
return isForAccountSpa ? "base" : ACCOUNT_V1_THEME_NAME;
|
||||||
case "login":
|
case "login":
|
||||||
return "keycloak";
|
return "keycloak";
|
||||||
}
|
}
|
||||||
assert<Equals<typeof themeType, never>>(false);
|
assert<Equals<typeof themeType, never>>(false);
|
||||||
})()}`,
|
})()}`,
|
||||||
...(isAccountV3 ? ["deprecatedMode=false"] : []),
|
...(isForAccountSpa ? ["deprecatedMode=false"] : []),
|
||||||
...(buildContext.extraThemeProperties ?? []),
|
...(buildContext.extraThemeProperties ?? []),
|
||||||
...buildContext.environmentVariables.map(
|
...buildContext.environmentVariables.map(
|
||||||
({ name, default: defaultValue }) =>
|
({ name, default: defaultValue }) =>
|
||||||
@ -275,7 +278,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
email: {
|
email: {
|
||||||
if (!buildContext.recordIsImplementedByThemeType.email) {
|
if (!buildContext.implementedThemeTypes.email.isImplemented) {
|
||||||
break email;
|
break email;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,11 +291,11 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bring_in_account_v1: {
|
bring_in_account_v1: {
|
||||||
if (buildContext.doUseAccountV3) {
|
if (!buildContext.implementedThemeTypes.account.isImplemented) {
|
||||||
break bring_in_account_v1;
|
break bring_in_account_v1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!buildContext.recordIsImplementedByThemeType.account) {
|
if (buildContext.implementedThemeTypes.account.type !== "Multi-Page") {
|
||||||
break bring_in_account_v1;
|
break bring_in_account_v1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +306,10 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bring_in_account_v3_i18n_messages: {
|
bring_in_account_v3_i18n_messages: {
|
||||||
if (!buildContext.doUseAccountV3) {
|
if (!buildContext.implementedThemeTypes.account.isImplemented) {
|
||||||
|
break bring_in_account_v3_i18n_messages;
|
||||||
|
}
|
||||||
|
if (buildContext.implementedThemeTypes.account.type !== "Single-Page") {
|
||||||
break bring_in_account_v3_i18n_messages;
|
break bring_in_account_v3_i18n_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,12 +346,12 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
|
|
||||||
metaInfKeycloakThemes.themes.push({
|
metaInfKeycloakThemes.themes.push({
|
||||||
name: themeName,
|
name: themeName,
|
||||||
types: objectEntries(buildContext.recordIsImplementedByThemeType)
|
types: objectEntries(buildContext.implementedThemeTypes)
|
||||||
.filter(([, isImplemented]) => isImplemented)
|
.filter(([, { isImplemented }]) => isImplemented)
|
||||||
.map(([themeType]) => themeType)
|
.map(([themeType]) => themeType)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (buildContext.recordIsImplementedByThemeType.account) {
|
if (buildContext.implementedThemeTypes.account.isImplemented) {
|
||||||
metaInfKeycloakThemes.themes.push({
|
metaInfKeycloakThemes.themes.push({
|
||||||
name: ACCOUNT_V1_THEME_NAME,
|
name: ACCOUNT_V1_THEME_NAME,
|
||||||
types: ["account"]
|
types: ["account"]
|
||||||
|
@ -20,11 +20,9 @@ import type { KeycloakVersionRange } from "./KeycloakVersionRange";
|
|||||||
import { exclude } from "tsafe";
|
import { exclude } from "tsafe";
|
||||||
import { crawl } from "../tools/crawl";
|
import { crawl } from "../tools/crawl";
|
||||||
import { THEME_TYPES } from "./constants";
|
import { THEME_TYPES } from "./constants";
|
||||||
import { objectFromEntries } from "tsafe/objectFromEntries";
|
|
||||||
import { objectEntries } from "tsafe/objectEntries";
|
import { objectEntries } from "tsafe/objectEntries";
|
||||||
import { type ThemeType } from "./constants";
|
import { type ThemeType } from "./constants";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { symToStr } from "tsafe/symToStr";
|
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
||||||
|
|
||||||
@ -49,11 +47,13 @@ export type BuildContext = {
|
|||||||
kcContextExclusionsFtlCode: string | undefined;
|
kcContextExclusionsFtlCode: string | undefined;
|
||||||
environmentVariables: { name: string; default: string }[];
|
environmentVariables: { name: string; default: string }[];
|
||||||
themeSrcDirPath: string;
|
themeSrcDirPath: string;
|
||||||
recordIsImplementedByThemeType: Readonly<Record<ThemeType | "email", boolean>>;
|
implementedThemeTypes: {
|
||||||
jarTargets: {
|
login: { isImplemented: boolean };
|
||||||
keycloakVersionRange: KeycloakVersionRange;
|
email: { isImplemented: boolean };
|
||||||
jarFileBasename: string;
|
account:
|
||||||
}[];
|
| { isImplemented: false }
|
||||||
|
| { isImplemented: true; type: "Single-Page" | "Multi-Page" };
|
||||||
|
};
|
||||||
bundler:
|
bundler:
|
||||||
| {
|
| {
|
||||||
type: "vite";
|
type: "vite";
|
||||||
@ -63,9 +63,14 @@ export type BuildContext = {
|
|||||||
packageJsonDirPath: string;
|
packageJsonDirPath: string;
|
||||||
packageJsonScripts: Record<string, string>;
|
packageJsonScripts: Record<string, string>;
|
||||||
};
|
};
|
||||||
doUseAccountV3: boolean;
|
jarTargets: {
|
||||||
|
keycloakVersionRange: KeycloakVersionRange;
|
||||||
|
jarFileBasename: string;
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert<Equals<keyof BuildContext["implementedThemeTypes"], ThemeType | "email">>();
|
||||||
|
|
||||||
export type BuildOptions = {
|
export type BuildOptions = {
|
||||||
themeName?: string | string[];
|
themeName?: string | string[];
|
||||||
themeVersion?: string;
|
themeVersion?: string;
|
||||||
@ -76,21 +81,30 @@ export type BuildOptions = {
|
|||||||
loginThemeResourcesFromKeycloakVersion?: string;
|
loginThemeResourcesFromKeycloakVersion?: string;
|
||||||
keycloakifyBuildDirPath?: string;
|
keycloakifyBuildDirPath?: string;
|
||||||
kcContextExclusionsFtl?: string;
|
kcContextExclusionsFtl?: string;
|
||||||
/** https://docs.keycloakify.dev/v/v10/targetting-specific-keycloak-versions */
|
} & BuildOptions.AccountThemeImplAndKeycloakVersionTargets;
|
||||||
keycloakVersionTargets?: BuildOptions.KeycloakVersionTargets;
|
|
||||||
doUseAccountV3?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export namespace BuildOptions {
|
export namespace BuildOptions {
|
||||||
export type KeycloakVersionTargets =
|
export type AccountThemeImplAndKeycloakVersionTargets =
|
||||||
| ({ hasAccountTheme: true } & Record<
|
| AccountThemeImplAndKeycloakVersionTargets.MultiPageApp
|
||||||
|
| AccountThemeImplAndKeycloakVersionTargets.SinglePageAppOrNone;
|
||||||
|
|
||||||
|
export namespace AccountThemeImplAndKeycloakVersionTargets {
|
||||||
|
export type MultiPageApp = {
|
||||||
|
accountThemeImplementation: "Multi-Page";
|
||||||
|
keycloakVersionTargets?: Record<
|
||||||
KeycloakVersionRange.WithAccountV1Theme,
|
KeycloakVersionRange.WithAccountV1Theme,
|
||||||
string | boolean
|
string | boolean
|
||||||
>)
|
>;
|
||||||
| ({ hasAccountTheme: false } & Record<
|
};
|
||||||
|
|
||||||
|
export type SinglePageAppOrNone = {
|
||||||
|
accountThemeImplementation: "Single-Page" | "none";
|
||||||
|
keycloakVersionTargets?: Record<
|
||||||
KeycloakVersionRange.WithoutAccountV1Theme,
|
KeycloakVersionRange.WithoutAccountV1Theme,
|
||||||
string | boolean
|
string | boolean
|
||||||
>);
|
>;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ResolvedViteConfig = {
|
export type ResolvedViteConfig = {
|
||||||
@ -231,7 +245,7 @@ export function getBuildContext(params: {
|
|||||||
projectBuildDirPath?: string;
|
projectBuildDirPath?: string;
|
||||||
staticDirPathInProjectBuildDirPath?: string;
|
staticDirPathInProjectBuildDirPath?: string;
|
||||||
publicDirPath?: string;
|
publicDirPath?: string;
|
||||||
doUseAccountV3?: boolean;
|
scripts?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ParsedPackageJson = {
|
type ParsedPackageJson = {
|
||||||
@ -241,20 +255,66 @@ export function getBuildContext(params: {
|
|||||||
keycloakify?: BuildOptions_packageJson;
|
keycloakify?: BuildOptions_packageJson;
|
||||||
};
|
};
|
||||||
|
|
||||||
const zParsedPackageJson = z.object({
|
const zMultiPageApp = (() => {
|
||||||
name: z.string().optional(),
|
type TargetType =
|
||||||
version: z.string().optional(),
|
BuildOptions.AccountThemeImplAndKeycloakVersionTargets.MultiPageApp;
|
||||||
homepage: z.string().optional(),
|
|
||||||
keycloakify: id<z.ZodType<BuildOptions_packageJson>>(
|
const zTargetType = z.object({
|
||||||
(() => {
|
accountThemeImplementation: z.literal("Multi-Page"),
|
||||||
const zBuildOptions_packageJson = z.object({
|
keycloakVersionTargets: z
|
||||||
extraThemeProperties: z.array(z.string()).optional(),
|
.object({
|
||||||
artifactId: z.string().optional(),
|
"21-and-below": z.union([z.boolean(), z.string()]),
|
||||||
groupId: z.string().optional(),
|
"23": z.union([z.boolean(), z.string()]),
|
||||||
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
"24": z.union([z.boolean(), z.string()]),
|
||||||
projectBuildDirPath: z.string().optional(),
|
"25-and-above": z.union([z.boolean(), z.string()])
|
||||||
keycloakifyBuildDirPath: z.string().optional(),
|
})
|
||||||
kcContextExclusionsFtl: z.string().optional(),
|
.optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const zSinglePageApp = (() => {
|
||||||
|
type TargetType =
|
||||||
|
BuildOptions.AccountThemeImplAndKeycloakVersionTargets.SinglePageAppOrNone;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
accountThemeImplementation: z.union([
|
||||||
|
z.literal("Single-Page"),
|
||||||
|
z.literal("none")
|
||||||
|
]),
|
||||||
|
keycloakVersionTargets: z
|
||||||
|
.object({
|
||||||
|
"21-and-below": z.union([z.boolean(), z.string()]),
|
||||||
|
"22-and-above": z.union([z.boolean(), z.string()])
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const zAccountThemeImplAndKeycloakVersionTargets = (() => {
|
||||||
|
type TargetType = BuildOptions.AccountThemeImplAndKeycloakVersionTargets;
|
||||||
|
|
||||||
|
const zTargetType = z.union([zMultiPageApp, zSinglePageApp]);
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const zBuildOptions = (() => {
|
||||||
|
type TargetType = BuildOptions;
|
||||||
|
|
||||||
|
const zTargetType = z.intersection(
|
||||||
|
z.object({
|
||||||
|
themeName: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
themeVersion: z.string().optional(),
|
||||||
environmentVariables: z
|
environmentVariables: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
@ -263,63 +323,53 @@ export function getBuildContext(params: {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
themeName: z.union([z.string(), z.array(z.string())]).optional(),
|
extraThemeProperties: z.array(z.string()).optional(),
|
||||||
themeVersion: z.string().optional(),
|
artifactId: z.string().optional(),
|
||||||
|
groupId: z.string().optional(),
|
||||||
|
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
||||||
|
keycloakifyBuildDirPath: z.string().optional(),
|
||||||
|
kcContextExclusionsFtl: z.string().optional()
|
||||||
|
}),
|
||||||
|
zAccountThemeImplAndKeycloakVersionTargets
|
||||||
|
);
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const zBuildOptions_packageJson = (() => {
|
||||||
|
type TargetType = BuildOptions_packageJson;
|
||||||
|
|
||||||
|
const zTargetType = z.intersection(
|
||||||
|
zBuildOptions,
|
||||||
|
z.object({
|
||||||
|
projectBuildDirPath: z.string().optional(),
|
||||||
staticDirPathInProjectBuildDirPath: z.string().optional(),
|
staticDirPathInProjectBuildDirPath: z.string().optional(),
|
||||||
publicDirPath: z.string().optional(),
|
publicDirPath: z.string().optional(),
|
||||||
keycloakVersionTargets: id<
|
scripts: z.record(z.string()).optional()
|
||||||
z.ZodType<BuildOptions.KeycloakVersionTargets>
|
|
||||||
>(
|
|
||||||
(() => {
|
|
||||||
const zKeycloakVersionTargets = z.union([
|
|
||||||
z.object({
|
|
||||||
hasAccountTheme: z.literal(true),
|
|
||||||
"21-and-below": z.union([
|
|
||||||
z.boolean(),
|
|
||||||
z.string()
|
|
||||||
]),
|
|
||||||
"23": z.union([z.boolean(), z.string()]),
|
|
||||||
"24": z.union([z.boolean(), z.string()]),
|
|
||||||
"25-and-above": z.union([z.boolean(), z.string()])
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
hasAccountTheme: z.literal(false),
|
|
||||||
"21-and-below": z.union([
|
|
||||||
z.boolean(),
|
|
||||||
z.string()
|
|
||||||
]),
|
|
||||||
"22-and-above": z.union([z.boolean(), z.string()])
|
|
||||||
})
|
})
|
||||||
]);
|
);
|
||||||
|
|
||||||
{
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
type Got = z.infer<typeof zKeycloakVersionTargets>;
|
|
||||||
type Expected = BuildOptions.KeycloakVersionTargets;
|
|
||||||
assert<Equals<Got, Expected>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return zKeycloakVersionTargets;
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
})()
|
})();
|
||||||
).optional(),
|
|
||||||
doUseAccountV3: z.boolean().optional()
|
const zParsedPackageJson = (() => {
|
||||||
|
type TargetType = ParsedPackageJson;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
name: z.string().optional(),
|
||||||
|
version: z.string().optional(),
|
||||||
|
homepage: z.string().optional(),
|
||||||
|
keycloakify: zBuildOptions_packageJson.optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
type Got = z.infer<typeof zBuildOptions_packageJson>;
|
|
||||||
type Expected = BuildOptions_packageJson;
|
|
||||||
assert<Equals<Got, Expected>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return zBuildOptions_packageJson;
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
})()
|
})();
|
||||||
).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
type Got = z.infer<typeof zParsedPackageJson>;
|
|
||||||
type Expected = ParsedPackageJson;
|
|
||||||
assert<Equals<Got, Expected>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const configurationPackageJsonFilePath = (() => {
|
const configurationPackageJsonFilePath = (() => {
|
||||||
const rootPackageJsonFilePath = pathJoin(projectDirPath, "package.json");
|
const rootPackageJsonFilePath = pathJoin(projectDirPath, "package.json");
|
||||||
@ -334,17 +384,63 @@ export function getBuildContext(params: {
|
|||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const buildOptions = {
|
const bundler = resolvedViteConfig !== undefined ? "vite" : "webpack";
|
||||||
...parsedPackageJson.keycloakify,
|
|
||||||
...resolvedViteConfig?.buildOptions
|
if (bundler === "vite" && parsedPackageJson.keycloakify !== undefined) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
`In vite projects, provide your Keycloakify options in vite.config.ts, not in package.json`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildOptions: BuildOptions = (() => {
|
||||||
|
switch (bundler) {
|
||||||
|
case "vite":
|
||||||
|
assert(resolvedViteConfig !== undefined);
|
||||||
|
return resolvedViteConfig.buildOptions;
|
||||||
|
case "webpack":
|
||||||
|
assert(parsedPackageJson.keycloakify !== undefined);
|
||||||
|
return parsedPackageJson.keycloakify;
|
||||||
|
}
|
||||||
|
assert<Equals<typeof bundler, never>>(false);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const implementedThemeTypes: BuildContext["implementedThemeTypes"] = {
|
||||||
|
login: {
|
||||||
|
isImplemented: fs.existsSync(pathJoin(themeSrcDirPath, "login"))
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
isImplemented: fs.existsSync(pathJoin(themeSrcDirPath, "email"))
|
||||||
|
},
|
||||||
|
account: (() => {
|
||||||
|
if (buildOptions.accountThemeImplementation === "none") {
|
||||||
|
return { isImplemented: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isImplemented: true,
|
||||||
|
type: buildOptions.accountThemeImplementation
|
||||||
|
};
|
||||||
|
})()
|
||||||
};
|
};
|
||||||
|
|
||||||
const recordIsImplementedByThemeType = objectFromEntries(
|
if (
|
||||||
(["login", "account", "email"] as const).map(themeType => [
|
implementedThemeTypes.account.isImplemented &&
|
||||||
themeType,
|
!fs.existsSync(pathJoin(themeSrcDirPath, "account"))
|
||||||
fs.existsSync(pathJoin(themeSrcDirPath, themeType))
|
) {
|
||||||
])
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
[
|
||||||
|
`You have set 'accountThemeImplementation' to '${implementedThemeTypes.account.type}'`,
|
||||||
|
`but the 'account' directory is missing in your theme source directory`,
|
||||||
|
"Use the `npx keycloakify initialize-account-theme` command to create it"
|
||||||
|
].join(" ")
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
const themeNames = ((): [string, ...string[]] => {
|
const themeNames = ((): [string, ...string[]] => {
|
||||||
if (buildOptions.themeName === undefined) {
|
if (buildOptions.themeName === undefined) {
|
||||||
@ -371,13 +467,15 @@ export function getBuildContext(params: {
|
|||||||
|
|
||||||
const projectBuildDirPath = (() => {
|
const projectBuildDirPath = (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (bundler !== "webpack") {
|
||||||
break webpack;
|
break webpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildOptions.projectBuildDirPath !== undefined) {
|
assert(parsedPackageJson.keycloakify !== undefined);
|
||||||
|
|
||||||
|
if (parsedPackageJson.keycloakify.projectBuildDirPath !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
pathIsh: buildOptions.projectBuildDirPath,
|
pathIsh: parsedPackageJson.keycloakify.projectBuildDirPath,
|
||||||
cwd: projectDirPath
|
cwd: projectDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -385,33 +483,25 @@ export function getBuildContext(params: {
|
|||||||
return pathJoin(projectDirPath, "build");
|
return pathJoin(projectDirPath, "build");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(bundler === "vite");
|
||||||
|
assert(resolvedViteConfig !== undefined);
|
||||||
|
|
||||||
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const bundler = resolvedViteConfig !== undefined ? "vite" : "webpack";
|
|
||||||
|
|
||||||
const doUseAccountV3 = buildOptions.doUseAccountV3 ?? false;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bundler:
|
bundler: (() => {
|
||||||
resolvedViteConfig !== undefined
|
switch (bundler) {
|
||||||
? { type: "vite" }
|
case "vite":
|
||||||
: (() => {
|
return { type: "vite" };
|
||||||
const { scripts } = z
|
case "webpack":
|
||||||
.object({
|
|
||||||
scripts: z.record(z.string()).optional()
|
|
||||||
})
|
|
||||||
.parse(
|
|
||||||
JSON.parse(
|
|
||||||
fs.readFileSync(packageJsonFilePath).toString("utf8")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "webpack",
|
type: "webpack",
|
||||||
packageJsonDirPath: pathDirname(packageJsonFilePath),
|
packageJsonDirPath: pathDirname(packageJsonFilePath),
|
||||||
packageJsonScripts: scripts ?? {}
|
packageJsonScripts: parsedPackageJson.keycloakify?.scripts ?? {}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
assert<Equals<typeof bundler, never>>(false);
|
||||||
})(),
|
})(),
|
||||||
themeVersion: buildOptions.themeVersion ?? parsedPackageJson.version ?? "0.0.0",
|
themeVersion: buildOptions.themeVersion ?? parsedPackageJson.version ?? "0.0.0",
|
||||||
themeNames,
|
themeNames,
|
||||||
@ -463,13 +553,15 @@ export function getBuildContext(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (bundler !== "webpack") {
|
||||||
break webpack;
|
break webpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildOptions.publicDirPath !== undefined) {
|
assert(parsedPackageJson.keycloakify !== undefined);
|
||||||
|
|
||||||
|
if (parsedPackageJson.keycloakify.publicDirPath !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
pathIsh: buildOptions.publicDirPath,
|
pathIsh: parsedPackageJson.keycloakify.publicDirPath,
|
||||||
cwd: projectDirPath
|
cwd: projectDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -477,6 +569,9 @@ export function getBuildContext(params: {
|
|||||||
return pathJoin(projectDirPath, "public");
|
return pathJoin(projectDirPath, "public");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(bundler === "vite");
|
||||||
|
assert(resolvedViteConfig !== undefined);
|
||||||
|
|
||||||
return pathJoin(projectDirPath, resolvedViteConfig.publicDir);
|
return pathJoin(projectDirPath, resolvedViteConfig.publicDir);
|
||||||
})(),
|
})(),
|
||||||
cacheDirPath: (() => {
|
cacheDirPath: (() => {
|
||||||
@ -502,7 +597,7 @@ export function getBuildContext(params: {
|
|||||||
})(),
|
})(),
|
||||||
urlPathname: (() => {
|
urlPathname: (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (bundler !== "webpack") {
|
||||||
break webpack;
|
break webpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,23 +617,35 @@ export function getBuildContext(params: {
|
|||||||
return out === "/" ? undefined : out;
|
return out === "/" ? undefined : out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(bundler === "vite");
|
||||||
|
assert(resolvedViteConfig !== undefined);
|
||||||
|
|
||||||
return resolvedViteConfig.urlPathname;
|
return resolvedViteConfig.urlPathname;
|
||||||
})(),
|
})(),
|
||||||
assetsDirPath: (() => {
|
assetsDirPath: (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (bundler !== "webpack") {
|
||||||
break webpack;
|
break webpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildOptions.staticDirPathInProjectBuildDirPath !== undefined) {
|
assert(parsedPackageJson.keycloakify !== undefined);
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedPackageJson.keycloakify.staticDirPathInProjectBuildDirPath !==
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
getAbsoluteAndInOsFormatPath({
|
getAbsoluteAndInOsFormatPath({
|
||||||
pathIsh: buildOptions.staticDirPathInProjectBuildDirPath,
|
pathIsh:
|
||||||
|
parsedPackageJson.keycloakify
|
||||||
|
.staticDirPathInProjectBuildDirPath,
|
||||||
cwd: projectBuildDirPath
|
cwd: projectBuildDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathJoin(projectBuildDirPath, "static");
|
return pathJoin(projectBuildDirPath, "static");
|
||||||
}
|
}
|
||||||
|
assert(bundler === "vite");
|
||||||
|
assert(resolvedViteConfig !== undefined);
|
||||||
|
|
||||||
return pathJoin(projectBuildDirPath, resolvedViteConfig.assetsDir);
|
return pathJoin(projectBuildDirPath, resolvedViteConfig.assetsDir);
|
||||||
})(),
|
})(),
|
||||||
@ -559,7 +666,7 @@ export function getBuildContext(params: {
|
|||||||
return buildOptions.kcContextExclusionsFtl;
|
return buildOptions.kcContextExclusionsFtl;
|
||||||
})(),
|
})(),
|
||||||
environmentVariables: buildOptions.environmentVariables ?? [],
|
environmentVariables: buildOptions.environmentVariables ?? [],
|
||||||
recordIsImplementedByThemeType,
|
implementedThemeTypes,
|
||||||
themeSrcDirPath,
|
themeSrcDirPath,
|
||||||
fetchOptions: getProxyFetchOptions({
|
fetchOptions: getProxyFetchOptions({
|
||||||
npmConfigGetCwd: (function callee(upCount: number): string {
|
npmConfigGetCwd: (function callee(upCount: number): string {
|
||||||
@ -613,10 +720,10 @@ export function getBuildContext(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const keycloakVersionRange: KeycloakVersionRange = (() => {
|
const keycloakVersionRange: KeycloakVersionRange = (() => {
|
||||||
const doesImplementAccountV1Theme =
|
if (
|
||||||
!doUseAccountV3 && recordIsImplementedByThemeType.account;
|
implementedThemeTypes.account.isImplemented &&
|
||||||
|
implementedThemeTypes.account.type === "Multi-Page"
|
||||||
if (doesImplementAccountV1Theme) {
|
) {
|
||||||
const keycloakVersionRange = (() => {
|
const keycloakVersionRange = (() => {
|
||||||
if (buildForKeycloakMajorVersionNumber <= 21) {
|
if (buildForKeycloakMajorVersionNumber <= 21) {
|
||||||
return "21-and-below" as const;
|
return "21-and-below" as const;
|
||||||
@ -703,7 +810,10 @@ export function getBuildContext(params: {
|
|||||||
const jarTargets_default = (() => {
|
const jarTargets_default = (() => {
|
||||||
const jarTargets: BuildContext["jarTargets"] = [];
|
const jarTargets: BuildContext["jarTargets"] = [];
|
||||||
|
|
||||||
if (!doUseAccountV3 && recordIsImplementedByThemeType.account) {
|
if (
|
||||||
|
implementedThemeTypes.account.isImplemented &&
|
||||||
|
implementedThemeTypes.account.type === "Multi-Page"
|
||||||
|
) {
|
||||||
for (const keycloakVersionRange of [
|
for (const keycloakVersionRange of [
|
||||||
"21-and-below",
|
"21-and-below",
|
||||||
"23",
|
"23",
|
||||||
@ -748,79 +858,11 @@ export function getBuildContext(params: {
|
|||||||
return jarTargets_default;
|
return jarTargets_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
buildOptions.keycloakVersionTargets.hasAccountTheme !== doUseAccountV3
|
|
||||||
? false
|
|
||||||
: recordIsImplementedByThemeType.account
|
|
||||||
) {
|
|
||||||
console.log(
|
|
||||||
chalk.red(
|
|
||||||
(() => {
|
|
||||||
const { keycloakVersionTargets } = buildOptions;
|
|
||||||
|
|
||||||
let message = `Bad ${symToStr({ keycloakVersionTargets })} configuration.\n`;
|
|
||||||
|
|
||||||
if (keycloakVersionTargets.hasAccountTheme) {
|
|
||||||
message +=
|
|
||||||
"Your codebase does not seem to implement an account theme ";
|
|
||||||
} else {
|
|
||||||
message += "Your codebase implements an account theme ";
|
|
||||||
}
|
|
||||||
|
|
||||||
const { hasAccountTheme } = keycloakVersionTargets;
|
|
||||||
|
|
||||||
message += `but you have set ${symToStr({ keycloakVersionTargets })}.${symToStr({ hasAccountTheme })}`;
|
|
||||||
message += ` to ${hasAccountTheme} in your `;
|
|
||||||
message += (() => {
|
|
||||||
switch (bundler) {
|
|
||||||
case "vite":
|
|
||||||
return "vite.config.ts";
|
|
||||||
case "webpack":
|
|
||||||
return "package.json";
|
|
||||||
}
|
|
||||||
assert<Equals<typeof bundler, never>>(false);
|
|
||||||
})();
|
|
||||||
message += `. Please set it to ${!hasAccountTheme} `;
|
|
||||||
message +=
|
|
||||||
"and fill up the relevant keycloak version ranges.\n";
|
|
||||||
message += "Example:\n";
|
|
||||||
message += JSON.stringify(
|
|
||||||
id<Pick<BuildOptions, "keycloakVersionTargets">>({
|
|
||||||
keycloakVersionTargets: {
|
|
||||||
hasAccountTheme:
|
|
||||||
recordIsImplementedByThemeType.account,
|
|
||||||
...objectFromEntries(
|
|
||||||
jarTargets_default.map(
|
|
||||||
({
|
|
||||||
keycloakVersionRange,
|
|
||||||
jarFileBasename
|
|
||||||
}) => [
|
|
||||||
keycloakVersionRange,
|
|
||||||
jarFileBasename
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
message +=
|
|
||||||
"\nSee: https://docs.keycloakify.dev/v/v10/targetting-specific-keycloak-versions";
|
|
||||||
|
|
||||||
return message;
|
|
||||||
})()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const jarTargets: BuildContext["jarTargets"] = [];
|
const jarTargets: BuildContext["jarTargets"] = [];
|
||||||
|
|
||||||
const { hasAccountTheme, ...rest } = buildOptions.keycloakVersionTargets;
|
for (const [keycloakVersionRange, jarNameOrBoolean] of objectEntries(
|
||||||
|
buildOptions.keycloakVersionTargets
|
||||||
for (const [keycloakVersionRange, jarNameOrBoolean] of objectEntries(rest)) {
|
)) {
|
||||||
if (jarNameOrBoolean === false) {
|
if (jarNameOrBoolean === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -871,7 +913,6 @@ export function getBuildContext(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return jarTargets;
|
return jarTargets;
|
||||||
})(),
|
})()
|
||||||
doUseAccountV3
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,14 @@ import {
|
|||||||
import MagicString from "magic-string";
|
import MagicString from "magic-string";
|
||||||
import { generateKcGenTs } from "../bin/shared/generateKcGenTs";
|
import { generateKcGenTs } from "../bin/shared/generateKcGenTs";
|
||||||
|
|
||||||
|
export namespace keycloakify {
|
||||||
export type Params = BuildOptions & {
|
export type Params = BuildOptions & {
|
||||||
postBuild?: (buildContext: Omit<BuildContext, "bundler">) => Promise<void>;
|
postBuild?: (buildContext: Omit<BuildContext, "bundler">) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function keycloakify(params?: Params) {
|
export function keycloakify(params: keycloakify.Params) {
|
||||||
const { postBuild, ...buildOptions } = params ?? {};
|
const { postBuild, ...buildOptions } = params;
|
||||||
|
|
||||||
let projectDirPath: string | undefined = undefined;
|
let projectDirPath: string | undefined = undefined;
|
||||||
let urlPathname: string | undefined = undefined;
|
let urlPathname: string | undefined = undefined;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user