bin general reresh, introducing termost

This commit is contained in:
Joseph Garrone 2024-05-15 05:14:01 +02:00
parent 931e002b12
commit d4f03b6b9e
57 changed files with 739 additions and 577 deletions

View File

@ -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"
}
}

View File

@ -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";

View File

@ -1,4 +1,4 @@
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/constants";
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/shared/constants";
import { assert } from "tsafe/assert";
/**

View File

@ -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";

View File

@ -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

View File

@ -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 { pageId: string }> = [KcContextExtension] extends [never]

View File

@ -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";

View File

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

View File

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

View File

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

View File

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

View File

@ -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";

View File

@ -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;

View File

@ -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 = {

View File

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

View File

@ -1 +0,0 @@
export * from "./buildOptions";

View File

@ -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";

View File

@ -1,2 +1 @@
export * from "./generateFtl";
export * from "./pageId";

View File

@ -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;

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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;

View File

@ -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[] {

View File

@ -1,8 +1 @@
#!/usr/bin/env node
export * from "./keycloakify";
import { main } from "./keycloakify";
if (require.main === module) {
main();
}

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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";

View File

@ -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";

View File

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

View File

@ -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,

View 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 };
}

View File

@ -0,0 +1,3 @@
export * from "./buildOptions";
export type { UserProvidedBuildOptions } from "./UserProvidedBuildOptions";
export type { ResolvedViteConfig } from "./resolvedViteConfig";

View File

@ -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.");

View File

@ -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"
};

View 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"));
}

View File

@ -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;

View 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 };
}
});
}
}
}
}
});
}

View File

@ -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;

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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 { pageId: string }> = [KcContextExtension] extends [never]
? KcContext

View File

@ -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[] = [

View File

@ -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<ResolvedViteConfig>({
"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<ResolvedViteConfig>({
"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) => {

View File

@ -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", () => {

View File

@ -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";

View File

@ -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"