Merge pull request #683 from keycloakify/feat_custom_handler
Feat custom handler
This commit is contained in:
commit
7d818f217a
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "11.2.10",
|
"version": "11.3.0-rc.7",
|
||||||
"description": "Framework to create custom Keycloak UIs",
|
"description": "Framework to create custom Keycloak UIs",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -38,12 +38,14 @@
|
|||||||
"dist/",
|
"dist/",
|
||||||
"!dist/tsconfig.tsbuildinfo",
|
"!dist/tsconfig.tsbuildinfo",
|
||||||
"!dist/bin/",
|
"!dist/bin/",
|
||||||
|
"dist/bin/**/*.d.ts",
|
||||||
"dist/bin/main.js",
|
"dist/bin/main.js",
|
||||||
"dist/bin/*.index.js",
|
"dist/bin/*.index.js",
|
||||||
"dist/bin/*.node",
|
"dist/bin/*.node",
|
||||||
"dist/bin/shared/constants.js",
|
"dist/bin/shared/constants.js",
|
||||||
"dist/bin/shared/*.d.ts",
|
"dist/bin/shared/constants.js.map",
|
||||||
"dist/bin/shared/*.js.map",
|
"dist/bin/shared/customHandler.js",
|
||||||
|
"dist/bin/shared/customHandler.js.map",
|
||||||
"!dist/vite-plugin/",
|
"!dist/vite-plugin/",
|
||||||
"dist/vite-plugin/index.js",
|
"dist/vite-plugin/index.js",
|
||||||
"dist/vite-plugin/index.d.ts",
|
"dist/vite-plugin/index.d.ts",
|
||||||
|
@ -13,16 +13,11 @@ import * as fs from "fs";
|
|||||||
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
|
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
|
||||||
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
||||||
import { assert, Equals } from "tsafe/assert";
|
import { assert, Equals } from "tsafe/assert";
|
||||||
import type { CliCommandOptions } from "./main";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import { getBuildContext } from "./shared/buildContext";
|
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({
|
|
||||||
cliCommandOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(chalk.cyan("Theme type:"));
|
console.log(chalk.cyan("Theme type:"));
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic";
|
import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic";
|
||||||
import { getBuildContext } from "./shared/buildContext";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import type { CliCommandOptions } from "./main";
|
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
maybeDelegateCommandToCustomHandler({
|
||||||
|
commandName: "copy-keycloak-resources-to-public",
|
||||||
|
buildContext
|
||||||
|
});
|
||||||
|
|
||||||
copyKeycloakResourcesToPublic({
|
copyKeycloakResourcesToPublic({
|
||||||
buildContext
|
buildContext
|
||||||
|
@ -20,16 +20,11 @@ import {
|
|||||||
} from "path";
|
} from "path";
|
||||||
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
||||||
import { assert, Equals } from "tsafe/assert";
|
import { assert, Equals } from "tsafe/assert";
|
||||||
import type { CliCommandOptions } from "./main";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import { getBuildContext } from "./shared/buildContext";
|
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({
|
|
||||||
cliCommandOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(chalk.cyan("Theme type:"));
|
console.log(chalk.cyan("Theme type:"));
|
||||||
|
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
import { getBuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import type { CliCommandOptions } from "../main";
|
|
||||||
import cliSelect from "cli-select";
|
import cliSelect from "cli-select";
|
||||||
import child_process from "child_process";
|
import child_process from "child_process";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
import { join as pathJoin, relative as pathRelative } from "path";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig";
|
import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig";
|
||||||
import { generateKcGenTs } from "../shared/generateKcGenTs";
|
import { command as updateKcGenCommand } from "../update-kc-gen";
|
||||||
|
import { maybeDelegateCommandToCustomHandler } from "../shared/customHandler_delegate";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
maybeDelegateCommandToCustomHandler({
|
||||||
|
commandName: "initialize-account-theme",
|
||||||
|
buildContext
|
||||||
|
});
|
||||||
|
|
||||||
const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account");
|
const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account");
|
||||||
|
|
||||||
@ -97,7 +100,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
updateAccountThemeImplementationInConfig({ buildContext, accountThemeType });
|
updateAccountThemeImplementationInConfig({ buildContext, accountThemeType });
|
||||||
|
|
||||||
await generateKcGenTs({
|
await updateKcGenCommand({
|
||||||
buildContext: {
|
buildContext: {
|
||||||
...buildContext,
|
...buildContext,
|
||||||
implementedThemeTypes: {
|
implementedThemeTypes: {
|
||||||
|
@ -8,12 +8,14 @@ import { id } from "tsafe/id";
|
|||||||
|
|
||||||
export type BuildContextLike = {
|
export type BuildContextLike = {
|
||||||
bundler: BuildContext["bundler"];
|
bundler: BuildContext["bundler"];
|
||||||
|
projectDirPath: string;
|
||||||
|
packageJsonFilePath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export function updateAccountThemeImplementationInConfig(params: {
|
export function updateAccountThemeImplementationInConfig(params: {
|
||||||
buildContext: BuildContext;
|
buildContext: BuildContextLike;
|
||||||
accountThemeType: "Single-Page" | "Multi-Page";
|
accountThemeType: "Single-Page" | "Multi-Page";
|
||||||
}) {
|
}) {
|
||||||
const { buildContext, accountThemeType } = params;
|
const { buildContext, accountThemeType } = params;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
import { join as pathJoin, relative as pathRelative } from "path";
|
||||||
import { transformCodebase } from "./tools/transformCodebase";
|
import { transformCodebase } from "./tools/transformCodebase";
|
||||||
import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
|
import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
|
||||||
import { getBuildContext } from "./shared/buildContext";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import type { CliCommandOptions } from "./main";
|
|
||||||
import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive";
|
import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive";
|
||||||
|
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
maybeDelegateCommandToCustomHandler({
|
||||||
|
commandName: "initialize-email-theme",
|
||||||
|
buildContext
|
||||||
|
});
|
||||||
|
|
||||||
const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
|
const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
|
||||||
|
|
||||||
|
@ -2,19 +2,16 @@ import { generateResources } from "./generateResources";
|
|||||||
import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
|
import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { getBuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import { VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES } from "../shared/constants";
|
import { VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES } from "../shared/constants";
|
||||||
import { buildJars } from "./buildJars";
|
import { buildJars } from "./buildJars";
|
||||||
import type { CliCommandOptions } from "../main";
|
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import { rmSync } from "../tools/fs.rmSync";
|
import { rmSync } from "../tools/fs.rmSync";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
|
||||||
|
|
||||||
exit_if_maven_not_installed: {
|
exit_if_maven_not_installed: {
|
||||||
let commandOutput: Buffer | undefined = undefined;
|
let commandOutput: Buffer | undefined = undefined;
|
||||||
|
@ -4,8 +4,9 @@ import { termost } from "termost";
|
|||||||
import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx";
|
import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx";
|
||||||
|
import { getBuildContext } from "./shared/buildContext";
|
||||||
|
|
||||||
export type CliCommandOptions = {
|
type CliCommandOptions = {
|
||||||
projectDirPath: string | undefined;
|
projectDirPath: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,10 +70,10 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./keycloakify");
|
const { command } = await import("./keycloakify");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,10 +131,13 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath, keycloakVersion, port, realmJsonFilePath }) => {
|
||||||
const { command } = await import("./start-keycloak");
|
const { command } = await import("./start-keycloak");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({
|
||||||
|
buildContext: getBuildContext({ projectDirPath }),
|
||||||
|
cliCommandOptions: { keycloakVersion, port, realmJsonFilePath }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -144,10 +148,10 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./eject-page");
|
const { command } = await import("./eject-page");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -158,24 +162,24 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./add-story");
|
const { command } = await import("./add-story");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
name: "initialize-email-theme",
|
name: "initialize-login-theme",
|
||||||
description: "Initialize an email theme."
|
description: "Initialize an email theme."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./initialize-email-theme");
|
const { command } = await import("./initialize-email-theme");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -186,10 +190,10 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./initialize-account-theme");
|
const { command } = await import("./initialize-account-theme");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -201,10 +205,10 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./copy-keycloak-resources-to-public");
|
const { command } = await import("./copy-keycloak-resources-to-public");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -216,10 +220,10 @@ program
|
|||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async cliCommandOptions => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./update-kc-gen");
|
const { command } = await import("./update-kc-gen");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
dirname as pathDirname
|
dirname as pathDirname
|
||||||
} from "path";
|
} from "path";
|
||||||
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
||||||
import type { CliCommandOptions } from "../main";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
@ -24,7 +23,7 @@ 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 chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
import { getProxyFetchOptions, type FetchOptionsLike } from "../tools/fetchProxyOptions";
|
||||||
import { is } from "tsafe/is";
|
import { is } from "tsafe/is";
|
||||||
|
|
||||||
export type BuildContext = {
|
export type BuildContext = {
|
||||||
@ -43,7 +42,7 @@ export type BuildContext = {
|
|||||||
* In this case the urlPathname will be "/my-app/" */
|
* In this case the urlPathname will be "/my-app/" */
|
||||||
urlPathname: string | undefined;
|
urlPathname: string | undefined;
|
||||||
assetsDirPath: string;
|
assetsDirPath: string;
|
||||||
fetchOptions: ProxyFetchOptions;
|
fetchOptions: FetchOptionsLike;
|
||||||
kcContextExclusionsFtlCode: string | undefined;
|
kcContextExclusionsFtlCode: string | undefined;
|
||||||
environmentVariables: { name: string; default: string }[];
|
environmentVariables: { name: string; default: string }[];
|
||||||
themeSrcDirPath: string;
|
themeSrcDirPath: string;
|
||||||
@ -129,14 +128,12 @@ export type ResolvedViteConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getBuildContext(params: {
|
export function getBuildContext(params: {
|
||||||
cliCommandOptions: CliCommandOptions;
|
projectDirPath: string | undefined;
|
||||||
}): BuildContext {
|
}): BuildContext {
|
||||||
const { cliCommandOptions } = params;
|
|
||||||
|
|
||||||
const projectDirPath =
|
const projectDirPath =
|
||||||
cliCommandOptions.projectDirPath !== undefined
|
params.projectDirPath !== undefined
|
||||||
? getAbsoluteAndInOsFormatPath({
|
? getAbsoluteAndInOsFormatPath({
|
||||||
pathIsh: cliCommandOptions.projectDirPath,
|
pathIsh: params.projectDirPath,
|
||||||
cwd: process.cwd()
|
cwd: process.cwd()
|
||||||
})
|
})
|
||||||
: process.cwd();
|
: process.cwd();
|
||||||
|
@ -71,3 +71,8 @@ export type AccountThemePageId = (typeof ACCOUNT_THEME_PAGE_IDS)[number];
|
|||||||
export const CONTAINER_NAME = "keycloak-keycloakify";
|
export const CONTAINER_NAME = "keycloak-keycloakify";
|
||||||
|
|
||||||
export const FALLBACK_LANGUAGE_TAG = "en";
|
export const FALLBACK_LANGUAGE_TAG = "en";
|
||||||
|
|
||||||
|
export const CUSTOM_HANDLER_ENV_NAMES = {
|
||||||
|
COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME",
|
||||||
|
BUILD_CONTEXT: "KEYCLOAKIFY_BUILD_CONTEXT"
|
||||||
|
};
|
||||||
|
41
src/bin/shared/customHandler.ts
Normal file
41
src/bin/shared/customHandler.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { BuildContext } from "./buildContext";
|
||||||
|
import { CUSTOM_HANDLER_ENV_NAMES } from "./constants";
|
||||||
|
|
||||||
|
export const BIN_NAME = "_keycloakify-custom-handler";
|
||||||
|
|
||||||
|
export const NOT_IMPLEMENTED_EXIT_CODE = 78;
|
||||||
|
|
||||||
|
export type CommandName =
|
||||||
|
| "update-kc-gen"
|
||||||
|
| "eject-page"
|
||||||
|
| "add-story"
|
||||||
|
| "initialize-account-theme"
|
||||||
|
| "initialize-email-theme"
|
||||||
|
| "copy-keycloak-resources-to-public";
|
||||||
|
|
||||||
|
export type ApiVersion = "v1";
|
||||||
|
|
||||||
|
export function readParams(params: { apiVersion: ApiVersion }) {
|
||||||
|
const { apiVersion } = params;
|
||||||
|
|
||||||
|
assert(apiVersion === "v1");
|
||||||
|
|
||||||
|
const commandName = (() => {
|
||||||
|
const envValue = process.env[CUSTOM_HANDLER_ENV_NAMES.COMMAND_NAME];
|
||||||
|
|
||||||
|
assert(envValue !== undefined);
|
||||||
|
|
||||||
|
return envValue as CommandName;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const buildContext = (() => {
|
||||||
|
const envValue = process.env[CUSTOM_HANDLER_ENV_NAMES.BUILD_CONTEXT];
|
||||||
|
|
||||||
|
assert(envValue !== undefined);
|
||||||
|
|
||||||
|
return JSON.parse(envValue) as BuildContext;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return { commandName, buildContext };
|
||||||
|
}
|
46
src/bin/shared/customHandler_delegate.ts
Normal file
46
src/bin/shared/customHandler_delegate.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
import type { BuildContext } from "./buildContext";
|
||||||
|
import { CUSTOM_HANDLER_ENV_NAMES } from "./constants";
|
||||||
|
import {
|
||||||
|
NOT_IMPLEMENTED_EXIT_CODE,
|
||||||
|
type CommandName,
|
||||||
|
BIN_NAME,
|
||||||
|
ApiVersion
|
||||||
|
} from "./customHandler";
|
||||||
|
import * as child_process from "child_process";
|
||||||
|
import { dirname as pathDirname } from "path";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
assert<Equals<ApiVersion, "v1">>();
|
||||||
|
|
||||||
|
export function maybeDelegateCommandToCustomHandler(params: {
|
||||||
|
commandName: CommandName;
|
||||||
|
buildContext: BuildContext;
|
||||||
|
}) {
|
||||||
|
const { commandName, buildContext } = params;
|
||||||
|
|
||||||
|
if (!fs.readdirSync(pathDirname(process.argv[1])).includes(BIN_NAME)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
child_process.execSync(`npx ${BIN_NAME}`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
[CUSTOM_HANDLER_ENV_NAMES.COMMAND_NAME]: commandName,
|
||||||
|
[CUSTOM_HANDLER_ENV_NAMES.BUILD_CONTEXT]: JSON.stringify(buildContext)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
const status = error.status;
|
||||||
|
|
||||||
|
if (status === NOT_IMPLEMENTED_EXIT_CODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
}
|
@ -1,175 +0,0 @@
|
|||||||
import { assert, type Equals } from "tsafe/assert";
|
|
||||||
import { id } from "tsafe/id";
|
|
||||||
import type { BuildContext } from "./buildContext";
|
|
||||||
import * as fs from "fs/promises";
|
|
||||||
import { join as pathJoin } from "path";
|
|
||||||
import { existsAsync } from "../tools/fs.existsAsync";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export type BuildContextLike = {
|
|
||||||
projectDirPath: string;
|
|
||||||
themeNames: string[];
|
|
||||||
environmentVariables: { name: string; default: string }[];
|
|
||||||
themeSrcDirPath: string;
|
|
||||||
implementedThemeTypes: Pick<
|
|
||||||
BuildContext["implementedThemeTypes"],
|
|
||||||
"login" | "account"
|
|
||||||
>;
|
|
||||||
packageJsonFilePath: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
|
||||||
|
|
||||||
export async function generateKcGenTs(params: {
|
|
||||||
buildContext: BuildContextLike;
|
|
||||||
}): Promise<void> {
|
|
||||||
const { buildContext } = params;
|
|
||||||
|
|
||||||
const isReactProject: boolean = await (async () => {
|
|
||||||
const parsedPackageJson = await (async () => {
|
|
||||||
type ParsedPackageJson = {
|
|
||||||
dependencies?: Record<string, string>;
|
|
||||||
devDependencies?: Record<string, string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const zParsedPackageJson = (() => {
|
|
||||||
type TargetType = ParsedPackageJson;
|
|
||||||
|
|
||||||
const zTargetType = z.object({
|
|
||||||
dependencies: z.record(z.string()).optional(),
|
|
||||||
devDependencies: z.record(z.string()).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
|
||||||
|
|
||||||
return id<z.ZodType<TargetType>>(zTargetType);
|
|
||||||
})();
|
|
||||||
|
|
||||||
return zParsedPackageJson.parse(
|
|
||||||
JSON.parse(
|
|
||||||
(await fs.readFile(buildContext.packageJsonFilePath)).toString("utf8")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
|
|
||||||
return (
|
|
||||||
{
|
|
||||||
...parsedPackageJson.dependencies,
|
|
||||||
...parsedPackageJson.devDependencies
|
|
||||||
}.react !== undefined
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
|
|
||||||
const filePath = pathJoin(
|
|
||||||
buildContext.themeSrcDirPath,
|
|
||||||
`kc.gen.ts${isReactProject ? "x" : ""}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentContent = (await existsAsync(filePath))
|
|
||||||
? await fs.readFile(filePath)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented;
|
|
||||||
const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented;
|
|
||||||
|
|
||||||
const newContent = Buffer.from(
|
|
||||||
[
|
|
||||||
`/* prettier-ignore-start */`,
|
|
||||||
``,
|
|
||||||
`/* eslint-disable */`,
|
|
||||||
``,
|
|
||||||
`// @ts-nocheck`,
|
|
||||||
``,
|
|
||||||
`// noinspection JSUnusedGlobalSymbols`,
|
|
||||||
``,
|
|
||||||
`// This file is auto-generated by Keycloakify`,
|
|
||||||
``,
|
|
||||||
isReactProject && `import { lazy, Suspense, type ReactNode } from "react";`,
|
|
||||||
``,
|
|
||||||
`export type ThemeName = ${buildContext.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`,
|
|
||||||
``,
|
|
||||||
`export const themeNames: ThemeName[] = [${buildContext.themeNames.map(themeName => `"${themeName}"`).join(", ")}];`,
|
|
||||||
``,
|
|
||||||
`export type KcEnvName = ${buildContext.environmentVariables.length === 0 ? "never" : buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(" | ")};`,
|
|
||||||
``,
|
|
||||||
`export const kcEnvNames: KcEnvName[] = [${buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(", ")}];`,
|
|
||||||
``,
|
|
||||||
`export const kcEnvDefaults: Record<KcEnvName, string> = ${JSON.stringify(
|
|
||||||
Object.fromEntries(
|
|
||||||
buildContext.environmentVariables.map(
|
|
||||||
({ name, default: defaultValue }) => [name, defaultValue]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)};`,
|
|
||||||
``,
|
|
||||||
`export type KcContext =`,
|
|
||||||
hasLoginTheme && ` | import("./login/KcContext").KcContext`,
|
|
||||||
hasAccountTheme && ` | import("./account/KcContext").KcContext`,
|
|
||||||
` ;`,
|
|
||||||
``,
|
|
||||||
`declare global {`,
|
|
||||||
` interface Window {`,
|
|
||||||
` kcContext?: KcContext;`,
|
|
||||||
` }`,
|
|
||||||
`}`,
|
|
||||||
``,
|
|
||||||
...(!isReactProject
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
hasLoginTheme &&
|
|
||||||
`export const KcLoginPage = lazy(() => import("./login/KcPage"));`,
|
|
||||||
hasAccountTheme &&
|
|
||||||
`export const KcAccountPage = lazy(() => import("./account/KcPage"));`,
|
|
||||||
``,
|
|
||||||
`export function KcPage(`,
|
|
||||||
` props: {`,
|
|
||||||
` kcContext: KcContext;`,
|
|
||||||
` fallback?: ReactNode;`,
|
|
||||||
` }`,
|
|
||||||
`) {`,
|
|
||||||
` const { kcContext, fallback } = props;`,
|
|
||||||
` return (`,
|
|
||||||
` <Suspense fallback={fallback}>`,
|
|
||||||
` {(() => {`,
|
|
||||||
` switch (kcContext.themeType) {`,
|
|
||||||
hasLoginTheme &&
|
|
||||||
` case "login": return <KcLoginPage kcContext={kcContext} />;`,
|
|
||||||
hasAccountTheme &&
|
|
||||||
` case "account": return <KcAccountPage kcContext={kcContext} />;`,
|
|
||||||
` }`,
|
|
||||||
` })()}`,
|
|
||||||
` </Suspense>`,
|
|
||||||
` );`,
|
|
||||||
`}`
|
|
||||||
]),
|
|
||||||
``,
|
|
||||||
`/* prettier-ignore-end */`,
|
|
||||||
``
|
|
||||||
]
|
|
||||||
.filter(item => typeof item === "string")
|
|
||||||
.join("\n"),
|
|
||||||
"utf8"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (currentContent !== undefined && currentContent.equals(newContent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.writeFile(filePath, newContent);
|
|
||||||
|
|
||||||
delete_legacy_file: {
|
|
||||||
if (!isReactProject) {
|
|
||||||
break delete_legacy_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
const legacyFilePath = filePath.replace(/tsx$/, "ts");
|
|
||||||
|
|
||||||
if (!(await existsAsync(legacyFilePath))) {
|
|
||||||
break delete_legacy_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.unlink(legacyFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import { getBuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import { exclude } from "tsafe/exclude";
|
import { exclude } from "tsafe/exclude";
|
||||||
import type { CliCommandOptions as CliCommandOptions_common } from "../main";
|
|
||||||
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
||||||
import { CONTAINER_NAME } from "../shared/constants";
|
import { CONTAINER_NAME } from "../shared/constants";
|
||||||
import { SemVer } from "../tools/SemVer";
|
import { SemVer } from "../tools/SemVer";
|
||||||
@ -29,13 +28,14 @@ import { existsAsync } from "../tools/fs.existsAsync";
|
|||||||
import { rm } from "../tools/fs.rm";
|
import { rm } from "../tools/fs.rm";
|
||||||
import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive";
|
import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive";
|
||||||
|
|
||||||
export type CliCommandOptions = CliCommandOptions_common & {
|
export async function command(params: {
|
||||||
|
buildContext: BuildContext;
|
||||||
|
cliCommandOptions: {
|
||||||
port: number | undefined;
|
port: number | undefined;
|
||||||
keycloakVersion: string | undefined;
|
keycloakVersion: string | undefined;
|
||||||
realmJsonFilePath: string | undefined;
|
realmJsonFilePath: string | undefined;
|
||||||
};
|
};
|
||||||
|
}) {
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
|
||||||
exit_if_docker_not_installed: {
|
exit_if_docker_not_installed: {
|
||||||
let commandOutput: string | undefined = undefined;
|
let commandOutput: string | undefined = undefined;
|
||||||
|
|
||||||
@ -88,9 +88,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { cliCommandOptions } = params;
|
const { cliCommandOptions, buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
|
||||||
|
|
||||||
const { dockerImageTag } = await (async () => {
|
const { dockerImageTag } = await (async () => {
|
||||||
if (cliCommandOptions.keycloakVersion !== undefined) {
|
if (cliCommandOptions.keycloakVersion !== undefined) {
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { type FetchOptions } from "make-fetch-happen";
|
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { exclude } from "tsafe/exclude";
|
import { exclude } from "tsafe/exclude";
|
||||||
|
|
||||||
export type ProxyFetchOptions = Pick<
|
export type FetchOptionsLike = {
|
||||||
FetchOptions,
|
proxy: string | undefined;
|
||||||
"proxy" | "noProxy" | "strictSSL" | "cert" | "ca"
|
noProxy: string | string[];
|
||||||
>;
|
strictSSL: boolean;
|
||||||
|
cert: string | string[] | undefined;
|
||||||
|
ca: string[] | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export function getProxyFetchOptions(params: {
|
export function getProxyFetchOptions(params: {
|
||||||
npmConfigGetCwd: string;
|
npmConfigGetCwd: string;
|
||||||
}): ProxyFetchOptions {
|
}): FetchOptionsLike {
|
||||||
const { npmConfigGetCwd } = params;
|
const { npmConfigGetCwd } = params;
|
||||||
|
|
||||||
const cfg = (() => {
|
const cfg = (() => {
|
||||||
|
@ -1,13 +1,116 @@
|
|||||||
import type { CliCommandOptions } from "./main";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import { getBuildContext } from "./shared/buildContext";
|
import * as fs from "fs/promises";
|
||||||
import { generateKcGenTs } from "./shared/generateKcGenTs";
|
import { join as pathJoin } from "path";
|
||||||
|
import { existsAsync } from "./tools/fs.existsAsync";
|
||||||
|
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||||
|
|
||||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { cliCommandOptions } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const buildContext = getBuildContext({
|
maybeDelegateCommandToCustomHandler({
|
||||||
cliCommandOptions
|
commandName: "update-kc-gen",
|
||||||
|
buildContext
|
||||||
});
|
});
|
||||||
|
|
||||||
await generateKcGenTs({ buildContext });
|
const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`);
|
||||||
|
|
||||||
|
const currentContent = (await existsAsync(filePath))
|
||||||
|
? await fs.readFile(filePath)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented;
|
||||||
|
const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented;
|
||||||
|
|
||||||
|
const newContent = Buffer.from(
|
||||||
|
[
|
||||||
|
`/* prettier-ignore-start */`,
|
||||||
|
``,
|
||||||
|
`/* eslint-disable */`,
|
||||||
|
``,
|
||||||
|
`// @ts-nocheck`,
|
||||||
|
``,
|
||||||
|
`// noinspection JSUnusedGlobalSymbols`,
|
||||||
|
``,
|
||||||
|
`// This file is auto-generated by Keycloakify`,
|
||||||
|
``,
|
||||||
|
`import { lazy, Suspense, type ReactNode } from "react";`,
|
||||||
|
``,
|
||||||
|
`export type ThemeName = ${buildContext.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`,
|
||||||
|
``,
|
||||||
|
`export const themeNames: ThemeName[] = [${buildContext.themeNames.map(themeName => `"${themeName}"`).join(", ")}];`,
|
||||||
|
``,
|
||||||
|
`export type KcEnvName = ${buildContext.environmentVariables.length === 0 ? "never" : buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(" | ")};`,
|
||||||
|
``,
|
||||||
|
`export const kcEnvNames: KcEnvName[] = [${buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(", ")}];`,
|
||||||
|
``,
|
||||||
|
`export const kcEnvDefaults: Record<KcEnvName, string> = ${JSON.stringify(
|
||||||
|
Object.fromEntries(
|
||||||
|
buildContext.environmentVariables.map(
|
||||||
|
({ name, default: defaultValue }) => [name, defaultValue]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)};`,
|
||||||
|
``,
|
||||||
|
`export type KcContext =`,
|
||||||
|
hasLoginTheme && ` | import("./login/KcContext").KcContext`,
|
||||||
|
hasAccountTheme && ` | import("./account/KcContext").KcContext`,
|
||||||
|
` ;`,
|
||||||
|
``,
|
||||||
|
`declare global {`,
|
||||||
|
` interface Window {`,
|
||||||
|
` kcContext?: KcContext;`,
|
||||||
|
` }`,
|
||||||
|
`}`,
|
||||||
|
``,
|
||||||
|
hasLoginTheme &&
|
||||||
|
`export const KcLoginPage = lazy(() => import("./login/KcPage"));`,
|
||||||
|
hasAccountTheme &&
|
||||||
|
`export const KcAccountPage = lazy(() => import("./account/KcPage"));`,
|
||||||
|
``,
|
||||||
|
`export function KcPage(`,
|
||||||
|
` props: {`,
|
||||||
|
` kcContext: KcContext;`,
|
||||||
|
` fallback?: ReactNode;`,
|
||||||
|
` }`,
|
||||||
|
`) {`,
|
||||||
|
` const { kcContext, fallback } = props;`,
|
||||||
|
` return (`,
|
||||||
|
` <Suspense fallback={fallback}>`,
|
||||||
|
` {(() => {`,
|
||||||
|
` switch (kcContext.themeType) {`,
|
||||||
|
hasLoginTheme &&
|
||||||
|
` case "login": return <KcLoginPage kcContext={kcContext} />;`,
|
||||||
|
hasAccountTheme &&
|
||||||
|
` case "account": return <KcAccountPage kcContext={kcContext} />;`,
|
||||||
|
` }`,
|
||||||
|
` })()}`,
|
||||||
|
` </Suspense>`,
|
||||||
|
` );`,
|
||||||
|
`}`,
|
||||||
|
``,
|
||||||
|
`/* prettier-ignore-end */`,
|
||||||
|
``
|
||||||
|
]
|
||||||
|
.filter(item => typeof item === "string")
|
||||||
|
.join("\n"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentContent !== undefined && currentContent.equals(newContent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, newContent);
|
||||||
|
|
||||||
|
delete_legacy_file: {
|
||||||
|
const legacyFilePath = filePath.replace(/tsx$/, "ts");
|
||||||
|
|
||||||
|
if (!(await existsAsync(legacyFilePath))) {
|
||||||
|
break delete_legacy_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.unlink(legacyFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
type ResolvedViteConfig
|
type ResolvedViteConfig
|
||||||
} from "../bin/shared/buildContext";
|
} from "../bin/shared/buildContext";
|
||||||
import MagicString from "magic-string";
|
import MagicString from "magic-string";
|
||||||
import { generateKcGenTs } from "../bin/shared/generateKcGenTs";
|
import { command as updateKcGenCommand } from "../bin/update-kc-gen";
|
||||||
|
|
||||||
export namespace keycloakify {
|
export namespace keycloakify {
|
||||||
export type Params = BuildOptions & {
|
export type Params = BuildOptions & {
|
||||||
@ -122,13 +122,12 @@ export function keycloakify(params: keycloakify.Params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const buildContext = getBuildContext({
|
const buildContext = getBuildContext({
|
||||||
cliCommandOptions: {
|
|
||||||
projectDirPath
|
projectDirPath
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
copyKeycloakResourcesToPublic({ buildContext }),
|
copyKeycloakResourcesToPublic({ buildContext });
|
||||||
await generateKcGenTs({ buildContext });
|
|
||||||
|
await updateKcGenCommand({ buildContext });
|
||||||
},
|
},
|
||||||
transform: (code, id) => {
|
transform: (code, id) => {
|
||||||
assert(command !== undefined);
|
assert(command !== undefined);
|
||||||
|
@ -115,7 +115,6 @@ export const WithFavoritePet: Story = {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const WithNewsletter: Story = {
|
export const WithNewsletter: Story = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<KcPageStory
|
<KcPageStory
|
||||||
@ -132,7 +131,7 @@ export const WithNewsletter: Story = {
|
|||||||
},
|
},
|
||||||
annotations: {
|
annotations: {
|
||||||
inputOptionLabels: {
|
inputOptionLabels: {
|
||||||
"yes": "I want my email inbox filled with spam"
|
yes: "I want my email inbox filled with spam"
|
||||||
},
|
},
|
||||||
inputType: "multiselect-checkboxes"
|
inputType: "multiselect-checkboxes"
|
||||||
},
|
},
|
||||||
@ -140,13 +139,12 @@ export const WithNewsletter: Story = {
|
|||||||
readOnly: false
|
readOnly: false
|
||||||
} satisfies Attribute
|
} satisfies Attribute
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const WithEmailAsUsername: Story = {
|
export const WithEmailAsUsername: Story = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<KcPageStory
|
<KcPageStory
|
||||||
|
Loading…
x
Reference in New Issue
Block a user