Refactor build option managment
This commit is contained in:
parent
8c3e9ff192
commit
18f0f3cce1
@ -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.`);
|
||||||
})();
|
})();
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 => {
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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`)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
)} 👈`,
|
)} 👈`,
|
||||||
"",
|
"",
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
15
src/bin/tools/getAbsoluteAndInOsFormatPath.ts
Normal file
15
src/bin/tools/getAbsoluteAndInOsFormatPath.ts
Normal 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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user