2023-04-18 03:10:29 +02:00
|
|
|
import { transformCodebase } from "../../tools/transformCodebase";
|
2021-02-21 18:55:06 +01:00
|
|
|
import * as fs from "fs";
|
2023-04-18 03:10:29 +02:00
|
|
|
import { join as pathJoin } from "path";
|
|
|
|
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
|
|
|
|
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
|
|
|
|
import { generateFtlFilesCodeFactory, loginThemePageIds, accountThemePageIds, themeTypes, type ThemeType } from "../generateFtl";
|
2023-04-19 05:04:11 +02:00
|
|
|
import { basenameOfKeycloakDirInPublicDir } from "../../mockTestingResourcesPath";
|
2023-04-18 03:10:29 +02:00
|
|
|
import { isInside } from "../../tools/isInside";
|
|
|
|
import type { BuildOptions } from "../BuildOptions";
|
2022-08-16 14:41:06 +07:00
|
|
|
import { assert } from "tsafe/assert";
|
2023-04-18 03:10:29 +02:00
|
|
|
import { downloadKeycloakStaticResources } from "./downloadKeycloakStaticResources";
|
2022-08-16 14:41:06 +07:00
|
|
|
|
|
|
|
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
|
|
|
|
|
|
|
export namespace BuildOptionsLike {
|
|
|
|
export type Common = {
|
|
|
|
themeName: string;
|
2023-03-16 22:13:46 +01:00
|
|
|
extraLoginPages?: string[];
|
|
|
|
extraAccountPages?: string[];
|
2022-08-16 14:41:06 +07:00
|
|
|
extraThemeProperties?: string[];
|
2022-09-08 12:06:26 +03:00
|
|
|
isSilent: boolean;
|
2023-04-01 13:31:35 +02:00
|
|
|
customUserAttributes: string[];
|
2023-04-06 16:38:13 +02:00
|
|
|
themeVersion: string;
|
2023-04-18 03:10:29 +02:00
|
|
|
keycloakVersionDefaultAssets: string;
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
export type Standalone = Common & {
|
|
|
|
isStandalone: true;
|
|
|
|
urlPathname: string | undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
|
|
|
|
|
|
|
|
export namespace ExternalAssets {
|
|
|
|
export type CommonExternalAssets = Common & {
|
|
|
|
isStandalone: false;
|
|
|
|
};
|
|
|
|
|
|
|
|
export type SameDomain = CommonExternalAssets & {
|
2022-09-07 11:25:46 +02:00
|
|
|
areAppAndKeycloakServerSharingSameDomain: true;
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
export type DifferentDomains = CommonExternalAssets & {
|
2022-09-07 11:25:46 +02:00
|
|
|
areAppAndKeycloakServerSharingSameDomain: false;
|
2022-08-16 14:41:06 +07:00
|
|
|
urlOrigin: string;
|
|
|
|
urlPathname: string | undefined;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-01 13:31:35 +02:00
|
|
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
2021-03-22 20:54:28 +01:00
|
|
|
|
2023-04-18 03:10:29 +02:00
|
|
|
export async function generateTheme(params: {
|
2021-10-11 21:35:40 +02:00
|
|
|
reactAppBuildDirPath: string;
|
|
|
|
keycloakThemeBuildingDirPath: string;
|
2023-03-24 05:43:34 +01:00
|
|
|
emailThemeSrcDirPath: string | undefined;
|
2022-08-16 14:41:06 +07:00
|
|
|
buildOptions: BuildOptionsLike;
|
2023-04-04 01:40:55 +02:00
|
|
|
keycloakifyVersion: string;
|
2023-01-16 14:42:20 +01:00
|
|
|
}): Promise<{ doBundlesEmailTemplate: boolean }> {
|
2023-04-18 03:10:29 +02:00
|
|
|
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, emailThemeSrcDirPath, buildOptions, keycloakifyVersion } = params;
|
2023-03-16 22:13:46 +01:00
|
|
|
|
|
|
|
const getThemeDirPath = (themeType: ThemeType | "email") =>
|
|
|
|
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
|
2021-02-21 18:55:06 +01:00
|
|
|
|
|
|
|
let allCssGlobalsToDefine: Record<string, string> = {};
|
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
let generateFtlFilesCode_glob: ReturnType<typeof generateFtlFilesCodeFactory>["generateFtlFilesCode"] | undefined = undefined;
|
|
|
|
|
|
|
|
for (const themeType of themeTypes) {
|
|
|
|
const themeDirPath = getThemeDirPath(themeType);
|
|
|
|
|
|
|
|
copy_app_resources_to_theme_path: {
|
|
|
|
const isFirstPass = themeType.indexOf(themeType) === 0;
|
|
|
|
|
|
|
|
if (!isFirstPass && !buildOptions.isStandalone) {
|
|
|
|
break copy_app_resources_to_theme_path;
|
2021-03-19 22:39:32 +01:00
|
|
|
}
|
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
transformCodebase({
|
|
|
|
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
|
|
|
|
"srcDirPath": reactAppBuildDirPath,
|
|
|
|
"transformSourceCode": ({ filePath, sourceCode }) => {
|
|
|
|
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
|
|
|
if (
|
|
|
|
buildOptions.isStandalone &&
|
|
|
|
isInside({
|
2023-04-19 05:04:11 +02:00
|
|
|
"dirPath": pathJoin(reactAppBuildDirPath, basenameOfKeycloakDirInPublicDir),
|
2023-03-16 22:13:46 +01:00
|
|
|
filePath
|
|
|
|
})
|
|
|
|
) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (/\.css?$/i.test(filePath)) {
|
|
|
|
if (!buildOptions.isStandalone) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
|
|
|
|
"cssCode": sourceCode.toString("utf8")
|
|
|
|
});
|
|
|
|
|
|
|
|
register_css_variables: {
|
|
|
|
if (!isFirstPass) {
|
|
|
|
break register_css_variables;
|
|
|
|
}
|
|
|
|
|
|
|
|
allCssGlobalsToDefine = {
|
|
|
|
...allCssGlobalsToDefine,
|
|
|
|
...cssGlobalsToDefine
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (/\.js?$/i.test(filePath)) {
|
|
|
|
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
|
|
|
"jsCode": sourceCode.toString("utf8"),
|
|
|
|
buildOptions
|
|
|
|
});
|
|
|
|
|
|
|
|
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
|
|
|
}
|
|
|
|
|
|
|
|
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
|
2022-08-16 14:41:06 +07:00
|
|
|
}
|
2023-03-16 22:13:46 +01:00
|
|
|
});
|
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
const generateFtlFilesCode = (() => {
|
|
|
|
if (generateFtlFilesCode_glob !== undefined) {
|
|
|
|
return generateFtlFilesCode_glob;
|
|
|
|
}
|
2021-02-21 18:55:06 +01:00
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
|
|
|
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
|
|
|
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
2023-04-04 01:40:55 +02:00
|
|
|
buildOptions,
|
2023-04-27 11:52:02 +02:00
|
|
|
keycloakifyVersion,
|
|
|
|
themeType
|
2023-03-16 22:13:46 +01:00
|
|
|
});
|
2021-02-21 18:55:06 +01:00
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
return generateFtlFilesCode;
|
|
|
|
})();
|
2021-02-21 18:55:06 +01:00
|
|
|
|
2023-03-20 01:48:03 +01:00
|
|
|
[
|
|
|
|
...(() => {
|
|
|
|
switch (themeType) {
|
|
|
|
case "login":
|
|
|
|
return loginThemePageIds;
|
|
|
|
case "account":
|
|
|
|
return accountThemePageIds;
|
|
|
|
}
|
|
|
|
})(),
|
|
|
|
...((() => {
|
|
|
|
switch (themeType) {
|
|
|
|
case "login":
|
|
|
|
return buildOptions.extraLoginPages;
|
|
|
|
case "account":
|
|
|
|
return buildOptions.extraAccountPages;
|
|
|
|
}
|
|
|
|
})() ?? [])
|
|
|
|
].forEach(pageId => {
|
2023-03-16 22:13:46 +01:00
|
|
|
const { ftlCode } = generateFtlFilesCode({ pageId });
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
fs.mkdirSync(themeDirPath, { "recursive": true });
|
2021-03-28 13:37:02 +02:00
|
|
|
|
2023-03-16 22:13:46 +01:00
|
|
|
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
|
|
|
|
});
|
|
|
|
|
2023-04-19 17:50:27 +02:00
|
|
|
//TODO: Remove this block we left it for now only for backward compatibility
|
|
|
|
// we now have a separate script for this
|
|
|
|
copy_keycloak_resources_to_public: {
|
|
|
|
const keycloakDirInPublicDir = pathJoin(reactAppBuildDirPath, "..", "public", basenameOfKeycloakDirInPublicDir);
|
|
|
|
|
|
|
|
if (fs.existsSync(keycloakDirInPublicDir)) {
|
|
|
|
break copy_keycloak_resources_to_public;
|
|
|
|
}
|
|
|
|
|
|
|
|
await downloadKeycloakStaticResources({
|
|
|
|
"isSilent": buildOptions.isSilent,
|
|
|
|
"keycloakVersion": buildOptions.keycloakVersionDefaultAssets,
|
|
|
|
"themeDirPath": keycloakDirInPublicDir,
|
|
|
|
themeType
|
|
|
|
});
|
|
|
|
|
|
|
|
if (themeType !== themeTypes[0]) {
|
|
|
|
break copy_keycloak_resources_to_public;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
pathJoin(keycloakDirInPublicDir, "README.txt"),
|
|
|
|
Buffer.from(
|
|
|
|
// prettier-ignore
|
|
|
|
[
|
|
|
|
"This is just a test folder that helps develop",
|
|
|
|
"the login and register page without having to run a Keycloak container"
|
|
|
|
].join(" ")
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
fs.writeFileSync(pathJoin(keycloakDirInPublicDir, ".gitignore"), Buffer.from("*", "utf8"));
|
|
|
|
}
|
|
|
|
|
2023-04-19 05:04:11 +02:00
|
|
|
await downloadKeycloakStaticResources({
|
|
|
|
"isSilent": buildOptions.isSilent,
|
|
|
|
"keycloakVersion": buildOptions.keycloakVersionDefaultAssets,
|
|
|
|
themeDirPath,
|
|
|
|
themeType
|
|
|
|
});
|
2023-03-16 22:13:46 +01:00
|
|
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
pathJoin(themeDirPath, "theme.properties"),
|
|
|
|
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
|
|
|
|
);
|
|
|
|
}
|
2021-02-21 18:55:06 +01:00
|
|
|
|
2022-08-16 14:41:06 +07:00
|
|
|
let doBundlesEmailTemplate: boolean;
|
2022-04-20 00:39:40 +02:00
|
|
|
|
|
|
|
email: {
|
2023-03-24 05:43:34 +01:00
|
|
|
if (emailThemeSrcDirPath === undefined) {
|
2022-08-16 14:41:06 +07:00
|
|
|
doBundlesEmailTemplate = false;
|
2022-04-20 00:39:40 +02:00
|
|
|
break email;
|
|
|
|
}
|
|
|
|
|
2022-08-16 14:41:06 +07:00
|
|
|
doBundlesEmailTemplate = true;
|
2022-04-20 00:39:40 +02:00
|
|
|
|
|
|
|
transformCodebase({
|
2023-03-24 05:43:34 +01:00
|
|
|
"srcDirPath": emailThemeSrcDirPath,
|
2023-03-16 22:13:46 +01:00
|
|
|
"destDirPath": getThemeDirPath("email")
|
2022-04-20 00:39:40 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-16 14:41:06 +07:00
|
|
|
return { doBundlesEmailTemplate };
|
2021-02-21 18:55:06 +01:00
|
|
|
}
|