bin general reresh, introducing termost
This commit is contained in:
@ -1,112 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic";
|
||||
import { readBuildOptions } from "./shared/buildOptions";
|
||||
import type { CliCommandOptions } from "./main";
|
||||
|
||||
import { downloadKeycloakStaticResources, type BuildOptionsLike } from "./keycloakify/generateTheme/downloadKeycloakStaticResources";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { readBuildOptions } from "./keycloakify/buildOptions";
|
||||
import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "./constants";
|
||||
import { readThisNpmProjectVersion } from "./tools/readThisNpmProjectVersion";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import * as fs from "fs";
|
||||
import { rmSync } from "./tools/fs.rmSync";
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
export async function copyKeycloakResourcesToPublic(params: { processArgv: string[] }) {
|
||||
const { processArgv } = params;
|
||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||
|
||||
const buildOptions = readBuildOptions({ processArgv });
|
||||
|
||||
const destDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
|
||||
|
||||
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
|
||||
|
||||
const { keycloakifyBuildinfoRaw } = generateKeycloakifyBuildinfoRaw({
|
||||
destDirPath,
|
||||
"keycloakifyVersion": readThisNpmProjectVersion(),
|
||||
buildOptions
|
||||
});
|
||||
|
||||
skip_if_already_done: {
|
||||
if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
|
||||
break skip_if_already_done;
|
||||
}
|
||||
|
||||
const keycloakifyBuildinfoRaw_previousRun = fs.readFileSync(keycloakifyBuildinfoFilePath).toString("utf8");
|
||||
|
||||
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
|
||||
break skip_if_already_done;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rmSync(destDirPath, { "force": true, "recursive": true });
|
||||
|
||||
for (const themeType of themeTypes) {
|
||||
await downloadKeycloakStaticResources({
|
||||
"keycloakVersion": (() => {
|
||||
switch (themeType) {
|
||||
case "login":
|
||||
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
||||
case "account":
|
||||
return lastKeycloakVersionWithAccountV1;
|
||||
}
|
||||
})(),
|
||||
themeType,
|
||||
"themeDirPath": destDirPath,
|
||||
buildOptions
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(destDirPath, "README.txt"),
|
||||
Buffer.from(
|
||||
// prettier-ignore
|
||||
[
|
||||
"This is just a test folder that helps develop",
|
||||
"the login and register page without having to run a Keycloak container"
|
||||
].join(" ")
|
||||
)
|
||||
);
|
||||
|
||||
fs.writeFileSync(pathJoin(buildOptions.publicDirPath, keycloak_resources, ".gitignore"), Buffer.from("*", "utf8"));
|
||||
|
||||
fs.writeFileSync(keycloakifyBuildinfoFilePath, Buffer.from(keycloakifyBuildinfoRaw, "utf8"));
|
||||
}
|
||||
|
||||
export function generateKeycloakifyBuildinfoRaw(params: {
|
||||
destDirPath: string;
|
||||
keycloakifyVersion: string;
|
||||
buildOptions: BuildOptionsLike & {
|
||||
loginThemeResourcesFromKeycloakVersion: string;
|
||||
};
|
||||
}) {
|
||||
const { destDirPath, keycloakifyVersion, buildOptions } = params;
|
||||
|
||||
const { cacheDirPath, npmWorkspaceRootDirPath, loginThemeResourcesFromKeycloakVersion, ...rest } = buildOptions;
|
||||
|
||||
assert<Equals<typeof rest, {}>>(true);
|
||||
|
||||
const keycloakifyBuildinfoRaw = JSON.stringify(
|
||||
{
|
||||
keycloakifyVersion,
|
||||
"buildOptions": {
|
||||
loginThemeResourcesFromKeycloakVersion,
|
||||
"cacheDirPath": pathRelative(destDirPath, cacheDirPath),
|
||||
"npmWorkspaceRootDirPath": pathRelative(destDirPath, npmWorkspaceRootDirPath)
|
||||
}
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
|
||||
return { keycloakifyBuildinfoRaw };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await copyKeycloakResourcesToPublic({
|
||||
"processArgv": process.argv.slice(2)
|
||||
"buildOptions": {
|
||||
...buildOptions,
|
||||
"publicDirPath": buildOptions.reactAppRootDirPath
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
@ -1,282 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
import { join as pathJoin } from "path";
|
||||
import { downloadAndUnzip } from "./downloadAndUnzip";
|
||||
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
|
||||
import { readBuildOptions } from "./shared/buildOptions";
|
||||
import { downloadBuiltinKeycloakTheme } from "./shared/downloadBuiltinKeycloakTheme";
|
||||
import type { CliCommandOptions } from "./main";
|
||||
import { getLogger } from "./tools/logger";
|
||||
import { readBuildOptions, type BuildOptions } from "./keycloakify/buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import * as child_process from "child_process";
|
||||
import * as fs from "fs";
|
||||
import { rmSync } from "./tools/fs.rmSync";
|
||||
import { lastKeycloakVersionWithAccountV1 } from "./constants";
|
||||
import { transformCodebase } from "./tools/transformCodebase";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
cacheDirPath: string;
|
||||
npmWorkspaceRootDirPath: string;
|
||||
};
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
|
||||
export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; buildOptions: BuildOptionsLike }) {
|
||||
const { keycloakVersion, destDirPath, buildOptions } = params;
|
||||
|
||||
await downloadAndUnzip({
|
||||
destDirPath,
|
||||
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
||||
"specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
|
||||
buildOptions,
|
||||
"preCacheTransform": {
|
||||
"actionCacheId": "npm install and build",
|
||||
"action": async ({ destDirPath }) => {
|
||||
install_common_node_modules: {
|
||||
const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
|
||||
|
||||
if (!fs.existsSync(commonResourcesDirPath)) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(pathJoin(commonResourcesDirPath, "package.json"))) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
if (fs.existsSync(pathJoin(commonResourcesDirPath, "node_modules"))) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
child_process.execSync("npm install --omit=dev", {
|
||||
"cwd": commonResourcesDirPath,
|
||||
"stdio": "ignore"
|
||||
});
|
||||
}
|
||||
|
||||
repatriate_common_resources_from_base_login_theme: {
|
||||
const baseLoginThemeResourceDir = pathJoin(destDirPath, "base", "login", "resources");
|
||||
|
||||
if (!fs.existsSync(baseLoginThemeResourceDir)) {
|
||||
break repatriate_common_resources_from_base_login_theme;
|
||||
}
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": baseLoginThemeResourceDir,
|
||||
"destDirPath": pathJoin(destDirPath, "keycloak", "login", "resources")
|
||||
});
|
||||
}
|
||||
|
||||
install_and_move_to_common_resources_generated_in_keycloak_v2: {
|
||||
if (!fs.readFileSync(pathJoin(destDirPath, "keycloak", "login", "theme.properties")).toString("utf8").includes("web_modules")) {
|
||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||
}
|
||||
|
||||
const accountV2DirSrcDirPath = pathJoin(destDirPath, "keycloak.v2", "account", "src");
|
||||
|
||||
if (!fs.existsSync(accountV2DirSrcDirPath)) {
|
||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||
}
|
||||
|
||||
const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
||||
|
||||
if (packageManager === "pnpm") {
|
||||
try {
|
||||
child_process.execSync(`which pnpm`);
|
||||
} catch {
|
||||
console.log(`Installing pnpm globally`);
|
||||
child_process.execSync(`npm install -g pnpm`);
|
||||
}
|
||||
}
|
||||
|
||||
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
||||
|
||||
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
|
||||
|
||||
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
|
||||
|
||||
const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8"));
|
||||
|
||||
parsedPackageJson.scripts.build = parsedPackageJson.scripts.build
|
||||
.replace(`${packageManager} run check-types`, "true")
|
||||
.replace(`${packageManager} run babel`, "true");
|
||||
|
||||
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
|
||||
|
||||
child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
||||
|
||||
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
|
||||
|
||||
fs.rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), { "recursive": true });
|
||||
}
|
||||
|
||||
remove_keycloak_v2: {
|
||||
const keycloakV2DirPath = pathJoin(destDirPath, "keycloak.v2");
|
||||
|
||||
if (!fs.existsSync(keycloakV2DirPath)) {
|
||||
break remove_keycloak_v2;
|
||||
}
|
||||
|
||||
rmSync(keycloakV2DirPath, { "recursive": true });
|
||||
}
|
||||
|
||||
// Note, this is an optimization for reducing the size of the jar
|
||||
remove_unused_node_modules: {
|
||||
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
||||
|
||||
if (!fs.existsSync(nodeModuleDirPath)) {
|
||||
break remove_unused_node_modules;
|
||||
}
|
||||
|
||||
const toDeletePerfixes = [
|
||||
"angular",
|
||||
"bootstrap",
|
||||
"rcue",
|
||||
"font-awesome",
|
||||
"ng-file-upload",
|
||||
pathJoin("patternfly", "dist", "sass"),
|
||||
pathJoin("patternfly", "dist", "less"),
|
||||
pathJoin("patternfly", "dist", "js"),
|
||||
"d3",
|
||||
pathJoin("jquery", "src"),
|
||||
"c3",
|
||||
"core-js",
|
||||
"eonasdan-bootstrap-datetimepicker",
|
||||
"moment",
|
||||
"react",
|
||||
"patternfly-bootstrap-treeview",
|
||||
"popper.js",
|
||||
"tippy.js",
|
||||
"jquery-match-height",
|
||||
"google-code-prettify",
|
||||
"patternfly-bootstrap-combobox",
|
||||
"focus-trap",
|
||||
"tabbable",
|
||||
"scheduler",
|
||||
"@types",
|
||||
"datatables.net",
|
||||
"datatables.net-colreorder",
|
||||
"tslib",
|
||||
"prop-types",
|
||||
"file-selector",
|
||||
"datatables.net-colreorder-bs",
|
||||
"object-assign",
|
||||
"warning",
|
||||
"js-tokens",
|
||||
"loose-envify",
|
||||
"prop-types-extra",
|
||||
"attr-accept",
|
||||
"datatables.net-select",
|
||||
"drmonty-datatables-colvis",
|
||||
"datatables.net-bs",
|
||||
pathJoin("@patternfly", "react"),
|
||||
pathJoin("@patternfly", "patternfly", "docs")
|
||||
];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": nodeModuleDirPath,
|
||||
"destDirPath": nodeModuleDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (fileRelativePath.endsWith(".map")) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (fileRelativePath.startsWith(pathJoin("patternfly", "dist", "fonts"))) {
|
||||
if (
|
||||
!fileRelativePath.endsWith(".woff2") &&
|
||||
!fileRelativePath.endsWith(".woff") &&
|
||||
!fileRelativePath.endsWith(".ttf")
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Just like node_modules
|
||||
remove_unused_lib: {
|
||||
const libDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "lib");
|
||||
|
||||
if (!fs.existsSync(libDirPath)) {
|
||||
break remove_unused_lib;
|
||||
}
|
||||
|
||||
const toDeletePerfixes = ["ui-ace", "filesaver", "fileupload", "angular", "ui-ace"];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": libDirPath,
|
||||
"destDirPath": libDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (fileRelativePath.endsWith(".map")) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
last_account_v1_transformations: {
|
||||
if (lastKeycloakVersionWithAccountV1 !== keycloakVersion) {
|
||||
break last_account_v1_transformations;
|
||||
}
|
||||
|
||||
{
|
||||
const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css");
|
||||
|
||||
fs.writeFileSync(
|
||||
accountCssFilePath,
|
||||
Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8")
|
||||
);
|
||||
}
|
||||
|
||||
// Note, this is an optimization for reducing the size of the jar,
|
||||
// For this version we know exactly which resources are used.
|
||||
{
|
||||
const nodeModulesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
||||
|
||||
const toKeepPrefixes = [
|
||||
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(fileBasename =>
|
||||
pathJoin("patternfly", "dist", "css", fileBasename)
|
||||
),
|
||||
pathJoin("patternfly", "dist", "fonts")
|
||||
];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": nodeModulesDirPath,
|
||||
"destDirPath": nodeModulesDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (toKeepPrefixes.find(prefix => fileRelativePath.startsWith(prefix)) === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const buildOptions = readBuildOptions({
|
||||
"processArgv": process.argv.slice(2)
|
||||
cliCommandOptions
|
||||
});
|
||||
|
||||
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
||||
const { log } = getLogger({ "isSilent": buildOptions.isSilent });
|
||||
|
||||
const { keycloakVersion } = await promptKeycloakVersion();
|
||||
|
||||
const destDirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme");
|
||||
|
||||
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
||||
log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
||||
|
||||
await downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
@ -284,7 +26,3 @@ async function main() {
|
||||
buildOptions
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
@ -2,24 +2,27 @@
|
||||
|
||||
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
||||
import cliSelect from "cli-select";
|
||||
import { loginThemePageIds, accountThemePageIds, type LoginThemePageId, type AccountThemePageId } from "./keycloakify/generateFtl";
|
||||
import { loginThemePageIds, accountThemePageIds, type LoginThemePageId, type AccountThemePageId } from "./shared/pageIds";
|
||||
import { capitalize } from "tsafe/capitalize";
|
||||
import { readFile, writeFile } from "fs/promises";
|
||||
import { existsSync } from "fs";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
||||
import { assert, Equals } from "tsafe/assert";
|
||||
import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
|
||||
import { themeTypes, type ThemeType } from "./constants";
|
||||
import { getReactAppRootDirPath } from "./keycloakify/buildOptions/getReactAppRootDirPath";
|
||||
import { getThemeSrcDirPath } from "./shared/getThemeSrcDirPath";
|
||||
import { themeTypes, type ThemeType } from "./shared/constants";
|
||||
import type { CliCommandOptions } from "./main";
|
||||
import { readBuildOptions } from "./shared/buildOptions";
|
||||
|
||||
(async () => {
|
||||
console.log("Select a theme type");
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const { reactAppRootDirPath } = getReactAppRootDirPath({
|
||||
"processArgv": process.argv.slice(2)
|
||||
const buildOptions = readBuildOptions({
|
||||
cliCommandOptions
|
||||
});
|
||||
|
||||
console.log("Select a theme type");
|
||||
|
||||
const { value: themeType } = await cliSelect<ThemeType>({
|
||||
"values": [...themeTypes]
|
||||
}).catch(() => {
|
||||
@ -48,7 +51,7 @@ import { getReactAppRootDirPath } from "./keycloakify/buildOptions/getReactAppRo
|
||||
|
||||
const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
|
||||
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath({ reactAppRootDirPath });
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
|
||||
|
||||
const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", pageBasename);
|
||||
|
||||
@ -61,4 +64,4 @@ import { getReactAppRootDirPath } from "./keycloakify/buildOptions/getReactAppRo
|
||||
await writeFile(targetFilePath, await readFile(pathJoin(getThisCodebaseRootDirPath(), "src", themeType, "pages", pageBasename)));
|
||||
|
||||
console.log(`${pathRelative(process.cwd(), targetFilePath)} created`);
|
||||
})();
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
|
||||
import { downloadBuiltinKeycloakTheme } from "./shared/downloadBuiltinKeycloakTheme";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { transformCodebase } from "./tools/transformCodebase";
|
||||
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
import { readBuildOptions } from "./keycloakify/buildOptions";
|
||||
import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
|
||||
import { readBuildOptions } from "./shared/buildOptions";
|
||||
import * as fs from "fs";
|
||||
import { getLogger } from "./tools/logger";
|
||||
import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
|
||||
import { getThemeSrcDirPath } from "./shared/getThemeSrcDirPath";
|
||||
import { rmSync } from "./tools/fs.rmSync";
|
||||
import type { CliCommandOptions } from "./main";
|
||||
|
||||
export async function main() {
|
||||
const buildOptions = readBuildOptions({
|
||||
"processArgv": process.argv.slice(2)
|
||||
});
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||
|
||||
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
||||
|
||||
@ -54,7 +53,3 @@ export async function main() {
|
||||
|
||||
rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ import { assert, type Equals } from "tsafe/assert";
|
||||
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import * as fs from "fs/promises";
|
||||
import { accountV1ThemeName } from "../../constants";
|
||||
import { accountV1ThemeName } from "../../shared/constants";
|
||||
import { generatePom, BuildOptionsLike as BuildOptionsLike_generatePom } from "./generatePom";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { isInside } from "../../tools/isInside";
|
||||
|
@ -3,7 +3,7 @@ import { exclude } from "tsafe/exclude";
|
||||
import { keycloakAccountV1Versions, keycloakThemeAdditionalInfoExtensionVersions } from "./extensionVersions";
|
||||
import { getKeycloakVersionRangeForJar } from "./getKeycloakVersionRangeForJar";
|
||||
import { buildJar, BuildOptionsLike as BuildOptionsLike_buildJar } from "./buildJar";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike_buildJar & {
|
||||
keycloakifyBuildDirPath: string;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
|
@ -1,23 +0,0 @@
|
||||
import parseArgv from "minimist";
|
||||
import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
|
||||
|
||||
export function getReactAppRootDirPath(params: { processArgv: string[] }) {
|
||||
const { processArgv } = params;
|
||||
|
||||
const argv = parseArgv(processArgv);
|
||||
|
||||
const reactAppRootDirPath = (() => {
|
||||
const arg = argv["project"] ?? argv["p"];
|
||||
|
||||
if (typeof arg !== "string") {
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
return getAbsoluteAndInOsFormatPath({
|
||||
"pathIsh": arg,
|
||||
"cwd": process.cwd()
|
||||
});
|
||||
})();
|
||||
|
||||
return { reactAppRootDirPath };
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from "./buildOptions";
|
@ -4,9 +4,9 @@ import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCss
|
||||
import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, resources_common } from "../../constants";
|
||||
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, resources_common } from "../../shared/constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
bundler: "vite" | "webpack";
|
||||
|
@ -1,2 +1 @@
|
||||
export * from "./generateFtl";
|
||||
export * from "./pageId";
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "./buildOptions";
|
||||
import { accountV1ThemeName } from "../constants";
|
||||
import type { BuildOptions } from "../shared/buildOptions";
|
||||
import { accountV1ThemeName } from "../shared/constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
keycloakifyBuildDirPath: string;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import { resources_common, lastKeycloakVersionWithAccountV1, accountV1ThemeName } from "../../constants";
|
||||
import { downloadBuiltinKeycloakTheme } from "../../download-builtin-keycloak-theme";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { resources_common, lastKeycloakVersionWithAccountV1, accountV1ThemeName } from "../../shared/constants";
|
||||
import { downloadBuiltinKeycloakTheme } from "../../shared/downloadBuiltinKeycloakTheme";
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
import { rmSync } from "../../tools/fs.rmSync";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { ThemeType } from "../../constants";
|
||||
import type { ThemeType } from "../../shared/constants";
|
||||
import { crawl } from "../../tools/crawl";
|
||||
import { join as pathJoin } from "path";
|
||||
import { readFileSync } from "fs";
|
||||
|
@ -3,18 +3,19 @@ import * as fs from "fs";
|
||||
import { join as pathJoin, resolve as pathResolve, dirname as pathDirname } from "path";
|
||||
import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode";
|
||||
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
|
||||
import { generateFtlFilesCodeFactory, loginThemePageIds, accountThemePageIds } from "../generateFtl";
|
||||
import { generateFtlFilesCodeFactory } from "../generateFtl";
|
||||
import { loginThemePageIds, accountThemePageIds } from "../../shared/pageIds";
|
||||
import {
|
||||
type ThemeType,
|
||||
lastKeycloakVersionWithAccountV1,
|
||||
keycloak_resources,
|
||||
accountV1ThemeName,
|
||||
basenameOfTheKeycloakifyResourcesDir
|
||||
} from "../../constants";
|
||||
} from "../../shared/constants";
|
||||
import { isInside } from "../../tools/isInside";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import { downloadKeycloakStaticResources } from "./downloadKeycloakStaticResources";
|
||||
import { downloadKeycloakStaticResources } from "../../shared/downloadKeycloakStaticResources";
|
||||
import { readFieldNameUsage } from "./readFieldNameUsage";
|
||||
import { readExtraPagesNames } from "./readExtraPageNames";
|
||||
import { generateMessageProperties } from "./generateMessageProperties";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { join as pathJoin } from "path";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { generateSrcMainResources, type BuildOptionsLike as BuildOptionsLike_generateSrcMainResources } from "./generateSrcMainResources";
|
||||
import { generateThemeVariations } from "./generateThemeVariants";
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { crawl } from "../../tools/crawl";
|
||||
import { accountThemePageIds, loginThemePageIds } from "../generateFtl";
|
||||
import { accountThemePageIds, loginThemePageIds } from "../../shared/pageIds";
|
||||
import { id } from "tsafe/id";
|
||||
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import type { ThemeType } from "../../constants";
|
||||
import type { ThemeType } from "../../shared/constants";
|
||||
|
||||
export function readExtraPagesNames(params: { themeSrcDirPath: string; themeType: ThemeType }): string[] {
|
||||
const { themeSrcDirPath, themeType } = params;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { crawl } from "../../tools/crawl";
|
||||
import { join as pathJoin } from "path";
|
||||
import * as fs from "fs";
|
||||
import type { ThemeType } from "../../constants";
|
||||
import type { ThemeType } from "../../shared/constants";
|
||||
|
||||
/** Assumes the theme type exists */
|
||||
export function readFieldNameUsage(params: { keycloakifySrcDirPath: string; themeSrcDirPath: string; themeType: ThemeType }): string[] {
|
||||
|
@ -1,8 +1 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
export * from "./keycloakify";
|
||||
import { main } from "./keycloakify";
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
@ -3,18 +3,19 @@ import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path
|
||||
import * as child_process from "child_process";
|
||||
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
|
||||
import * as fs from "fs";
|
||||
import { readBuildOptions } from "./buildOptions";
|
||||
import { readBuildOptions } from "../shared/buildOptions";
|
||||
import { getLogger } from "../tools/logger";
|
||||
import { getThemeSrcDirPath } from "../getThemeSrcDirPath";
|
||||
import { getThemeSrcDirPath } from "../shared/getThemeSrcDirPath";
|
||||
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
|
||||
import { readThisNpmProjectVersion } from "../tools/readThisNpmProjectVersion";
|
||||
import { keycloakifyBuildOptionsForPostPostBuildScriptEnvName } from "../constants";
|
||||
import { vitePluginSubScriptEnvNames } from "../shared/constants";
|
||||
import { buildJars } from "./buildJars";
|
||||
import type { CliCommandOptions } from "../main";
|
||||
|
||||
export async function main() {
|
||||
const buildOptions = readBuildOptions({
|
||||
"processArgv": process.argv.slice(2)
|
||||
});
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||
|
||||
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
||||
logger.log("🔏 Building the keycloak theme...⌚");
|
||||
@ -45,7 +46,7 @@ export async function main() {
|
||||
"cwd": buildOptions.reactAppRootDirPath,
|
||||
"env": {
|
||||
...process.env,
|
||||
[keycloakifyBuildOptionsForPostPostBuildScriptEnvName]: JSON.stringify(buildOptions)
|
||||
[vitePluginSubScriptEnvNames.runPostBuildScript]: JSON.stringify(buildOptions)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as crypto from "crypto";
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../constants";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../shared/constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
urlPathname: string | undefined;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { BuildOptions } from "../buildOptions";
|
||||
import type { BuildOptions } from "../../shared/buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../constants";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../shared/constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
urlPathname: string | undefined;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../../buildOptions";
|
||||
import type { BuildOptions } from "../../../shared/buildOptions";
|
||||
import { replaceImportsInJsCode_vite } from "./vite";
|
||||
import { replaceImportsInJsCode_webpack } from "./webpack";
|
||||
import * as fs from "fs";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../constants";
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../../buildOptions";
|
||||
import type { BuildOptions } from "../../../shared/buildOptions";
|
||||
import * as nodePath from "path";
|
||||
import { replaceAll } from "../../../tools/String.prototype.replaceAll";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../constants";
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../../buildOptions";
|
||||
import type { BuildOptions } from "../../../shared/buildOptions";
|
||||
import * as nodePath from "path";
|
||||
import { replaceAll } from "../../../tools/String.prototype.replaceAll";
|
||||
|
||||
|
87
src/bin/main.ts
Normal file
87
src/bin/main.ts
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { termost } from "termost";
|
||||
|
||||
export type CliCommandOptions = {
|
||||
isSilent: boolean;
|
||||
reactAppRootDirPath: string | undefined;
|
||||
};
|
||||
|
||||
const program = termost<CliCommandOptions>("keycloakify");
|
||||
|
||||
program
|
||||
.option({
|
||||
"key": "reactAppRootDirPath",
|
||||
"name": { "long": "project", "short": "p" },
|
||||
"description": "https://docs.keycloakify.dev/build-options#project-or-p-cli-option",
|
||||
"defaultValue": undefined
|
||||
})
|
||||
.option({
|
||||
"key": "isSilent",
|
||||
"name": "silent",
|
||||
"description": "https://docs.keycloakify.dev/build-options#silent",
|
||||
"defaultValue": false
|
||||
});
|
||||
|
||||
program
|
||||
.command({
|
||||
"name": "build",
|
||||
"description": "Build the theme (default subcommand)."
|
||||
})
|
||||
.task({
|
||||
"handler": async cliCommandOptions => {
|
||||
const { command } = await import("./keycloakify");
|
||||
return command({ cliCommandOptions });
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command({
|
||||
"name": "download-builtin-keycloak-theme",
|
||||
"description": "Download the built-in Keycloak theme."
|
||||
})
|
||||
.task({
|
||||
"handler": async cliCommandOptions => {
|
||||
const { command } = await import("./download-builtin-keycloak-theme");
|
||||
return command({ cliCommandOptions });
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command({
|
||||
"name": "eject-keycloak-page",
|
||||
"description": "Eject a Keycloak page."
|
||||
})
|
||||
.task({
|
||||
"handler": async cliCommandOptions => {
|
||||
const { command } = await import("./eject-keycloak-page");
|
||||
return command({ cliCommandOptions });
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command({
|
||||
"name": "initialize-email-theme",
|
||||
"description": "Initialize an email theme."
|
||||
})
|
||||
.task({
|
||||
"handler": async cliCommandOptions => {
|
||||
const { command } = await import("./initialize-email-theme");
|
||||
return command({ cliCommandOptions });
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command({
|
||||
"name": "copy-keycloak-resources-to-public",
|
||||
"description": [
|
||||
"Copy Keycloak default theme resources to the public directory.",
|
||||
"This command is meant to be explicitly used in Webpack projects only."
|
||||
].join("\n")
|
||||
})
|
||||
.task({
|
||||
"handler": async cliCommandOptions => {
|
||||
const { command } = await import("./copy-keycloak-resources-to-public");
|
||||
return command({ cliCommandOptions });
|
||||
}
|
||||
});
|
@ -1,13 +1,12 @@
|
||||
import { parse as urlParse } from "url";
|
||||
import { readParsedPackageJson } from "./parsedPackageJson";
|
||||
import { join as pathJoin } from "path";
|
||||
import parseArgv from "minimist";
|
||||
import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
|
||||
import { readResolvedViteConfig } from "./resolvedViteConfig";
|
||||
import * as fs from "fs";
|
||||
import { getResolvedViteConfig } from "./resolvedViteConfig";
|
||||
import { getCacheDirPath } from "./getCacheDirPath";
|
||||
import { getReactAppRootDirPath } from "./getReactAppRootDirPath";
|
||||
import { getNpmWorkspaceRootDirPath } from "./getNpmWorkspaceRootDirPath";
|
||||
import type { CliCommandOptions } from "../../main";
|
||||
|
||||
/** Consolidated build option gathered form CLI arguments and config in package.json */
|
||||
export type BuildOptions = {
|
||||
@ -32,18 +31,17 @@ export type BuildOptions = {
|
||||
npmWorkspaceRootDirPath: string;
|
||||
};
|
||||
|
||||
export function readBuildOptions(params: { processArgv: string[] }): BuildOptions {
|
||||
const { processArgv } = params;
|
||||
export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions }): BuildOptions {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const { reactAppRootDirPath } = getReactAppRootDirPath({ processArgv });
|
||||
const { reactAppRootDirPath } = getReactAppRootDirPath({ cliCommandOptions });
|
||||
|
||||
const { cacheDirPath } = getCacheDirPath({ reactAppRootDirPath });
|
||||
|
||||
const { resolvedViteConfig } = readResolvedViteConfig({ cacheDirPath });
|
||||
|
||||
if (resolvedViteConfig === undefined && fs.existsSync(pathJoin(reactAppRootDirPath, "vite.config.ts"))) {
|
||||
throw new Error("Keycloakify's Vite plugin output not found");
|
||||
}
|
||||
const { resolvedViteConfig } = getResolvedViteConfig({
|
||||
cacheDirPath,
|
||||
reactAppRootDirPath
|
||||
});
|
||||
|
||||
const { keycloakify: userProvidedBuildOptionsFromPackageJson, ...parsedPackageJson } = readParsedPackageJson({ reactAppRootDirPath });
|
||||
|
||||
@ -88,13 +86,11 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
|
||||
return pathJoin(reactAppRootDirPath, resolvedViteConfig.buildDir);
|
||||
})();
|
||||
|
||||
const argv = parseArgv(processArgv);
|
||||
|
||||
const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({ reactAppRootDirPath });
|
||||
|
||||
return {
|
||||
"bundler": resolvedViteConfig !== undefined ? "vite" : "webpack",
|
||||
"isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false,
|
||||
"isSilent": cliCommandOptions.isSilent,
|
||||
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
|
||||
themeNames,
|
||||
"extraThemeProperties": userProvidedBuildOptions.extraThemeProperties,
|
26
src/bin/shared/buildOptions/getReactAppRootDirPath.ts
Normal file
26
src/bin/shared/buildOptions/getReactAppRootDirPath.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { CliCommandOptions } from "../../main";
|
||||
|
||||
type CliCommandOptionsLike = {
|
||||
reactAppRootDirPath: string | undefined;
|
||||
};
|
||||
|
||||
assert<CliCommandOptions extends CliCommandOptionsLike ? true : false>();
|
||||
|
||||
export function getReactAppRootDirPath(params: { cliCommandOptions: CliCommandOptionsLike }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const reactAppRootDirPath = (() => {
|
||||
if (cliCommandOptions.reactAppRootDirPath === undefined) {
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
return getAbsoluteAndInOsFormatPath({
|
||||
"pathIsh": cliCommandOptions.reactAppRootDirPath,
|
||||
"cwd": process.cwd()
|
||||
});
|
||||
})();
|
||||
|
||||
return { reactAppRootDirPath };
|
||||
}
|
3
src/bin/shared/buildOptions/index.ts
Normal file
3
src/bin/shared/buildOptions/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./buildOptions";
|
||||
export type { UserProvidedBuildOptions } from "./UserProvidedBuildOptions";
|
||||
export type { ResolvedViteConfig } from "./resolvedViteConfig";
|
@ -3,9 +3,10 @@ import { assert } from "tsafe";
|
||||
import type { Equals } from "tsafe";
|
||||
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";
|
||||
import * as child_process from "child_process";
|
||||
import { vitePluginSubScriptEnvNames } from "../constants";
|
||||
|
||||
export type ResolvedViteConfig = {
|
||||
buildDir: string;
|
||||
@ -30,17 +31,25 @@ const zResolvedViteConfig = z.object({
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
export function readResolvedViteConfig(params: { cacheDirPath: string }): {
|
||||
export function getResolvedViteConfig(params: { cacheDirPath: string; reactAppRootDirPath: string }): {
|
||||
resolvedViteConfig: ResolvedViteConfig | undefined;
|
||||
} {
|
||||
const { cacheDirPath } = params;
|
||||
const { cacheDirPath, reactAppRootDirPath } = params;
|
||||
|
||||
const resolvedViteConfigJsonFilePath = pathJoin(cacheDirPath, resolvedViteConfigJsonBasename);
|
||||
const resolvedViteConfigJsonFilePath = pathJoin(cacheDirPath, "vite.json");
|
||||
|
||||
if (!fs.existsSync(resolvedViteConfigJsonFilePath)) {
|
||||
if (fs.readdirSync(reactAppRootDirPath).find(fileBasename => fileBasename.startsWith("vite.config")) === undefined) {
|
||||
return { "resolvedViteConfig": undefined };
|
||||
}
|
||||
|
||||
child_process.execSync("npx vite", {
|
||||
"cwd": reactAppRootDirPath,
|
||||
"env": {
|
||||
...process.env,
|
||||
[vitePluginSubScriptEnvNames.createResolvedViteConfig]: resolvedViteConfigJsonFilePath
|
||||
}
|
||||
});
|
||||
|
||||
const resolvedViteConfig = (() => {
|
||||
if (!fs.existsSync(resolvedViteConfigJsonFilePath)) {
|
||||
throw new Error("Missing Keycloakify Vite plugin output.");
|
@ -2,7 +2,6 @@ export const nameOfTheGlobal = "kcContext";
|
||||
export const keycloak_resources = "keycloak-resources";
|
||||
export const resources_common = "resources-common";
|
||||
export const lastKeycloakVersionWithAccountV1 = "21.1.2";
|
||||
export const resolvedViteConfigJsonBasename = "vite.json";
|
||||
export const basenameOfTheKeycloakifyResourcesDir = "build";
|
||||
|
||||
export const themeTypes = ["login", "account"] as const;
|
||||
@ -10,4 +9,7 @@ export const accountV1ThemeName = "account-v1";
|
||||
|
||||
export type ThemeType = (typeof themeTypes)[number];
|
||||
|
||||
export const keycloakifyBuildOptionsForPostPostBuildScriptEnvName = "KEYCLOAKIFY_BUILD_OPTIONS_POST_POST_BUILD_SCRIPT";
|
||||
export const vitePluginSubScriptEnvNames = {
|
||||
"runPostBuildScript": "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
|
||||
"createResolvedViteConfig": "KEYCLOAKIFY_CREATE_RESOLVED_VITE_CONFIG"
|
||||
};
|
87
src/bin/shared/copyKeycloakResourcesToPublic.ts
Normal file
87
src/bin/shared/copyKeycloakResourcesToPublic.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import {
|
||||
downloadKeycloakStaticResources,
|
||||
type BuildOptionsLike as BuildOptionsLike_downloadKeycloakStaticResources
|
||||
} from "./downloadKeycloakStaticResources";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "../shared/constants";
|
||||
import { readThisNpmProjectVersion } from "../tools/readThisNpmProjectVersion";
|
||||
import { assert } from "tsafe/assert";
|
||||
import * as fs from "fs";
|
||||
import { rmSync } from "../tools/fs.rmSync";
|
||||
import type { BuildOptions } from "./buildOptions";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike_downloadKeycloakStaticResources & {
|
||||
loginThemeResourcesFromKeycloakVersion: string;
|
||||
publicDirPath: string;
|
||||
};
|
||||
|
||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
|
||||
export async function copyKeycloakResourcesToPublic(params: { buildOptions: BuildOptionsLike }) {
|
||||
const { buildOptions } = params;
|
||||
|
||||
const destDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
|
||||
|
||||
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
|
||||
|
||||
const keycloakifyBuildinfoRaw = JSON.stringify(
|
||||
{
|
||||
destDirPath,
|
||||
"keycloakifyVersion": readThisNpmProjectVersion(),
|
||||
"buildOptions": {
|
||||
"loginThemeResourcesFromKeycloakVersion": readThisNpmProjectVersion(),
|
||||
"cacheDirPath": pathRelative(destDirPath, buildOptions.cacheDirPath),
|
||||
"npmWorkspaceRootDirPath": pathRelative(destDirPath, buildOptions.npmWorkspaceRootDirPath)
|
||||
}
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
|
||||
skip_if_already_done: {
|
||||
if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
|
||||
break skip_if_already_done;
|
||||
}
|
||||
|
||||
const keycloakifyBuildinfoRaw_previousRun = fs.readFileSync(keycloakifyBuildinfoFilePath).toString("utf8");
|
||||
|
||||
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
|
||||
break skip_if_already_done;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rmSync(destDirPath, { "force": true, "recursive": true });
|
||||
|
||||
for (const themeType of themeTypes) {
|
||||
await downloadKeycloakStaticResources({
|
||||
"keycloakVersion": (() => {
|
||||
switch (themeType) {
|
||||
case "login":
|
||||
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
||||
case "account":
|
||||
return lastKeycloakVersionWithAccountV1;
|
||||
}
|
||||
})(),
|
||||
themeType,
|
||||
"themeDirPath": destDirPath,
|
||||
buildOptions
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(destDirPath, "README.txt"),
|
||||
Buffer.from(
|
||||
// prettier-ignore
|
||||
[
|
||||
"This is just a test folder that helps develop",
|
||||
"the login and register page without having to run a Keycloak container"
|
||||
].join(" ")
|
||||
)
|
||||
);
|
||||
|
||||
fs.writeFileSync(pathJoin(buildOptions.publicDirPath, keycloak_resources, ".gitignore"), Buffer.from("*", "utf8"));
|
||||
|
||||
fs.writeFileSync(keycloakifyBuildinfoFilePath, Buffer.from(keycloakifyBuildinfoRaw, "utf8"));
|
||||
}
|
@ -3,13 +3,13 @@ import { mkdir, writeFile, unlink } from "fs/promises";
|
||||
import fetch from "make-fetch-happen";
|
||||
import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { transformCodebase } from "./tools/transformCodebase";
|
||||
import { unzip, zip } from "./tools/unzip";
|
||||
import { rm } from "./tools/fs.rm";
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
import { unzip, zip } from "../tools/unzip";
|
||||
import { rm } from "../tools/fs.rm";
|
||||
import * as child_process from "child_process";
|
||||
import { existsAsync } from "./tools/fs.existsAsync";
|
||||
import type { BuildOptions } from "./keycloakify/buildOptions";
|
||||
import { getProxyFetchOptions } from "./tools/fetchProxyOptions";
|
||||
import { existsAsync } from "../tools/fs.existsAsync";
|
||||
import type { BuildOptions } from "./buildOptions";
|
||||
import { getProxyFetchOptions } from "../tools/fetchProxyOptions";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
cacheDirPath: string;
|
264
src/bin/shared/downloadBuiltinKeycloakTheme.ts
Normal file
264
src/bin/shared/downloadBuiltinKeycloakTheme.ts
Normal file
@ -0,0 +1,264 @@
|
||||
import { join as pathJoin } from "path";
|
||||
import { downloadAndUnzip } from "./downloadAndUnzip";
|
||||
import { type BuildOptions } from "./buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import * as child_process from "child_process";
|
||||
import * as fs from "fs";
|
||||
import { rmSync } from "../tools/fs.rmSync";
|
||||
import { lastKeycloakVersionWithAccountV1 } from "./constants";
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
cacheDirPath: string;
|
||||
npmWorkspaceRootDirPath: 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({
|
||||
destDirPath,
|
||||
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
||||
"specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
|
||||
buildOptions,
|
||||
"preCacheTransform": {
|
||||
"actionCacheId": "npm install and build",
|
||||
"action": async ({ destDirPath }) => {
|
||||
install_common_node_modules: {
|
||||
const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
|
||||
|
||||
if (!fs.existsSync(commonResourcesDirPath)) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(pathJoin(commonResourcesDirPath, "package.json"))) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
if (fs.existsSync(pathJoin(commonResourcesDirPath, "node_modules"))) {
|
||||
break install_common_node_modules;
|
||||
}
|
||||
|
||||
child_process.execSync("npm install --omit=dev", {
|
||||
"cwd": commonResourcesDirPath,
|
||||
"stdio": "ignore"
|
||||
});
|
||||
}
|
||||
|
||||
repatriate_common_resources_from_base_login_theme: {
|
||||
const baseLoginThemeResourceDir = pathJoin(destDirPath, "base", "login", "resources");
|
||||
|
||||
if (!fs.existsSync(baseLoginThemeResourceDir)) {
|
||||
break repatriate_common_resources_from_base_login_theme;
|
||||
}
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": baseLoginThemeResourceDir,
|
||||
"destDirPath": pathJoin(destDirPath, "keycloak", "login", "resources")
|
||||
});
|
||||
}
|
||||
|
||||
install_and_move_to_common_resources_generated_in_keycloak_v2: {
|
||||
if (!fs.readFileSync(pathJoin(destDirPath, "keycloak", "login", "theme.properties")).toString("utf8").includes("web_modules")) {
|
||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||
}
|
||||
|
||||
const accountV2DirSrcDirPath = pathJoin(destDirPath, "keycloak.v2", "account", "src");
|
||||
|
||||
if (!fs.existsSync(accountV2DirSrcDirPath)) {
|
||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||
}
|
||||
|
||||
const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
||||
|
||||
if (packageManager === "pnpm") {
|
||||
try {
|
||||
child_process.execSync(`which pnpm`);
|
||||
} catch {
|
||||
console.log(`Installing pnpm globally`);
|
||||
child_process.execSync(`npm install -g pnpm`);
|
||||
}
|
||||
}
|
||||
|
||||
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
||||
|
||||
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
|
||||
|
||||
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
|
||||
|
||||
const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8"));
|
||||
|
||||
parsedPackageJson.scripts.build = parsedPackageJson.scripts.build
|
||||
.replace(`${packageManager} run check-types`, "true")
|
||||
.replace(`${packageManager} run babel`, "true");
|
||||
|
||||
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
|
||||
|
||||
child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
||||
|
||||
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
|
||||
|
||||
fs.rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), { "recursive": true });
|
||||
}
|
||||
|
||||
remove_keycloak_v2: {
|
||||
const keycloakV2DirPath = pathJoin(destDirPath, "keycloak.v2");
|
||||
|
||||
if (!fs.existsSync(keycloakV2DirPath)) {
|
||||
break remove_keycloak_v2;
|
||||
}
|
||||
|
||||
rmSync(keycloakV2DirPath, { "recursive": true });
|
||||
}
|
||||
|
||||
// Note, this is an optimization for reducing the size of the jar
|
||||
remove_unused_node_modules: {
|
||||
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
||||
|
||||
if (!fs.existsSync(nodeModuleDirPath)) {
|
||||
break remove_unused_node_modules;
|
||||
}
|
||||
|
||||
const toDeletePerfixes = [
|
||||
"angular",
|
||||
"bootstrap",
|
||||
"rcue",
|
||||
"font-awesome",
|
||||
"ng-file-upload",
|
||||
pathJoin("patternfly", "dist", "sass"),
|
||||
pathJoin("patternfly", "dist", "less"),
|
||||
pathJoin("patternfly", "dist", "js"),
|
||||
"d3",
|
||||
pathJoin("jquery", "src"),
|
||||
"c3",
|
||||
"core-js",
|
||||
"eonasdan-bootstrap-datetimepicker",
|
||||
"moment",
|
||||
"react",
|
||||
"patternfly-bootstrap-treeview",
|
||||
"popper.js",
|
||||
"tippy.js",
|
||||
"jquery-match-height",
|
||||
"google-code-prettify",
|
||||
"patternfly-bootstrap-combobox",
|
||||
"focus-trap",
|
||||
"tabbable",
|
||||
"scheduler",
|
||||
"@types",
|
||||
"datatables.net",
|
||||
"datatables.net-colreorder",
|
||||
"tslib",
|
||||
"prop-types",
|
||||
"file-selector",
|
||||
"datatables.net-colreorder-bs",
|
||||
"object-assign",
|
||||
"warning",
|
||||
"js-tokens",
|
||||
"loose-envify",
|
||||
"prop-types-extra",
|
||||
"attr-accept",
|
||||
"datatables.net-select",
|
||||
"drmonty-datatables-colvis",
|
||||
"datatables.net-bs",
|
||||
pathJoin("@patternfly", "react"),
|
||||
pathJoin("@patternfly", "patternfly", "docs")
|
||||
];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": nodeModuleDirPath,
|
||||
"destDirPath": nodeModuleDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (fileRelativePath.endsWith(".map")) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (fileRelativePath.startsWith(pathJoin("patternfly", "dist", "fonts"))) {
|
||||
if (
|
||||
!fileRelativePath.endsWith(".woff2") &&
|
||||
!fileRelativePath.endsWith(".woff") &&
|
||||
!fileRelativePath.endsWith(".ttf")
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Just like node_modules
|
||||
remove_unused_lib: {
|
||||
const libDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "lib");
|
||||
|
||||
if (!fs.existsSync(libDirPath)) {
|
||||
break remove_unused_lib;
|
||||
}
|
||||
|
||||
const toDeletePerfixes = ["ui-ace", "filesaver", "fileupload", "angular", "ui-ace"];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": libDirPath,
|
||||
"destDirPath": libDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (fileRelativePath.endsWith(".map")) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
last_account_v1_transformations: {
|
||||
if (lastKeycloakVersionWithAccountV1 !== keycloakVersion) {
|
||||
break last_account_v1_transformations;
|
||||
}
|
||||
|
||||
{
|
||||
const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css");
|
||||
|
||||
fs.writeFileSync(
|
||||
accountCssFilePath,
|
||||
Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8")
|
||||
);
|
||||
}
|
||||
|
||||
// Note, this is an optimization for reducing the size of the jar,
|
||||
// For this version we know exactly which resources are used.
|
||||
{
|
||||
const nodeModulesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
||||
|
||||
const toKeepPrefixes = [
|
||||
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(fileBasename =>
|
||||
pathJoin("patternfly", "dist", "css", fileBasename)
|
||||
),
|
||||
pathJoin("patternfly", "dist", "fonts")
|
||||
];
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": nodeModulesDirPath,
|
||||
"destDirPath": nodeModulesDirPath,
|
||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
||||
if (toKeepPrefixes.find(prefix => fileRelativePath.startsWith(prefix)) === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
import { join as pathJoin } from "path";
|
||||
import { downloadBuiltinKeycloakTheme } from "../../download-builtin-keycloak-theme";
|
||||
import { resources_common, type ThemeType } from "../../constants";
|
||||
import { BuildOptions } from "../buildOptions";
|
||||
import { downloadBuiltinKeycloakTheme } from "./downloadBuiltinKeycloakTheme";
|
||||
import { resources_common, type ThemeType } from "./constants";
|
||||
import type { BuildOptions } from "./buildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import * as crypto from "crypto";
|
||||
import { rmSync } from "../../tools/fs.rmSync";
|
||||
import { rmSync } from "../tools/fs.rmSync";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
cacheDirPath: string;
|
@ -1,6 +1,6 @@
|
||||
import * as fs from "fs";
|
||||
import { exclude } from "tsafe";
|
||||
import { crawl } from "./tools/crawl";
|
||||
import { crawl } from "../tools/crawl";
|
||||
import { join as pathJoin } from "path";
|
||||
import { themeTypes } from "./constants";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag";
|
||||
import { getLatestsSemVersionedTagFactory } from "../tools/octokit-addons/getLatestsSemVersionedTag";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import cliSelect from "cli-select";
|
||||
import { lastKeycloakVersionWithAccountV1 } from "./constants";
|
Reference in New Issue
Block a user