From bc586eceef4f8002835d63258eaf8403a3bb6ea5 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 09:03:15 +0200 Subject: [PATCH] Make sure the update-kc-gen command is delegated when building with vite --- .../initialize-account-theme.ts | 4 +- src/bin/main.ts | 293 +++++++----------- ...er_caller.ts => customHandler_delegate.ts} | 2 +- src/bin/shared/generateKcGenTs.ts | 127 -------- src/bin/update-kc-gen.ts | 112 ++++++- src/vite-plugin/vite-plugin.ts | 7 +- stories/login/pages/Register.stories.tsx | 6 +- 7 files changed, 239 insertions(+), 312 deletions(-) rename src/bin/shared/{customHandler_caller.ts => customHandler_delegate.ts} (95%) delete mode 100644 src/bin/shared/generateKcGenTs.ts diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index e29b6733..3caed327 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -5,7 +5,7 @@ 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"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; @@ -94,7 +94,7 @@ export async function command(params: { buildContext: BuildContext }) { updateAccountThemeImplementationInConfig({ buildContext, accountThemeType }); - await generateKcGenTs({ + await updateKcGenCommand({ buildContext: { ...buildContext, implementedThemeTypes: { diff --git a/src/bin/main.ts b/src/bin/main.ts index 7ef2a9b8..89b03d7c 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -4,7 +4,6 @@ import { termost } from "termost"; import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion"; import * as child_process from "child_process"; import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx"; -import { callHandlerIfAny } from "./shared/customHandler_caller"; import { getBuildContext } from "./shared/buildContext"; type CliCommandOptions = { @@ -72,153 +71,117 @@ program .task({ skip, handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - const { command } = await import("./keycloakify"); - await command({ buildContext }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); -{ - const commandName = "start-keycloak"; +program + .command<{ + port: number | undefined; + keycloakVersion: string | undefined; + realmJsonFilePath: string | undefined; + }>({ + name: "start-keycloak", + description: + "Spin up a pre configured Docker image of Keycloak to test your theme." + }) + .option({ + key: "port", + name: (() => { + const name = "port"; - program - .command<{ - port: number | undefined; - keycloakVersion: string | undefined; - realmJsonFilePath: string | undefined; - }>({ - name: commandName, - description: - "Spin up a pre configured Docker image of Keycloak to test your theme." - }) - .option({ - key: "port", - name: (() => { - const name = "port"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), + defaultValue: undefined + }) + .option({ + key: "keycloakVersion", + name: (() => { + const name = "keycloak-version"; - return name; - })(), - description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), - defaultValue: undefined - }) - .option({ - key: "keycloakVersion", - name: (() => { - const name = "keycloak-version"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: [ + "Use a specific version of Keycloak.", + "Example `--keycloak-version 21.1.1`" + ].join(" "), + defaultValue: undefined + }) + .option({ + key: "realmJsonFilePath", + name: (() => { + const name = "import"; - return name; - })(), - description: [ - "Use a specific version of Keycloak.", - "Example `--keycloak-version 21.1.1`" - ].join(" "), - defaultValue: undefined - }) - .option({ - key: "realmJsonFilePath", - name: (() => { - const name = "import"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + defaultValue: undefined, + description: [ + "Import your own realm configuration file", + "Example `--import path/to/myrealm-realm.json`" + ].join(" ") + }) + .task({ + skip, + handler: async ({ projectDirPath, keycloakVersion, port, realmJsonFilePath }) => { + const { command } = await import("./start-keycloak"); - return name; - })(), - defaultValue: undefined, - description: [ - "Import your own realm configuration file", - "Example `--import path/to/myrealm-realm.json`" - ].join(" ") - }) - .task({ - skip, - handler: async ({ - projectDirPath, - keycloakVersion, - port, - realmJsonFilePath - }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ + buildContext: getBuildContext({ projectDirPath }), + cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } + }); + } + }); - const { command } = await import("./start-keycloak"); +program + .command({ + name: "eject-page", + description: "Eject a Keycloak page." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./eject-page"); - await command({ - buildContext, - cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } - }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); -{ - const commandName = "eject-page"; +program + .command({ + name: "add-story", + description: "Add *.stories.tsx file for a specific page to in your Storybook." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./add-story"); - program - .command({ - name: commandName, - description: "Eject a Keycloak page." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); - callHandlerIfAny({ buildContext, commandName }); +program + .command({ + name: "initialize-login-theme", + description: "Initialize an email theme." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./initialize-email-theme"); - const { command } = await import("./eject-page"); - - await command({ buildContext }); - } - }); -} - -{ - const commandName = "add-story"; - - program - .command({ - name: commandName, - description: - "Add *.stories.tsx file for a specific page to in your Storybook." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - callHandlerIfAny({ buildContext, commandName }); - - const { command } = await import("./add-story"); - - await command({ buildContext }); - } - }); -} - -{ - const comandName = "initialize-login-theme"; - - program - .command({ - name: comandName, - description: "Initialize an email theme." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - const { command } = await import("./initialize-email-theme"); - - await command({ buildContext }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); program .command({ @@ -228,57 +191,41 @@ program .task({ skip, handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - const { command } = await import("./initialize-account-theme"); - await command({ buildContext }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); -{ - const commandName = "copy-keycloak-resources-to-public"; +program + .command({ + name: "copy-keycloak-resources-to-public", + description: + "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./copy-keycloak-resources-to-public"); - program - .command({ - name: commandName, - description: - "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); - const { command } = await import("./copy-keycloak-resources-to-public"); +program + .command({ + name: "update-kc-gen", + description: + "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./update-kc-gen"); - await command({ buildContext }); - } - }); -} - -{ - const commandName = "update-kc-gen"; - - program - .command({ - name: commandName, - description: - "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - callHandlerIfAny({ buildContext, commandName }); - - const { command } = await import("./update-kc-gen"); - - await command({ buildContext }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); // Fallback to build command if no command is provided { diff --git a/src/bin/shared/customHandler_caller.ts b/src/bin/shared/customHandler_delegate.ts similarity index 95% rename from src/bin/shared/customHandler_caller.ts rename to src/bin/shared/customHandler_delegate.ts index 88d5a41c..a41cbc7b 100644 --- a/src/bin/shared/customHandler_caller.ts +++ b/src/bin/shared/customHandler_delegate.ts @@ -13,7 +13,7 @@ import * as fs from "fs"; assert>(); -export function callHandlerIfAny(params: { +export function maybeDelegateCommandToCustomHandler(params: { commandName: CommandName; buildContext: BuildContext; }) { diff --git a/src/bin/shared/generateKcGenTs.ts b/src/bin/shared/generateKcGenTs.ts deleted file mode 100644 index e5bb7e86..00000000 --- a/src/bin/shared/generateKcGenTs.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { assert } from "tsafe/assert"; -import type { BuildContext } from "./buildContext"; -import * as fs from "fs/promises"; -import { join as pathJoin } from "path"; -import { existsAsync } from "../tools/fs.existsAsync"; - -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 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/bin/update-kc-gen.ts b/src/bin/update-kc-gen.ts index b3d9ce15..f126bcb1 100644 --- a/src/bin/update-kc-gen.ts +++ b/src/bin/update-kc-gen.ts @@ -1,8 +1,116 @@ import type { BuildContext } from "./shared/buildContext"; -import { generateKcGenTs } from "./shared/generateKcGenTs"; +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: { buildContext: BuildContext }) { const { buildContext } = params; - await generateKcGenTs({ buildContext }); + maybeDelegateCommandToCustomHandler({ + commandName: "update-kc-gen", + 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 3a7f3d26..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 & { @@ -125,8 +125,9 @@ export function keycloakify(params: keycloakify.Params) { 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: () => (