From d4f03b6b9e41a79419da3465eeca9906315b7d51 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 15 May 2024 05:14:01 +0200 Subject: [PATCH] bin general reresh, introducing termost --- package.json | 9 +- scripts/generate-i18n-messages.ts | 2 +- src/PUBLIC_URL.ts | 2 +- src/account/index.ts | 2 +- src/account/kcContext/KcContext.ts | 4 +- .../kcContext/getKcContextFromWindow.ts | 2 +- src/account/kcContext/kcContextMocks.ts | 2 +- src/bin/copy-keycloak-resources-to-public.ts | 116 +------ src/bin/download-builtin-keycloak-theme.ts | 282 +----------------- src/bin/eject-keycloak-page.ts | 23 +- src/bin/initialize-email-theme.ts | 23 +- src/bin/keycloakify/buildJars/buildJar.ts | 4 +- src/bin/keycloakify/buildJars/buildJars.ts | 2 +- src/bin/keycloakify/buildJars/generatePom.ts | 2 +- .../buildOptions/getReactAppRootDirPath.ts | 23 -- src/bin/keycloakify/buildOptions/index.ts | 1 - .../keycloakify/generateFtl/generateFtl.ts | 4 +- src/bin/keycloakify/generateFtl/index.ts | 1 - .../generateStartKeycloakTestingContainer.ts | 4 +- .../generateTheme/bringInAccountV1.ts | 6 +- .../generateMessageProperties.ts | 2 +- .../generateTheme/generateSrcMainResources.ts | 9 +- .../generateTheme/generateTheme.ts | 2 +- .../generateTheme/readExtraPageNames.ts | 4 +- .../generateTheme/readFieldNameUsage.ts | 2 +- src/bin/keycloakify/index.ts | 7 - src/bin/keycloakify/keycloakify.ts | 17 +- .../replacers/replaceImportsInCssCode.ts | 4 +- .../replaceImportsInInlineCssCode.ts | 4 +- .../replaceImportsInJsCode.ts | 2 +- .../replacers/replaceImportsInJsCode/vite.ts | 4 +- .../replaceImportsInJsCode/webpack.ts | 4 +- src/bin/main.ts | 87 ++++++ .../buildOptions/UserProvidedBuildOptions.ts | 0 .../buildOptions/buildOptions.ts | 24 +- .../buildOptions/getCacheDirPath.ts | 0 .../getNpmWorkspaceRootDirPath.ts | 0 .../buildOptions/getReactAppRootDirPath.ts | 26 ++ src/bin/shared/buildOptions/index.ts | 3 + .../buildOptions/parsedPackageJson.ts | 0 .../buildOptions/resolvedViteConfig.ts | 19 +- src/bin/{ => shared}/constants.ts | 6 +- .../shared/copyKeycloakResourcesToPublic.ts | 87 ++++++ src/bin/{ => shared}/downloadAndUnzip.ts | 12 +- .../shared/downloadBuiltinKeycloakTheme.ts | 264 ++++++++++++++++ .../downloadKeycloakStaticResources.ts | 10 +- src/bin/{ => shared}/getThemeSrcDirPath.ts | 2 +- .../pageId.ts => shared/pageIds.ts} | 0 src/bin/{ => shared}/promptKeycloakVersion.ts | 2 +- src/login/index.ts | 2 +- src/login/kcContext/KcContext.ts | 4 +- src/login/kcContext/getKcContextFromWindow.ts | 2 +- src/login/kcContext/kcContextMocks.ts | 4 +- src/vite-plugin/vite-plugin.ts | 96 +++--- test/bin/replacers.spec.ts | 2 +- test/bin/setupSampleReactProject.ts | 2 +- yarn.lock | 87 +++++- 57 files changed, 739 insertions(+), 577 deletions(-) delete mode 100644 src/bin/keycloakify/buildOptions/getReactAppRootDirPath.ts delete mode 100644 src/bin/keycloakify/buildOptions/index.ts create mode 100644 src/bin/main.ts rename src/bin/{keycloakify => shared}/buildOptions/UserProvidedBuildOptions.ts (100%) rename src/bin/{keycloakify => shared}/buildOptions/buildOptions.ts (90%) rename src/bin/{keycloakify => shared}/buildOptions/getCacheDirPath.ts (100%) rename src/bin/{keycloakify => shared}/buildOptions/getNpmWorkspaceRootDirPath.ts (100%) create mode 100644 src/bin/shared/buildOptions/getReactAppRootDirPath.ts create mode 100644 src/bin/shared/buildOptions/index.ts rename src/bin/{keycloakify => shared}/buildOptions/parsedPackageJson.ts (100%) rename src/bin/{keycloakify => shared}/buildOptions/resolvedViteConfig.ts (76%) rename src/bin/{ => shared}/constants.ts (70%) create mode 100644 src/bin/shared/copyKeycloakResourcesToPublic.ts rename src/bin/{ => shared}/downloadAndUnzip.ts (95%) create mode 100644 src/bin/shared/downloadBuiltinKeycloakTheme.ts rename src/bin/{keycloakify/generateTheme => shared}/downloadKeycloakStaticResources.ts (80%) rename src/bin/{ => shared}/getThemeSrcDirPath.ts (97%) rename src/bin/{keycloakify/generateFtl/pageId.ts => shared/pageIds.ts} (100%) rename src/bin/{ => shared}/promptKeycloakVersion.ts (92%) diff --git a/package.json b/package.json index 9406cd9a..d8048248 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,7 @@ "build-storybook": "yarn build && yarn copy-keycloak-resources-to-storybook-static && build-storybook" }, "bin": { - "copy-keycloak-resources-to-public": "dist/bin/copy-keycloak-resources-to-public.js", - "download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js", - "eject-keycloak-page": "dist/bin/eject-keycloak-page.js", - "initialize-email-theme": "dist/bin/initialize-email-theme.js", - "keycloakify": "dist/bin/keycloakify/index.js" + "keycloakify": "dist/bin/main.js" }, "lint-staged": { "*.{ts,tsx,json,md}": [ @@ -124,6 +120,7 @@ "yauzl": "^2.10.0", "yazl": "^2.5.1", "zod": "^3.17.10", - "magic-string": "^0.30.7" + "magic-string": "^0.30.7", + "termost": "^0.12.0" } } diff --git a/scripts/generate-i18n-messages.ts b/scripts/generate-i18n-messages.ts index aedc357d..e3c935f5 100644 --- a/scripts/generate-i18n-messages.ts +++ b/scripts/generate-i18n-messages.ts @@ -2,7 +2,7 @@ import "minimal-polyfills/Object.fromEntries"; import * as fs from "fs"; import { join as pathJoin, relative as pathRelative, dirname as pathDirname, sep as pathSep } from "path"; import { crawl } from "../src/bin/tools/crawl"; -import { downloadBuiltinKeycloakTheme } from "../src/bin/download-builtin-keycloak-theme"; +import { downloadBuiltinKeycloakTheme } from "../src/bin/shared/downloadBuiltinKeycloakTheme"; import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath"; import { getLogger } from "../src/bin/tools/logger"; diff --git a/src/PUBLIC_URL.ts b/src/PUBLIC_URL.ts index af0f0bb3..a1d87dd0 100644 --- a/src/PUBLIC_URL.ts +++ b/src/PUBLIC_URL.ts @@ -1,4 +1,4 @@ -import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/constants"; +import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/shared/constants"; import { assert } from "tsafe/assert"; /** diff --git a/src/account/index.ts b/src/account/index.ts index b04046d7..6615ea3d 100644 --- a/src/account/index.ts +++ b/src/account/index.ts @@ -4,7 +4,7 @@ export default Fallback; export { getKcContext } from "keycloakify/account/kcContext/getKcContext"; export { createGetKcContext } from "keycloakify/account/kcContext/createGetKcContext"; -export type { AccountThemePageId as PageId } from "keycloakify/bin/keycloakify/generateFtl"; +export type { AccountThemePageId as PageId } from "keycloakify/bin/shared/pageIds"; export { createUseI18n } from "keycloakify/account/i18n/i18n"; export type { PageProps } from "keycloakify/account/pages/PageProps"; diff --git a/src/account/kcContext/KcContext.ts b/src/account/kcContext/KcContext.ts index 8186d487..764693e0 100644 --- a/src/account/kcContext/KcContext.ts +++ b/src/account/kcContext/KcContext.ts @@ -1,7 +1,7 @@ -import type { AccountThemePageId } from "keycloakify/bin/keycloakify/generateFtl"; +import type { AccountThemePageId } from "keycloakify/bin/shared/pageIds"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; -import { type ThemeType } from "keycloakify/bin/constants"; +import { type ThemeType } from "keycloakify/bin/shared/constants"; export type KcContext = | KcContext.Password diff --git a/src/account/kcContext/getKcContextFromWindow.ts b/src/account/kcContext/getKcContextFromWindow.ts index 0c4de775..84f39340 100644 --- a/src/account/kcContext/getKcContextFromWindow.ts +++ b/src/account/kcContext/getKcContextFromWindow.ts @@ -1,5 +1,5 @@ import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey"; -import { nameOfTheGlobal } from "keycloakify/bin/constants"; +import { nameOfTheGlobal } from "keycloakify/bin/shared/constants"; import type { KcContext } from "./KcContext"; export type ExtendKcContext = [KcContextExtension] extends [never] diff --git a/src/account/kcContext/kcContextMocks.ts b/src/account/kcContext/kcContextMocks.ts index 1053b367..1697cca1 100644 --- a/src/account/kcContext/kcContextMocks.ts +++ b/src/account/kcContext/kcContextMocks.ts @@ -1,5 +1,5 @@ import "minimal-polyfills/Object.fromEntries"; -import { resources_common, keycloak_resources } from "keycloakify/bin/constants"; +import { resources_common, keycloak_resources } from "keycloakify/bin/shared/constants"; import { id } from "tsafe/id"; import type { KcContext } from "./KcContext"; import { BASE_URL } from "keycloakify/lib/BASE_URL"; diff --git a/src/bin/copy-keycloak-resources-to-public.ts b/src/bin/copy-keycloak-resources-to-public.ts index f877594f..8bc4abf9 100644 --- a/src/bin/copy-keycloak-resources-to-public.ts +++ b/src/bin/copy-keycloak-resources-to-public.ts @@ -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>(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(); -} diff --git a/src/bin/download-builtin-keycloak-theme.ts b/src/bin/download-builtin-keycloak-theme.ts index dd9329a1..772d1c23 100644 --- a/src/bin/download-builtin-keycloak-theme.ts +++ b/src/bin/download-builtin-keycloak-theme.ts @@ -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(); - -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(); -} diff --git a/src/bin/eject-keycloak-page.ts b/src/bin/eject-keycloak-page.ts index f777f281..a8439898 100644 --- a/src/bin/eject-keycloak-page.ts +++ b/src/bin/eject-keycloak-page.ts @@ -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({ "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`); -})(); +} diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index 4e2be98f..db585f39 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -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(); -} diff --git a/src/bin/keycloakify/buildJars/buildJar.ts b/src/bin/keycloakify/buildJars/buildJar.ts index 86e36c2b..cb40d8f6 100644 --- a/src/bin/keycloakify/buildJars/buildJar.ts +++ b/src/bin/keycloakify/buildJars/buildJar.ts @@ -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"; diff --git a/src/bin/keycloakify/buildJars/buildJars.ts b/src/bin/keycloakify/buildJars/buildJars.ts index 4e6707d6..47639e6c 100644 --- a/src/bin/keycloakify/buildJars/buildJars.ts +++ b/src/bin/keycloakify/buildJars/buildJars.ts @@ -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; diff --git a/src/bin/keycloakify/buildJars/generatePom.ts b/src/bin/keycloakify/buildJars/generatePom.ts index d1f024e2..13fa4cf0 100644 --- a/src/bin/keycloakify/buildJars/generatePom.ts +++ b/src/bin/keycloakify/buildJars/generatePom.ts @@ -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 = { diff --git a/src/bin/keycloakify/buildOptions/getReactAppRootDirPath.ts b/src/bin/keycloakify/buildOptions/getReactAppRootDirPath.ts deleted file mode 100644 index 49b9e9d3..00000000 --- a/src/bin/keycloakify/buildOptions/getReactAppRootDirPath.ts +++ /dev/null @@ -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 }; -} diff --git a/src/bin/keycloakify/buildOptions/index.ts b/src/bin/keycloakify/buildOptions/index.ts deleted file mode 100644 index 0d6efd40..00000000 --- a/src/bin/keycloakify/buildOptions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./buildOptions"; diff --git a/src/bin/keycloakify/generateFtl/generateFtl.ts b/src/bin/keycloakify/generateFtl/generateFtl.ts index 1642d54b..29b34d59 100644 --- a/src/bin/keycloakify/generateFtl/generateFtl.ts +++ b/src/bin/keycloakify/generateFtl/generateFtl.ts @@ -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"; diff --git a/src/bin/keycloakify/generateFtl/index.ts b/src/bin/keycloakify/generateFtl/index.ts index 2d57d5e4..3a6d8e56 100644 --- a/src/bin/keycloakify/generateFtl/index.ts +++ b/src/bin/keycloakify/generateFtl/index.ts @@ -1,2 +1 @@ export * from "./generateFtl"; -export * from "./pageId"; diff --git a/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts b/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts index 618e9e5c..151719f9 100644 --- a/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +++ b/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts @@ -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; diff --git a/src/bin/keycloakify/generateTheme/bringInAccountV1.ts b/src/bin/keycloakify/generateTheme/bringInAccountV1.ts index d76adaef..a006791d 100644 --- a/src/bin/keycloakify/generateTheme/bringInAccountV1.ts +++ b/src/bin/keycloakify/generateTheme/bringInAccountV1.ts @@ -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"; diff --git a/src/bin/keycloakify/generateTheme/generateMessageProperties.ts b/src/bin/keycloakify/generateTheme/generateMessageProperties.ts index 6cd38d5d..053c4f72 100644 --- a/src/bin/keycloakify/generateTheme/generateMessageProperties.ts +++ b/src/bin/keycloakify/generateTheme/generateMessageProperties.ts @@ -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"; diff --git a/src/bin/keycloakify/generateTheme/generateSrcMainResources.ts b/src/bin/keycloakify/generateTheme/generateSrcMainResources.ts index 24196759..f69958fc 100644 --- a/src/bin/keycloakify/generateTheme/generateSrcMainResources.ts +++ b/src/bin/keycloakify/generateTheme/generateSrcMainResources.ts @@ -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"; diff --git a/src/bin/keycloakify/generateTheme/generateTheme.ts b/src/bin/keycloakify/generateTheme/generateTheme.ts index 94775955..c6d4411e 100644 --- a/src/bin/keycloakify/generateTheme/generateTheme.ts +++ b/src/bin/keycloakify/generateTheme/generateTheme.ts @@ -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"; diff --git a/src/bin/keycloakify/generateTheme/readExtraPageNames.ts b/src/bin/keycloakify/generateTheme/readExtraPageNames.ts index 1e5b96d0..18e8b06b 100644 --- a/src/bin/keycloakify/generateTheme/readExtraPageNames.ts +++ b/src/bin/keycloakify/generateTheme/readExtraPageNames.ts @@ -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; diff --git a/src/bin/keycloakify/generateTheme/readFieldNameUsage.ts b/src/bin/keycloakify/generateTheme/readFieldNameUsage.ts index 210e230c..a3a611d7 100644 --- a/src/bin/keycloakify/generateTheme/readFieldNameUsage.ts +++ b/src/bin/keycloakify/generateTheme/readFieldNameUsage.ts @@ -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[] { diff --git a/src/bin/keycloakify/index.ts b/src/bin/keycloakify/index.ts index 258b6e23..d07e3d4a 100644 --- a/src/bin/keycloakify/index.ts +++ b/src/bin/keycloakify/index.ts @@ -1,8 +1 @@ -#!/usr/bin/env node - export * from "./keycloakify"; -import { main } from "./keycloakify"; - -if (require.main === module) { - main(); -} diff --git a/src/bin/keycloakify/keycloakify.ts b/src/bin/keycloakify/keycloakify.ts index 5fe48ef5..2867595b 100644 --- a/src/bin/keycloakify/keycloakify.ts +++ b/src/bin/keycloakify/keycloakify.ts @@ -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) } }); } diff --git a/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts b/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts index ae271970..15e89eb2 100644 --- a/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts +++ b/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts @@ -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; diff --git a/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts b/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts index 3bb52c12..5ab068cd 100644 --- a/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts +++ b/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts @@ -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; diff --git a/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts b/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts index 30bcad9b..4327a737 100644 --- a/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts +++ b/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts @@ -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"; diff --git a/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts b/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts index 9a60aae2..8fb7ff8f 100644 --- a/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts +++ b/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts @@ -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"; diff --git a/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts b/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts index 74a02f5d..35154c39 100644 --- a/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts +++ b/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts @@ -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"; diff --git a/src/bin/main.ts b/src/bin/main.ts new file mode 100644 index 00000000..273e903f --- /dev/null +++ b/src/bin/main.ts @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +import { termost } from "termost"; + +export type CliCommandOptions = { + isSilent: boolean; + reactAppRootDirPath: string | undefined; +}; + +const program = termost("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 }); + } + }); diff --git a/src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts b/src/bin/shared/buildOptions/UserProvidedBuildOptions.ts similarity index 100% rename from src/bin/keycloakify/buildOptions/UserProvidedBuildOptions.ts rename to src/bin/shared/buildOptions/UserProvidedBuildOptions.ts diff --git a/src/bin/keycloakify/buildOptions/buildOptions.ts b/src/bin/shared/buildOptions/buildOptions.ts similarity index 90% rename from src/bin/keycloakify/buildOptions/buildOptions.ts rename to src/bin/shared/buildOptions/buildOptions.ts index 3e51c7f4..f64e0b12 100644 --- a/src/bin/keycloakify/buildOptions/buildOptions.ts +++ b/src/bin/shared/buildOptions/buildOptions.ts @@ -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, diff --git a/src/bin/keycloakify/buildOptions/getCacheDirPath.ts b/src/bin/shared/buildOptions/getCacheDirPath.ts similarity index 100% rename from src/bin/keycloakify/buildOptions/getCacheDirPath.ts rename to src/bin/shared/buildOptions/getCacheDirPath.ts diff --git a/src/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.ts b/src/bin/shared/buildOptions/getNpmWorkspaceRootDirPath.ts similarity index 100% rename from src/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.ts rename to src/bin/shared/buildOptions/getNpmWorkspaceRootDirPath.ts diff --git a/src/bin/shared/buildOptions/getReactAppRootDirPath.ts b/src/bin/shared/buildOptions/getReactAppRootDirPath.ts new file mode 100644 index 00000000..46a970a6 --- /dev/null +++ b/src/bin/shared/buildOptions/getReactAppRootDirPath.ts @@ -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(); + +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 }; +} diff --git a/src/bin/shared/buildOptions/index.ts b/src/bin/shared/buildOptions/index.ts new file mode 100644 index 00000000..2ff4cfd4 --- /dev/null +++ b/src/bin/shared/buildOptions/index.ts @@ -0,0 +1,3 @@ +export * from "./buildOptions"; +export type { UserProvidedBuildOptions } from "./UserProvidedBuildOptions"; +export type { ResolvedViteConfig } from "./resolvedViteConfig"; diff --git a/src/bin/keycloakify/buildOptions/parsedPackageJson.ts b/src/bin/shared/buildOptions/parsedPackageJson.ts similarity index 100% rename from src/bin/keycloakify/buildOptions/parsedPackageJson.ts rename to src/bin/shared/buildOptions/parsedPackageJson.ts diff --git a/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts b/src/bin/shared/buildOptions/resolvedViteConfig.ts similarity index 76% rename from src/bin/keycloakify/buildOptions/resolvedViteConfig.ts rename to src/bin/shared/buildOptions/resolvedViteConfig.ts index d2f37c34..5c566025 100644 --- a/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts +++ b/src/bin/shared/buildOptions/resolvedViteConfig.ts @@ -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>(); } -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."); diff --git a/src/bin/constants.ts b/src/bin/shared/constants.ts similarity index 70% rename from src/bin/constants.ts rename to src/bin/shared/constants.ts index 84ba505a..98c5acc4 100644 --- a/src/bin/constants.ts +++ b/src/bin/shared/constants.ts @@ -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" +}; diff --git a/src/bin/shared/copyKeycloakResourcesToPublic.ts b/src/bin/shared/copyKeycloakResourcesToPublic.ts new file mode 100644 index 00000000..2037fa3e --- /dev/null +++ b/src/bin/shared/copyKeycloakResourcesToPublic.ts @@ -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(); + +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")); +} diff --git a/src/bin/downloadAndUnzip.ts b/src/bin/shared/downloadAndUnzip.ts similarity index 95% rename from src/bin/downloadAndUnzip.ts rename to src/bin/shared/downloadAndUnzip.ts index e11a4c1e..c4a1727a 100644 --- a/src/bin/downloadAndUnzip.ts +++ b/src/bin/shared/downloadAndUnzip.ts @@ -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; diff --git a/src/bin/shared/downloadBuiltinKeycloakTheme.ts b/src/bin/shared/downloadBuiltinKeycloakTheme.ts new file mode 100644 index 00000000..478b582a --- /dev/null +++ b/src/bin/shared/downloadBuiltinKeycloakTheme.ts @@ -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(); + +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 }; + } + }); + } + } + } + } + }); +} diff --git a/src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts b/src/bin/shared/downloadKeycloakStaticResources.ts similarity index 80% rename from src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts rename to src/bin/shared/downloadKeycloakStaticResources.ts index 811df236..afc81c8c 100644 --- a/src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts +++ b/src/bin/shared/downloadKeycloakStaticResources.ts @@ -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; diff --git a/src/bin/getThemeSrcDirPath.ts b/src/bin/shared/getThemeSrcDirPath.ts similarity index 97% rename from src/bin/getThemeSrcDirPath.ts rename to src/bin/shared/getThemeSrcDirPath.ts index 48622eed..08aaf659 100644 --- a/src/bin/getThemeSrcDirPath.ts +++ b/src/bin/shared/getThemeSrcDirPath.ts @@ -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"; diff --git a/src/bin/keycloakify/generateFtl/pageId.ts b/src/bin/shared/pageIds.ts similarity index 100% rename from src/bin/keycloakify/generateFtl/pageId.ts rename to src/bin/shared/pageIds.ts diff --git a/src/bin/promptKeycloakVersion.ts b/src/bin/shared/promptKeycloakVersion.ts similarity index 92% rename from src/bin/promptKeycloakVersion.ts rename to src/bin/shared/promptKeycloakVersion.ts index ffc22895..88835228 100644 --- a/src/bin/promptKeycloakVersion.ts +++ b/src/bin/shared/promptKeycloakVersion.ts @@ -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"; diff --git a/src/login/index.ts b/src/login/index.ts index 38846b6b..f1b9436e 100644 --- a/src/login/index.ts +++ b/src/login/index.ts @@ -5,7 +5,7 @@ export default Fallback; export { useDownloadTerms } from "keycloakify/login/lib/useDownloadTerms"; export { getKcContext } from "keycloakify/login/kcContext/getKcContext"; export { createGetKcContext } from "keycloakify/login/kcContext/createGetKcContext"; -export type { LoginThemePageId as PageId } from "keycloakify/bin/keycloakify/generateFtl"; +export type { LoginThemePageId as PageId } from "keycloakify/bin/shared/pageIds"; export { createUseI18n } from "keycloakify/login/i18n/i18n"; export type { PageProps } from "keycloakify/login/pages/PageProps"; diff --git a/src/login/kcContext/KcContext.ts b/src/login/kcContext/KcContext.ts index 69b3813a..28d44cae 100644 --- a/src/login/kcContext/KcContext.ts +++ b/src/login/kcContext/KcContext.ts @@ -1,5 +1,5 @@ -import type { LoginThemePageId } from "keycloakify/bin/keycloakify/generateFtl"; -import { type ThemeType } from "keycloakify/bin/constants"; +import type { LoginThemePageId } from "keycloakify/bin/shared/pageIds"; +import { type ThemeType } from "keycloakify/bin/shared/constants"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; import type { MessageKey } from "../i18n/i18n"; diff --git a/src/login/kcContext/getKcContextFromWindow.ts b/src/login/kcContext/getKcContextFromWindow.ts index c3360657..138ba345 100644 --- a/src/login/kcContext/getKcContextFromWindow.ts +++ b/src/login/kcContext/getKcContextFromWindow.ts @@ -1,6 +1,6 @@ import type { KcContext } from "./KcContext"; import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey"; -import { nameOfTheGlobal } from "keycloakify/bin/constants"; +import { nameOfTheGlobal } from "keycloakify/bin/shared/constants"; export type ExtendKcContext = [KcContextExtension] extends [never] ? KcContext diff --git a/src/login/kcContext/kcContextMocks.ts b/src/login/kcContext/kcContextMocks.ts index 67ae8573..e0a8006a 100644 --- a/src/login/kcContext/kcContextMocks.ts +++ b/src/login/kcContext/kcContextMocks.ts @@ -1,9 +1,9 @@ import "minimal-polyfills/Object.fromEntries"; import type { KcContext, Attribute } from "./KcContext"; -import { resources_common, keycloak_resources } from "keycloakify/bin/constants"; +import { resources_common, keycloak_resources } from "keycloakify/bin/shared/constants"; import { id } from "tsafe/id"; import { assert, type Equals } from "tsafe/assert"; -import type { LoginThemePageId } from "keycloakify/bin/keycloakify/generateFtl"; +import type { LoginThemePageId } from "keycloakify/bin/shared/pageIds"; import { BASE_URL } from "keycloakify/lib/BASE_URL"; const attributes: Attribute[] = [ diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index 43ad5495..7c804970 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -1,21 +1,12 @@ -import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path"; +import { join as pathJoin, relative as pathRelative, sep as pathSep, dirname as pathDirname } from "path"; import type { Plugin } from "vite"; import * as fs from "fs"; -import { - resolvedViteConfigJsonBasename, - nameOfTheGlobal, - basenameOfTheKeycloakifyResourcesDir, - keycloak_resources, - keycloakifyBuildOptionsForPostPostBuildScriptEnvName -} from "../bin/constants"; -import type { ResolvedViteConfig } from "../bin/keycloakify/buildOptions/resolvedViteConfig"; -import { getCacheDirPath } from "../bin/keycloakify/buildOptions/getCacheDirPath"; +import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, keycloak_resources, vitePluginSubScriptEnvNames } from "../bin/shared/constants"; import { id } from "tsafe/id"; import { rm } from "../bin/tools/fs.rm"; -import { copyKeycloakResourcesToPublic } from "../bin/copy-keycloak-resources-to-public"; +import { copyKeycloakResourcesToPublic } from "../bin/shared/copyKeycloakResourcesToPublic"; import { assert } from "tsafe/assert"; -import type { BuildOptions } from "../bin/keycloakify/buildOptions"; -import type { UserProvidedBuildOptions } from "../bin/keycloakify/buildOptions/UserProvidedBuildOptions"; +import { readBuildOptions, type BuildOptions, type UserProvidedBuildOptions, type ResolvedViteConfig } from "../bin/shared/buildOptions"; import MagicString from "magic-string"; export type Params = UserProvidedBuildOptions & { @@ -36,20 +27,14 @@ export function keycloakify(params?: Params) { "configResolved": async resolvedConfig => { shouldGenerateSourcemap = resolvedConfig.build.sourcemap !== false; - run_post_build_script: { - const buildOptionJson = process.env[keycloakifyBuildOptionsForPostPostBuildScriptEnvName]; + run_post_build_script_case: { + const postBuildArgJson = process.env[vitePluginSubScriptEnvNames.runPostBuildScript]; - if (buildOptionJson === undefined) { - break run_post_build_script; + if (postBuildArgJson === undefined) { + break run_post_build_script_case; } - if (postBuild === undefined) { - process.exit(0); - } - - const buildOptions: BuildOptions = JSON.parse(buildOptionJson); - - await postBuild(buildOptions); + await postBuild?.(JSON.parse(postBuildArgJson)); process.exit(0); } @@ -86,34 +71,49 @@ export function keycloakify(params?: Params) { buildDirPath = pathJoin(reactAppRootDirPath, resolvedConfig.build.outDir); - const { cacheDirPath } = getCacheDirPath({ - reactAppRootDirPath - }); + create_resolved_vite_config_case: { + const resolvedViteConfigJsonFilePath = process.env[vitePluginSubScriptEnvNames.createResolvedViteConfig]; - if (!fs.existsSync(cacheDirPath)) { - fs.mkdirSync(cacheDirPath, { "recursive": true }); + if (resolvedViteConfigJsonFilePath === undefined) { + break create_resolved_vite_config_case; + } + + { + const dirPath = pathDirname(resolvedViteConfigJsonFilePath); + + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { "recursive": true }); + } + } + + fs.writeFileSync( + resolvedViteConfigJsonFilePath, + Buffer.from( + JSON.stringify( + id({ + "publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir), + "assetsDir": resolvedConfig.build.assetsDir, + "buildDir": resolvedConfig.build.outDir, + urlPathname, + userProvidedBuildOptions + }), + null, + 2 + ), + "utf8" + ) + ); + + process.exit(0); } - fs.writeFileSync( - pathJoin(cacheDirPath, resolvedViteConfigJsonBasename), - Buffer.from( - JSON.stringify( - id({ - "publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir), - "assetsDir": resolvedConfig.build.assetsDir, - "buildDir": resolvedConfig.build.outDir, - urlPathname, - userProvidedBuildOptions - }), - null, - 2 - ), - "utf8" - ) - ); - await copyKeycloakResourcesToPublic({ - "processArgv": ["--project", reactAppRootDirPath] + "buildOptions": readBuildOptions({ + "cliCommandOptions": { + "isSilent": true, + reactAppRootDirPath + } + }) }); }, "transform": (code, id) => { diff --git a/test/bin/replacers.spec.ts b/test/bin/replacers.spec.ts index eee699a2..02e6c89a 100644 --- a/test/bin/replacers.spec.ts +++ b/test/bin/replacers.spec.ts @@ -5,7 +5,7 @@ import { replaceImportsInInlineCssCode } from "keycloakify/bin/keycloakify/repla import { same } from "evt/tools/inDepth/same"; import { expect, it, describe } from "vitest"; import { isSameCode } from "../tools/isSameCode"; -import { basenameOfTheKeycloakifyResourcesDir, nameOfTheGlobal } from "keycloakify/bin/constants"; +import { basenameOfTheKeycloakifyResourcesDir, nameOfTheGlobal } from "keycloakify/bin/shared/constants"; describe("js replacer - vite", () => { it("replaceImportsInJsCode_vite - 1", () => { diff --git a/test/bin/setupSampleReactProject.ts b/test/bin/setupSampleReactProject.ts index 79e7516a..d486762c 100644 --- a/test/bin/setupSampleReactProject.ts +++ b/test/bin/setupSampleReactProject.ts @@ -1,4 +1,4 @@ -import { downloadAndUnzip } from "keycloakify/bin/downloadAndUnzip"; +import { downloadAndUnzip } from "keycloakify/bin/shared/downloadAndUnzip"; import { join as pathJoin } from "path"; import { getThisCodebaseRootDirPath } from "keycloakify/bin/tools/getThisCodebaseRootDirPath"; diff --git a/yarn.lock b/yarn.lock index 95b2990f..fae634ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3802,6 +3802,13 @@ ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + ansi-html-community@0.0.8, ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -3841,7 +3848,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -4872,6 +4879,13 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + cli-select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/cli-select/-/cli-select-1.1.2.tgz#456dced464b3346ca661b16a0e37fc4b28db4818" @@ -4988,7 +5002,7 @@ colorette@^1.2.2, colorette@^1.4.0: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== -colorette@^2.0.16: +colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -5833,6 +5847,14 @@ enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" +enquirer@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -6179,6 +6201,11 @@ event-emitter@^0.3.5: d "1" es5-ext "~0.10.14" +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -8200,6 +8227,18 @@ listr2@^3.12.2: through "^2.3.8" wrap-ansi "^7.0.0" +listr2@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-7.0.2.tgz#3aa3e1549dfaf3c57ab5eeaba754da3b87f33063" + integrity sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^5.0.1" + rfdc "^1.3.0" + wrap-ansi "^8.1.0" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -8296,6 +8335,17 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +log-update@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-5.0.1.tgz#9e928bf70cb183c1f0c9e91d9e6b7115d597ce09" + integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== + dependencies: + ansi-escapes "^5.0.0" + cli-cursor "^4.0.0" + slice-ansi "^5.0.0" + strip-ansi "^7.0.1" + wrap-ansi "^8.0.1" + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -10505,6 +10555,14 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -11222,7 +11280,7 @@ string-argv@0.3.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.0: +string-width@^5.0.0, string-width@^5.0.1: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -11487,6 +11545,15 @@ telejson@^6.0.8: lodash "^4.17.21" memoizerific "^1.11.3" +termost@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/termost/-/termost-0.12.0.tgz#ca15c83fe3b2dfc83be0dcf4215aafad3bc6c018" + integrity sha512-7MbYVQvdhaZHSy/mO8TM6yXcDTS2wkeLiWIwoUL43n6wo4NSO38V3GNsBRlI4zcl8vQzm+bmqplyqOTYVZCpFw== + dependencies: + enquirer "^2.4.1" + listr2 "^7.0.1" + picocolors "^1.0.0" + terser-webpack-plugin@^1.4.3: version "1.4.5" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" @@ -11800,6 +11867,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -12592,6 +12664,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"