diff --git a/.prettierignore b/.prettierignore index 14f75d98..33581755 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,4 +6,5 @@ node_modules/ /src/test/apps/ /src/tools/types/ /sample_react_project -/build_keycloak/ \ No newline at end of file +/build_keycloak/ +/src/lib/i18n/generated_messages/ \ No newline at end of file diff --git a/package.json b/package.json index c8e66e1a..b9a0cb8d 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "link_in_test_app": "node dist/bin/link_in_test_app.js", "_format": "prettier '**/*.{ts,tsx,json,md}'", "format": "yarn _format --write", - "format:check": "yarn _format --list-different" + "format:check": "yarn _format --list-different", + "link-in-app": "ts-node --skipProject src/scripts/link-in-app.ts", + "link-in-starter": "yarn link-in-app keycloakify-advanced-starter", + "tsc-watch": "tsc -p src/bin -w & tsc -p src/lib -w " }, "bin": { "keycloakify": "dist/bin/keycloakify/index.js", @@ -40,6 +43,7 @@ "license": "MIT", "files": [ "src/", + "!src/scripts", "dist/", "!dist/tsconfig.tsbuildinfo" ], @@ -61,7 +65,7 @@ "@babel/core": "^7.0.0", "@types/memoizee": "^0.4.7", "@types/minimist": "^1.2.2", - "@types/node": "^17.0.25", + "@types/node": "^18.14.1", "@types/react": "18.0.9", "copyfiles": "^2.4.1", "husky": "^4.3.8", @@ -70,22 +74,20 @@ "properties-parser": "^0.3.1", "react": "18.1.0", "rimraf": "^3.0.2", - "@emotion/react": "^11.10.4", - "typescript": "^4.2.3" + "typescript": "^4.9.5", + "ts-node": "^10.9.1", + "scripting-tools": "^0.19.13" }, "dependencies": { "@octokit/rest": "^18.12.0", "cheerio": "^1.0.0-rc.5", "cli-select": "^1.1.2", "evt": "^2.4.13", - "memoizee": "^0.4.15", "minimal-polyfills": "^2.2.2", "minimist": "^1.2.6", "path-browserify": "^1.0.1", - "powerhooks": "^0.26.0", "react-markdown": "^5.0.3", "rfc4648": "^1.5.2", - "scripting-tools": "^0.19.13", "tsafe": "^1.4.3", "tss-react": "4.4.1-rc.0", "zod": "^3.17.10" diff --git a/src/bin/link_in_test_app.ts b/src/bin/link_in_test_app.ts deleted file mode 100644 index c169c8cc..00000000 --- a/src/bin/link_in_test_app.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { execSync } from "child_process"; -import { join as pathJoin, relative as pathRelative } from "path"; -import { exclude } from "tsafe/exclude"; -import * as fs from "fs"; - -const keycloakifyDirPath = pathJoin(__dirname, "..", ".."); - -fs.writeFileSync( - pathJoin(keycloakifyDirPath, "dist", "package.json"), - Buffer.from( - JSON.stringify( - (() => { - const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(keycloakifyDirPath, "package.json")).toString("utf8")); - - return { - ...packageJsonParsed, - "main": packageJsonParsed["main"].replace(/^dist\//, ""), - "types": packageJsonParsed["types"].replace(/^dist\//, ""), - "bin": Object.fromEntries(Object.entries(packageJsonParsed["bin"]).map(([k, v]) => [k, v.replace(/^dist\//, "")])) - }; - })(), - null, - 2 - ), - "utf8" - ) -); - -const commonThirdPartyDeps = (() => { - const namespaceModuleNames = ["@emotion"]; - const standaloneModuleNames = ["react", "@types/react", "powerhooks", "tss-react", "evt"]; - - return [ - ...namespaceModuleNames - .map(namespaceModuleName => - fs - .readdirSync(pathJoin(keycloakifyDirPath, "node_modules", namespaceModuleName)) - .map(submoduleName => `${namespaceModuleName}/${submoduleName}`) - ) - .reduce((prev, curr) => [...prev, ...curr], []), - ...standaloneModuleNames - ]; -})(); - -const yarnHomeDirPath = pathJoin(keycloakifyDirPath, ".yarn_home"); - -fs.rmSync(yarnHomeDirPath, { "recursive": true, "force": true }); -fs.mkdirSync(yarnHomeDirPath); - -const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => { - const { targetModuleName, cwd } = params; - - const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : [])].join(" "); - - console.log(`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`); - - execSync(cmd, { - cwd, - "env": { - ...process.env, - "HOME": yarnHomeDirPath - } - }); -}; - -const testAppPaths = (() => { - const arg = process.argv[2]; - - const testAppNames = arg !== undefined ? [arg] : ["keycloakify-starter", "keycloakify-advanced-starter"]; - - return testAppNames - .map(testAppName => { - const testAppPath = pathJoin(keycloakifyDirPath, "..", testAppName); - - if (fs.existsSync(testAppPath)) { - return testAppPath; - } - - console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`); - - return undefined; - }) - .filter(exclude(undefined)); -})(); - -if (testAppPaths.length === 0) { - console.error("No test app to link into!"); - process.exit(-1); -} - -testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath })); - -console.log("=== Linking common dependencies ==="); - -const total = commonThirdPartyDeps.length; -let current = 0; - -commonThirdPartyDeps.forEach(commonThirdPartyDep => { - current++; - - console.log(`${current}/${total} ${commonThirdPartyDep}`); - - const localInstallPath = pathJoin( - ...[keycloakifyDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])] - ); - - execYarnLink({ "cwd": localInstallPath }); -}); - -commonThirdPartyDeps.forEach(commonThirdPartyDep => - testAppPaths.forEach(testAppPath => - execYarnLink({ - "cwd": testAppPath, - "targetModuleName": commonThirdPartyDep - }) - ) -); - -console.log("=== Linking in house dependencies ==="); - -execYarnLink({ "cwd": pathJoin(keycloakifyDirPath, "dist") }); - -testAppPaths.forEach(testAppPath => - execYarnLink({ - "cwd": testAppPath, - "targetModuleName": "keycloakify" - }) -); diff --git a/src/lib/components/Error.tsx b/src/lib/components/Error.tsx index 06fcee6b..20f59ba1 100644 --- a/src/lib/components/Error.tsx +++ b/src/lib/components/Error.tsx @@ -1,19 +1,10 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; +import React from "react"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type ErrorProps = KcProps & { - kcContext: KcContextBase.Error; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const Error = memo((props: ErrorProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function Error(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { message, client } = kcContext; @@ -38,6 +29,4 @@ const Error = memo((props: ErrorProps) => { } /> ); -}); - -export default Error; +} diff --git a/src/lib/components/IdpReviewUserProfile.tsx b/src/lib/components/IdpReviewUserProfile.tsx index a14cf4b2..2147073d 100644 --- a/src/lib/components/IdpReviewUserProfile.tsx +++ b/src/lib/components/IdpReviewUserProfile.tsx @@ -1,21 +1,12 @@ -import React, { useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; import { UserProfileFormFields } from "./shared/UserProfileCommons"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type IdpReviewUserProfileProps = KcProps & { - kcContext: KcContextBase.IdpReviewUserProfile; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const IdpReviewUserProfile = memo((props: IdpReviewUserProfileProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function IdpReviewUserProfile(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg, msgStr } = i18n; @@ -53,6 +44,4 @@ const IdpReviewUserProfile = memo((props: IdpReviewUserProfileProps) => { } /> ); -}); - -export default IdpReviewUserProfile; +} diff --git a/src/lib/components/Info.tsx b/src/lib/components/Info.tsx index 851327c5..e11e57fe 100644 --- a/src/lib/components/Info.tsx +++ b/src/lib/components/Info.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; +import React from "react"; import { assert } from "../tools/assert"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type InfoProps = KcProps & { - kcContext: KcContextBase.Info; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const Info = memo((props: InfoProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function Info(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msgStr, msg } = i18n; @@ -55,6 +46,4 @@ const Info = memo((props: InfoProps) => { } /> ); -}); - -export default Info; +} diff --git a/src/lib/components/KcApp.tsx b/src/lib/components/KcApp.tsx index cf22d9c8..fd34d1b1 100644 --- a/src/lib/components/KcApp.tsx +++ b/src/lib/components/KcApp.tsx @@ -1,10 +1,9 @@ -import React, { lazy, memo, Suspense } from "react"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { KcProps } from "./KcProps"; +import React, { lazy, Suspense } from "react"; import { __unsafe_useI18n as useI18n } from "../i18n"; -import type { I18n } from "../i18n"; import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; const Login = lazy(() => import("./Login")); const Register = lazy(() => import("./Register")); @@ -28,14 +27,7 @@ const LogoutConfirm = lazy(() => import("./LogoutConfirm")); const UpdateUserProfile = lazy(() => import("./UpdateUserProfile")); const IdpReviewUserProfile = lazy(() => import("./IdpReviewUserProfile")); -export type KcAppProps = KcProps & { - kcContext: KcContextBase; - i18n?: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const KcApp = memo((props_: KcAppProps) => { +export default function KcApp(props_: PageProps) { const { kcContext, i18n: userProvidedI18n, Template = DefaultTemplate, ...kcProps } = props_; const i18n = (function useClosure() { @@ -104,6 +96,4 @@ const KcApp = memo((props_: KcAppProps) => { })()} ); -}); - -export default KcApp; +} diff --git a/src/lib/components/Login.tsx b/src/lib/components/Login.tsx index a12a2c4c..719fd57d 100644 --- a/src/lib/components/Login.tsx +++ b/src/lib/components/Login.tsx @@ -1,22 +1,13 @@ -import React, { useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import { useConstCallback } from "../tools/useConstCallback"; import type { FormEventHandler } from "react"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginProps = KcProps & { - kcContext: KcContextBase.Login; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const Login = memo((props: LoginProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function Login(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext; @@ -199,6 +190,4 @@ const Login = memo((props: LoginProps) => { } /> ); -}); - -export default Login; +} diff --git a/src/lib/components/LoginConfigTotp.tsx b/src/lib/components/LoginConfigTotp.tsx index 1f4303b0..cf02aec8 100644 --- a/src/lib/components/LoginConfigTotp.tsx +++ b/src/lib/components/LoginConfigTotp.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginConfigTotpProps = KcProps & { - kcContext: KcContextBase.LoginConfigTotp; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginConfigTotp = memo((props: LoginConfigTotpProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginConfigTotp(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext; @@ -188,6 +179,4 @@ const LoginConfigTotp = memo((props: LoginConfigTotpProps) => { } /> ); -}); - -export default LoginConfigTotp; +} diff --git a/src/lib/components/LoginIdpLinkConfirm.tsx b/src/lib/components/LoginIdpLinkConfirm.tsx index 0bd9045d..3d6a7416 100644 --- a/src/lib/components/LoginIdpLinkConfirm.tsx +++ b/src/lib/components/LoginIdpLinkConfirm.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginIdpLinkConfirmProps = KcProps & { - kcContext: KcContextBase.LoginIdpLinkConfirm; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginIdpLinkConfirm = memo((props: LoginIdpLinkConfirmProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginIdpLinkConfirm(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, idpAlias } = kcContext; @@ -60,6 +51,4 @@ const LoginIdpLinkConfirm = memo((props: LoginIdpLinkConfirmProps) => { } /> ); -}); - -export default LoginIdpLinkConfirm; +} diff --git a/src/lib/components/LoginIdpLinkEmail.tsx b/src/lib/components/LoginIdpLinkEmail.tsx index 66396a19..9956ca2b 100644 --- a/src/lib/components/LoginIdpLinkEmail.tsx +++ b/src/lib/components/LoginIdpLinkEmail.tsx @@ -1,19 +1,10 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; +import React from "react"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginIdpLinkEmailProps = KcProps & { - kcContext: KcContextBase.LoginIdpLinkEmail; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginIdpLinkEmail = memo((props: LoginIdpLinkEmailProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginIdpLinkEmail(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, realm, brokerContext, idpAlias } = kcContext; @@ -38,6 +29,4 @@ const LoginIdpLinkEmail = memo((props: LoginIdpLinkEmailProps) => { } /> ); -}); - -export default LoginIdpLinkEmail; +} diff --git a/src/lib/components/LoginOtp.tsx b/src/lib/components/LoginOtp.tsx index 36502d42..77fd885b 100644 --- a/src/lib/components/LoginOtp.tsx +++ b/src/lib/components/LoginOtp.tsx @@ -1,22 +1,13 @@ -import React, { useEffect, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useEffect } from "react"; import { headInsert } from "../tools/headInsert"; import { pathJoin } from "../../bin/tools/pathJoin"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginOtpProps = KcProps & { - kcContext: KcContextBase.LoginOtp; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginOtp = memo((props: LoginOtpProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginOtp(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { otpLogin, url } = kcContext; @@ -96,7 +87,7 @@ const LoginOtp = memo((props: LoginOtpProps) => { } /> ); -}); +} declare const $: any; @@ -121,5 +112,3 @@ function evaluateInlineScript() { } }); } - -export default LoginOtp; diff --git a/src/lib/components/LoginPageExpired.tsx b/src/lib/components/LoginPageExpired.tsx index 3669712b..b2b363a1 100644 --- a/src/lib/components/LoginPageExpired.tsx +++ b/src/lib/components/LoginPageExpired.tsx @@ -1,19 +1,10 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; +import React from "react"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginPageExpired = KcProps & { - kcContext: KcContextBase.LoginPageExpired; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginPageExpired = memo((props: LoginPageExpired) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginPageExpired(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url } = kcContext; @@ -42,6 +33,4 @@ const LoginPageExpired = memo((props: LoginPageExpired) => { } /> ); -}); - -export default LoginPageExpired; +} diff --git a/src/lib/components/LoginPassword.tsx b/src/lib/components/LoginPassword.tsx index 573a6917..aecbfdb0 100644 --- a/src/lib/components/LoginPassword.tsx +++ b/src/lib/components/LoginPassword.tsx @@ -1,22 +1,13 @@ -import React, { useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import { useConstCallback } from "../tools/useConstCallback"; import type { FormEventHandler } from "react"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginPasswordProps = KcProps & { - kcContext: KcContextBase.LoginPassword; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginPassword = memo((props: LoginPasswordProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginPassword(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { realm, url, login } = kcContext; @@ -92,6 +83,4 @@ const LoginPassword = memo((props: LoginPasswordProps) => { } /> ); -}); - -export default LoginPassword; +} diff --git a/src/lib/components/LoginResetPassword.tsx b/src/lib/components/LoginResetPassword.tsx index 3c5e6f68..317d14a4 100644 --- a/src/lib/components/LoginResetPassword.tsx +++ b/src/lib/components/LoginResetPassword.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginResetPasswordProps = KcProps & { - kcContext: KcContextBase.LoginResetPassword; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginResetPassword = memo((props: LoginResetPasswordProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginResetPassword(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, realm, auth } = kcContext; @@ -75,6 +66,4 @@ const LoginResetPassword = memo((props: LoginResetPasswordProps) => { infoNode={msg("emailInstruction")} /> ); -}); - -export default LoginResetPassword; +} diff --git a/src/lib/components/LoginUpdatePassword.tsx b/src/lib/components/LoginUpdatePassword.tsx index c14b973a..35a187b0 100644 --- a/src/lib/components/LoginUpdatePassword.tsx +++ b/src/lib/components/LoginUpdatePassword.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginUpdatePasswordProps = KcProps & { - kcContext: KcContextBase.LoginUpdatePassword; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginUpdatePassword = memo((props: LoginUpdatePasswordProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginUpdatePassword(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg, msgStr } = i18n; @@ -123,6 +114,4 @@ const LoginUpdatePassword = memo((props: LoginUpdatePasswordProps) => { } /> ); -}); - -export default LoginUpdatePassword; +} diff --git a/src/lib/components/LoginUpdateProfile.tsx b/src/lib/components/LoginUpdateProfile.tsx index 9f45f03d..50a03b03 100644 --- a/src/lib/components/LoginUpdateProfile.tsx +++ b/src/lib/components/LoginUpdateProfile.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginUpdateProfile = KcProps & { - kcContext: KcContextBase.LoginUpdateProfile; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginUpdateProfile = memo((props: LoginUpdateProfile) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginUpdateProfile(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg, msgStr } = i18n; @@ -130,6 +121,4 @@ const LoginUpdateProfile = memo((props: LoginUpdateProfile) => { } /> ); -}); - -export default LoginUpdateProfile; +} diff --git a/src/lib/components/LoginUsername.tsx b/src/lib/components/LoginUsername.tsx index 6fb4c7af..8d2aeb54 100644 --- a/src/lib/components/LoginUsername.tsx +++ b/src/lib/components/LoginUsername.tsx @@ -1,22 +1,13 @@ -import React, { useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import { useConstCallback } from "../tools/useConstCallback"; import type { FormEventHandler } from "react"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginUsernameProps = KcProps & { - kcContext: KcContextBase.LoginUsername; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginUsername = memo((props: LoginUsernameProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginUsername(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext; @@ -164,6 +155,4 @@ const LoginUsername = memo((props: LoginUsernameProps) => { } /> ); -}); - -export default LoginUsername; +} diff --git a/src/lib/components/LoginVerifyEmail.tsx b/src/lib/components/LoginVerifyEmail.tsx index 8ea479af..3312da03 100644 --- a/src/lib/components/LoginVerifyEmail.tsx +++ b/src/lib/components/LoginVerifyEmail.tsx @@ -1,19 +1,10 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; +import React from "react"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LoginVerifyEmailProps = KcProps & { - kcContext: KcContextBase.LoginVerifyEmail; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LoginVerifyEmail = memo((props: LoginVerifyEmailProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LoginVerifyEmail(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg } = i18n; @@ -38,6 +29,4 @@ const LoginVerifyEmail = memo((props: LoginVerifyEmailProps) => { } /> ); -}); - -export default LoginVerifyEmail; +} diff --git a/src/lib/components/LogoutConfirm.tsx b/src/lib/components/LogoutConfirm.tsx index 0ddcf6bc..1bc29c2b 100644 --- a/src/lib/components/LogoutConfirm.tsx +++ b/src/lib/components/LogoutConfirm.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; +import React from "react"; import { clsx } from "../tools/clsx"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; import type { KcContextBase } from "../getKcContext/KcContextBase"; -import type { I18n } from "../i18n"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type LogoutConfirmProps = KcProps & { - kcContext: KcContextBase.LogoutConfirm; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const LogoutConfirm = memo((props: LogoutConfirmProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function LogoutConfirm(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, client, logoutConfirm } = kcContext; @@ -64,6 +55,4 @@ const LogoutConfirm = memo((props: LogoutConfirmProps) => { } /> ); -}); - -export default LogoutConfirm; +} diff --git a/src/lib/components/Register.tsx b/src/lib/components/Register.tsx index 61c559d6..6742eae4 100644 --- a/src/lib/components/Register.tsx +++ b/src/lib/components/Register.tsx @@ -1,20 +1,11 @@ -import React, { memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type RegisterProps = KcProps & { - kcContext: KcContextBase.Register; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const Register = memo((props: RegisterProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function Register(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext; @@ -167,6 +158,4 @@ const Register = memo((props: RegisterProps) => { } /> ); -}); - -export default Register; +} diff --git a/src/lib/components/RegisterUserProfile.tsx b/src/lib/components/RegisterUserProfile.tsx index e4b348a2..21db54ff 100644 --- a/src/lib/components/RegisterUserProfile.tsx +++ b/src/lib/components/RegisterUserProfile.tsx @@ -1,21 +1,12 @@ -import React, { memo, useState } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; import { UserProfileFormFields } from "./shared/UserProfileCommons"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type RegisterUserProfileProps = KcProps & { - kcContext: KcContextBase.RegisterUserProfile; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const RegisterUserProfile = memo((props: RegisterUserProfileProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function RegisterUserProfile(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext; @@ -66,6 +57,4 @@ const RegisterUserProfile = memo((props: RegisterUserProfileProps) => { } /> ); -}); - -export default RegisterUserProfile; +} diff --git a/src/lib/components/Template.tsx b/src/lib/components/Template.tsx index fbfc2611..a9438dca 100644 --- a/src/lib/components/Template.tsx +++ b/src/lib/components/Template.tsx @@ -1,32 +1,15 @@ -import React, { useReducer, useEffect, memo } from "react"; -import type { ReactNode } from "react"; +import React, { useReducer, useEffect } from "react"; import type { KcContextBase } from "../getKcContext/KcContextBase"; import { assert } from "../tools/assert"; -import { useCallbackFactory } from "powerhooks/useCallbackFactory"; +import { useCallbackFactory } from "../tools/useCallbackFactory"; import { headInsert } from "../tools/headInsert"; import { pathJoin } from "../../bin/tools/pathJoin"; -import { useConstCallback } from "powerhooks/useConstCallback"; -import type { KcTemplateProps } from "./KcProps"; +import { useConstCallback } from "../tools/useConstCallback"; +import type { TemplateProps } from "./shared/KcProps"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; +import type { I18nBase } from "../i18n"; -export type TemplateProps = { - displayInfo?: boolean; - displayMessage?: boolean; - displayRequiredFields?: boolean; - displayWide?: boolean; - showAnotherWayIfPresent?: boolean; - headerNode: ReactNode; - showUsernameNode?: ReactNode; - formNode: ReactNode; - infoNode?: ReactNode; - /** If you write your own page you probably want - * to avoid pulling the default theme assets. - */ - doFetchDefaultThemeResources: boolean; -} & { kcContext: KcContextBase; i18n: I18n } & KcTemplateProps; - -const Template = memo((props: TemplateProps) => { +export default function Template(props: TemplateProps) { const { displayInfo = false, displayMessage = true, @@ -244,6 +227,4 @@ const Template = memo((props: TemplateProps) => { ); -}); - -export default Template; +} diff --git a/src/lib/components/Terms.tsx b/src/lib/components/Terms.tsx index 6b7f6fcb..483a0b7d 100644 --- a/src/lib/components/Terms.tsx +++ b/src/lib/components/Terms.tsx @@ -1,70 +1,20 @@ -import React, { useEffect, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useEffect } from "react"; +import { memoize } from "../tools/memoize"; import { clsx } from "../tools/clsx"; import { Evt } from "evt"; import { useRerenderOnStateChange } from "evt/hooks"; import { assert } from "tsafe/assert"; import { fallbackLanguageTag } from "../i18n"; -import type { I18n } from "../i18n"; -import memoize from "memoizee"; -import { useConst } from "powerhooks/useConst"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import { useConst } from "../tools/useConst"; +import { useConstCallback } from "../tools/useConstCallback"; import { Markdown } from "../tools/Markdown"; import type { Extends } from "tsafe"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export const evtTermMarkdown = Evt.create(undefined); - -export type KcContextLike = { - pageId: KcContextBase["pageId"]; - locale?: { - currentLanguageTag: string; - }; -}; - -assert>(); - -/** Allow to avoid bundling the terms and download it on demand*/ -export function useDownloadTerms(params: { - kcContext: KcContextLike; - downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise; -}) { - const { kcContext } = params; - - const { downloadTermMarkdownMemoized } = (function useClosure() { - const { downloadTermMarkdown } = params; - - const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown); - - const downloadTermMarkdownMemoized = useConst(() => - memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag }), { "promise": true }) - ); - - return { downloadTermMarkdownMemoized }; - })(); - - useEffect(() => { - if (kcContext.pageId !== "terms.ftl") { - return; - } - - downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then( - thermMarkdown => (evtTermMarkdown.state = thermMarkdown) - ); - }, []); -} - -export type TermsProps = KcProps & { - kcContext: KcContextBase.Terms; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const Terms = memo((props: TermsProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function Terms(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg, msgStr } = i18n; @@ -111,6 +61,45 @@ const Terms = memo((props: TermsProps) => { } /> ); -}); +} -export default Terms; +export const evtTermMarkdown = Evt.create(undefined); + +export type KcContextLike = { + pageId: KcContextBase["pageId"]; + locale?: { + currentLanguageTag: string; + }; +}; + +assert>(); + +/** Allow to avoid bundling the terms and download it on demand*/ +export function useDownloadTerms(params: { + kcContext: KcContextLike; + downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise; +}) { + const { kcContext } = params; + + const { downloadTermMarkdownMemoized } = (function useClosure() { + const { downloadTermMarkdown } = params; + + const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown); + + const downloadTermMarkdownMemoized = useConst(() => + memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag })) + ); + + return { downloadTermMarkdownMemoized }; + })(); + + useEffect(() => { + if (kcContext.pageId !== "terms.ftl") { + return; + } + + downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then( + thermMarkdown => (evtTermMarkdown.state = thermMarkdown) + ); + }, []); +} diff --git a/src/lib/components/UpdateUserProfile.tsx b/src/lib/components/UpdateUserProfile.tsx index 46325029..c3f1a7b5 100644 --- a/src/lib/components/UpdateUserProfile.tsx +++ b/src/lib/components/UpdateUserProfile.tsx @@ -1,21 +1,12 @@ -import React, { useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useState } from "react"; import { clsx } from "../tools/clsx"; -import type { I18n } from "../i18n"; import { UserProfileFormFields } from "./shared/UserProfileCommons"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type UpdateUserProfileProps = KcProps & { - kcContext: KcContextBase.UpdateUserProfile; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const UpdateUserProfile = memo((props: UpdateUserProfileProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function UpdateUserProfile(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { msg, msgStr } = i18n; @@ -73,6 +64,4 @@ const UpdateUserProfile = memo((props: UpdateUserProfileProps) => { } /> ); -}); - -export default UpdateUserProfile; +} diff --git a/src/lib/components/WebauthnAuthenticate.tsx b/src/lib/components/WebauthnAuthenticate.tsx index 0acad816..7b40909d 100644 --- a/src/lib/components/WebauthnAuthenticate.tsx +++ b/src/lib/components/WebauthnAuthenticate.tsx @@ -1,22 +1,14 @@ -import React, { useRef, useState, memo } from "react"; -import DefaultTemplate from "./Template"; -import type { TemplateProps } from "./Template"; -import type { KcProps } from "./KcProps"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; +import React, { useRef, useState } from "react"; import { clsx } from "../tools/clsx"; -import type { I18n, MessageKeyBase } from "../i18n"; +import type { MessageKeyBase } from "../i18n"; import { base64url } from "rfc4648"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import { useConstCallback } from "../tools/useConstCallback"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import type { PageProps } from "./shared/KcProps"; +import type { I18nBase } from "../i18n"; -export type WebauthnAuthenticateProps = KcProps & { - kcContext: KcContextBase.WebauthnAuthenticate; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template?: (props: TemplateProps) => JSX.Element | null; -}; - -const WebauthnAuthenticate = memo((props: WebauthnAuthenticateProps) => { - const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props; +export default function WebauthnAuthenticate(props: PageProps) { + const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props; const { url } = kcContext; @@ -198,6 +190,4 @@ const WebauthnAuthenticate = memo((props: WebauthnAuthenticateProps) => { } /> ); -}); - -export default WebauthnAuthenticate; +} diff --git a/src/lib/components/KcProps.ts b/src/lib/components/shared/KcProps.ts similarity index 89% rename from src/lib/components/KcProps.ts rename to src/lib/components/shared/KcProps.ts index 03551616..50c9a055 100644 --- a/src/lib/components/KcProps.ts +++ b/src/lib/components/shared/KcProps.ts @@ -1,5 +1,8 @@ -import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined"; +import { allPropertiesValuesToUndefined } from "../../tools/allPropertiesValuesToUndefined"; import { assert } from "tsafe/assert"; +import type { KcContextBase } from "../../getKcContext"; +import type { ReactNode } from "react"; +import { I18nBase } from "../../i18n"; /** Class names can be provided as an array or separated by whitespace */ export type KcPropsGeneric = { @@ -205,6 +208,29 @@ export const defaultKcProps = { "kcFormOptionsWrapperClass": [] } as const; +export type TemplateProps = { + kcContext: KcContext; + i18n: I18n; + doFetchDefaultThemeResources: boolean; +} & { + displayInfo?: boolean; + displayMessage?: boolean; + displayRequiredFields?: boolean; + displayWide?: boolean; + showAnotherWayIfPresent?: boolean; + headerNode: ReactNode; + showUsernameNode?: ReactNode; + formNode: ReactNode; + infoNode?: ReactNode; +} & KcTemplateProps; + +export type PageProps = { + kcContext: KcContext; + i18n: I18n; + doFetchDefaultThemeResources?: boolean; + Template: (props: TemplateProps) => JSX.Element | null; +} & KcProps; + assert(); /** Tu use if you don't want any default */ diff --git a/src/lib/components/shared/Template.tsx b/src/lib/components/shared/Template.tsx new file mode 100644 index 00000000..3fac5233 --- /dev/null +++ b/src/lib/components/shared/Template.tsx @@ -0,0 +1,233 @@ +import React, { useReducer, useEffect } from "react"; +import { useCallbackFactory } from "../../tools/useCallbackFactory"; +import { useConstCallback } from "../../tools/useConstCallback"; +import { assert } from "../../tools/assert"; +import { headInsert } from "../../tools/headInsert"; +import { pathJoin } from "../../../bin/tools/pathJoin"; +import { clsx } from "../../tools/clsx"; +import type { TemplateProps } from "./KcProps"; +import type { KcContextBase } from "../../getKcContext/KcContextBase"; +import type { I18nBase } from "../../i18n"; + +export default function Template(props: TemplateProps) { + const { + displayInfo = false, + displayMessage = true, + displayRequiredFields = false, + displayWide = false, + showAnotherWayIfPresent = true, + headerNode, + showUsernameNode = null, + formNode, + infoNode = null, + kcContext, + i18n, + doFetchDefaultThemeResources + } = props; + + const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n; + + const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag)); + + const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false)); + + const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext; + + const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false); + + useEffect(() => { + if (!doFetchDefaultThemeResources) { + setExtraCssLoaded(); + return; + } + + let isUnmounted = false; + const cleanups: (() => void)[] = []; + + const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []); + + Promise.all( + [ + ...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)), + ...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath)) + ] + .reverse() + .map(href => + headInsert({ + "type": "css", + href, + "position": "prepend" + }) + ) + ).then(() => { + if (isUnmounted) { + return; + } + + setExtraCssLoaded(); + }); + + toArr(props.scripts).forEach(relativePath => + headInsert({ + "type": "javascript", + "src": pathJoin(url.resourcesPath, relativePath) + }) + ); + + if (props.kcHtmlClass !== undefined) { + const htmlClassList = document.getElementsByTagName("html")[0].classList; + + const tokens = clsx(props.kcHtmlClass).split(" "); + + htmlClassList.add(...tokens); + + cleanups.push(() => htmlClassList.remove(...tokens)); + } + + return () => { + isUnmounted = true; + + cleanups.forEach(f => f()); + }; + }, [props.kcHtmlClass]); + + if (!isExtraCssLoaded) { + return null; + } + + return ( +
+
+
+ {msg("loginTitleHtml", realm.displayNameHtml)} +
+
+ +
+
+ {realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && ( +
+
+
+ {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + + {labelBySupportedLanguageTag[currentLanguageTag]} + + +
+
+
+ )} + {!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? ( + displayRequiredFields ? ( +
+
+ + * + {msg("requiredFields")} + +
+
+

{headerNode}

+
+
+ ) : ( +

{headerNode}

+ ) + ) : displayRequiredFields ? ( +
+
+ + * {msg("requiredFields")} + +
+
+ {showUsernameNode} +
+
+ + +
+ + {msg("restartLoginTooltip")} +
+
+
+
+
+
+ ) : ( + <> + {showUsernameNode} +
+
+ + +
+ + {msg("restartLoginTooltip")} +
+
+
+
+ + )} +
+
+
+ {/* App-initiated actions should not see warning messages about the need to complete the action during login. */} + {displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && ( +
+ {message.type === "success" && } + {message.type === "warning" && } + {message.type === "error" && } + {message.type === "info" && } + +
+ )} + {formNode} + {auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && ( +
+
+
+ + {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + + {msg("doTryAnotherWay")} + +
+
+
+ )} + {displayInfo && ( +
+
+ {infoNode} +
+
+ )} +
+
+
+
+ ); +} diff --git a/src/lib/components/shared/UserProfileCommons.tsx b/src/lib/components/shared/UserProfileCommons.tsx index 5b029ee9..f052b6e4 100644 --- a/src/lib/components/shared/UserProfileCommons.tsx +++ b/src/lib/components/shared/UserProfileCommons.tsx @@ -1,173 +1,178 @@ -import React, { memo, useEffect, Fragment } from "react"; -import type { KcProps } from "../KcProps"; +import React, { useEffect, Fragment } from "react"; +import type { KcProps } from "./KcProps"; import type { Attribute } from "../../getKcContext/KcContextBase"; import { clsx } from "../../tools/clsx"; import type { ReactComponent } from "../../tools/ReactComponent"; -import { useCallbackFactory } from "powerhooks/useCallbackFactory"; +import { useCallbackFactory } from "../../tools/useCallbackFactory"; import { useFormValidationSlice } from "../../useFormValidationSlice"; -import type { I18n } from "../../i18n"; +import type { I18nBase } from "../../i18n"; import type { Param0 } from "tsafe/Param0"; export type UserProfileFormFieldsProps = { kcContext: Param0["kcContext"]; - i18n: I18n; + i18n: I18nBase; } & KcProps & Partial>> & { onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void; }; -export const UserProfileFormFields = memo( - ({ kcContext, onIsFormSubmittableValueChange, i18n, BeforeField, AfterField, ...props }: UserProfileFormFieldsProps) => { - const { advancedMsg } = i18n; +export function UserProfileFormFields({ + kcContext, + onIsFormSubmittableValueChange, + i18n, + BeforeField, + AfterField, + ...props +}: UserProfileFormFieldsProps) { + const { advancedMsg } = i18n; - const { - formValidationState: { fieldStateByAttributeName, isFormSubmittable }, - formValidationReducer, - attributesWithPassword - } = useFormValidationSlice({ - kcContext, - i18n - }); + const { + formValidationState: { fieldStateByAttributeName, isFormSubmittable }, + formValidationReducer, + attributesWithPassword + } = useFormValidationSlice({ + kcContext, + i18n + }); - useEffect(() => { - onIsFormSubmittableValueChange(isFormSubmittable); - }, [isFormSubmittable]); + useEffect(() => { + onIsFormSubmittableValueChange(isFormSubmittable); + }, [isFormSubmittable]); - const onChangeFactory = useCallbackFactory( - ( - [name]: [string], - [ - { - target: { value } - } - ]: [React.ChangeEvent] - ) => - formValidationReducer({ - "action": "update value", - name, - "newValue": value - }) - ); - - const onBlurFactory = useCallbackFactory(([name]: [string]) => + const onChangeFactory = useCallbackFactory( + ( + [name]: [string], + [ + { + target: { value } + } + ]: [React.ChangeEvent] + ) => formValidationReducer({ - "action": "focus lost", - name + "action": "update value", + name, + "newValue": value }) - ); + ); - let currentGroup = ""; + const onBlurFactory = useCallbackFactory(([name]: [string]) => + formValidationReducer({ + "action": "focus lost", + name + }) + ); - return ( - <> - {attributesWithPassword.map((attribute, i) => { - const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute; + let currentGroup = ""; - const { value, displayableErrors } = fieldStateByAttributeName[attribute.name]; + return ( + <> + {attributesWithPassword.map((attribute, i) => { + const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute; - const formGroupClassName = clsx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass); + const { value, displayableErrors } = fieldStateByAttributeName[attribute.name]; - return ( - - {group !== currentGroup && (currentGroup = group) !== "" && ( -
-
-
+ )} - {BeforeField && } + {BeforeField && } -
-
- - {attribute.required && <>*} -
-
- {(() => { - const { options } = attribute.validators; - - if (options !== undefined) { - return ( - - ); - } +
+
+ + {attribute.required && <>*} +
+
+ {(() => { + const { options } = attribute.validators; + if (options !== undefined) { return ( - { - switch (attribute.name) { - case "password-confirm": - case "password": - return "password"; - default: - return "text"; - } - })()} + + ); + } + + return ( + { + switch (attribute.name) { + case "password-confirm": + case "password": + return "password"; + default: + return "text"; + } + })()} + id={attribute.name} + name={attribute.name} + value={value} + onChange={onChangeFactory(attribute.name)} + className={clsx(props.kcInputClass)} + aria-invalid={displayableErrors.length !== 0} + disabled={attribute.readOnly} + autoComplete={attribute.autocomplete} + onBlur={onBlurFactory(attribute.name)} + /> + ); + })()} + {displayableErrors.length !== 0 && + (() => { + const divId = `input-error-${attribute.name}`; + + return ( + <> + + + {displayableErrors.map(({ errorMessage }) => errorMessage)} + + ); })()} - {displayableErrors.length !== 0 && - (() => { - const divId = `input-error-${attribute.name}`; - - return ( - <> - - - {displayableErrors.map(({ errorMessage }) => errorMessage)} - - - ); - })()} -
+
- {AfterField && } - - ); - })} - - ); - } -); + {AfterField && } + + ); + })} + + ); +} diff --git a/src/lib/getKcContext/KcContextBase.ts b/src/lib/getKcContext/KcContextBase.ts index e2290847..be81964c 100644 --- a/src/lib/getKcContext/KcContextBase.ts +++ b/src/lib/getKcContext/KcContextBase.ts @@ -2,7 +2,7 @@ import type { PageId } from "../../bin/keycloakify/generateFtl"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; import type { MessageKeyBase } from "../i18n"; -import type { KcTemplateClassKey } from "../components/KcProps"; +import type { KcTemplateClassKey } from "../components/shared/KcProps"; type ExtractAfterStartingWith = StrEnum extends `${Prefix}${infer U}` ? U : never; diff --git a/src/lib/i18n/i18n.tsx b/src/lib/i18n/i18n.tsx new file mode 100644 index 00000000..37204902 --- /dev/null +++ b/src/lib/i18n/i18n.tsx @@ -0,0 +1,292 @@ +import "minimal-polyfills/Object.fromEntries"; +//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35 +import React, { useEffect, useState, useRef } from "react"; +import type baseMessages from "./generated_messages/18.0.1/login/en"; +import { assert } from "tsafe/assert"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import { Markdown } from "../tools/Markdown"; + +export const fallbackLanguageTag = "en"; + +export type KcContextLike = { + locale?: { + currentLanguageTag: string; + supported: { languageTag: string; url: string; label: string }[]; + }; +}; + +assert(); + +export type MessageKeyBase = keyof typeof baseMessages | keyof typeof keycloakifyExtraMessages[typeof fallbackLanguageTag]; + +export type I18n = { + /** + * e.g: "en", "fr", "zh-CN" + * + * The current language + */ + currentLanguageTag: string; + /** + * To call when the user switch language. + * This will cause the page to be reloaded, + * on next load currentLanguageTag === newLanguageTag + */ + changeLocale: (newLanguageTag: string) => never; + /** + * e.g. "en" => "English", "fr" => "Français", ... + * + * Used to render a select that enable user to switch language. + * ex: https://user-images.githubusercontent.com/6702424/186044799-38801eec-4e89-483b-81dd-8e9233e8c0eb.png + * */ + labelBySupportedLanguageTag: Record; + /** + * Examples assuming currentLanguageTag === "en" + * + * msg("access-denied") === Access denied + * msg("impersonateTitleHtml", "Foo") === Foo Impersonate User + */ + msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element; + /** + * It's the same thing as msg() but instead of returning a JSX.Element it returns a string. + * It can be more convenient to manipulate strings but if there are HTML tags it wont render. + * msgStr("impersonateTitleHtml", "Foo") === "Foo Impersonate User" + */ + msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string; + /** + * Examples assuming currentLanguageTag === "en" + * advancedMsg("${access-denied} foo bar") === ${msgStr("access-denied")} foo bar === Access denied foo bar + * advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied") === Access denied + * advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === not-a-message-key + */ + advancedMsg: (key: string, ...args: (string | undefined)[]) => JSX.Element; + /** + * Examples assuming currentLanguageTag === "en" + * advancedMsg("${access-denied} foo bar") === msg("access-denied") + " foo bar" === "Access denied foo bar" + * advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === "not-a-message-key" + */ + advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string; +}; + +export type I18nBase = I18n; + +export function __unsafe_useI18n(params: { + kcContext: KcContextLike; + extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }; + doSkip: boolean; +}): I18n | null { + const { kcContext, extraMessages, doSkip } = params; + + const [i18n, setI18n] = useState | undefined>(undefined); + + const refHasStartedFetching = useRef(false); + + useEffect(() => { + if (doSkip || refHasStartedFetching.current) { + return; + } + + refHasStartedFetching.current = true; + + (async () => { + const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {}; + + const [fallbackMessages, messages] = await Promise.all([ + import("./generated_messages/18.0.1/login/en"), + (() => { + switch (currentLanguageTag) { + case "ca": + return import("./generated_messages/18.0.1/login/ca"); + case "cs": + return import("./generated_messages/18.0.1/login/cs"); + case "da": + return import("./generated_messages/18.0.1/login/da"); + case "de": + return import("./generated_messages/18.0.1/login/de"); + case "en": + return import("./generated_messages/18.0.1/login/en"); + case "es": + return import("./generated_messages/18.0.1/login/es"); + case "fi": + return import("./generated_messages/18.0.1/login/fi"); + case "fr": + return import("./generated_messages/18.0.1/login/fr"); + case "hu": + return import("./generated_messages/18.0.1/login/hu"); + case "it": + return import("./generated_messages/18.0.1/login/it"); + case "ja": + return import("./generated_messages/18.0.1/login/ja"); + case "lt": + return import("./generated_messages/18.0.1/login/lt"); + case "lv": + return import("./generated_messages/18.0.1/login/lv"); + case "nl": + return import("./generated_messages/18.0.1/login/nl"); + case "no": + return import("./generated_messages/18.0.1/login/no"); + case "pl": + return import("./generated_messages/18.0.1/login/pl"); + case "pt-BR": + return import("./generated_messages/18.0.1/login/pt-BR"); + case "ru": + return import("./generated_messages/18.0.1/login/ru"); + case "sk": + return import("./generated_messages/18.0.1/login/sk"); + case "sv": + return import("./generated_messages/18.0.1/login/sv"); + case "tr": + return import("./generated_messages/18.0.1/login/tr"); + case "zh-CN": + return import("./generated_messages/18.0.1/login/zh-CN"); + default: + return { "default": {} }; + } + })() + ]).then(modules => modules.map(module => module.default)); + + setI18n({ + ...createI18nTranslationFunctions({ + "fallbackMessages": { + ...fallbackMessages, + ...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}), + ...(extraMessages[fallbackLanguageTag] ?? {}) + } as any, + "messages": { + ...messages, + ...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}), + ...(extraMessages[currentLanguageTag] ?? {}) + } as any + }), + currentLanguageTag, + "changeLocale": newLanguageTag => { + const { locale } = kcContext; + + assert(locale !== undefined, "Internationalization not enabled"); + + const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag); + + assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`); + + window.location.href = targetSupportedLocale.url; + + assert(false, "never"); + }, + "labelBySupportedLanguageTag": Object.fromEntries( + (kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]) + ) + }); + })(); + }, []); + + return i18n ?? null; +} + +const useI18n_private = __unsafe_useI18n; + +export function useI18n(params: { + kcContext: KcContextLike; + extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }; +}): I18n | null { + return useI18n_private({ + ...params, + "doSkip": false + }); +} + +function createI18nTranslationFunctions(params: { + fallbackMessages: Record; + messages: Record; +}): Pick, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> { + const { fallbackMessages, messages } = params; + + function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): string | JSX.Element | undefined { + const { key, args, doRenderMarkdown } = props; + + const messageOrUndefined: string | undefined = (messages as any)[key] ?? (fallbackMessages as any)[key]; + + if (messageOrUndefined === undefined) { + return undefined; + } + + const message = messageOrUndefined; + + const messageWithArgsInjectedIfAny = (() => { + const startIndex = message + .match(/{[0-9]+}/g) + ?.map(g => g.match(/{([0-9]+)}/)![1]) + .map(indexStr => parseInt(indexStr)) + .sort((a, b) => a - b)[0]; + + if (startIndex === undefined) { + // No {0} in message (no arguments expected) + return message; + } + + let messageWithArgsInjected = message; + + args.forEach((arg, i) => { + if (arg === undefined) { + return; + } + + messageWithArgsInjected = messageWithArgsInjected.replace(new RegExp(`\\{${i + startIndex}\\}`, "g"), arg); + }); + + return messageWithArgsInjected; + })(); + + return doRenderMarkdown ? ( + + {messageWithArgsInjectedIfAny} + + ) : ( + messageWithArgsInjectedIfAny + ); + } + + function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): JSX.Element | string { + const { key, args, doRenderMarkdown } = props; + + const match = key.match(/^\$\{([^{]+)\}$/); + + const keyUnwrappedFromCurlyBraces = match === null ? key : match[1]; + + const out = resolveMsg({ + "key": keyUnwrappedFromCurlyBraces, + args, + doRenderMarkdown + }); + + return (out !== undefined ? out : doRenderMarkdown ? {keyUnwrappedFromCurlyBraces} : keyUnwrappedFromCurlyBraces) as any; + } + + return { + "msgStr": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": false }) as string, + "msg": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": true }) as JSX.Element, + "advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": true }) as JSX.Element, + "advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": false }) as string + }; +} + +const keycloakifyExtraMessages = { + "en": { + "shouldBeEqual": "{0} should be equal to {1}", + "shouldBeDifferent": "{0} should be different to {1}", + "shouldMatchPattern": "Pattern should match: `/{0}/`", + "mustBeAnInteger": "Must be an integer", + "notAValidOption": "Not a valid option" + }, + "fr": { + /* spell-checker: disable */ + "shouldBeEqual": "{0} doit être égal à {1}", + "shouldBeDifferent": "{0} doit être différent de {1}", + "shouldMatchPattern": "Dois respecter le schéma: `/{0}/`", + "mustBeAnInteger": "Doit être un nombre entier", + "notAValidOption": "N'est pas une option valide", + + "logoutConfirmTitle": "Déconnexion", + "logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?", + "doLogout": "Se déconnecter" + /* spell-checker: enable */ + } +}; diff --git a/src/lib/i18n/index.tsx b/src/lib/i18n/index.tsx index 8b74c175..0c4205e0 100644 --- a/src/lib/i18n/index.tsx +++ b/src/lib/i18n/index.tsx @@ -1,290 +1 @@ -import "minimal-polyfills/Object.fromEntries"; -//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35 -import React, { useEffect, useState, useRef } from "react"; -import type baseMessages from "./generated_messages/18.0.1/login/en"; -import { assert } from "tsafe/assert"; -import type { KcContextBase } from "../getKcContext/KcContextBase"; -import { Markdown } from "../tools/Markdown"; - -export const fallbackLanguageTag = "en"; - -export type KcContextLike = { - locale?: { - currentLanguageTag: string; - supported: { languageTag: string; url: string; label: string }[]; - }; -}; - -assert(); - -export type MessageKeyBase = keyof typeof baseMessages | keyof typeof keycloakifyExtraMessages[typeof fallbackLanguageTag]; - -export type I18n = { - /** - * e.g: "en", "fr", "zh-CN" - * - * The current language - */ - currentLanguageTag: string; - /** - * To call when the user switch language. - * This will cause the page to be reloaded, - * on next load currentLanguageTag === newLanguageTag - */ - changeLocale: (newLanguageTag: string) => never; - /** - * e.g. "en" => "English", "fr" => "Français", ... - * - * Used to render a select that enable user to switch language. - * ex: https://user-images.githubusercontent.com/6702424/186044799-38801eec-4e89-483b-81dd-8e9233e8c0eb.png - * */ - labelBySupportedLanguageTag: Record; - /** - * Examples assuming currentLanguageTag === "en" - * - * msg("access-denied") === Access denied - * msg("impersonateTitleHtml", "Foo") === Foo Impersonate User - */ - msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element; - /** - * It's the same thing as msg() but instead of returning a JSX.Element it returns a string. - * It can be more convenient to manipulate strings but if there are HTML tags it wont render. - * msgStr("impersonateTitleHtml", "Foo") === "Foo Impersonate User" - */ - msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string; - /** - * Examples assuming currentLanguageTag === "en" - * advancedMsg("${access-denied} foo bar") === ${msgStr("access-denied")} foo bar === Access denied foo bar - * advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied") === Access denied - * advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === not-a-message-key - */ - advancedMsg: (key: string, ...args: (string | undefined)[]) => JSX.Element; - /** - * Examples assuming currentLanguageTag === "en" - * advancedMsg("${access-denied} foo bar") === msg("access-denied") + " foo bar" === "Access denied foo bar" - * advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === "not-a-message-key" - */ - advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string; -}; - -export function __unsafe_useI18n(params: { - kcContext: KcContextLike; - extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }; - doSkip: boolean; -}): I18n | null { - const { kcContext, extraMessages, doSkip } = params; - - const [i18n, setI18n] = useState | undefined>(undefined); - - const refHasStartedFetching = useRef(false); - - useEffect(() => { - if (doSkip || refHasStartedFetching.current) { - return; - } - - refHasStartedFetching.current = true; - - (async () => { - const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {}; - - const [fallbackMessages, messages] = await Promise.all([ - import("./generated_messages/18.0.1/login/en"), - (() => { - switch (currentLanguageTag) { - case "ca": - return import("./generated_messages/18.0.1/login/ca"); - case "cs": - return import("./generated_messages/18.0.1/login/cs"); - case "da": - return import("./generated_messages/18.0.1/login/da"); - case "de": - return import("./generated_messages/18.0.1/login/de"); - case "en": - return import("./generated_messages/18.0.1/login/en"); - case "es": - return import("./generated_messages/18.0.1/login/es"); - case "fi": - return import("./generated_messages/18.0.1/login/fi"); - case "fr": - return import("./generated_messages/18.0.1/login/fr"); - case "hu": - return import("./generated_messages/18.0.1/login/hu"); - case "it": - return import("./generated_messages/18.0.1/login/it"); - case "ja": - return import("./generated_messages/18.0.1/login/ja"); - case "lt": - return import("./generated_messages/18.0.1/login/lt"); - case "lv": - return import("./generated_messages/18.0.1/login/lv"); - case "nl": - return import("./generated_messages/18.0.1/login/nl"); - case "no": - return import("./generated_messages/18.0.1/login/no"); - case "pl": - return import("./generated_messages/18.0.1/login/pl"); - case "pt-BR": - return import("./generated_messages/18.0.1/login/pt-BR"); - case "ru": - return import("./generated_messages/18.0.1/login/ru"); - case "sk": - return import("./generated_messages/18.0.1/login/sk"); - case "sv": - return import("./generated_messages/18.0.1/login/sv"); - case "tr": - return import("./generated_messages/18.0.1/login/tr"); - case "zh-CN": - return import("./generated_messages/18.0.1/login/zh-CN"); - default: - return { "default": {} }; - } - })() - ]).then(modules => modules.map(module => module.default)); - - setI18n({ - ...createI18nTranslationFunctions({ - "fallbackMessages": { - ...fallbackMessages, - ...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}), - ...(extraMessages[fallbackLanguageTag] ?? {}) - } as any, - "messages": { - ...messages, - ...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}), - ...(extraMessages[currentLanguageTag] ?? {}) - } as any - }), - currentLanguageTag, - "changeLocale": newLanguageTag => { - const { locale } = kcContext; - - assert(locale !== undefined, "Internationalization not enabled"); - - const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag); - - assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`); - - window.location.href = targetSupportedLocale.url; - - assert(false, "never"); - }, - "labelBySupportedLanguageTag": Object.fromEntries( - (kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]) - ) - }); - })(); - }, []); - - return i18n ?? null; -} - -const useI18n_private = __unsafe_useI18n; - -export function useI18n(params: { - kcContext: KcContextLike; - extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }; -}): I18n | null { - return useI18n_private({ - ...params, - "doSkip": false - }); -} - -function createI18nTranslationFunctions(params: { - fallbackMessages: Record; - messages: Record; -}): Pick, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> { - const { fallbackMessages, messages } = params; - - function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): string | JSX.Element | undefined { - const { key, args, doRenderMarkdown } = props; - - const messageOrUndefined: string | undefined = (messages as any)[key] ?? (fallbackMessages as any)[key]; - - if (messageOrUndefined === undefined) { - return undefined; - } - - const message = messageOrUndefined; - - const messageWithArgsInjectedIfAny = (() => { - const startIndex = message - .match(/{[0-9]+}/g) - ?.map(g => g.match(/{([0-9]+)}/)![1]) - .map(indexStr => parseInt(indexStr)) - .sort((a, b) => a - b)[0]; - - if (startIndex === undefined) { - // No {0} in message (no arguments expected) - return message; - } - - let messageWithArgsInjected = message; - - args.forEach((arg, i) => { - if (arg === undefined) { - return; - } - - messageWithArgsInjected = messageWithArgsInjected.replace(new RegExp(`\\{${i + startIndex}\\}`, "g"), arg); - }); - - return messageWithArgsInjected; - })(); - - return doRenderMarkdown ? ( - - {messageWithArgsInjectedIfAny} - - ) : ( - messageWithArgsInjectedIfAny - ); - } - - function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): JSX.Element | string { - const { key, args, doRenderMarkdown } = props; - - const match = key.match(/^\$\{([^{]+)\}$/); - - const keyUnwrappedFromCurlyBraces = match === null ? key : match[1]; - - const out = resolveMsg({ - "key": keyUnwrappedFromCurlyBraces, - args, - doRenderMarkdown - }); - - return (out !== undefined ? out : doRenderMarkdown ? {keyUnwrappedFromCurlyBraces} : keyUnwrappedFromCurlyBraces) as any; - } - - return { - "msgStr": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": false }) as string, - "msg": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": true }) as JSX.Element, - "advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": true }) as JSX.Element, - "advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": false }) as string - }; -} - -const keycloakifyExtraMessages = { - "en": { - "shouldBeEqual": "{0} should be equal to {1}", - "shouldBeDifferent": "{0} should be different to {1}", - "shouldMatchPattern": "Pattern should match: `/{0}/`", - "mustBeAnInteger": "Must be an integer", - "notAValidOption": "Not a valid option" - }, - "fr": { - /* spell-checker: disable */ - "shouldBeEqual": "{0} doit être égal à {1}", - "shouldBeDifferent": "{0} doit être différent de {1}", - "shouldMatchPattern": "Dois respecter le schéma: `/{0}/`", - "mustBeAnInteger": "Doit être un nombre entier", - "notAValidOption": "N'est pas une option valide", - - "logoutConfirmTitle": "Déconnexion", - "logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?", - "doLogout": "Se déconnecter" - /* spell-checker: enable */ - } -}; +export * from "./i18n"; diff --git a/src/lib/index.ts b/src/lib/index.ts index e51b6bac..15fd34af 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,7 +4,7 @@ export * from "./i18n"; export { useDownloadTerms } from "./components/Terms"; -export * from "./components/KcProps"; +export * from "./components/shared/KcProps"; export * from "./keycloakJsAdapter"; export * from "./useFormValidationSlice"; diff --git a/src/lib/tools/clsx.ts b/src/lib/tools/clsx.ts index 5f3553fe..164f0f0b 100644 --- a/src/lib/tools/clsx.ts +++ b/src/lib/tools/clsx.ts @@ -1,7 +1,44 @@ -import { classnames } from "tss-react/tools/classnames"; -import type { Cx } from "tss-react"; +import { assert } from "tsafe/assert"; +import { typeGuard } from "tsafe/typeGuard"; -/** Drop in replacement for https://www.npmjs.com/package/clsx */ -export const clsx: Cx = (...args) => { - return classnames(args); +export type CxArg = undefined | null | string | boolean | Partial> | readonly CxArg[]; + +export const clsx = (...args: CxArg[]): string => { + const len = args.length; + let i = 0; + let cls = ""; + for (; i < len; i++) { + const arg = args[i]; + if (arg == null) continue; + + let toAdd; + switch (typeof arg) { + case "boolean": + break; + case "object": { + if (Array.isArray(arg)) { + toAdd = clsx(arg); + } else { + assert(!typeGuard<{ length: number }>(arg, false)); + + toAdd = ""; + for (const k in arg) { + if (arg[k as string] && k) { + toAdd && (toAdd += " "); + toAdd += k; + } + } + } + break; + } + default: { + toAdd = arg; + } + } + if (toAdd) { + cls && (cls += " "); + cls += toAdd; + } + } + return cls; }; diff --git a/src/lib/tools/memoize.ts b/src/lib/tools/memoize.ts new file mode 100644 index 00000000..689720dc --- /dev/null +++ b/src/lib/tools/memoize.ts @@ -0,0 +1,55 @@ +type SimpleType = number | string | boolean | null | undefined; +type FuncWithSimpleParams = (...args: T) => R; + +export function memoize( + fn: FuncWithSimpleParams, + options?: { + argsLength?: number; + max?: number; + } +): FuncWithSimpleParams { + const cache = new Map>>(); + + const { argsLength = fn.length, max = Infinity } = options ?? {}; + + return ((...args: Parameters>) => { + const key = JSON.stringify( + args + .slice(0, argsLength) + .map(v => { + if (v === null) { + return "null"; + } + if (v === undefined) { + return "undefined"; + } + switch (typeof v) { + case "number": + return `number-${v}`; + case "string": + return `string-${v}`; + case "boolean": + return `boolean-${v ? "true" : "false"}`; + } + }) + .join("-sIs9sAslOdeWlEdIos3-") + ); + + if (cache.has(key)) { + return cache.get(key); + } + + if (max === cache.size) { + for (const key of cache.keys()) { + cache.delete(key); + break; + } + } + + const value = fn(...args); + + cache.set(key, value); + + return value; + }) as any; +} diff --git a/src/lib/tools/useCallbackFactory.ts b/src/lib/tools/useCallbackFactory.ts new file mode 100644 index 00000000..2529a49e --- /dev/null +++ b/src/lib/tools/useCallbackFactory.ts @@ -0,0 +1,45 @@ +import { useRef, useState } from "react"; +import { id } from "tsafe/id"; +import { memoize } from "./memoize"; + +export type CallbackFactory = (...factoryArgs: FactoryArgs) => (...args: Args) => R; + +/** + * https://docs.powerhooks.dev/api-reference/usecallbackfactory + * + * const callbackFactory= useCallbackFactory( + * ([key]: [string], [params]: [{ foo: number; }]) => { + * ... + * }, + * [] + * ); + * + * WARNING: Factory args should not be of variable length. + * + */ +export function useCallbackFactory( + callback: (...callbackArgs: [FactoryArgs, Args]) => R +): CallbackFactory { + type Out = CallbackFactory; + + const callbackRef = useRef(callback); + + callbackRef.current = callback; + + const memoizedRef = useRef(undefined); + + return useState(() => + id((...factoryArgs) => { + if (memoizedRef.current === undefined) { + memoizedRef.current = memoize( + (...factoryArgs: FactoryArgs) => + (...args: Args) => + callbackRef.current(factoryArgs, args), + { "argsLength": factoryArgs.length } + ); + } + + return memoizedRef.current(...factoryArgs); + }) + )[0]; +} diff --git a/src/lib/tools/useConst.ts b/src/lib/tools/useConst.ts new file mode 100644 index 00000000..23df1b6a --- /dev/null +++ b/src/lib/tools/useConst.ts @@ -0,0 +1,10 @@ +import { useState } from "react"; + +/** + * Compute a value on first render and never again, + * Equivalent of const [x] = useState(()=> ...) + */ +export function useConst(getValue: () => T): T { + const [value] = useState(getValue); + return value; +} diff --git a/src/lib/tools/useConstCallback.ts b/src/lib/tools/useConstCallback.ts new file mode 100644 index 00000000..f9facb98 --- /dev/null +++ b/src/lib/tools/useConstCallback.ts @@ -0,0 +1,15 @@ +import { useRef, useState } from "react"; +import { Parameters } from "tsafe/Parameters"; + +/** https://stackoverflow.com/questions/65890278/why-cant-usecallback-always-return-the-same-ref */ +export function useConstCallback unknown) | undefined | null>(callback: NonNullable): T { + const callbackRef = useRef(null as any); + + callbackRef.current = callback; + + return useState( + () => + (...args: Parameters) => + callbackRef.current(...args) + )[0] as T; +} diff --git a/src/lib/useFormValidationSlice.tsx b/src/lib/useFormValidationSlice.tsx index a6ba942c..9104410b 100644 --- a/src/lib/useFormValidationSlice.tsx +++ b/src/lib/useFormValidationSlice.tsx @@ -1,8 +1,8 @@ import "./tools/Array.prototype.every"; import React, { useMemo, useReducer, Fragment } from "react"; import type { KcContextBase, Validators, Attribute } from "./getKcContext/KcContextBase"; -import type { I18n, MessageKeyBase } from "./i18n"; -import { useConstCallback } from "powerhooks/useConstCallback"; +import type { I18nBase, MessageKeyBase } from "./i18n"; +import { useConstCallback } from "./tools/useConstCallback"; import { id } from "tsafe/id"; import { emailRegexp } from "./tools/emailRegExp"; @@ -14,7 +14,7 @@ export function useGetErrors(params: { attributes: { name: string; value?: string; validators: Validators }[]; }; }; - i18n: I18n; + i18n: I18nBase; }) { const { kcContext, i18n } = params; @@ -319,7 +319,7 @@ export function useFormValidationSlice(params: { }; /** NOTE: Try to avoid passing a new ref every render for better performances. */ passwordValidators?: Validators; - i18n: I18n; + i18n: I18nBase; }) { const { kcContext, diff --git a/src/scripts/link-in-app.ts b/src/scripts/link-in-app.ts new file mode 100644 index 00000000..e370bcbd --- /dev/null +++ b/src/scripts/link-in-app.ts @@ -0,0 +1,153 @@ +import { execSync } from "child_process"; +import { join as pathJoin, relative as pathRelative } from "path"; +import * as fs from "fs"; + +const singletonDependencies: string[] = ["react", "@types/react"]; + +const rootDirPath = pathJoin(__dirname, "..", ".."); + +//NOTE: This is only required because of: https://github.com/garronej/ts-ci/blob/c0e207b9677523d4ec97fe672ddd72ccbb3c1cc4/README.md?plain=1#L54-L58 +fs.writeFileSync( + pathJoin(rootDirPath, "dist", "package.json"), + Buffer.from( + JSON.stringify( + (() => { + const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8")); + + return { + ...packageJsonParsed, + "main": packageJsonParsed["main"]?.replace(/^dist\//, ""), + "types": packageJsonParsed["types"]?.replace(/^dist\//, ""), + "module": packageJsonParsed["module"]?.replace(/^dist\//, ""), + "exports": !("exports" in packageJsonParsed) + ? undefined + : Object.fromEntries( + Object.entries(packageJsonParsed["exports"]).map(([key, value]) => [ + key, + (value as string).replace(/^\.\/dist\//, "./") + ]) + ) + }; + })(), + null, + 2 + ), + "utf8" + ) +); + +const commonThirdPartyDeps = (() => { + // For example [ "@emotion" ] it's more convenient than + // having to list every sub emotion packages (@emotion/css @emotion/utils ...) + // in singletonDependencies + const namespaceSingletonDependencies: string[] = []; + + return [ + ...namespaceSingletonDependencies + .map(namespaceModuleName => + fs + .readdirSync(pathJoin(rootDirPath, "node_modules", namespaceModuleName)) + .map(submoduleName => `${namespaceModuleName}/${submoduleName}`) + ) + .reduce((prev, curr) => [...prev, ...curr], []), + ...singletonDependencies + ]; +})(); + +const { yarnLinkFolderPath, yarnGlobalFolderPath } = (() => { + const yarnGlobalDirPath = pathJoin(rootDirPath, ".yarn_home"); + + return { + "yarnLinkFolderPath": pathJoin(yarnGlobalDirPath, "link"), + "yarnGlobalFolderPath": pathJoin(yarnGlobalDirPath, "global") + }; +})(); + +[yarnLinkFolderPath, yarnGlobalFolderPath].forEach(path => { + fs.rmSync(path, { "recursive": true, "force": true }); + fs.mkdirSync(path, { "recursive": true }); +}); + +const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => { + const { targetModuleName, cwd } = params; + + const cmd = [ + "yarn", + "link", + ...(targetModuleName !== undefined ? [targetModuleName] : []), + "--link-folder", + yarnLinkFolderPath, + "--global-folder", + yarnGlobalFolderPath + ].join(" "); + + console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`); + + execSync(cmd, { + cwd + }); +}; + +const testAppPaths = (() => { + const [, , ...testAppNames] = process.argv; + + return testAppNames + .map(testAppName => { + const testAppPath = pathJoin(rootDirPath, "..", testAppName); + + if (fs.existsSync(testAppPath)) { + return testAppPath; + } + + console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`); + + return undefined; + }) + .filter((path): path is string => path !== undefined); +})(); + +if (testAppPaths.length === 0) { + console.error("No test app to link into!"); + process.exit(-1); +} + +testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath })); + +console.log("=== Linking common dependencies ==="); + +const total = commonThirdPartyDeps.length; +let current = 0; + +commonThirdPartyDeps.forEach(commonThirdPartyDep => { + current++; + + console.log(`${current}/${total} ${commonThirdPartyDep}`); + + const localInstallPath = pathJoin( + ...[rootDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])] + ); + + execYarnLink({ "cwd": localInstallPath }); +}); + +commonThirdPartyDeps.forEach(commonThirdPartyDep => + testAppPaths.forEach(testAppPath => + execYarnLink({ + "cwd": testAppPath, + "targetModuleName": commonThirdPartyDep + }) + ) +); + +console.log("=== Linking in house dependencies ==="); + +execYarnLink({ "cwd": pathJoin(rootDirPath, "dist") }); + +testAppPaths.forEach(testAppPath => + execYarnLink({ + "cwd": testAppPath, + "targetModuleName": JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"))["name"] + }) +); + +export {}; diff --git a/yarn.lock b/yarn.lock index 52c52ff7..2ea78440 100644 --- a/yarn.lock +++ b/yarn.lock @@ -82,7 +82,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": +"@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== @@ -103,11 +103,6 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" -"@babel/helper-plugin-utils@^7.18.6": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" - integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== - "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -165,20 +160,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a" integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ== -"@babel/plugin-syntax-jsx@^7.17.12": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" - integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/template@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" @@ -222,94 +203,12 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" -"@emotion/babel-plugin@^11.10.0": - version "11.10.2" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7" - integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.17.12" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.0" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.0.13" - -"@emotion/cache@^11.10.0": - version "11.10.3" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" - integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== - dependencies: - "@emotion/memoize" "^0.8.0" - "@emotion/sheet" "^1.2.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - stylis "4.0.13" - -"@emotion/hash@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" - integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== - -"@emotion/memoize@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" - integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== - -"@emotion/react@^11.10.4": - version "11.10.4" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199" - integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.0" - "@emotion/cache" "^11.10.0" - "@emotion/serialize" "^1.1.0" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - hoist-non-react-statics "^3.3.1" - -"@emotion/serialize@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8" - integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA== - dependencies: - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/unitless" "^0.8.0" - "@emotion/utils" "^1.2.0" - csstype "^3.0.2" - -"@emotion/sheet@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" - integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== - -"@emotion/unitless@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" - integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== - -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" - integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== - -"@emotion/utils@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" - integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== - -"@emotion/weak-memoize@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" - integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== + "@jridgewell/trace-mapping" "0.3.9" "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" @@ -343,6 +242,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.15" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" @@ -452,6 +359,26 @@ dependencies: "@octokit/openapi-types" "^12.11.0" +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@types/mdast@^3.0.0", "@types/mdast@^3.0.3": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" @@ -469,10 +396,10 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node@^17.0.25": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^18.14.1": + version "18.14.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" + integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== "@types/parse-json@^4.0.0": version "4.0.0" @@ -503,6 +430,16 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -547,20 +484,16 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -763,11 +696,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.5.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -804,6 +732,11 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -834,14 +767,6 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - debug@^4.0.0, debug@^4.1.0, debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -854,6 +779,11 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -913,42 +843,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -es6-weak-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -959,19 +853,6 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - evt@^2.4.13: version "2.4.13" resolved "https://registry.yarnpkg.com/evt/-/evt-2.4.13.tgz#5ef873159ce62e099d58801a3e4b8e0f5b648017" @@ -996,13 +877,6 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -1015,11 +889,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-root@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== - find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -1040,11 +909,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1092,20 +956,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hoist-non-react-statics@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - html-to-react@^1.3.4: version "1.5.0" resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.5.0.tgz#6e0cf47ae1b091ba2f28a3832389fbce4d199ccc" @@ -1195,13 +1045,6 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" @@ -1237,11 +1080,6 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-promise@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" @@ -1355,12 +1193,10 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== - dependencies: - es5-ext "~0.10.2" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== mdast-add-list-metadata@1.0.1: version "1.0.1" @@ -1385,20 +1221,6 @@ mdast-util-to-string@^2.0.0: resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== -memoizee@^0.4.15: - version "0.4.15" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" - integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.53" - es6-weak-map "^2.0.3" - event-emitter "^0.3.5" - is-promise "^2.2.2" - lru-queue "^0.1.0" - next-tick "^1.1.0" - timers-ext "^0.1.7" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -1452,11 +1274,6 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -next-tick@1, next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -1605,11 +1422,6 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -1639,16 +1451,6 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" -powerhooks@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.26.0.tgz#260f8e5c019ea117911c880bf3b371ecd09786df" - integrity sha512-FvoEuj340Jm9HPGV98gjDVVdlolaIYXvQ4Xg7fiNtYdeClAJk264uFHAwFo9isXP92OW2ksvy9wVMCwMhFUGGQ== - dependencies: - evt "^2.4.13" - memoizee "^0.4.15" - resize-observer-polyfill "^1.5.1" - tsafe "^1.4.2" - prettier@^2.3.0: version "2.7.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" @@ -1675,7 +1477,7 @@ properties-parser@^0.3.1: dependencies: string.prototype.codepointat "^0.2.0" -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -1726,11 +1528,6 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -regenerator-runtime@^0.13.4: - version "0.13.10" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" - integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== - remark-parse@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" @@ -1743,25 +1540,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.19.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -1866,11 +1649,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - string-argv@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -1923,11 +1701,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -stylis@4.0.13: - version "4.0.13" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" - integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== - supports-color@8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -1949,11 +1722,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - through2@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -1967,14 +1735,6 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -timers-ext@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" - integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== - dependencies: - es5-ext "~0.10.46" - next-tick "1" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -1997,16 +1757,30 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsafe@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.4.1.tgz#59cdad8ac41babf88e56dcd1a683ae2fb145d059" integrity sha512-3IDBalvf6SyvHFS14UiwCWzqdSdo+Q0k2J7DZyJYaHW/iraW9DJpaBKDJpry3yQs3o/t/A+oGaRW3iVt2lKxzA== -tsafe@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.4.2.tgz#1cc597f6e286ef8a64b918a0a6284bd04347e11f" - integrity sha512-KfP0PYzRjl1LY1DnJPNlD4a0tZSg4uUuZxI4aG04T+j7WJvKZbh1zpaukphhSmtoJMgX1RV8eZVKTEiH8wOGbA== - tsafe@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.4.3.tgz#a98ce83616f0d9c01e3c6167a2ead45ba455b2ae" @@ -2027,20 +1801,10 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - -typescript@^4.2.3: - version "4.8.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790" - integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== unified@^9.0.0: version "9.2.2" @@ -2111,6 +1875,11 @@ util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -2210,6 +1979,11 @@ yargs@^16.1.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"