Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
70a524da46 | |||
bf6c846fac | |||
b83e4bef3f | |||
9f7fe0d8f7 | |||
741dee57e4 | |||
fff4dba708 | |||
f4f7ab3e49 | |||
88fe99b1b8 | |||
92c1486f6a | |||
caea64cef3 | |||
90783d8ee8 | |||
be57801e21 | |||
ff84786b4e | |||
1e863672cb |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "7.1.0",
|
||||
"version": "7.2.2",
|
||||
"description": "Create Keycloak themes using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -16,6 +16,7 @@ import { existsSync } from "fs";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
||||
import { assert, Equals } from "tsafe/assert";
|
||||
import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
|
||||
|
||||
(async () => {
|
||||
const projectRootDir = getProjectRoot();
|
||||
@ -50,7 +51,13 @@ import { assert, Equals } from "tsafe/assert";
|
||||
|
||||
const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
|
||||
|
||||
const targetFilePath = pathJoin(process.cwd(), "src", "keycloak-theme", themeType, "pages", pageBasename);
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath();
|
||||
|
||||
if (themeSrcDirPath === undefined) {
|
||||
throw new Error("Couldn't locate your theme sources");
|
||||
}
|
||||
|
||||
const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", pageBasename);
|
||||
|
||||
if (existsSync(targetFilePath)) {
|
||||
console.log(`${pageId} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`);
|
||||
|
33
src/bin/getThemeSrcDirPath.ts
Normal file
33
src/bin/getThemeSrcDirPath.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { join as pathJoin } from "path";
|
||||
import * as fs from "fs";
|
||||
import { crawl } from "./tools/crawl";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
|
||||
const themeSrcDirBasename = "keycloak-theme";
|
||||
|
||||
export function getThemeSrcDirPath() {
|
||||
const srcDirPath = pathJoin(reactProjectDirPath, "src");
|
||||
|
||||
const themeSrcDirPath: string | undefined = crawl(srcDirPath)
|
||||
.map(fileRelativePath => {
|
||||
const split = fileRelativePath.split(themeSrcDirBasename);
|
||||
|
||||
if (split.length !== 2) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return pathJoin(srcDirPath, split[0] + themeSrcDirBasename);
|
||||
})
|
||||
.filter(exclude(undefined))[0];
|
||||
|
||||
if (themeSrcDirBasename === undefined) {
|
||||
if (fs.existsSync(pathJoin(srcDirPath, "login")) || fs.existsSync(pathJoin(srcDirPath, "account"))) {
|
||||
return { "themeSrcDirPath": srcDirPath };
|
||||
}
|
||||
return { "themeSrcDirPath": undefined };
|
||||
}
|
||||
|
||||
return { themeSrcDirPath };
|
||||
}
|
@ -1,27 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
|
||||
import { keycloakThemeEmailDirPath } from "./keycloakify";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { transformCodebase } from "./tools/transformCodebase";
|
||||
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
import * as fs from "fs";
|
||||
import { getCliOptions } from "./tools/cliOptions";
|
||||
import { getLogger } from "./tools/logger";
|
||||
import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
|
||||
|
||||
(async () => {
|
||||
export function getEmailThemeSrcDirPath() {
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath();
|
||||
|
||||
const emailThemeSrcDirPath = themeSrcDirPath === undefined ? undefined : pathJoin(themeSrcDirPath, "email");
|
||||
|
||||
return { emailThemeSrcDirPath };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { isSilent } = getCliOptions(process.argv.slice(2));
|
||||
const logger = getLogger({ isSilent });
|
||||
|
||||
if (fs.existsSync(keycloakThemeEmailDirPath)) {
|
||||
logger.warn(`There is already a ${pathRelative(process.cwd(), keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
|
||||
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath();
|
||||
|
||||
if (emailThemeSrcDirPath === undefined) {
|
||||
logger.warn("Couldn't locate your theme source directory");
|
||||
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(emailThemeSrcDirPath)) {
|
||||
logger.warn(`There is already a ${pathRelative(process.cwd(), emailThemeSrcDirPath)} directory in your project. Aborting.`);
|
||||
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const { keycloakVersion } = await promptKeycloakVersion();
|
||||
|
||||
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
|
||||
const builtinKeycloakThemeTmpDirPath = pathJoin(emailThemeSrcDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
|
||||
|
||||
await downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
@ -31,18 +47,20 @@ import { getLogger } from "./tools/logger";
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
|
||||
"destDirPath": keycloakThemeEmailDirPath
|
||||
"destDirPath": emailThemeSrcDirPath
|
||||
});
|
||||
|
||||
{
|
||||
const themePropertyFilePath = pathJoin(keycloakThemeEmailDirPath, "theme.properties");
|
||||
const themePropertyFilePath = pathJoin(emailThemeSrcDirPath, "theme.properties");
|
||||
|
||||
fs.writeFileSync(themePropertyFilePath, Buffer.from(`parent=base\n${fs.readFileSync(themePropertyFilePath).toString("utf8")}`, "utf8"));
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`${pathRelative(process.cwd(), keycloakThemeEmailDirPath)} ready to be customized, feel free to remove every file you do not customize`
|
||||
);
|
||||
logger.log(`${pathRelative(process.cwd(), emailThemeSrcDirPath)} ready to be customized, feel free to remove every file you do not customize`);
|
||||
|
||||
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
|
||||
})();
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ type ParsedPackageJson = {
|
||||
artifactId?: string;
|
||||
groupId?: string;
|
||||
bundler?: Bundler;
|
||||
keycloakVersionDefaultAssets?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@ -38,7 +39,8 @@ const zParsedPackageJson = z.object({
|
||||
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
|
||||
"artifactId": z.string().optional(),
|
||||
"groupId": z.string().optional(),
|
||||
"bundler": z.enum(bundlers).optional()
|
||||
"bundler": z.enum(bundlers).optional(),
|
||||
"keycloakVersionDefaultAssets": z.string().optional()
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
@ -59,6 +61,7 @@ export namespace BuildOptions {
|
||||
groupId: string;
|
||||
artifactId: string;
|
||||
bundler: Bundler;
|
||||
keycloakVersionDefaultAssets: string;
|
||||
};
|
||||
|
||||
export type Standalone = Common & {
|
||||
@ -125,7 +128,8 @@ export function readBuildOptions(params: {
|
||||
const common: BuildOptions.Common = (() => {
|
||||
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
|
||||
|
||||
const { extraPages, extraLoginPages, extraAccountPages, extraThemeProperties, groupId, artifactId, bundler } = keycloakify ?? {};
|
||||
const { extraPages, extraLoginPages, extraAccountPages, extraThemeProperties, groupId, artifactId, bundler, keycloakVersionDefaultAssets } =
|
||||
keycloakify ?? {};
|
||||
|
||||
const themeName = name
|
||||
.replace(/^@(.*)/, "$1")
|
||||
@ -167,7 +171,8 @@ export function readBuildOptions(params: {
|
||||
"extraLoginPages": [...(extraPages ?? []), ...(extraLoginPages ?? [])],
|
||||
extraAccountPages,
|
||||
extraThemeProperties,
|
||||
isSilent
|
||||
isSilent,
|
||||
"keycloakVersionDefaultAssets": keycloakVersionDefaultAssets ?? "11.0.3"
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -10,7 +10,6 @@ import { isInside } from "../tools/isInside";
|
||||
import type { BuildOptions } from "./BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import { getLogger } from "../tools/logger";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
@ -56,13 +55,11 @@ export namespace BuildOptionsLike {
|
||||
export async function generateKeycloakThemeResources(params: {
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
keycloakThemeEmailDirPath: string;
|
||||
emailThemeSrcDirPath: string | undefined;
|
||||
keycloakVersion: string;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}): Promise<{ doBundlesEmailTemplate: boolean }> {
|
||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
|
||||
|
||||
const logger = getLogger({ isSilent: buildOptions.isSilent });
|
||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, emailThemeSrcDirPath, keycloakVersion, buildOptions } = params;
|
||||
|
||||
const getThemeDirPath = (themeType: ThemeType | "email") =>
|
||||
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
|
||||
@ -228,13 +225,7 @@ export async function generateKeycloakThemeResources(params: {
|
||||
let doBundlesEmailTemplate: boolean;
|
||||
|
||||
email: {
|
||||
if (!fs.existsSync(keycloakThemeEmailDirPath)) {
|
||||
logger.log(
|
||||
[
|
||||
`Not bundling email template because ${pathBasename(keycloakThemeEmailDirPath)} does not exist`,
|
||||
`To start customizing the email template, run: 👉 npx create-keycloak-email-directory 👈`
|
||||
].join("\n")
|
||||
);
|
||||
if (emailThemeSrcDirPath === undefined) {
|
||||
doBundlesEmailTemplate = false;
|
||||
break email;
|
||||
}
|
||||
@ -242,7 +233,7 @@ export async function generateKeycloakThemeResources(params: {
|
||||
doBundlesEmailTemplate = true;
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": keycloakThemeEmailDirPath,
|
||||
"srcDirPath": emailThemeSrcDirPath,
|
||||
"destDirPath": getThemeDirPath("email")
|
||||
});
|
||||
}
|
||||
|
@ -9,12 +9,12 @@ import { getLogger } from "../tools/logger";
|
||||
import { getCliOptions } from "../tools/cliOptions";
|
||||
import jar from "../tools/jar";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import { Equals } from "tsafe";
|
||||
import { getEmailThemeSrcDirPath } from "../initialize-email-theme";
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
|
||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||
export const keycloakThemeEmailDirPath = pathJoin(reactProjectDirPath, "src", "keycloak-theme", "email");
|
||||
|
||||
export async function main() {
|
||||
const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
|
||||
@ -38,13 +38,18 @@ export async function main() {
|
||||
|
||||
const { doBundlesEmailTemplate } = await generateKeycloakThemeResources({
|
||||
keycloakThemeBuildingDirPath,
|
||||
keycloakThemeEmailDirPath,
|
||||
"emailThemeSrcDirPath": (() => {
|
||||
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath();
|
||||
|
||||
if (emailThemeSrcDirPath === undefined || !fs.existsSync(emailThemeSrcDirPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return emailThemeSrcDirPath;
|
||||
})(),
|
||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||
buildOptions,
|
||||
//We have to leave it at that otherwise we break our default theme.
|
||||
//Problem is that we can`t guarantee that the the old resources
|
||||
//will still be available on the newer keycloak version.
|
||||
"keycloakVersion": "11.0.3"
|
||||
"keycloakVersion": buildOptions.keycloakVersionDefaultAssets
|
||||
});
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
|
@ -7,7 +7,7 @@ setupSampleReactProject();
|
||||
generateKeycloakThemeResources({
|
||||
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
|
||||
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
||||
"keycloakThemeEmailDirPath": pathJoin(sampleReactProjectDirPath, "keycloak_email"),
|
||||
"emailThemeSrcDirPath": undefined,
|
||||
"keycloakVersion": "11.0.3",
|
||||
"buildOptions": {
|
||||
"themeName": "keycloakify-demo-app",
|
||||
|
Reference in New Issue
Block a user