From ae757ee37140a529c161282e1e11594702620a6f Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 23 Feb 2024 19:15:59 +0100 Subject: [PATCH] Enable to provide the configuration to the Vite plugin, enable user to provide a post build script #148 --- src/bin/constants.ts | 2 + .../buildOptions/UserProvidedBuildOptions.ts | 25 ++++++++++++ .../keycloakify/buildOptions/buildOptions.ts | 35 ++++++++++------- .../buildOptions/parsedPackageJson.ts | 27 ++----------- .../buildOptions/resolvedViteConfig.ts | 5 ++- src/bin/keycloakify/keycloakify.ts | 34 ++++++++++------ src/vite-plugin/vite-plugin.ts | 39 +++++++++++++++++-- 7 files changed, 113 insertions(+), 54 deletions(-) create mode 100644 src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts diff --git a/src/bin/constants.ts b/src/bin/constants.ts index 4a8b4023..bea2eee8 100644 --- a/src/bin/constants.ts +++ b/src/bin/constants.ts @@ -10,3 +10,5 @@ export const retrocompatPostfix = "_retrocompat"; export const accountV1ThemeName = "account-v1"; export type ThemeType = (typeof themeTypes)[number]; + +export const keycloakifyBuildOptionsForPostPostBuildScriptEnvName = "KEYCLOAKIFY_BUILD_OPTIONS_POST_POST_BUILD_SCRIPT"; diff --git a/src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts b/src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts new file mode 100644 index 00000000..f4f5933e --- /dev/null +++ b/src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; + +export type UserProvidedBuildOptions = { + extraThemeProperties?: string[]; + artifactId?: string; + groupId?: string; + doCreateJar?: boolean; + loginThemeResourcesFromKeycloakVersion?: string; + reactAppBuildDirPath?: string; + keycloakifyBuildDirPath?: string; + themeName?: string | string[]; + doBuildRetrocompatAccountTheme?: boolean; +}; + +export const zUserProvidedBuildOptions = z.object({ + "extraThemeProperties": z.array(z.string()).optional(), + "artifactId": z.string().optional(), + "groupId": z.string().optional(), + "doCreateJar": z.boolean().optional(), + "loginThemeResourcesFromKeycloakVersion": z.string().optional(), + "reactAppBuildDirPath": z.string().optional(), + "keycloakifyBuildDirPath": z.string().optional(), + "themeName": z.union([z.string(), z.array(z.string())]).optional(), + "doBuildRetrocompatAccountTheme": z.boolean().optional() +}); diff --git a/src/bin/keycloakify/buildOptions/buildOptions.ts b/src/bin/keycloakify/buildOptions/buildOptions.ts index a9efb540..97b38845 100644 --- a/src/bin/keycloakify/buildOptions/buildOptions.ts +++ b/src/bin/keycloakify/buildOptions/buildOptions.ts @@ -47,10 +47,15 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption throw new Error("Keycloakify's Vite plugin output not found"); } - const parsedPackageJson = readParsedPackageJson({ reactAppRootDirPath }); + const { keycloakify: userProvidedBuildOptionsFromPackageJson, ...parsedPackageJson } = readParsedPackageJson({ reactAppRootDirPath }); + + const userProvidedBuildOptions = { + ...userProvidedBuildOptionsFromPackageJson, + ...resolvedViteConfig?.userProvidedBuildOptions + }; const themeNames = (() => { - if (parsedPackageJson.keycloakify?.themeName === undefined) { + if (userProvidedBuildOptions.themeName === undefined) { return [ parsedPackageJson.name .replace(/^@(.*)/, "$1") @@ -59,11 +64,11 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption ]; } - if (typeof parsedPackageJson.keycloakify.themeName === "string") { - return [parsedPackageJson.keycloakify.themeName]; + if (typeof userProvidedBuildOptions.themeName === "string") { + return [userProvidedBuildOptions.themeName]; } - return parsedPackageJson.keycloakify.themeName; + return userProvidedBuildOptions.themeName; })(); const reactAppBuildDirPath = (() => { @@ -72,9 +77,9 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption break webpack; } - if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) { + if (userProvidedBuildOptions.reactAppBuildDirPath !== undefined) { return getAbsoluteAndInOsFormatPath({ - "pathIsh": parsedPackageJson.keycloakify?.reactAppBuildDirPath, + "pathIsh": userProvidedBuildOptions.reactAppBuildDirPath, "cwd": reactAppRootDirPath }); } @@ -94,13 +99,13 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption "isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false, "themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0", themeNames, - "extraThemeProperties": parsedPackageJson.keycloakify?.extraThemeProperties, + "extraThemeProperties": userProvidedBuildOptions.extraThemeProperties, "groupId": (() => { const fallbackGroupId = `${themeNames[0]}.keycloak`; return ( process.env.KEYCLOAKIFY_GROUP_ID ?? - parsedPackageJson.keycloakify?.groupId ?? + userProvidedBuildOptions.groupId ?? (parsedPackageJson.homepage === undefined ? fallbackGroupId : urlParse(parsedPackageJson.homepage) @@ -110,15 +115,15 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption .join(".") ?? fallbackGroupId) + ".keycloak" ); })(), - "artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? parsedPackageJson.keycloakify?.artifactId ?? `${themeNames[0]}-keycloak-theme`, - "doCreateJar": parsedPackageJson.keycloakify?.doCreateJar ?? true, - "loginThemeResourcesFromKeycloakVersion": parsedPackageJson.keycloakify?.loginThemeResourcesFromKeycloakVersion ?? "11.0.3", + "artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? userProvidedBuildOptions.artifactId ?? `${themeNames[0]}-keycloak-theme`, + "doCreateJar": userProvidedBuildOptions.doCreateJar ?? true, + "loginThemeResourcesFromKeycloakVersion": userProvidedBuildOptions.loginThemeResourcesFromKeycloakVersion ?? "11.0.3", reactAppRootDirPath, reactAppBuildDirPath, "keycloakifyBuildDirPath": (() => { - if (parsedPackageJson.keycloakify?.keycloakifyBuildDirPath !== undefined) { + if (userProvidedBuildOptions.keycloakifyBuildDirPath !== undefined) { return getAbsoluteAndInOsFormatPath({ - "pathIsh": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath, + "pathIsh": userProvidedBuildOptions.keycloakifyBuildDirPath, "cwd": reactAppRootDirPath }); } @@ -179,7 +184,7 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir); })(), - "doBuildRetrocompatAccountTheme": parsedPackageJson.keycloakify?.doBuildRetrocompatAccountTheme ?? true, + "doBuildRetrocompatAccountTheme": userProvidedBuildOptions.doBuildRetrocompatAccountTheme ?? true, npmWorkspaceRootDirPath }; } diff --git a/src/bin/keycloakify/buildOptions/parsedPackageJson.ts b/src/bin/keycloakify/buildOptions/parsedPackageJson.ts index 4b2aafff..a0ce604c 100644 --- a/src/bin/keycloakify/buildOptions/parsedPackageJson.ts +++ b/src/bin/keycloakify/buildOptions/parsedPackageJson.ts @@ -3,41 +3,20 @@ import { assert } from "tsafe"; import type { Equals } from "tsafe"; import { z } from "zod"; import { join as pathJoin } from "path"; +import { type UserProvidedBuildOptions, zUserProvidedBuildOptions } from "./UserProvidedBuildOptions"; export type ParsedPackageJson = { name: string; version?: string; homepage?: string; - keycloakify?: { - extraThemeProperties?: string[]; - artifactId?: string; - groupId?: string; - doCreateJar?: boolean; - loginThemeResourcesFromKeycloakVersion?: string; - reactAppBuildDirPath?: string; - keycloakifyBuildDirPath?: string; - themeName?: string | string[]; - doBuildRetrocompatAccountTheme?: boolean; - }; + keycloakify?: UserProvidedBuildOptions; }; const zParsedPackageJson = z.object({ "name": z.string(), "version": z.string().optional(), "homepage": z.string().optional(), - "keycloakify": z - .object({ - "extraThemeProperties": z.array(z.string()).optional(), - "artifactId": z.string().optional(), - "groupId": z.string().optional(), - "doCreateJar": z.boolean().optional(), - "loginThemeResourcesFromKeycloakVersion": z.string().optional(), - "reactAppBuildDirPath": z.string().optional(), - "keycloakifyBuildDirPath": z.string().optional(), - "themeName": z.union([z.string(), z.array(z.string())]).optional(), - "doBuildRetrocompatAccountTheme": z.boolean().optional() - }) - .optional() + "keycloakify": zUserProvidedBuildOptions.optional() }); assert, ParsedPackageJson>>(); diff --git a/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts b/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts index 4d4fd3fe..d2f37c34 100644 --- a/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts +++ b/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts @@ -5,19 +5,22 @@ import { z } from "zod"; import { join as pathJoin } from "path"; import { resolvedViteConfigJsonBasename } from "../../constants"; import type { OptionalIfCanBeUndefined } from "../../tools/OptionalIfCanBeUndefined"; +import { UserProvidedBuildOptions, zUserProvidedBuildOptions } from "./UserProvidedBuildOptions"; export type ResolvedViteConfig = { buildDir: string; publicDir: string; assetsDir: string; urlPathname: string | undefined; + userProvidedBuildOptions: UserProvidedBuildOptions; }; const zResolvedViteConfig = z.object({ "buildDir": z.string(), "publicDir": z.string(), "assetsDir": z.string(), - "urlPathname": z.string().optional() + "urlPathname": z.string().optional(), + "userProvidedBuildOptions": zUserProvidedBuildOptions }); { diff --git a/src/bin/keycloakify/keycloakify.ts b/src/bin/keycloakify/keycloakify.ts index 3022cdf9..df75ceee 100644 --- a/src/bin/keycloakify/keycloakify.ts +++ b/src/bin/keycloakify/keycloakify.ts @@ -9,6 +9,7 @@ import { getLogger } from "../tools/logger"; import { getThemeSrcDirPath } from "../getThemeSrcDirPath"; import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath"; import { readThisNpmProjectVersion } from "../tools/readThisNpmProjectVersion"; +import { keycloakifyBuildOptionsForPostPostBuildScriptEnvName } from "../constants"; export async function main() { const buildOptions = readBuildOptions({ @@ -36,9 +37,30 @@ export async function main() { fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8")); } + const containerKeycloakVersion = "23.0.6"; + const jarFilePath = pathJoin(buildOptions.keycloakifyBuildDirPath, "target", `${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`); - if (buildOptions.doCreateJar) { + generateStartKeycloakTestingContainer({ + "keycloakVersion": containerKeycloakVersion, + jarFilePath, + buildOptions + }); + + fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, ".gitignore"), Buffer.from("*", "utf8")); + + child_process.execSync("npx vite", { + "env": { + ...process.env, + [keycloakifyBuildOptionsForPostPostBuildScriptEnvName]: JSON.stringify(buildOptions) + } + }); + + create_jar: { + if (!buildOptions.doCreateJar) { + break create_jar; + } + child_process.execSync("mvn clean install", { "cwd": buildOptions.keycloakifyBuildDirPath }); const jarDirPath = pathDirname(jarFilePath); @@ -59,16 +81,6 @@ export async function main() { ); } - const containerKeycloakVersion = "23.0.6"; - - generateStartKeycloakTestingContainer({ - "keycloakVersion": containerKeycloakVersion, - jarFilePath, - buildOptions - }); - - fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, ".gitignore"), Buffer.from("*", "utf8")); - logger.log( [ "", diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index 46df1a31..5175e232 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -1,7 +1,13 @@ import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path"; import type { Plugin } from "vite"; import * as fs from "fs"; -import { resolvedViteConfigJsonBasename, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, keycloak_resources } from "../bin/constants"; +import { + resolvedViteConfigJsonBasename, + nameOfTheGlobal, + basenameOfTheKeycloakifyResourcesDir, + keycloak_resources, + keycloakifyBuildOptionsForPostPostBuildScriptEnvName +} from "../bin/constants"; import type { ResolvedViteConfig } from "../bin/keycloakify/buildOptions/resolvedViteConfig"; import { getCacheDirPath } from "../bin/keycloakify/buildOptions/getCacheDirPath"; import { replaceAll } from "../bin/tools/String.prototype.replaceAll"; @@ -9,8 +15,16 @@ import { id } from "tsafe/id"; import { rm } from "../bin/tools/fs.rm"; import { copyKeycloakResourcesToPublic } from "../bin/copy-keycloak-resources-to-public"; import { assert } from "tsafe/assert"; +import type { BuildOptions } from "../bin/keycloakify/buildOptions"; +import type { UserProvidedBuildOptions } from "../bin/keycloakify/buildOptions/UserProvidedBuildOptions"; + +export type Params = UserProvidedBuildOptions & { + postBuildScript?: (buildOptions: Omit) => Promise; +}; + +export function keycloakify(params: Params) { + const { postBuildScript, ...userProvidedBuildOptions } = params; -export function keycloakify() { let reactAppRootDirPath: string | undefined = undefined; let urlPathname: string | undefined = undefined; let buildDirPath: string | undefined = undefined; @@ -19,6 +33,24 @@ export function keycloakify() { const plugin = { "name": "keycloakify" as const, "configResolved": async resolvedConfig => { + run_post_build_script: { + const buildOptionJson = process.env[keycloakifyBuildOptionsForPostPostBuildScriptEnvName]; + + if (buildOptionJson === undefined) { + break run_post_build_script; + } + + if (params.postBuildScript === undefined) { + process.exit(0); + } + + const buildOptions: BuildOptions = JSON.parse(buildOptionJson); + + await params.postBuildScript(buildOptions); + + process.exit(0); + } + command = resolvedConfig.command; reactAppRootDirPath = resolvedConfig.root; @@ -67,7 +99,8 @@ export function keycloakify() { "publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir), "assetsDir": resolvedConfig.build.assetsDir, "buildDir": resolvedConfig.build.outDir, - urlPathname + urlPathname, + userProvidedBuildOptions }), null, 2