Refactor build option managment

This commit is contained in:
garronej 2023-09-03 23:26:34 +02:00
parent 8c3e9ff192
commit 18f0f3cce1
13 changed files with 148 additions and 128 deletions

View File

@ -1,40 +1,23 @@
#!/usr/bin/env node #!/usr/bin/env node
import { downloadKeycloakStaticResources } from "./keycloakify/generateTheme/downloadKeycloakStaticResources"; import { downloadKeycloakStaticResources } from "./keycloakify/generateTheme/downloadKeycloakStaticResources";
import { join as pathJoin, relative as pathRelative, isAbsolute as pathIsAbsolute } from "path"; import { join as pathJoin, relative as pathRelative } from "path";
import { readBuildOptions } from "./keycloakify/BuildOptions"; import { readBuildOptions } from "./keycloakify/BuildOptions";
import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "./constants"; import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "./constants";
import * as fs from "fs"; import * as fs from "fs";
(async () => { (async () => {
const cwd = process.cwd(); const reactAppRootDirPath = process.cwd();
const projectDirPath = cwd;
const publicDirPath = (() => {
from_env_var: {
const value = process.env["PUBLIC_DIR_PATH"];
if (value === undefined) {
break from_env_var;
}
return pathIsAbsolute(value) ? value : pathJoin(cwd, value);
}
return pathJoin(projectDirPath, "public");
})();
const buildOptions = readBuildOptions({ const buildOptions = readBuildOptions({
"processArgv": process.argv.slice(2), reactAppRootDirPath,
"projectDirPath": process.cwd() "processArgv": process.argv.slice(2)
}); });
const reservedDirPath = pathJoin(publicDirPath, keycloak_resources); const reservedDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
for (const themeType of themeTypes) { for (const themeType of themeTypes) {
await downloadKeycloakStaticResources({ await downloadKeycloakStaticResources({
projectDirPath,
"keycloakVersion": (() => { "keycloakVersion": (() => {
switch (themeType) { switch (themeType) {
case "login": case "login":
@ -45,7 +28,8 @@ import * as fs from "fs";
})(), })(),
themeType, themeType,
"themeDirPath": reservedDirPath, "themeDirPath": reservedDirPath,
"usedResources": undefined "usedResources": undefined,
buildOptions
}); });
} }
@ -60,7 +44,7 @@ import * as fs from "fs";
) )
); );
fs.writeFileSync(pathJoin(publicDirPath, "keycloak-resources", ".gitignore"), Buffer.from("*", "utf8")); fs.writeFileSync(pathJoin(buildOptions.publicDirPath, "keycloak-resources", ".gitignore"), Buffer.from("*", "utf8"));
console.log(`${pathRelative(projectDirPath, reservedDirPath)} directory created.`); console.log(`${pathRelative(reactAppRootDirPath, reservedDirPath)} directory created.`);
})(); })();

View File

