Compare commits

..

8 Commits

11 changed files with 125 additions and 113 deletions

View File

@ -43,6 +43,11 @@
Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)
> 📣 **Keycloakify 26 Released**
> Themes built with Keycloakify versions **prior** to Keycloak 26 are **incompatible** with Keycloak 26.
> To ensure compatibility, simply upgrade to the latest Keycloakify version for your major release (v10 or v11) and rebuild your theme.
> No breaking changes have been introduced, but the target version ranges have been updated. For more details, see [this guide](https://docs.keycloakify.dev/targeting-specific-keycloak-versions).
## Sponsors
Friends for the project, we trust and recommend their services.

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "11.3.0",
"version": "11.3.3",
"description": "Framework to create custom Keycloak UIs",
"repository": {
"type": "git",

View File

@ -1,16 +1,96 @@
import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic";
import type { BuildContext } from "./shared/buildContext";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
import { join as pathJoin, dirname as pathDirname } from "path";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "./shared/constants";
import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion";
import * as fs from "fs";
import { rmSync } from "./tools/fs.rmSync";
import type { BuildContext } from "./shared/buildContext";
import { transformCodebase } from "./tools/transformCodebase";
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
maybeDelegateCommandToCustomHandler({
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
commandName: "copy-keycloak-resources-to-public",
buildContext
});
copyKeycloakResourcesToPublic({
buildContext
if (hasBeenHandled) {
return;
}
const destDirPath = pathJoin(
buildContext.publicDirPath,
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
);
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
const keycloakifyBuildinfoRaw = JSON.stringify(
{
keycloakifyVersion: readThisNpmPackageVersion()
},
null,
2
);
skip_if_already_done: {
if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
break skip_if_already_done;
}
const keycloakifyBuildinfoRaw_previousRun = fs
.readFileSync(keycloakifyBuildinfoFilePath)
.toString("utf8");
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
break skip_if_already_done;
}
return;
}
rmSync(destDirPath, { force: true, recursive: true });
// NOTE: To remove in a while, remove the legacy keycloak-resources directory
rmSync(pathJoin(pathDirname(destDirPath), "keycloak-resources"), {
force: true,
recursive: true
});
rmSync(pathJoin(pathDirname(destDirPath), ".keycloakify"), {
force: true,
recursive: true
});
fs.mkdirSync(destDirPath, { recursive: true });
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
transformCodebase({
srcDirPath: pathJoin(
getThisCodebaseRootDirPath(),
"res",
"public",
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
),
destDirPath
});
fs.writeFileSync(
pathJoin(destDirPath, "README.txt"),
Buffer.from(
// prettier-ignore
[
"This directory is only used in dev mode by Keycloakify",
"It won't be included in your final build.",
"Do not modify anything in this directory.",
].join("\n")
)
);
fs.writeFileSync(
keycloakifyBuildinfoFilePath,
Buffer.from(keycloakifyBuildinfoRaw, "utf8")
);
}

View File

@ -22,10 +22,20 @@ import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
import { assert, Equals } from "tsafe/assert";
import type { BuildContext } from "./shared/buildContext";
import chalk from "chalk";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
commandName: "eject-page",
buildContext
});
if (hasBeenHandled) {
return;
}
console.log(chalk.cyan("Theme type:"));
const themeType = await (async () => {
@ -239,12 +249,12 @@ export async function command(params: { buildContext: BuildContext }) {
)} copy pasted from the Keycloakify source code into your project`
);
edit_KcApp: {
edit_KcPage: {
if (
pageIdOrComponent !== templateValue &&
pageIdOrComponent !== userProfileFormFieldsValue
) {
break edit_KcApp;
break edit_KcPage;
}
const kcAppTsxPath = pathJoin(

View File

@ -11,11 +11,15 @@ import { maybeDelegateCommandToCustomHandler } from "../shared/customHandler_del
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
maybeDelegateCommandToCustomHandler({
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
commandName: "initialize-account-theme",
buildContext
});
if (hasBeenHandled) {
return;
}
const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account");
if (

View File

@ -9,11 +9,15 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
maybeDelegateCommandToCustomHandler({
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
commandName: "initialize-email-theme",
buildContext
});
if (hasBeenHandled) {
return;
}
const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
if (

View File

@ -171,7 +171,7 @@ program
program
.command({
name: "initialize-login-theme",
name: "initialize-email-theme",
description: "Initialize an email theme."
})
.task({

View File

@ -1,95 +0,0 @@
import { join as pathJoin, dirname as pathDirname } from "path";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "../shared/constants";
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
import { assert } from "tsafe/assert";
import * as fs from "fs";
import { rmSync } from "../tools/fs.rmSync";
import type { BuildContext } from "./buildContext";
import { transformCodebase } from "../tools/transformCodebase";
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
export type BuildContextLike = {
publicDirPath: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export function copyKeycloakResourcesToPublic(params: {
buildContext: BuildContextLike;
}) {
const { buildContext } = params;
const destDirPath = pathJoin(
buildContext.publicDirPath,
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
);
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
const keycloakifyBuildinfoRaw = JSON.stringify(
{
keycloakifyVersion: readThisNpmPackageVersion()
},
null,
2
);
skip_if_already_done: {
if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
break skip_if_already_done;
}
const keycloakifyBuildinfoRaw_previousRun = fs
.readFileSync(keycloakifyBuildinfoFilePath)
.toString("utf8");
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
break skip_if_already_done;
}
return;
}
rmSync(destDirPath, { force: true, recursive: true });
// NOTE: To remove in a while, remove the legacy keycloak-resources directory
rmSync(pathJoin(pathDirname(destDirPath), "keycloak-resources"), {
force: true,
recursive: true
});
rmSync(pathJoin(pathDirname(destDirPath), ".keycloakify"), {
force: true,
recursive: true
});
fs.mkdirSync(destDirPath, { recursive: true });
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
transformCodebase({
srcDirPath: pathJoin(
getThisCodebaseRootDirPath(),
"res",
"public",
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
),
destDirPath
});
fs.writeFileSync(
pathJoin(destDirPath, "README.txt"),
Buffer.from(
// prettier-ignore
[
"This directory is only used in dev mode by Keycloakify",
"It won't be included in your final build.",
"Do not modify anything in this directory.",
].join("\n")
)
);
fs.writeFileSync(
keycloakifyBuildinfoFilePath,
Buffer.from(keycloakifyBuildinfoRaw, "utf8")
);
}

View File

@ -16,11 +16,11 @@ assert<Equals<ApiVersion, "v1">>();
export function maybeDelegateCommandToCustomHandler(params: {
commandName: CommandName;
buildContext: BuildContext;
}) {
}): { hasBeenHandled: boolean } {
const { commandName, buildContext } = params;
if (!fs.readdirSync(pathDirname(process.argv[1])).includes(BIN_NAME)) {
return;
return { hasBeenHandled: false };
}
try {
@ -36,11 +36,11 @@ export function maybeDelegateCommandToCustomHandler(params: {
const status = error.status;
if (status === NOT_IMPLEMENTED_EXIT_CODE) {
return;
return { hasBeenHandled: false };
}
process.exit(status);
}
process.exit(0);
return { hasBeenHandled: true };
}

View File

@ -7,11 +7,15 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
maybeDelegateCommandToCustomHandler({
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
commandName: "update-kc-gen",
buildContext
});
if (hasBeenHandled) {
return;
}
const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`);
const currentContent = (await existsAsync(filePath))

View File

@ -6,7 +6,7 @@ import {
} from "../bin/shared/constants";
import { id } from "tsafe/id";
import { rm } from "../bin/tools/fs.rm";
import { copyKeycloakResourcesToPublic } from "../bin/shared/copyKeycloakResourcesToPublic";
import { command as copyKeycloakResourcesToPublicCommand } from "../bin/copy-keycloak-resources-to-public";
import { assert } from "tsafe/assert";
import {
getBuildContext,
@ -125,7 +125,7 @@ export function keycloakify(params: keycloakify.Params) {
projectDirPath
});
copyKeycloakResourcesToPublic({ buildContext });
await copyKeycloakResourcesToPublicCommand({ buildContext });
await updateKcGenCommand({ buildContext });
},