Enable to provide the configuration to the Vite plugin, enable user to provide a post build script #148

This commit is contained in:
Joseph Garrone 2024-02-23 19:15:59 +01:00
parent 69936750d5
commit ae757ee371
7 changed files with 113 additions and 54 deletions

View File

@ -10,3 +10,5 @@ export const retrocompatPostfix = "_retrocompat";
export const accountV1ThemeName = "account-v1"; export const accountV1ThemeName = "account-v1";
export type ThemeType = (typeof themeTypes)[number]; export type ThemeType = (typeof themeTypes)[number];
export const keycloakifyBuildOptionsForPostPostBuildScriptEnvName = "KEYCLOAKIFY_BUILD_OPTIONS_POST_POST_BUILD_SCRIPT";

View File

@ -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()
});

View File

@ -47,10 +47,15 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
throw new Error("Keycloakify's Vite plugin output not found"); 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 = (() => { const themeNames = (() => {
if (parsedPackageJson.keycloakify?.themeName === undefined) { if (userProvidedBuildOptions.themeName === undefined) {
return [ return [
parsedPackageJson.name parsedPackageJson.name
.replace(/^@(.*)/, "$1") .replace(/^@(.*)/, "$1")
@ -59,11 +64,11 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
]; ];
} }
if (typeof parsedPackageJson.keycloakify.themeName === "string") { if (typeof userProvidedBuildOptions.themeName === "string") {
return [parsedPackageJson.keycloakify.themeName]; return [userProvidedBuildOptions.themeName];
} }
return parsedPackageJson.keycloakify.themeName; return userProvidedBuildOptions.themeName;
})(); })();
const reactAppBuildDirPath = (() => { const reactAppBuildDirPath = (() => {
@ -72,9 +77,9 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
break webpack; break webpack;
} }
if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) { if (userProvidedBuildOptions.reactAppBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({ return getAbsoluteAndInOsFormatPath({
"pathIsh": parsedPackageJson.keycloakify?.reactAppBuildDirPath, "pathIsh": userProvidedBuildOptions.reactAppBuildDirPath,
"cwd": reactAppRootDirPath "cwd": reactAppRootDirPath
}); });
} }
@ -94,13 +99,13 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
"isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false, "isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false,
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0", "themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
themeNames, themeNames,
"extraThemeProperties": parsedPackageJson.keycloakify?.extraThemeProperties, "extraThemeProperties": userProvidedBuildOptions.extraThemeProperties,
"groupId": (() => { "groupId": (() => {
const fallbackGroupId = `${themeNames[0]}.keycloak`; const fallbackGroupId = `${themeNames[0]}.keycloak`;
return ( return (
process.env.KEYCLOAKIFY_GROUP_ID ?? process.env.KEYCLOAKIFY_GROUP_ID ??
parsedPackageJson.keycloakify?.groupId ?? userProvidedBuildOptions.groupId ??
(parsedPackageJson.homepage === undefined (parsedPackageJson.homepage === undefined
? fallbackGroupId ? fallbackGroupId
: urlParse(parsedPackageJson.homepage) : urlParse(parsedPackageJson.homepage)
@ -110,15 +115,15 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
.join(".") ?? fallbackGroupId) + ".keycloak" .join(".") ?? fallbackGroupId) + ".keycloak"
); );
})(), })(),
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? parsedPackageJson.keycloakify?.artifactId ?? `${themeNames[0]}-keycloak-theme`, "artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? userProvidedBuildOptions.artifactId ?? `${themeNames[0]}-keycloak-theme`,
"doCreateJar": parsedPackageJson.keycloakify?.doCreateJar ?? true, "doCreateJar": userProvidedBuildOptions.doCreateJar ?? true,
"loginThemeResourcesFromKeycloakVersion": parsedPackageJson.keycloakify?.loginThemeResourcesFromKeycloakVersion ?? "11.0.3", "loginThemeResourcesFromKeycloakVersion": userProvidedBuildOptions.loginThemeResourcesFromKeycloakVersion ?? "11.0.3",
reactAppRootDirPath, reactAppRootDirPath,
reactAppBuildDirPath, reactAppBuildDirPath,
"keycloakifyBuildDirPath": (() => { "keycloakifyBuildDirPath": (() => {
if (parsedPackageJson.keycloakify?.keycloakifyBuildDirPath !== undefined) { if (userProvidedBuildOptions.keycloakifyBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({ return getAbsoluteAndInOsFormatPath({
"pathIsh": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath, "pathIsh": userProvidedBuildOptions.keycloakifyBuildDirPath,
"cwd": reactAppRootDirPath "cwd": reactAppRootDirPath
}); });
} }
@ -179,7 +184,7 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir); return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir);
})(), })(),
"doBuildRetrocompatAccountTheme": parsedPackageJson.keycloakify?.doBuildRetrocompatAccountTheme ?? true, "doBuildRetrocompatAccountTheme": userProvidedBuildOptions.doBuildRetrocompatAccountTheme ?? true,
npmWorkspaceRootDirPath npmWorkspaceRootDirPath
}; };
} }

View File