@ -4,15 +4,23 @@ import { downloadAndUnzip } from "./tools/downloadAndUnzip";
import { promptKeycloakVersion } from "./promptKeycloakVersion"; import { promptKeycloakVersion } from "./promptKeycloakVersion";
import { getLogger } from "./tools/logger"; import { getLogger } from "./tools/logger";
import { readBuildOptions } from "./keycloakify/BuildOptions"; import { readBuildOptions } from "./keycloakify/BuildOptions";
import { assert } from "tsafe/assert";
import type { BuildOptions } from "./keycloakify/BuildOptions";
import * as child_process from "child_process"; import * as child_process from "child_process";
import * as fs from "fs"; import * as fs from "fs";
export async function downloadBuiltinKeycloakTheme(params: { projectDirPath: string; keycloakVersion: string; destDirPath: string }) { export type BuildOptionsLike = {
const { projectDirPath, keycloakVersion, destDirPath } = params; cacheDirPath: string;
};
assert<BuildOptions extends BuildOptionsLike ? true : false>();
export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; buildOptions: BuildOptionsLike }) {
const { keycloakVersion, destDirPath, buildOptions } = params;
await downloadAndUnzip({ await downloadAndUnzip({
"doUseCache": true, "doUseCache": true,
projectDirPath, "cacheDirPath": buildOptions.cacheDirPath,
destDirPath, destDirPath,
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`, "url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
"specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`), "specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
@ -74,7 +82,7 @@ export async function downloadBuiltinKeycloakTheme(params: { projectDirPath: str
async function main() { async function main() {
const buildOptions = readBuildOptions({ const buildOptions = readBuildOptions({
"projectDirPath": process.cwd(), "reactAppRootDirPath": process.cwd(),
"processArgv": process.argv.slice(2) "processArgv": process.argv.slice(2)
}); });
@ -86,9 +94,9 @@ async function main() {
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`); logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
await downloadBuiltinKeycloakTheme({ await downloadBuiltinKeycloakTheme({
"projectDirPath": process.cwd(),
keycloakVersion, keycloakVersion,
destDirPath destDirPath,
buildOptions
}); });
} }

View File

@ -15,6 +15,8 @@ import { themeTypes, type ThemeType } from "./constants";
(async () => { (async () => {
console.log("Select a theme type"); console.log("Select a theme type");
const reactAppRootDirPath = process.cwd();
const { value: themeType } = await cliSelect<ThemeType>({ const { value: themeType } = await cliSelect<ThemeType>({
"values": [...themeTypes] "values": [...themeTypes]
}).catch(() => { }).catch(() => {
@ -43,7 +45,7 @@ import { themeTypes, type ThemeType } from "./constants";
const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx"); const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
const { themeSrcDirPath } = getThemeSrcDirPath({ "projectDirPath": process.cwd() }); const { themeSrcDirPath } = getThemeSrcDirPath({ reactAppRootDirPath });
const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", pageBasename); const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", pageBasename);

View File

@ -7,10 +7,10 @@ import { themeTypes } from "./constants";
const themeSrcDirBasename = "keycloak-theme"; const themeSrcDirBasename = "keycloak-theme";
/** Can't catch error, if the directory isn't found, this function will just exit the process with an error message. */ /** Can't catch error, if the directory isn't found, this function will just exit the process with an error message. */
export function getThemeSrcDirPath(params: { projectDirPath: string }) { export function getThemeSrcDirPath(params: { reactAppRootDirPath: string }) {
const { projectDirPath } = params; const { reactAppRootDirPath } = params;
const srcDirPath = pathJoin(projectDirPath, "src"); const srcDirPath = pathJoin(reactAppRootDirPath, "src");
const themeSrcDirPath: string | undefined = crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" }) const themeSrcDirPath: string | undefined = crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })
.map(fileRelativePath => { .map(fileRelativePath => {

View File

@ -10,17 +10,17 @@ import { getLogger } from "./tools/logger";
import { getThemeSrcDirPath } from "./getSrcDirPath"; import { getThemeSrcDirPath } from "./getSrcDirPath";
export async function main() { export async function main() {
const projectDirPath = process.cwd(); const reactAppRootDirPath = process.cwd();
const { isSilent } = readBuildOptions({ const buildOptions = readBuildOptions({
projectDirPath, reactAppRootDirPath,
"processArgv": process.argv.slice(2) "processArgv": process.argv.slice(2)
}); });
const logger = getLogger({ isSilent }); const logger = getLogger({ "isSilent": buildOptions.isSilent });
const { themeSrcDirPath } = getThemeSrcDirPath({ const { themeSrcDirPath } = getThemeSrcDirPath({
projectDirPath reactAppRootDirPath
}); });
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email"); const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");
@ -36,9 +36,9 @@ export async function main() {
const builtinKeycloakThemeTmpDirPath = pathJoin(emailThemeSrcDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme"); const builtinKeycloakThemeTmpDirPath = pathJoin(emailThemeSrcDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
await downloadBuiltinKeycloakTheme({ await downloadBuiltinKeycloakTheme({
projectDirPath,
keycloakVersion, keycloakVersion,
"destDirPath": builtinKeycloakThemeTmpDirPath "destDirPath": builtinKeycloakThemeTmpDirPath,
buildOptions
}); });
transformCodebase({ transformCodebase({

View File

@ -1,7 +1,8 @@
import { parse as urlParse } from "url"; import { parse as urlParse } from "url";
import { getParsedPackageJson } from "./parsedPackageJson"; import { getParsedPackageJson } from "./parsedPackageJson";
import { join as pathJoin, sep as pathSep } from "path"; import { join as pathJoin } from "path";
import parseArgv from "minimist"; import parseArgv from "minimist";
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
/** Consolidated build option gathered form CLI arguments and config in package.json */ /** Consolidated build option gathered form CLI arguments and config in package.json */
export type BuildOptions = { export type BuildOptions = {
@ -14,17 +15,20 @@ export type BuildOptions = {
artifactId: string; artifactId: string;
doCreateJar: boolean; doCreateJar: boolean;
loginThemeResourcesFromKeycloakVersion: string; loginThemeResourcesFromKeycloakVersion: string;
reactAppRootDirPath: string;
/** Directory of your built react project. Defaults to {cwd}/build */ /** Directory of your built react project. Defaults to {cwd}/build */
reactAppBuildDirPath: string; reactAppBuildDirPath: string;
/** Directory that keycloakify outputs to. Defaults to {cwd}/build_keycloak */ /** Directory that keycloakify outputs to. Defaults to {cwd}/build_keycloak */
keycloakifyBuildDirPath: string; keycloakifyBuildDirPath: string;
publicDirPath: string;
cacheDirPath: string;
/** If your app is hosted under a subpath, it's the case in CRA if you have "homepage": "https://example.com/my-app" in your package.json /** If your app is hosted under a subpath, it's the case in CRA if you have "homepage": "https://example.com/my-app" in your package.json
* In this case the urlPathname will be "/my-app/" */ * In this case the urlPathname will be "/my-app/" */
urlPathname: string | undefined; urlPathname: string | undefined;
}; };
export function readBuildOptions(params: { projectDirPath: string; processArgv: string[] }): BuildOptions { export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions {
const { projectDirPath, processArgv } = params; const { reactAppRootDirPath, processArgv } = params;
const { isSilentCliParamProvided } = (() => { const { isSilentCliParamProvided } = (() => {
const argv = parseArgv(processArgv); const argv = parseArgv(processArgv);
@ -34,7 +38,7 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
}; };
})(); })();
const parsedPackageJson = getParsedPackageJson({ projectDirPath }); const parsedPackageJson = getParsedPackageJson({ reactAppRootDirPath });
const { name, keycloakify = {}, version, homepage } = parsedPackageJson; const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
@ -55,6 +59,7 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
.join("-"); .join("-");
return { return {
reactAppRootDirPath,
themeName, themeName,
extraThemeNames, extraThemeNames,
"doCreateJar": doCreateJar ?? true, "doCreateJar": doCreateJar ?? true,
@ -78,40 +83,57 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
extraThemeProperties, extraThemeProperties,
"isSilent": isSilentCliParamProvided, "isSilent": isSilentCliParamProvided,
"loginThemeResourcesFromKeycloakVersion": loginThemeResourcesFromKeycloakVersion ?? "11.0.3", "loginThemeResourcesFromKeycloakVersion": loginThemeResourcesFromKeycloakVersion ?? "11.0.3",
"publicDirPath": (() => {
let { PUBLIC_DIR_PATH } = process.env;
if (PUBLIC_DIR_PATH !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": PUBLIC_DIR_PATH,
"cwd": reactAppRootDirPath
});
}
return pathJoin(reactAppRootDirPath, "public");
})(),
"reactAppBuildDirPath": (() => { "reactAppBuildDirPath": (() => {
let { reactAppBuildDirPath = undefined } = parsedPackageJson.keycloakify ?? {}; const { reactAppBuildDirPath } = parsedPackageJson.keycloakify ?? {};
if (reactAppBuildDirPath === undefined) { if (reactAppBuildDirPath !== undefined) {
return pathJoin(projectDirPath, "build"); return getAbsoluteAndInOsFormatPath({
"pathIsh": reactAppBuildDirPath,
"cwd": reactAppRootDirPath
});
} }
if (pathSep === "\\") { return pathJoin(reactAppRootDirPath, "build");
reactAppBuildDirPath = reactAppBuildDirPath.replace(/\//g, pathSep);
}
if (reactAppBuildDirPath.startsWith(`.${pathSep}`)) {
return pathJoin(projectDirPath, reactAppBuildDirPath);
}
return reactAppBuildDirPath;
})(), })(),
"keycloakifyBuildDirPath": (() => { "keycloakifyBuildDirPath": (() => {
let { keycloakifyBuildDirPath = undefined } = parsedPackageJson.keycloakify ?? {}; const { keycloakifyBuildDirPath } = parsedPackageJson.keycloakify ?? {};
if (keycloakifyBuildDirPath === undefined) { if (keycloakifyBuildDirPath !== undefined) {
return pathJoin(projectDirPath, "build_keycloak"); return getAbsoluteAndInOsFormatPath({
"pathIsh": keycloakifyBuildDirPath,
"cwd": reactAppRootDirPath
});
} }
if (pathSep === "\\") { return pathJoin(reactAppRootDirPath, "build_keycloak");
keycloakifyBuildDirPath = keycloakifyBuildDirPath.replace(/\//g, pathSep);
}
if (keycloakifyBuildDirPath.startsWith(`.${pathSep}`)) {
return pathJoin(projectDirPath, keycloakifyBuildDirPath);
}
return keycloakifyBuildDirPath;
})(), })(),
"cacheDirPath": pathJoin(
(() => {
let { XDG_CACHE_HOME } = process.env;
if (XDG_CACHE_HOME !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": XDG_CACHE_HOME,
"cwd": reactAppRootDirPath
});
}
return pathJoin(reactAppRootDirPath, "node_modules", ".cache");
})(),
"keycloakify"
),
"urlPathname": (() => { "urlPathname": (() => {
const { homepage } = parsedPackageJson; const { homepage } = parsedPackageJson;

View File

@ -13,6 +13,7 @@ export type BuildOptionsLike = {
groupId: string; groupId: string;
artifactId: string; artifactId: string;
themeVersion: string; themeVersion: string;
cacheDirPath: string;
}; };
{ {
@ -22,19 +23,13 @@ export type BuildOptionsLike = {
} }
export async function generateJavaStackFiles(params: { export async function generateJavaStackFiles(params: {
projectDirPath: string;
keycloakThemeBuildingDirPath: string; keycloakThemeBuildingDirPath: string;
implementedThemeTypes: Record<ThemeType | "email", boolean>; implementedThemeTypes: Record<ThemeType | "email", boolean>;
buildOptions: BuildOptionsLike; buildOptions: BuildOptionsLike;
}): Promise<{ }): Promise<{
jarFilePath: string; jarFilePath: string;
}> { }> {
const { const { keycloakThemeBuildingDirPath, implementedThemeTypes, buildOptions } = params;
projectDirPath,
buildOptions: { groupId, themeName, extraThemeNames, themeVersion, artifactId },
keycloakThemeBuildingDirPath,
implementedThemeTypes
} = params;
{ {
const { pomFileCode } = (function generatePomFileCode(): { const { pomFileCode } = (function generatePomFileCode(): {
@ -46,10 +41,10 @@ export async function generateJavaStackFiles(params: {
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`, ` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`, ` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
` <modelVersion>4.0.0</modelVersion>`, ` <modelVersion>4.0.0</modelVersion>`,
` <groupId>${groupId}</groupId>`, ` <groupId>${buildOptions.groupId}</groupId>`,
` <artifactId>${artifactId}</artifactId>`, ` <artifactId>${buildOptions.artifactId}</artifactId>`,
` <version>${themeVersion}</version>`, ` <version>${buildOptions.themeVersion}</version>`,
` <name>${artifactId}</name>`, ` <name>${buildOptions.artifactId}</name>`,
` <description />`, ` <description />`,
` <packaging>jar</packaging>`, ` <packaging>jar</packaging>`,
` <properties>`, ` <properties>`,
@ -165,9 +160,9 @@ export async function generateJavaStackFiles(params: {
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "tmp_yxdE2_builtin_keycloak_theme"); const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "tmp_yxdE2_builtin_keycloak_theme");
await downloadBuiltinKeycloakTheme({ await downloadBuiltinKeycloakTheme({
projectDirPath,
"destDirPath": builtinKeycloakThemeTmpDirPath, "destDirPath": builtinKeycloakThemeTmpDirPath,
"keycloakVersion": lastKeycloakVersionWithAccountV1 "keycloakVersion": lastKeycloakVersionWithAccountV1,
buildOptions
}); });
const accountV1DirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", accountV1, "account"); const accountV1DirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", accountV1, "account");
@ -249,7 +244,7 @@ export async function generateJavaStackFiles(params: {
"name": accountV1, "name": accountV1,
"types": ["account"] "types": ["account"]
}, },
...[themeName, ...extraThemeNames].map(themeName => ({ ...[buildOptions.themeName, ...buildOptions.extraThemeNames].map(themeName => ({
"name": themeName, "name": themeName,
"types": Object.entries(implementedThemeTypes) "types": Object.entries(implementedThemeTypes)
.filter(([, isImplemented]) => isImplemented) .filter(([, isImplemented]) => isImplemented)
@ -266,6 +261,6 @@ export async function generateJavaStackFiles(params: {
} }
return { return {
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${artifactId}-${themeVersion}.jar`) "jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`)
}; };
} }

View File

@ -3,22 +3,30 @@ import * as fs from "fs";
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { downloadBuiltinKeycloakTheme } from "../../download-builtin-keycloak-theme"; import { downloadBuiltinKeycloakTheme } from "../../download-builtin-keycloak-theme";
import { resources_common, type ThemeType } from "../../constants"; import { resources_common, type ThemeType } from "../../constants";
import { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import * as crypto from "crypto"; import * as crypto from "crypto";
export type BuildOptionsLike = {
cacheDirPath: string;
};
assert<BuildOptions extends BuildOptionsLike ? true : false>();
export async function downloadKeycloakStaticResources( export async function downloadKeycloakStaticResources(
// prettier-ignore // prettier-ignore
params: { params: {
projectDirPath: string;
themeType: ThemeType; themeType: ThemeType;
themeDirPath: string; themeDirPath: string;
keycloakVersion: string; keycloakVersion: string;
usedResources: { usedResources: {
resourcesCommonFilePaths: string[]; resourcesCommonFilePaths: string[];
resourcesFilePaths: string[]; resourcesFilePaths: string[];
} | undefined } | undefined;
buildOptions: BuildOptionsLike;
} }
) { ) {
const { projectDirPath, themeType, themeDirPath, keycloakVersion, usedResources } = params; const { themeType, themeDirPath, keycloakVersion, usedResources, buildOptions } = params;
const tmpDirPath = pathJoin( const tmpDirPath = pathJoin(
themeDirPath, themeDirPath,
@ -27,9 +35,9 @@ export async function downloadKeycloakStaticResources(
); );
await downloadBuiltinKeycloakTheme({ await downloadBuiltinKeycloakTheme({
projectDirPath,
keycloakVersion, keycloakVersion,
"destDirPath": tmpDirPath "destDirPath": tmpDirPath,
buildOptions
}); });
const resourcesPath = pathJoin(themeDirPath, themeType, "resources"); const resourcesPath = pathJoin(themeDirPath, themeType, "resources");

View File

@ -20,31 +20,23 @@ export type BuildOptionsLike = {
themeVersion: string; themeVersion: string;
loginThemeResourcesFromKeycloakVersion: string; loginThemeResourcesFromKeycloakVersion: string;
urlPathname: string | undefined; urlPathname: string | undefined;
keycloakifyBuildDirPath: string;
reactAppBuildDirPath: string;
cacheDirPath: string;
}; };
assert<BuildOptions extends BuildOptionsLike ? true : false>(); assert<BuildOptions extends BuildOptionsLike ? true : false>();
export async function generateTheme(params: { export async function generateTheme(params: {
projectDirPath: string;
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
themeSrcDirPath: string; themeSrcDirPath: string;
keycloakifySrcDirPath: string; keycloakifySrcDirPath: string;
buildOptions: BuildOptionsLike; buildOptions: BuildOptionsLike;
keycloakifyVersion: string; keycloakifyVersion: string;
}): Promise<void> { }): Promise<void> {
const { const { themeSrcDirPath, keycloakifySrcDirPath, buildOptions, keycloakifyVersion } = params;
projectDirPath,
reactAppBuildDirPath,
keycloakThemeBuildingDirPath,
themeSrcDirPath,
keycloakifySrcDirPath,
buildOptions,
keycloakifyVersion
} = params;
const getThemeDirPath = (themeType: ThemeType | "email") => const getThemeDirPath = (themeType: ThemeType | "email") =>
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType); pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
let allCssGlobalsToDefine: Record<string, string> = {}; let allCssGlobalsToDefine: Record<string, string> = {};
@ -66,12 +58,12 @@ export async function generateTheme(params: {
transformCodebase({ transformCodebase({
"destDirPath": pathJoin(themeDirPath, "resources", "build"), "destDirPath": pathJoin(themeDirPath, "resources", "build"),
"srcDirPath": reactAppBuildDirPath, "srcDirPath": buildOptions.reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => { "transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/ //NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if ( if (
isInside({ isInside({
"dirPath": pathJoin(reactAppBuildDirPath, keycloak_resources), "dirPath": pathJoin(buildOptions.reactAppBuildDirPath, keycloak_resources),
filePath filePath
}) })
) { ) {
@ -114,7 +106,7 @@ export async function generateTheme(params: {
generateFtlFilesCode_glob !== undefined generateFtlFilesCode_glob !== undefined
? generateFtlFilesCode_glob ? generateFtlFilesCode_glob
: generateFtlFilesCodeFactory({ : generateFtlFilesCodeFactory({
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"), "indexHtmlCode": fs.readFileSync(pathJoin(buildOptions.reactAppBuildDirPath, "index.html")).toString("utf8"),
"cssGlobalsToDefine": allCssGlobalsToDefine, "cssGlobalsToDefine": allCssGlobalsToDefine,
buildOptions, buildOptions,
keycloakifyVersion, keycloakifyVersion,
@ -161,7 +153,6 @@ export async function generateTheme(params: {
}); });
await downloadKeycloakStaticResources({ await downloadKeycloakStaticResources({
projectDirPath,
"keycloakVersion": (() => { "keycloakVersion": (() => {
switch (themeType) { switch (themeType) {
case "account": case "account":
@ -176,7 +167,8 @@ export async function generateTheme(params: {
keycloakifySrcDirPath, keycloakifySrcDirPath,
themeSrcDirPath, themeSrcDirPath,
themeType themeType
}) }),
buildOptions
}); });
fs.writeFileSync( fs.writeFileSync(

View File

@ -12,10 +12,10 @@ import { getProjectRoot } from "../tools/getProjectRoot";
import { objectKeys } from "tsafe/objectKeys"; import { objectKeys } from "tsafe/objectKeys";
export async function main() { export async function main() {
const projectDirPath = process.cwd(); const reactAppRootDirPath = process.cwd();
const buildOptions = readBuildOptions({ const buildOptions = readBuildOptions({
projectDirPath, reactAppRootDirPath,
"processArgv": process.argv.slice(2) "processArgv": process.argv.slice(2)
}); });
@ -24,15 +24,12 @@ export async function main() {
const keycloakifyDirPath = getProjectRoot(); const keycloakifyDirPath = getProjectRoot();
const { themeSrcDirPath } = getThemeSrcDirPath({ projectDirPath }); const { themeSrcDirPath } = getThemeSrcDirPath({ reactAppRootDirPath });
for (const themeName of [buildOptions.themeName, ...buildOptions.extraThemeNames]) { for (const themeName of [buildOptions.themeName, ...buildOptions.extraThemeNames]) {
await generateTheme({ await generateTheme({
projectDirPath,
"keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
themeSrcDirPath, themeSrcDirPath,
"keycloakifySrcDirPath": pathJoin(keycloakifyDirPath, "src"), "keycloakifySrcDirPath": pathJoin(keycloakifyDirPath, "src"),
"reactAppBuildDirPath": buildOptions.reactAppBuildDirPath,
"buildOptions": { "buildOptions": {
...buildOptions, ...buildOptions,
"themeName": themeName "themeName": themeName
@ -48,7 +45,6 @@ export async function main() {
} }
const { jarFilePath } = await generateJavaStackFiles({ const { jarFilePath } = await generateJavaStackFiles({
projectDirPath,
"keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath, "keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
"implementedThemeTypes": (() => { "implementedThemeTypes": (() => {
const implementedThemeTypes = { const implementedThemeTypes = {
@ -89,7 +85,7 @@ export async function main() {
logger.log( logger.log(
[ [
"", "",
`✅ Your keycloak theme has been generated and bundled into .${pathSep}${pathRelative(projectDirPath, jarFilePath)} 🚀`, `✅ Your keycloak theme has been generated and bundled into .${pathSep}${pathRelative(reactAppRootDirPath, jarFilePath)} 🚀`,
`It is to be placed in "/opt/keycloak/providers" in the container running a quay.io/keycloak/keycloak Docker image.`, `It is to be placed in "/opt/keycloak/providers" in the container running a quay.io/keycloak/keycloak Docker image.`,
"", "",
//TODO: Restore when we find a good Helm chart for Keycloak. //TODO: Restore when we find a good Helm chart for Keycloak.
@ -124,7 +120,7 @@ export async function main() {
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`, `To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
"", "",
`👉 $ .${pathSep}${pathRelative( `👉 $ .${pathSep}${pathRelative(
projectDirPath, reactAppRootDirPath,
pathJoin(buildOptions.keycloakifyBuildDirPath, generateStartKeycloakTestingContainer.basename) pathJoin(buildOptions.keycloakifyBuildDirPath, generateStartKeycloakTestingContainer.basename)
)} 👈`, )} 👈`,
"", "",

View File

@ -45,11 +45,11 @@ export const zParsedPackageJson = z.object({
assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>(); assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>();
let parsedPackageJson: undefined | ReturnType<(typeof zParsedPackageJson)["parse"]>; let parsedPackageJson: undefined | ReturnType<(typeof zParsedPackageJson)["parse"]>;
export function getParsedPackageJson(params: { projectDirPath: string }) { export function getParsedPackageJson(params: { reactAppRootDirPath: string }) {
const { projectDirPath } = params; const { reactAppRootDirPath } = params;
if (parsedPackageJson) { if (parsedPackageJson) {
return parsedPackageJson; return parsedPackageJson;
} }
parsedPackageJson = zParsedPackageJson.parse(JSON.parse(fs.readFileSync(pathJoin(projectDirPath, "package.json")).toString("utf8"))); parsedPackageJson = zParsedPackageJson.parse(JSON.parse(fs.readFileSync(pathJoin(reactAppRootDirPath, "package.json")).toString("utf8")));
return parsedPackageJson; return parsedPackageJson;
} }

View File

@ -162,8 +162,7 @@ export async function downloadAndUnzip(
} & ( } & (
| { | {
doUseCache: true; doUseCache: true;
// TODO: Get rid of this parameter, it's a pain to pass around cacheDirPath: string;
projectDirPath: string;
} }
| { | {
doUseCache: false; doUseCache: false;
@ -183,11 +182,10 @@ export async function downloadAndUnzip(
} }
}); });
const cacheRoot = !rest.doUseCache const cacheDirPath = !rest.doUseCache ? `tmp_${Math.random().toString().slice(2, 12)}` : rest.cacheDirPath;
? `tmp_${Math.random().toString().slice(2, 12)}`
: pathJoin(process.env.XDG_CACHE_HOME ?? pathJoin(rest.projectDirPath, "node_modules", ".cache"), "keycloakify"); const zipFilePath = pathJoin(cacheDirPath, `${zipFileBasename}.zip`);
const zipFilePath = pathJoin(cacheRoot, `${zipFileBasename}.zip`); const extractDirPath = pathJoin(cacheDirPath, `tmp_unzip_${zipFileBasename}`);
const extractDirPath = pathJoin(cacheRoot, `tmp_unzip_${zipFileBasename}`);
if (!(await exists(zipFilePath))) { if (!(await exists(zipFilePath))) {
const opts = await getFetchOptions(); const opts = await getFetchOptions();
@ -227,7 +225,7 @@ export async function downloadAndUnzip(
}); });
if (!rest.doUseCache) { if (!rest.doUseCache) {
await rm(cacheRoot, { "recursive": true }); await rm(cacheDirPath, { "recursive": true });
} else { } else {
await rm(extractDirPath, { "recursive": true }); await rm(extractDirPath, { "recursive": true });
} }

View File

@ -0,0 +1,15 @@
import { isAbsolute as pathIsAbsolute, sep as pathSep, join as pathJoin } from "path";
export function getAbsoluteAndInOsFormatPath(params: { pathIsh: string; cwd: string }): string {
const { pathIsh, cwd } = params;
let pathOut = pathIsh;
pathOut = pathOut.replace(/\//g, pathSep);
if (!pathIsAbsolute(pathOut)) {
pathOut = pathJoin(cwd, pathOut);
}
return pathOut;
}