Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
9f001f1521 | |||
368e3a32c5 | |||
002e3d4b3d | |||
f94f9b51c9 | |||
055b15bd46 | |||
0e70b0b0de | |||
8faf9a3eed | |||
075d9f9de5 | |||
840079be32 | |||
50ae962f09 | |||
61aa1f9896 |
@ -336,6 +336,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bacongobbler",
|
||||
"name": "Matthew Fisher",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1360539?v=4",
|
||||
"profile": "https://blog.bacongobbler.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@ -170,6 +170,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "11.8.16",
|
||||
"version": "11.8.20",
|
||||
"description": "Framework to create custom Keycloak UIs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -20,7 +20,7 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "add-story",
|
||||
buildContext
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "copy-keycloak-resources-to-public",
|
||||
buildContext
|
||||
});
|
||||
|
@ -22,7 +22,7 @@ import { runPrettier, getIsPrettierAvailable } from "./tools/runPrettier";
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "eject-page",
|
||||
buildContext
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath"
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "initialize-account-theme",
|
||||
buildContext
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import { command as updateKcGenCommand } from "./update-kc-gen";
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "initialize-admin-theme",
|
||||
buildContext
|
||||
});
|
||||
|
@ -17,8 +17,8 @@ import chalk from "chalk";
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
commandName: "initialize-account-theme",
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "initialize-email-theme",
|
||||
buildContext
|
||||
});
|
||||
|
||||
|
@ -190,7 +190,7 @@ function decodeHtmlEntities(htmlStr){
|
||||
<#-- https://github.com/keycloakify/keycloakify/discussions/406#discussioncomment-7514787 -->
|
||||
key == "loginAction" &&
|
||||
areSamePath(path, ["url"]) &&
|
||||
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl"]?seq_contains(xKeycloakify.pageId) &&
|
||||
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl", "frontchannel-logout.ftl"]?seq_contains(xKeycloakify.pageId) &&
|
||||
!(auth?has_content && auth.showTryAnotherWayLink())
|
||||
) || (
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/362 -->
|
||||
|
@ -13,13 +13,15 @@ import * as fs from "fs";
|
||||
|
||||
assert<Equals<ApiVersion, "v1">>();
|
||||
|
||||
export function maybeDelegateCommandToCustomHandler(params: {
|
||||
export async function maybeDelegateCommandToCustomHandler(params: {
|
||||
commandName: CommandName;
|
||||
buildContext: BuildContext;
|
||||
}): { hasBeenHandled: boolean } {
|
||||
}): Promise<{ hasBeenHandled: boolean }> {
|
||||
const { commandName, buildContext } = params;
|
||||
|
||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
||||
const nodeModulesBinDirPath = await getNodeModulesBinDirPath({
|
||||
packageJsonFilePath: buildContext.packageJsonFilePath
|
||||
});
|
||||
|
||||
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
||||
return { hasBeenHandled: false };
|
||||
|
@ -1,10 +1,29 @@
|
||||
import { sep as pathSep } from "path";
|
||||
import { sep as pathSep, dirname as pathDirname, join as pathJoin } from "path";
|
||||
import { getThisCodebaseRootDirPath } from "./getThisCodebaseRootDirPath";
|
||||
import { getInstalledModuleDirPath } from "./getInstalledModuleDirPath";
|
||||
import { existsAsync } from "./fs.existsAsync";
|
||||
import { z } from "zod";
|
||||
import * as fs from "fs/promises";
|
||||
import { assert, is, type Equals } from "tsafe/assert";
|
||||
import { id } from "tsafe/id";
|
||||
|
||||
let cache: string | undefined = undefined;
|
||||
let cache_bestEffort: string | undefined = undefined;
|
||||
|
||||
export function getNodeModulesBinDirPath() {
|
||||
if (cache !== undefined) {
|
||||
return cache;
|
||||
/** NOTE: Careful, this function can fail when the binary
|
||||
* Used is not in the node_modules directory of the project
|
||||
* (for example when running tests with vscode extension we'll get
|
||||
* '/Users/dylan/.vscode/extensions/vitest.explorer-1.16.0/dist/worker.js'
|
||||
*
|
||||
* instead of
|
||||
* '/Users/joseph/.nvm/versions/node/v22.12.0/bin/node'
|
||||
* or
|
||||
* '/Users/joseph/github/keycloakify-starter/node_modules/.bin/vite'
|
||||
*
|
||||
* as the value of process.argv[1]
|
||||
*/
|
||||
function getNodeModulesBinDirPath_bestEffort() {
|
||||
if (cache_bestEffort !== undefined) {
|
||||
return cache_bestEffort;
|
||||
}
|
||||
|
||||
const binPath = process.argv[1];
|
||||
@ -30,9 +49,122 @@ export function getNodeModulesBinDirPath() {
|
||||
segments.unshift(segment);
|
||||
}
|
||||
|
||||
if (!foundNodeModules) {
|
||||
throw new Error(`Could not find node_modules in path ${binPath}`);
|
||||
}
|
||||
|
||||
const nodeModulesBinDirPath = segments.join(pathSep);
|
||||
|
||||
cache = nodeModulesBinDirPath;
|
||||
cache_bestEffort = nodeModulesBinDirPath;
|
||||
|
||||
return nodeModulesBinDirPath;
|
||||
}
|
||||
|
||||
let cache_withPackageJsonFileDirPath:
|
||||
| { packageJsonFilePath: string; nodeModulesBinDirPath: string }
|
||||
| undefined = undefined;
|
||||
|
||||
async function getNodeModulesBinDirPath_withPackageJsonFileDirPath(params: {
|
||||
packageJsonFilePath: string;
|
||||
}): Promise<string> {
|
||||
const { packageJsonFilePath } = params;
|
||||
|
||||
use_cache: {
|
||||
if (cache_withPackageJsonFileDirPath === undefined) {
|
||||
break use_cache;
|
||||
}
|
||||
|
||||
if (
|
||||
cache_withPackageJsonFileDirPath.packageJsonFilePath !== packageJsonFilePath
|
||||
) {
|
||||
cache_withPackageJsonFileDirPath = undefined;
|
||||
break use_cache;
|
||||
}
|
||||
|
||||
return cache_withPackageJsonFileDirPath.nodeModulesBinDirPath;
|
||||
}
|
||||
|
||||
// [...]node_modules/keycloakify
|
||||
const installedModuleDirPath = await getInstalledModuleDirPath({
|
||||
// Here it will always be "keycloakify" but since we are in tools/ we make something generic
|
||||
moduleName: await (async () => {
|
||||
type ParsedPackageJson = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
const zParsedPackageJson = (() => {
|
||||
type TargetType = ParsedPackageJson;
|
||||
|
||||
const zTargetType = z.object({
|
||||
name: z.string()
|
||||
});
|
||||
|
||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>;
|
||||
|
||||
return id<z.ZodType<TargetType>>(zTargetType);
|
||||
})();
|
||||
|
||||
const parsedPackageJson = JSON.parse(
|
||||
(
|
||||
await fs.readFile(
|
||||
pathJoin(getThisCodebaseRootDirPath(), "package.json")
|
||||
)
|
||||
).toString("utf8")
|
||||
);
|
||||
|
||||
zParsedPackageJson.parse(parsedPackageJson);
|
||||
|
||||
assert(is<ParsedPackageJson>(parsedPackageJson));
|
||||
|
||||
return parsedPackageJson.name;
|
||||
})(),
|
||||
packageJsonDirPath: pathDirname(packageJsonFilePath)
|
||||
});
|
||||
|
||||
const segments = installedModuleDirPath.split(pathSep);
|
||||
|
||||
while (true) {
|
||||
const segment = segments.pop();
|
||||
|
||||
if (segment === undefined) {
|
||||
throw new Error(
|
||||
`Could not find .bin directory relative to ${packageJsonFilePath}`
|
||||
);
|
||||
}
|
||||
|
||||
if (segment !== "node_modules") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const candidate = pathJoin(segments.join(pathSep), segment, ".bin");
|
||||
|
||||
if (!(await existsAsync(candidate))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cache_withPackageJsonFileDirPath = {
|
||||
packageJsonFilePath,
|
||||
nodeModulesBinDirPath: candidate
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return cache_withPackageJsonFileDirPath.nodeModulesBinDirPath;
|
||||
}
|
||||
|
||||
export function getNodeModulesBinDirPath(params: {
|
||||
packageJsonFilePath: string;
|
||||
}): Promise<string>;
|
||||
export function getNodeModulesBinDirPath(params: {
|
||||
packageJsonFilePath: undefined;
|
||||
}): string;
|
||||
export function getNodeModulesBinDirPath(params: {
|
||||
packageJsonFilePath: string | undefined;
|
||||
}): string | Promise<string> {
|
||||
const { packageJsonFilePath } = params ?? {};
|
||||
|
||||
return packageJsonFilePath === undefined
|
||||
? getNodeModulesBinDirPath_bestEffort()
|
||||
: getNodeModulesBinDirPath_withPackageJsonFileDirPath({ packageJsonFilePath });
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ export async function getIsPrettierAvailable(): Promise<boolean> {
|
||||
return getIsPrettierAvailable.cache;
|
||||
}
|
||||
|
||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath({
|
||||
packageJsonFilePath: undefined
|
||||
});
|
||||
|
||||
const prettierBinPath = pathJoin(nodeModulesBinDirPath, "prettier");
|
||||
|
||||
@ -51,7 +53,7 @@ export async function getPrettier(): Promise<PrettierAndConfigHash> {
|
||||
// We make sure to only do that when linking, otherwise we import properly.
|
||||
if (readThisNpmPackageVersion().startsWith("0.0.0")) {
|
||||
eval(
|
||||
`${symToStr({ prettier })} = require("${pathResolve(pathJoin(getNodeModulesBinDirPath(), "..", "prettier"))}")`
|
||||
`${symToStr({ prettier })} = require("${pathResolve(pathJoin(getNodeModulesBinDirPath({ packageJsonFilePath: undefined }), "..", "prettier"))}")`
|
||||
);
|
||||
|
||||
assert(!is<undefined>(prettier));
|
||||
@ -64,7 +66,7 @@ export async function getPrettier(): Promise<PrettierAndConfigHash> {
|
||||
|
||||
const configHash = await (async () => {
|
||||
const configFilePath = await prettier.resolveConfigFile(
|
||||
pathJoin(getNodeModulesBinDirPath(), "..")
|
||||
pathJoin(getNodeModulesBinDirPath({ packageJsonFilePath: undefined }), "..")
|
||||
);
|
||||
|
||||
if (configFilePath === null) {
|
||||
|
@ -19,7 +19,7 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
await command({ buildContext });
|
||||
}
|
||||
|
||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
||||
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||
commandName: "update-kc-gen",
|
||||
buildContext
|
||||
});
|
||||
|
@ -217,6 +217,25 @@ export function createGetI18n<
|
||||
return enabledLanguages;
|
||||
})();
|
||||
|
||||
// See: https://github.com/keycloak/keycloak/issues/38029
|
||||
patch_keycloak_issue_38029: {
|
||||
const enabledLanguage_current = enabledLanguages.find(({ languageTag }) => languageTag === currentLanguage.languageTag);
|
||||
|
||||
assert(enabledLanguage_current !== undefined);
|
||||
|
||||
if (!enabledLanguage_current.href.includes("kc_locale=")) {
|
||||
// NOTE: Probably a mock
|
||||
break patch_keycloak_issue_38029;
|
||||
}
|
||||
|
||||
// NOTE: Best effort, we don't wait for it to be done
|
||||
// and we don't handle errors
|
||||
fetch(enabledLanguage_current.href).then(
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
|
||||
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({
|
||||
themeName: kcContext.themeName,
|
||||
messages_themeDefined:
|
||||
|
Reference in New Issue
Block a user