@ -3,41 +3,20 @@ import { assert } from "tsafe";
import type { Equals } from "tsafe"; import type { Equals } from "tsafe";
import { z } from "zod"; import { z } from "zod";
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { type UserProvidedBuildOptions, zUserProvidedBuildOptions } from "./UserProvidedBuildOptions";
export type ParsedPackageJson = { export type ParsedPackageJson = {
name: string; name: string;
version?: string; version?: string;
homepage?: string; homepage?: string;
keycloakify?: { keycloakify?: UserProvidedBuildOptions;
extraThemeProperties?: string[];
artifactId?: string;
groupId?: string;
doCreateJar?: boolean;
loginThemeResourcesFromKeycloakVersion?: string;
reactAppBuildDirPath?: string;
keycloakifyBuildDirPath?: string;
themeName?: string | string[];
doBuildRetrocompatAccountTheme?: boolean;
};
}; };
const zParsedPackageJson = z.object({ const zParsedPackageJson = z.object({
"name": z.string(), "name": z.string(),
"version": z.string().optional(), "version": z.string().optional(),
"homepage": z.string().optional(), "homepage": z.string().optional(),
"keycloakify": z "keycloakify": zUserProvidedBuildOptions.optional()
.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()
}); });
assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>(); assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>();

View File

@ -5,19 +5,22 @@ import { z } from "zod";
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { resolvedViteConfigJsonBasename } from "../../constants"; import { resolvedViteConfigJsonBasename } from "../../constants";
import type { OptionalIfCanBeUndefined } from "../../tools/OptionalIfCanBeUndefined"; import type { OptionalIfCanBeUndefined } from "../../tools/OptionalIfCanBeUndefined";
import { UserProvidedBuildOptions, zUserProvidedBuildOptions } from "./UserProvidedBuildOptions";
export type ResolvedViteConfig = { export type ResolvedViteConfig = {
buildDir: string; buildDir: string;
publicDir: string; publicDir: string;
assetsDir: string; assetsDir: string;
urlPathname: string | undefined; urlPathname: string | undefined;
userProvidedBuildOptions: UserProvidedBuildOptions;
}; };
const zResolvedViteConfig = z.object({ const zResolvedViteConfig = z.object({
"buildDir": z.string(), "buildDir": z.string(),
"publicDir": z.string(), "publicDir": z.string(),
"assetsDir": z.string(), "assetsDir": z.string(),
"urlPathname": z.string().optional() "urlPathname": z.string().optional(),
"userProvidedBuildOptions": zUserProvidedBuildOptions
}); });
{ {

View File

@ -9,6 +9,7 @@ import { getLogger } from "../tools/logger";
import { getThemeSrcDirPath } from "../getThemeSrcDirPath"; import { getThemeSrcDirPath } from "../getThemeSrcDirPath";
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath"; import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
import { readThisNpmProjectVersion } from "../tools/readThisNpmProjectVersion"; import { readThisNpmProjectVersion } from "../tools/readThisNpmProjectVersion";
import { keycloakifyBuildOptionsForPostPostBuildScriptEnvName } from "../constants";
export async function main() { export async function main() {
const buildOptions = readBuildOptions({ const buildOptions = readBuildOptions({
@ -36,9 +37,30 @@ export async function main() {
fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8")); 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`); 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 }); child_process.execSync("mvn clean install", { "cwd": buildOptions.keycloakifyBuildDirPath });
const jarDirPath = pathDirname(jarFilePath); 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( logger.log(
[ [
"", "",

View File

@ -1,7 +1,13 @@
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 type { Plugin } from "vite"; import type { Plugin } from "vite";
import * as fs from "fs"; 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 type { ResolvedViteConfig } from "../bin/keycloakify/buildOptions/resolvedViteConfig";
import { getCacheDirPath } from "../bin/keycloakify/buildOptions/getCacheDirPath"; import { getCacheDirPath } from "../bin/keycloakify/buildOptions/getCacheDirPath";
import { replaceAll } from "../bin/tools/String.prototype.replaceAll"; 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 { rm } from "../bin/tools/fs.rm";
import { copyKeycloakResourcesToPublic } from "../bin/copy-keycloak-resources-to-public"; import { copyKeycloakResourcesToPublic } from "../bin/copy-keycloak-resources-to-public";
import { assert } from "tsafe/assert"; 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<BuildOptions, "bundler">) => Promise<void>;
};
export function keycloakify(params: Params) {
const { postBuildScript, ...userProvidedBuildOptions } = params;
export function keycloakify() {
let reactAppRootDirPath: string | undefined = undefined; let reactAppRootDirPath: string | undefined = undefined;
let urlPathname: string | undefined = undefined; let urlPathname: string | undefined = undefined;
let buildDirPath: string | undefined = undefined; let buildDirPath: string | undefined = undefined;
@ -19,6 +33,24 @@ export function keycloakify() {
const plugin = { const plugin = {
"name": "keycloakify" as const, "name": "keycloakify" as const,
"configResolved": async resolvedConfig => { "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; command = resolvedConfig.command;
reactAppRootDirPath = resolvedConfig.root; reactAppRootDirPath = resolvedConfig.root;
@ -67,7 +99,8 @@ export function keycloakify() {
"publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir), "publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir),
"assetsDir": resolvedConfig.build.assetsDir, "assetsDir": resolvedConfig.build.assetsDir,
"buildDir": resolvedConfig.build.outDir, "buildDir": resolvedConfig.build.outDir,
urlPathname urlPathname,
userProvidedBuildOptions
}), }),
null, null,
2 2