diff --git a/package.json b/package.json index 0564ddf0..ae774e39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.10", + "version": "11.3.0-rc.7", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", @@ -38,12 +38,14 @@ "dist/", "!dist/tsconfig.tsbuildinfo", "!dist/bin/", + "dist/bin/**/*.d.ts", "dist/bin/main.js", "dist/bin/*.index.js", "dist/bin/*.node", "dist/bin/shared/constants.js", - "dist/bin/shared/*.d.ts", - "dist/bin/shared/*.js.map", + "dist/bin/shared/constants.js.map", + "dist/bin/shared/customHandler.js", + "dist/bin/shared/customHandler.js.map", "!dist/vite-plugin/", "dist/vite-plugin/index.js", "dist/vite-plugin/index.d.ts", diff --git a/src/bin/add-story.ts b/src/bin/add-story.ts index b3ed5be7..b2be6d65 100644 --- a/src/bin/add-story.ts +++ b/src/bin/add-story.ts @@ -13,16 +13,11 @@ import * as fs from "fs"; import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path"; import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase"; import { assert, Equals } from "tsafe/assert"; -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import chalk from "chalk"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ - cliCommandOptions - }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; console.log(chalk.cyan("Theme type:")); diff --git a/src/bin/copy-keycloak-resources-to-public.ts b/src/bin/copy-keycloak-resources-to-public.ts index b245076a..b48f9d0e 100644 --- a/src/bin/copy-keycloak-resources-to-public.ts +++ b/src/bin/copy-keycloak-resources-to-public.ts @@ -1,11 +1,14 @@ import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic"; -import { getBuildContext } from "./shared/buildContext"; -import type { CliCommandOptions } from "./main"; +import type { BuildContext } from "./shared/buildContext"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; - const buildContext = getBuildContext({ cliCommandOptions }); + maybeDelegateCommandToCustomHandler({ + commandName: "copy-keycloak-resources-to-public", + buildContext + }); copyKeycloakResourcesToPublic({ buildContext diff --git a/src/bin/eject-page.ts b/src/bin/eject-page.ts index 404483ec..8874fd99 100644 --- a/src/bin/eject-page.ts +++ b/src/bin/eject-page.ts @@ -20,16 +20,11 @@ import { } from "path"; import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase"; import { assert, Equals } from "tsafe/assert"; -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import chalk from "chalk"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ - cliCommandOptions - }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; console.log(chalk.cyan("Theme type:")); diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index e15b95ba..e7ae9223 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -1,17 +1,20 @@ -import { getBuildContext } from "../shared/buildContext"; -import type { CliCommandOptions } from "../main"; +import type { BuildContext } from "../shared/buildContext"; import cliSelect from "cli-select"; import child_process from "child_process"; import chalk from "chalk"; import { join as pathJoin, relative as pathRelative } from "path"; import * as fs from "fs"; 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 }) { - const { cliCommandOptions } = params; +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; - const buildContext = getBuildContext({ cliCommandOptions }); + maybeDelegateCommandToCustomHandler({ + commandName: "initialize-account-theme", + buildContext + }); const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account"); @@ -97,7 +100,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions }) updateAccountThemeImplementationInConfig({ buildContext, accountThemeType }); - await generateKcGenTs({ + await updateKcGenCommand({ buildContext: { ...buildContext, implementedThemeTypes: { diff --git a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts index 976beb9a..1c60ef03 100644 --- a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts +++ b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts @@ -8,12 +8,14 @@ import { id } from "tsafe/id"; export type BuildContextLike = { bundler: BuildContext["bundler"]; + projectDirPath: string; + packageJsonFilePath: string; }; assert(); export function updateAccountThemeImplementationInConfig(params: { - buildContext: BuildContext; + buildContext: BuildContextLike; accountThemeType: "Single-Page" | "Multi-Page"; }) { const { buildContext, accountThemeType } = params; diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index f2520645..d5ce0fd8 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -1,15 +1,18 @@ import { join as pathJoin, relative as pathRelative } from "path"; import { transformCodebase } from "./tools/transformCodebase"; import { promptKeycloakVersion } from "./shared/promptKeycloakVersion"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import * as fs from "fs"; -import type { CliCommandOptions } from "./main"; import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; - const buildContext = getBuildContext({ cliCommandOptions }); + maybeDelegateCommandToCustomHandler({ + commandName: "initialize-email-theme", + buildContext + }); const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email"); diff --git a/src/bin/keycloakify/keycloakify.ts b/src/bin/keycloakify/keycloakify.ts index b39dcad8..0d027103 100644 --- a/src/bin/keycloakify/keycloakify.ts +++ b/src/bin/keycloakify/keycloakify.ts @@ -2,19 +2,16 @@ import { generateResources } from "./generateResources"; import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path"; import * as child_process from "child_process"; 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 { buildJars } from "./buildJars"; -import type { CliCommandOptions } from "../main"; import chalk from "chalk"; import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion"; import * as os from "os"; import { rmSync } from "../tools/fs.rmSync"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; exit_if_maven_not_installed: { let commandOutput: Buffer | undefined = undefined; diff --git a/src/bin/main.ts b/src/bin/main.ts index 9f54242f..89b03d7c 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -4,8 +4,9 @@ import { termost } from "termost"; import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion"; import * as child_process from "child_process"; import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx"; +import { getBuildContext } from "./shared/buildContext"; -export type CliCommandOptions = { +type CliCommandOptions = { projectDirPath: string | undefined; }; @@ -69,10 +70,10 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./keycloakify"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); @@ -130,10 +131,13 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath, keycloakVersion, port, realmJsonFilePath }) => { const { command } = await import("./start-keycloak"); - await command({ cliCommandOptions }); + await command({ + buildContext: getBuildContext({ projectDirPath }), + cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } + }); } }); @@ -144,10 +148,10 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./eject-page"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); @@ -158,24 +162,24 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./add-story"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); program .command({ - name: "initialize-email-theme", + name: "initialize-login-theme", description: "Initialize an email theme." }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./initialize-email-theme"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); @@ -186,10 +190,10 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./initialize-account-theme"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); @@ -201,10 +205,10 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./copy-keycloak-resources-to-public"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); @@ -216,10 +220,10 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { const { command } = await import("./update-kc-gen"); - await command({ cliCommandOptions }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index 5fc09c3a..26c746b8 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -7,7 +7,6 @@ import { dirname as pathDirname } from "path"; import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath"; -import type { CliCommandOptions } from "../main"; import { z } from "zod"; import * as fs from "fs"; import { assert, type Equals } from "tsafe/assert"; @@ -24,7 +23,7 @@ import { objectEntries } from "tsafe/objectEntries"; import { type ThemeType } from "./constants"; import { id } from "tsafe/id"; import chalk from "chalk"; -import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions"; +import { getProxyFetchOptions, type FetchOptionsLike } from "../tools/fetchProxyOptions"; import { is } from "tsafe/is"; export type BuildContext = { @@ -43,7 +42,7 @@ export type BuildContext = { * In this case the urlPathname will be "/my-app/" */ urlPathname: string | undefined; assetsDirPath: string; - fetchOptions: ProxyFetchOptions; + fetchOptions: FetchOptionsLike; kcContextExclusionsFtlCode: string | undefined; environmentVariables: { name: string; default: string }[]; themeSrcDirPath: string; @@ -129,14 +128,12 @@ export type ResolvedViteConfig = { }; export function getBuildContext(params: { - cliCommandOptions: CliCommandOptions; + projectDirPath: string | undefined; }): BuildContext { - const { cliCommandOptions } = params; - const projectDirPath = - cliCommandOptions.projectDirPath !== undefined + params.projectDirPath !== undefined ? getAbsoluteAndInOsFormatPath({ - pathIsh: cliCommandOptions.projectDirPath, + pathIsh: params.projectDirPath, cwd: process.cwd() }) : process.cwd(); diff --git a/src/bin/shared/constants.ts b/src/bin/shared/constants.ts index d0707a90..d2137b14 100644 --- a/src/bin/shared/constants.ts +++ b/src/bin/shared/constants.ts @@ -71,3 +71,8 @@ export type AccountThemePageId = (typeof ACCOUNT_THEME_PAGE_IDS)[number]; export const CONTAINER_NAME = "keycloak-keycloakify"; export const FALLBACK_LANGUAGE_TAG = "en"; + +export const CUSTOM_HANDLER_ENV_NAMES = { + COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME", + BUILD_CONTEXT: "KEYCLOAKIFY_BUILD_CONTEXT" +}; diff --git a/src/bin/shared/customHandler.ts b/src/bin/shared/customHandler.ts new file mode 100644 index 00000000..7c0b9e1c --- /dev/null +++ b/src/bin/shared/customHandler.ts @@ -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 }; +} diff --git a/src/bin/shared/customHandler_delegate.ts b/src/bin/shared/customHandler_delegate.ts new file mode 100644 index 00000000..a41cbc7b --- /dev/null +++ b/src/bin/shared/customHandler_delegate.ts @@ -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>(); + +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); +} diff --git a/src/bin/shared/generateKcGenTs.ts b/src/bin/shared/generateKcGenTs.ts deleted file mode 100644 index 7191a55c..00000000 --- a/src/bin/shared/generateKcGenTs.ts +++ /dev/null @@ -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(); - -export async function generateKcGenTs(params: { - buildContext: BuildContextLike; -}): Promise { - const { buildContext } = params; - - const isReactProject: boolean = await (async () => { - const parsedPackageJson = await (async () => { - type ParsedPackageJson = { - dependencies?: Record; - devDependencies?: Record; - }; - - const zParsedPackageJson = (() => { - type TargetType = ParsedPackageJson; - - const zTargetType = z.object({ - dependencies: z.record(z.string()).optional(), - devDependencies: z.record(z.string()).optional() - }); - - assert, TargetType>>(); - - return id>(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 = ${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 (`, - ` `, - ` {(() => {`, - ` switch (kcContext.themeType) {`, - hasLoginTheme && - ` case "login": return ;`, - hasAccountTheme && - ` case "account": return ;`, - ` }`, - ` })()}`, - ` `, - ` );`, - `}` - ]), - ``, - `/* 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); - } -} diff --git a/src/bin/start-keycloak/start-keycloak.ts b/src/bin/start-keycloak/start-keycloak.ts index c85b09dc..e243fe2b 100644 --- a/src/bin/start-keycloak/start-keycloak.ts +++ b/src/bin/start-keycloak/start-keycloak.ts @@ -1,6 +1,5 @@ -import { getBuildContext } from "../shared/buildContext"; +import type { BuildContext } from "../shared/buildContext"; import { exclude } from "tsafe/exclude"; -import type { CliCommandOptions as CliCommandOptions_common } from "../main"; import { promptKeycloakVersion } from "../shared/promptKeycloakVersion"; import { CONTAINER_NAME } from "../shared/constants"; import { SemVer } from "../tools/SemVer"; @@ -29,13 +28,14 @@ import { existsAsync } from "../tools/fs.existsAsync"; import { rm } from "../tools/fs.rm"; import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive"; -export type CliCommandOptions = CliCommandOptions_common & { - port: number | undefined; - keycloakVersion: string | undefined; - realmJsonFilePath: string | undefined; -}; - -export async function command(params: { cliCommandOptions: CliCommandOptions }) { +export async function command(params: { + buildContext: BuildContext; + cliCommandOptions: { + port: number | undefined; + keycloakVersion: string | undefined; + realmJsonFilePath: string | undefined; + }; +}) { exit_if_docker_not_installed: { let commandOutput: string | undefined = undefined; @@ -88,9 +88,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions }) process.exit(1); } - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); + const { cliCommandOptions, buildContext } = params; const { dockerImageTag } = await (async () => { if (cliCommandOptions.keycloakVersion !== undefined) { diff --git a/src/bin/tools/fetchProxyOptions.ts b/src/bin/tools/fetchProxyOptions.ts index e6f8f497..c607feb9 100644 --- a/src/bin/tools/fetchProxyOptions.ts +++ b/src/bin/tools/fetchProxyOptions.ts @@ -1,16 +1,18 @@ -import { type FetchOptions } from "make-fetch-happen"; import * as child_process from "child_process"; import * as fs from "fs"; import { exclude } from "tsafe/exclude"; -export type ProxyFetchOptions = Pick< - FetchOptions, - "proxy" | "noProxy" | "strictSSL" | "cert" | "ca" ->; +export type FetchOptionsLike = { + proxy: string | undefined; + noProxy: string | string[]; + strictSSL: boolean; + cert: string | string[] | undefined; + ca: string[] | undefined; +}; export function getProxyFetchOptions(params: { npmConfigGetCwd: string; -}): ProxyFetchOptions { +}): FetchOptionsLike { const { npmConfigGetCwd } = params; const cfg = (() => { diff --git a/src/bin/update-kc-gen.ts b/src/bin/update-kc-gen.ts index 585b0f22..f126bcb1 100644 --- a/src/bin/update-kc-gen.ts +++ b/src/bin/update-kc-gen.ts @@ -1,13 +1,116 @@ -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; -import { generateKcGenTs } from "./shared/generateKcGenTs"; +import type { BuildContext } from "./shared/buildContext"; +import * as fs from "fs/promises"; +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 }) { - const { cliCommandOptions } = params; +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; - const buildContext = getBuildContext({ - cliCommandOptions + maybeDelegateCommandToCustomHandler({ + 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 = ${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 (`, + ` `, + ` {(() => {`, + ` switch (kcContext.themeType) {`, + hasLoginTheme && + ` case "login": return ;`, + hasAccountTheme && + ` case "account": return ;`, + ` }`, + ` })()}`, + ` `, + ` );`, + `}`, + ``, + `/* 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); + } } diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index f22cb62a..e6dfb054 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -15,7 +15,7 @@ import { type ResolvedViteConfig } from "../bin/shared/buildContext"; import MagicString from "magic-string"; -import { generateKcGenTs } from "../bin/shared/generateKcGenTs"; +import { command as updateKcGenCommand } from "../bin/update-kc-gen"; export namespace keycloakify { export type Params = BuildOptions & { @@ -122,13 +122,12 @@ export function keycloakify(params: keycloakify.Params) { } const buildContext = getBuildContext({ - cliCommandOptions: { - projectDirPath - } + projectDirPath }); - copyKeycloakResourcesToPublic({ buildContext }), - await generateKcGenTs({ buildContext }); + copyKeycloakResourcesToPublic({ buildContext }); + + await updateKcGenCommand({ buildContext }); }, transform: (code, id) => { assert(command !== undefined); diff --git a/stories/login/pages/Register.stories.tsx b/stories/login/pages/Register.stories.tsx index f25568cc..09cdf249 100644 --- a/stories/login/pages/Register.stories.tsx +++ b/stories/login/pages/Register.stories.tsx @@ -115,7 +115,6 @@ export const WithFavoritePet: Story = { ) }; - export const WithNewsletter: Story = { render: () => ( ) }; - export const WithEmailAsUsername: Story = { render: () => (