Update prettier configuration
This commit is contained in:
@ -1,11 +1,24 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 150,
|
"printWidth": 90,
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"quoteProps": "preserve",
|
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"arrowParens": "avoid"
|
"arrowParens": "avoid",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.tsx",
|
||||||
|
"options": {
|
||||||
|
"printWidth": 150
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": "useUserProfileForm.tsx",
|
||||||
|
"options": {
|
||||||
|
"printWidth": 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
"make-fetch-happen": "^11.0.3",
|
"make-fetch-happen": "^11.0.3",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"powerhooks": "^1.0.10",
|
"powerhooks": "^1.0.10",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^3.2.5",
|
||||||
"properties-parser": "^0.3.1",
|
"properties-parser": "^0.3.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -10,7 +10,10 @@ console.log(chalk.cyan("Building Keycloakify..."));
|
|||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
if (fs.existsSync(join("dist", "bin", "main.original.js"))) {
|
if (fs.existsSync(join("dist", "bin", "main.original.js"))) {
|
||||||
fs.renameSync(join("dist", "bin", "main.original.js"), join("dist", "bin", "main.js"));
|
fs.renameSync(
|
||||||
|
join("dist", "bin", "main.original.js"),
|
||||||
|
join("dist", "bin", "main.js")
|
||||||
|
);
|
||||||
|
|
||||||
fs.readdirSync(join("dist", "bin")).forEach(fileBasename => {
|
fs.readdirSync(join("dist", "bin")).forEach(fileBasename => {
|
||||||
if (/[0-9]\.index.js/.test(fileBasename)) {
|
if (/[0-9]\.index.js/.test(fileBasename)) {
|
||||||
@ -28,55 +31,74 @@ if (!fs.readFileSync(join("dist", "bin", "main.js")).toString("utf8").includes("
|
|||||||
run(`npx ncc build ${join("dist", "bin", "main.js")} -o ${join("dist", "ncc_out")}`);
|
run(`npx ncc build ${join("dist", "bin", "main.js")} -o ${join("dist", "ncc_out")}`);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": join("dist", "ncc_out"),
|
srcDirPath: join("dist", "ncc_out"),
|
||||||
"destDirPath": join("dist", "bin"),
|
destDirPath: join("dist", "bin"),
|
||||||
"transformSourceCode": ({ fileRelativePath, sourceCode }) => {
|
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||||
if (fileRelativePath === "index.js") {
|
if (fileRelativePath === "index.js") {
|
||||||
return {
|
return {
|
||||||
"newFileName": "main.js",
|
newFileName: "main.js",
|
||||||
"modifiedSourceCode": sourceCode
|
modifiedSourceCode: sourceCode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.rmSync(join("dist", "ncc_out"), { "recursive": true });
|
fs.rmSync(join("dist", "ncc_out"), { recursive: true });
|
||||||
|
|
||||||
patchDeprecatedBufferApiUsage(join("dist", "bin", "main.js"));
|
patchDeprecatedBufferApiUsage(join("dist", "bin", "main.js"));
|
||||||
|
|
||||||
fs.chmodSync(
|
fs.chmodSync(
|
||||||
join("dist", "bin", "main.js"),
|
join("dist", "bin", "main.js"),
|
||||||
fs.statSync(join("dist", "bin", "main.js")).mode | fs.constants.S_IXUSR | fs.constants.S_IXGRP | fs.constants.S_IXOTH
|
fs.statSync(join("dist", "bin", "main.js")).mode |
|
||||||
|
fs.constants.S_IXUSR |
|
||||||
|
fs.constants.S_IXGRP |
|
||||||
|
fs.constants.S_IXOTH
|
||||||
);
|
);
|
||||||
|
|
||||||
run(`npx tsc -p ${join("src", "tsconfig.json")}`);
|
run(`npx tsc -p ${join("src", "tsconfig.json")}`);
|
||||||
run(`npx tsc-alias -p ${join("src", "tsconfig.json")}`);
|
run(`npx tsc-alias -p ${join("src", "tsconfig.json")}`);
|
||||||
|
|
||||||
if (fs.existsSync(join("dist", "vite-plugin", "index.original.js"))) {
|
if (fs.existsSync(join("dist", "vite-plugin", "index.original.js"))) {
|
||||||
fs.renameSync(join("dist", "vite-plugin", "index.original.js"), join("dist", "vite-plugin", "index.js"));
|
fs.renameSync(
|
||||||
|
join("dist", "vite-plugin", "index.original.js"),
|
||||||
|
join("dist", "vite-plugin", "index.js")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
run(`npx tsc -p ${join("src", "vite-plugin", "tsconfig.json")}`);
|
run(`npx tsc -p ${join("src", "vite-plugin", "tsconfig.json")}`);
|
||||||
|
|
||||||
if (!fs.readFileSync(join("dist", "vite-plugin", "index.js")).toString("utf8").includes("ncc")) {
|
if (
|
||||||
fs.cpSync(join("dist", "vite-plugin", "index.js"), join("dist", "vite-plugin", "index.original.js"));
|
!fs
|
||||||
|
.readFileSync(join("dist", "vite-plugin", "index.js"))
|
||||||
|
.toString("utf8")
|
||||||
|
.includes("ncc")
|
||||||
|
) {
|
||||||
|
fs.cpSync(
|
||||||
|
join("dist", "vite-plugin", "index.js"),
|
||||||
|
join("dist", "vite-plugin", "index.original.js")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
run(`npx ncc build ${join("dist", "vite-plugin", "index.js")} -o ${join("dist", "ncc_out")}`);
|
run(
|
||||||
|
`npx ncc build ${join("dist", "vite-plugin", "index.js")} -o ${join(
|
||||||
|
"dist",
|
||||||
|
"ncc_out"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": join("dist", "ncc_out"),
|
srcDirPath: join("dist", "ncc_out"),
|
||||||
"destDirPath": join("dist", "vite-plugin"),
|
destDirPath: join("dist", "vite-plugin"),
|
||||||
"transformSourceCode": ({ fileRelativePath, sourceCode }) => {
|
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||||
assert(fileRelativePath === "index.js");
|
assert(fileRelativePath === "index.js");
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.rmSync(join("dist", "ncc_out"), { "recursive": true });
|
fs.rmSync(join("dist", "ncc_out"), { recursive: true });
|
||||||
|
|
||||||
patchDeprecatedBufferApiUsage(join("dist", "vite-plugin", "index.js"));
|
patchDeprecatedBufferApiUsage(join("dist", "vite-plugin", "index.js"));
|
||||||
|
|
||||||
@ -85,7 +107,7 @@ console.log(chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixe
|
|||||||
function run(command: string) {
|
function run(command: string) {
|
||||||
console.log(chalk.grey(`$ ${command}`));
|
console.log(chalk.grey(`$ ${command}`));
|
||||||
|
|
||||||
child_process.execSync(command, { "stdio": "inherit" });
|
child_process.execSync(command, { stdio: "inherit" });
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchDeprecatedBufferApiUsage(filePath: string) {
|
function patchDeprecatedBufferApiUsage(filePath: string) {
|
||||||
|
@ -4,15 +4,34 @@ import { SemVer } from "../src/bin/tools/SemVer";
|
|||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
import { join as pathJoin, relative as pathRelative } from "path";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
|
|
||||||
run([`docker exec -it ${containerName}`, `/opt/keycloak/bin/kc.sh export`, `--dir /tmp`, `--realm myrealm`, `--users realm_file`].join(" "));
|
run(
|
||||||
|
[
|
||||||
|
`docker exec -it ${containerName}`,
|
||||||
|
`/opt/keycloak/bin/kc.sh export`,
|
||||||
|
`--dir /tmp`,
|
||||||
|
`--realm myrealm`,
|
||||||
|
`--users realm_file`
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
|
||||||
const keycloakMajorVersionNumber = SemVer.parse(
|
const keycloakMajorVersionNumber = SemVer.parse(
|
||||||
child_process.execSync(`docker inspect --format '{{.Config.Image}}' ${containerName}`).toString("utf8").trim().split(":")[1]
|
child_process
|
||||||
|
.execSync(`docker inspect --format '{{.Config.Image}}' ${containerName}`)
|
||||||
|
.toString("utf8")
|
||||||
|
.trim()
|
||||||
|
.split(":")[1]
|
||||||
).major;
|
).major;
|
||||||
|
|
||||||
const targetFilePath = pathRelative(
|
const targetFilePath = pathRelative(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
pathJoin(__dirname, "..", "src", "bin", "start-keycloak", `myrealm-realm-${keycloakMajorVersionNumber}.json`)
|
pathJoin(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"src",
|
||||||
|
"bin",
|
||||||
|
"start-keycloak",
|
||||||
|
`myrealm-realm-${keycloakMajorVersionNumber}.json`
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
run(`docker cp ${containerName}:/tmp/myrealm-realm.json ${targetFilePath}`);
|
run(`docker cp ${containerName}:/tmp/myrealm-realm.json ${targetFilePath}`);
|
||||||
@ -22,5 +41,5 @@ console.log(`${chalk.green(`✓ Exported realm to`)} ${chalk.bold(targetFilePath
|
|||||||
function run(command: string) {
|
function run(command: string) {
|
||||||
console.log(chalk.grey(`$ ${command}`));
|
console.log(chalk.grey(`$ ${command}`));
|
||||||
|
|
||||||
return child_process.execSync(command, { "stdio": "inherit" });
|
return child_process.execSync(command, { stdio: "inherit" });
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import "minimal-polyfills/Object.fromEntries";
|
import "minimal-polyfills/Object.fromEntries";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin, relative as pathRelative, dirname as pathDirname, sep as pathSep } from "path";
|
import {
|
||||||
|
join as pathJoin,
|
||||||
|
relative as pathRelative,
|
||||||
|
dirname as pathDirname,
|
||||||
|
sep as pathSep
|
||||||
|
} from "path";
|
||||||
import { crawl } from "../src/bin/tools/crawl";
|
import { crawl } from "../src/bin/tools/crawl";
|
||||||
import { downloadKeycloakDefaultTheme } from "../src/bin/shared/downloadKeycloakDefaultTheme";
|
import { downloadKeycloakDefaultTheme } from "../src/bin/shared/downloadKeycloakDefaultTheme";
|
||||||
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
|
||||||
@ -19,18 +24,26 @@ async function main() {
|
|||||||
|
|
||||||
const tmpDirPath = pathJoin(thisCodebaseRootDirPath, "tmp_xImOef9dOd44");
|
const tmpDirPath = pathJoin(thisCodebaseRootDirPath, "tmp_xImOef9dOd44");
|
||||||
|
|
||||||
rmSync(tmpDirPath, { "recursive": true, "force": true });
|
rmSync(tmpDirPath, { recursive: true, force: true });
|
||||||
|
|
||||||
fs.mkdirSync(tmpDirPath);
|
fs.mkdirSync(tmpDirPath);
|
||||||
|
|
||||||
fs.writeFileSync(pathJoin(tmpDirPath, ".gitignore"), Buffer.from("/*\n!.gitignore\n", "utf8"));
|
fs.writeFileSync(
|
||||||
|
pathJoin(tmpDirPath, ".gitignore"),
|
||||||
|
Buffer.from("/*\n!.gitignore\n", "utf8")
|
||||||
|
);
|
||||||
|
|
||||||
await downloadKeycloakDefaultTheme({
|
await downloadKeycloakDefaultTheme({
|
||||||
keycloakVersion,
|
keycloakVersion,
|
||||||
"destDirPath": tmpDirPath,
|
destDirPath: tmpDirPath,
|
||||||
"buildOptions": {
|
buildOptions: {
|
||||||
"cacheDirPath": pathJoin(thisCodebaseRootDirPath, "node_modules", ".cache", "keycloakify"),
|
cacheDirPath: pathJoin(
|
||||||
"npmWorkspaceRootDirPath": thisCodebaseRootDirPath
|
thisCodebaseRootDirPath,
|
||||||
|
"node_modules",
|
||||||
|
".cache",
|
||||||
|
"keycloakify"
|
||||||
|
),
|
||||||
|
npmWorkspaceRootDirPath: thisCodebaseRootDirPath
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -40,11 +53,13 @@ async function main() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
|
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
|
||||||
const re = new RegExp(`^([^\\${pathSep}]+)\\${pathSep}messages\\${pathSep}messages_([^.]+).properties$`);
|
const re = new RegExp(
|
||||||
|
`^([^\\${pathSep}]+)\\${pathSep}messages\\${pathSep}messages_([^.]+).properties$`
|
||||||
|
);
|
||||||
|
|
||||||
crawl({
|
crawl({
|
||||||
"dirPath": baseThemeDirPath,
|
dirPath: baseThemeDirPath,
|
||||||
"returnedPathsType": "relative to dirPath"
|
returnedPathsType: "relative to dirPath"
|
||||||
}).forEach(filePath => {
|
}).forEach(filePath => {
|
||||||
const match = filePath.match(re);
|
const match = filePath.match(re);
|
||||||
|
|
||||||
@ -55,14 +70,21 @@ async function main() {
|
|||||||
const [, typeOfPage, language] = match;
|
const [, typeOfPage, language] = match;
|
||||||
|
|
||||||
(record[typeOfPage] ??= {})[language.replace(/_/g, "-")] = Object.fromEntries(
|
(record[typeOfPage] ??= {})[language.replace(/_/g, "-")] = Object.fromEntries(
|
||||||
Object.entries(propertiesParser.parse(fs.readFileSync(pathJoin(baseThemeDirPath, filePath)).toString("utf8"))).map(
|
Object.entries(
|
||||||
([key, value]: any) => [key === "locale_pt_BR" ? "locale_pt-BR" : key, value.replace(/''/g, "'")]
|
propertiesParser.parse(
|
||||||
)
|
fs
|
||||||
|
.readFileSync(pathJoin(baseThemeDirPath, filePath))
|
||||||
|
.toString("utf8")
|
||||||
|
)
|
||||||
|
).map(([key, value]: any) => [
|
||||||
|
key === "locale_pt_BR" ? "locale_pt-BR" : key,
|
||||||
|
value.replace(/''/g, "'")
|
||||||
|
])
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rmSync(tmpDirPath, { "recursive": true });
|
rmSync(tmpDirPath, { recursive: true });
|
||||||
|
|
||||||
Object.keys(record).forEach(themeType => {
|
Object.keys(record).forEach(themeType => {
|
||||||
const recordForPageType = record[themeType];
|
const recordForPageType = record[themeType];
|
||||||
@ -71,19 +93,28 @@ async function main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseMessagesDirPath = pathJoin(thisCodebaseRootDirPath, "src", themeType, "i18n", "baseMessages");
|
const baseMessagesDirPath = pathJoin(
|
||||||
|
thisCodebaseRootDirPath,
|
||||||
|
"src",
|
||||||
|
themeType,
|
||||||
|
"i18n",
|
||||||
|
"baseMessages"
|
||||||
|
);
|
||||||
|
|
||||||
const languages = Object.keys(recordForPageType);
|
const languages = Object.keys(recordForPageType);
|
||||||
|
|
||||||
const generatedFileHeader = [
|
const generatedFileHeader = [
|
||||||
`//This code was automatically generated by running ${pathRelative(thisCodebaseRootDirPath, __filename)}`,
|
`//This code was automatically generated by running ${pathRelative(
|
||||||
|
thisCodebaseRootDirPath,
|
||||||
|
__filename
|
||||||
|
)}`,
|
||||||
"//PLEASE DO NOT EDIT MANUALLY"
|
"//PLEASE DO NOT EDIT MANUALLY"
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
languages.forEach(language => {
|
languages.forEach(language => {
|
||||||
const filePath = pathJoin(baseMessagesDirPath, `${language}.ts`);
|
const filePath = pathJoin(baseMessagesDirPath, `${language}.ts`);
|
||||||
|
|
||||||
fs.mkdirSync(pathDirname(filePath), { "recursive": true });
|
fs.mkdirSync(pathDirname(filePath), { recursive: true });
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
filePath,
|
filePath,
|
||||||
@ -92,7 +123,11 @@ async function main() {
|
|||||||
generatedFileHeader,
|
generatedFileHeader,
|
||||||
"",
|
"",
|
||||||
"/* spell-checker: disable */",
|
"/* spell-checker: disable */",
|
||||||
`const messages= ${JSON.stringify(recordForPageType[language], null, 2)};`,
|
`const messages= ${JSON.stringify(
|
||||||
|
recordForPageType[language],
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)};`,
|
||||||
"",
|
"",
|
||||||
"export default messages;",
|
"export default messages;",
|
||||||
"/* spell-checker: enable */"
|
"/* spell-checker: enable */"
|
||||||
@ -117,7 +152,10 @@ async function main() {
|
|||||||
` case "en": return en;`,
|
` case "en": return en;`,
|
||||||
...languages
|
...languages
|
||||||
.filter(language => language !== "en")
|
.filter(language => language !== "en")
|
||||||
.map(language => ` case "${language}": return import("./${language}");`),
|
.map(
|
||||||
|
language =>
|
||||||
|
` case "${language}": return import("./${language}");`
|
||||||
|
),
|
||||||
' default: return { "default": {} };',
|
' default: return { "default": {} };',
|
||||||
" }",
|
" }",
|
||||||
" })();",
|
" })();",
|
||||||
|
@ -10,7 +10,8 @@ import { chmod, stat } from "fs/promises";
|
|||||||
const promises = Object.values<string>(bin).map(async scriptPath => {
|
const promises = Object.values<string>(bin).map(async scriptPath => {
|
||||||
const fullPath = pathJoin(thisCodebaseRootDirPath, scriptPath);
|
const fullPath = pathJoin(thisCodebaseRootDirPath, scriptPath);
|
||||||
const oldMode = (await stat(fullPath)).mode;
|
const oldMode = (await stat(fullPath)).mode;
|
||||||
const newMode = oldMode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH;
|
const newMode =
|
||||||
|
oldMode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH;
|
||||||
await chmod(fullPath, newMode);
|
await chmod(fullPath, newMode);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,20 +13,26 @@ fs.writeFileSync(
|
|||||||
Buffer.from(
|
Buffer.from(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
(() => {
|
(() => {
|
||||||
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"));
|
const packageJsonParsed = JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(pathJoin(rootDirPath, "package.json"))
|
||||||
|
.toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...packageJsonParsed,
|
...packageJsonParsed,
|
||||||
"main": packageJsonParsed["main"]?.replace(/^dist\//, ""),
|
main: packageJsonParsed["main"]?.replace(/^dist\//, ""),
|
||||||
"types": packageJsonParsed["types"]?.replace(/^dist\//, ""),
|
types: packageJsonParsed["types"]?.replace(/^dist\//, ""),
|
||||||
"module": packageJsonParsed["module"]?.replace(/^dist\//, ""),
|
module: packageJsonParsed["module"]?.replace(/^dist\//, ""),
|
||||||
"exports": !("exports" in packageJsonParsed)
|
exports: !("exports" in packageJsonParsed)
|
||||||
? undefined
|
? undefined
|
||||||
: Object.fromEntries(
|
: Object.fromEntries(
|
||||||
Object.entries(packageJsonParsed["exports"]).map(([key, value]) => [
|
Object.entries(packageJsonParsed["exports"]).map(
|
||||||
key,
|
([key, value]) => [
|
||||||
(value as string).replace(/^\.\/dist\//, "./")
|
key,
|
||||||
])
|
(value as string).replace(/^\.\/dist\//, "./")
|
||||||
|
]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
})(),
|
})(),
|
||||||
@ -39,9 +45,9 @@ fs.writeFileSync(
|
|||||||
|
|
||||||
const destSrcDirPath = pathJoin(rootDirPath, "dist", "src");
|
const destSrcDirPath = pathJoin(rootDirPath, "dist", "src");
|
||||||
|
|
||||||
fs.rmSync(destSrcDirPath, { "recursive": true, "force": true });
|
fs.rmSync(destSrcDirPath, { recursive: true, force: true });
|
||||||
|
|
||||||
fs.cpSync(pathJoin(rootDirPath, "src"), destSrcDirPath, { "recursive": true });
|
fs.cpSync(pathJoin(rootDirPath, "src"), destSrcDirPath, { recursive: true });
|
||||||
|
|
||||||
const commonThirdPartyDeps = (() => {
|
const commonThirdPartyDeps = (() => {
|
||||||
// For example [ "@emotion" ] it's more convenient than
|
// For example [ "@emotion" ] it's more convenient than
|
||||||
@ -53,7 +59,9 @@ const commonThirdPartyDeps = (() => {
|
|||||||
...namespaceSingletonDependencies
|
...namespaceSingletonDependencies
|
||||||
.map(namespaceModuleName =>
|
.map(namespaceModuleName =>
|
||||||
fs
|
fs
|
||||||
.readdirSync(pathJoin(rootDirPath, "node_modules", namespaceModuleName))
|
.readdirSync(
|
||||||
|
pathJoin(rootDirPath, "node_modules", namespaceModuleName)
|
||||||
|
)
|
||||||
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
|
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
|
||||||
)
|
)
|
||||||
.reduce((prev, curr) => [...prev, ...curr], []),
|
.reduce((prev, curr) => [...prev, ...curr], []),
|
||||||
@ -63,21 +71,25 @@ const commonThirdPartyDeps = (() => {
|
|||||||
|
|
||||||
const yarnGlobalDirPath = pathJoin(rootDirPath, ".yarn_home");
|
const yarnGlobalDirPath = pathJoin(rootDirPath, ".yarn_home");
|
||||||
|
|
||||||
fs.rmSync(yarnGlobalDirPath, { "recursive": true, "force": true });
|
fs.rmSync(yarnGlobalDirPath, { recursive: true, force: true });
|
||||||
fs.mkdirSync(yarnGlobalDirPath);
|
fs.mkdirSync(yarnGlobalDirPath);
|
||||||
|
|
||||||
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
|
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
|
||||||
const { targetModuleName, cwd } = params;
|
const { targetModuleName, cwd } = params;
|
||||||
|
|
||||||
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : ["--no-bin-links"])].join(" ");
|
const cmd = [
|
||||||
|
"yarn",
|
||||||
|
"link",
|
||||||
|
...(targetModuleName !== undefined ? [targetModuleName] : ["--no-bin-links"])
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`);
|
console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`);
|
||||||
|
|
||||||
execSync(cmd, {
|
execSync(cmd, {
|
||||||
cwd,
|
cwd,
|
||||||
"env": {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
"HOME": yarnGlobalDirPath
|
HOME: yarnGlobalDirPath
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -93,7 +105,9 @@ const testAppPaths = (() => {
|
|||||||
return testAppPath;
|
return testAppPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`);
|
console.warn(
|
||||||
|
`Skipping ${testAppName} since it cant be found here: ${testAppPath}`
|
||||||
|
);
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
})
|
})
|
||||||
@ -105,7 +119,7 @@ if (testAppPaths.length === 0) {
|
|||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath }));
|
testAppPaths.forEach(testAppPath => execSync("yarn install", { cwd: testAppPath }));
|
||||||
|
|
||||||
console.log("=== Linking common dependencies ===");
|
console.log("=== Linking common dependencies ===");
|
||||||
|
|
||||||
@ -118,29 +132,37 @@ commonThirdPartyDeps.forEach(commonThirdPartyDep => {
|
|||||||
console.log(`${current}/${total} ${commonThirdPartyDep}`);
|
console.log(`${current}/${total} ${commonThirdPartyDep}`);
|
||||||
|
|
||||||
const localInstallPath = pathJoin(
|
const localInstallPath = pathJoin(
|
||||||
...[rootDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])]
|
...[
|
||||||
|
rootDirPath,
|
||||||
|
"node_modules",
|
||||||
|
...(commonThirdPartyDep.startsWith("@")
|
||||||
|
? commonThirdPartyDep.split("/")
|
||||||
|
: [commonThirdPartyDep])
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
execYarnLink({ "cwd": localInstallPath });
|
execYarnLink({ cwd: localInstallPath });
|
||||||
});
|
});
|
||||||
|
|
||||||
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
|
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
|
||||||
testAppPaths.forEach(testAppPath =>
|
testAppPaths.forEach(testAppPath =>
|
||||||
execYarnLink({
|
execYarnLink({
|
||||||
"cwd": testAppPath,
|
cwd: testAppPath,
|
||||||
"targetModuleName": commonThirdPartyDep
|
targetModuleName: commonThirdPartyDep
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("=== Linking in house dependencies ===");
|
console.log("=== Linking in house dependencies ===");
|
||||||
|
|
||||||
execYarnLink({ "cwd": pathJoin(rootDirPath, "dist") });
|
execYarnLink({ cwd: pathJoin(rootDirPath, "dist") });
|
||||||
|
|
||||||
testAppPaths.forEach(testAppPath =>
|
testAppPaths.forEach(testAppPath =>
|
||||||
execYarnLink({
|
execYarnLink({
|
||||||
"cwd": testAppPath,
|
cwd: testAppPath,
|
||||||
"targetModuleName": JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"))["name"]
|
targetModuleName: JSON.parse(
|
||||||
|
fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8")
|
||||||
|
)["name"]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,16 +2,19 @@ import * as child_process from "child_process";
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
fs.rmSync("node_modules", { "recursive": true, "force": true });
|
fs.rmSync("node_modules", { recursive: true, force: true });
|
||||||
fs.rmSync("dist", { "recursive": true, "force": true });
|
fs.rmSync("dist", { recursive: true, force: true });
|
||||||
fs.rmSync(".yarn_home", { "recursive": true, "force": true });
|
fs.rmSync(".yarn_home", { recursive: true, force: true });
|
||||||
|
|
||||||
run("yarn install");
|
run("yarn install");
|
||||||
run("yarn build");
|
run("yarn build");
|
||||||
|
|
||||||
fs.rmSync(join("..", "keycloakify-starter", "node_modules"), { "recursive": true, "force": true });
|
fs.rmSync(join("..", "keycloakify-starter", "node_modules"), {
|
||||||
|
recursive: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
run("yarn install", { "cwd": join("..", "keycloakify-starter") });
|
run("yarn install", { cwd: join("..", "keycloakify-starter") });
|
||||||
|
|
||||||
run(`npx ts-node --skipProject ${join("scripts", "link-in-app.ts")} keycloakify-starter`);
|
run(`npx ts-node --skipProject ${join("scripts", "link-in-app.ts")} keycloakify-starter`);
|
||||||
|
|
||||||
@ -20,5 +23,5 @@ run(`npx chokidar '${join("src", "**", "*")}' -c 'yarn build'`);
|
|||||||
function run(command: string, options?: { cwd: string }) {
|
function run(command: string, options?: { cwd: string }) {
|
||||||
console.log(`$ ${command}`);
|
console.log(`$ ${command}`);
|
||||||
|
|
||||||
child_process.execSync(command, { "stdio": "inherit", ...options });
|
child_process.execSync(command, { stdio: "inherit", ...options });
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/shared/constants";
|
import {
|
||||||
|
nameOfTheGlobal,
|
||||||
|
basenameOfTheKeycloakifyResourcesDir
|
||||||
|
} from "keycloakify/bin/shared/constants";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,13 +24,13 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useSetClassName({
|
useSetClassName({
|
||||||
"qualifiedName": "html",
|
qualifiedName: "html",
|
||||||
"className": getClassName("kcHtmlClass")
|
className: getClassName("kcHtmlClass")
|
||||||
});
|
});
|
||||||
|
|
||||||
useSetClassName({
|
useSetClassName({
|
||||||
"qualifiedName": "body",
|
qualifiedName: "body",
|
||||||
"className": clsx("admin-console", "user", getClassName("kcBodyClass"))
|
className: clsx("admin-console", "user", getClassName("kcBodyClass"))
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -46,7 +46,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
|
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
|
||||||
"hrefs": !doUseDefaultCss
|
hrefs: !doUseDefaultCss
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`,
|
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`,
|
||||||
|
@ -2,7 +2,10 @@ import type { ReactNode } from "react";
|
|||||||
import type { KcContext } from "./kcContext";
|
import type { KcContext } from "./kcContext";
|
||||||
import type { I18n } from "./i18n";
|
import type { I18n } from "./i18n";
|
||||||
|
|
||||||
export type TemplateProps<KcContext extends KcContext.Common, I18nExtended extends I18n> = {
|
export type TemplateProps<
|
||||||
|
KcContext extends KcContext.Common,
|
||||||
|
I18nExtended extends I18n
|
||||||
|
> = {
|
||||||
kcContext: KcContext;
|
kcContext: KcContext;
|
||||||
i18n: I18nExtended;
|
i18n: I18nExtended;
|
||||||
doUseDefaultCss: boolean;
|
doUseDefaultCss: boolean;
|
||||||
|
@ -91,19 +91,19 @@ export function createUseI18n<ExtraMessageKey extends string = never>(extraMessa
|
|||||||
|
|
||||||
setI18n({
|
setI18n({
|
||||||
...createI18nTranslationFunctions({
|
...createI18nTranslationFunctions({
|
||||||
"fallbackMessages": {
|
fallbackMessages: {
|
||||||
...fallbackMessages,
|
...fallbackMessages,
|
||||||
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
||||||
...(extraMessages[fallbackLanguageTag] ?? {})
|
...(extraMessages[fallbackLanguageTag] ?? {})
|
||||||
} as any,
|
} as any,
|
||||||
"messages": {
|
messages: {
|
||||||
...(await getMessages(currentLanguageTag)),
|
...(await getMessages(currentLanguageTag)),
|
||||||
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
||||||
...(extraMessages[currentLanguageTag] ?? {})
|
...(extraMessages[currentLanguageTag] ?? {})
|
||||||
} as any
|
} as any
|
||||||
}),
|
}),
|
||||||
currentLanguageTag,
|
currentLanguageTag,
|
||||||
"getChangeLocalUrl": newLanguageTag => {
|
getChangeLocalUrl: newLanguageTag => {
|
||||||
const { locale } = kcContext;
|
const { locale } = kcContext;
|
||||||
|
|
||||||
assert(locale !== undefined, "Internationalization not enabled");
|
assert(locale !== undefined, "Internationalization not enabled");
|
||||||
@ -114,7 +114,7 @@ export function createUseI18n<ExtraMessageKey extends string = never>(extraMessa
|
|||||||
|
|
||||||
return targetSupportedLocale.url;
|
return targetSupportedLocale.url;
|
||||||
},
|
},
|
||||||
"labelBySupportedLanguageTag": Object.fromEntries(
|
labelBySupportedLanguageTag: Object.fromEntries(
|
||||||
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -170,7 +170,7 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return doRenderMarkdown ? (
|
return doRenderMarkdown ? (
|
||||||
<Markdown allowDangerousHtml renderers={{ "paragraph": "span" }}>
|
<Markdown allowDangerousHtml renderers={{ paragraph: "span" }}>
|
||||||
{messageWithArgsInjectedIfAny}
|
{messageWithArgsInjectedIfAny}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
) : (
|
) : (
|
||||||
@ -186,7 +186,7 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
|
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
|
||||||
|
|
||||||
const out = resolveMsg({
|
const out = resolveMsg({
|
||||||
"key": keyUnwrappedFromCurlyBraces,
|
key: keyUnwrappedFromCurlyBraces,
|
||||||
args,
|
args,
|
||||||
doRenderMarkdown
|
doRenderMarkdown
|
||||||
});
|
});
|
||||||
@ -195,36 +195,46 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"msgStr": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": false }) as string,
|
msgStr: (key, ...args) => resolveMsg({ key, args, doRenderMarkdown: false }) as string,
|
||||||
"msg": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
msg: (key, ...args) => resolveMsg({ key, args, doRenderMarkdown: true }) as JSX.Element,
|
||||||
"advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
advancedMsg: (key, ...args) =>
|
||||||
"advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": false }) as string
|
resolveMsgAdvanced({
|
||||||
|
key,
|
||||||
|
args,
|
||||||
|
doRenderMarkdown: true
|
||||||
|
}) as JSX.Element,
|
||||||
|
advancedMsgStr: (key, ...args) =>
|
||||||
|
resolveMsgAdvanced({
|
||||||
|
key,
|
||||||
|
args,
|
||||||
|
doRenderMarkdown: false
|
||||||
|
}) as string
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const keycloakifyExtraMessages = {
|
const keycloakifyExtraMessages = {
|
||||||
"en": {
|
en: {
|
||||||
"shouldBeEqual": "{0} should be equal to {1}",
|
shouldBeEqual: "{0} should be equal to {1}",
|
||||||
"shouldBeDifferent": "{0} should be different to {1}",
|
shouldBeDifferent: "{0} should be different to {1}",
|
||||||
"shouldMatchPattern": "Pattern should match: `/{0}/`",
|
shouldMatchPattern: "Pattern should match: `/{0}/`",
|
||||||
"mustBeAnInteger": "Must be an integer",
|
mustBeAnInteger: "Must be an integer",
|
||||||
"notAValidOption": "Not a valid option",
|
notAValidOption: "Not a valid option",
|
||||||
"newPasswordSameAsOld": "New password must be different from the old one",
|
newPasswordSameAsOld: "New password must be different from the old one",
|
||||||
"passwordConfirmNotMatch": "Password confirmation does not match"
|
passwordConfirmNotMatch: "Password confirmation does not match"
|
||||||
},
|
},
|
||||||
"fr": {
|
fr: {
|
||||||
/* spell-checker: disable */
|
/* spell-checker: disable */
|
||||||
"shouldBeEqual": "{0} doit être égal à {1}",
|
shouldBeEqual: "{0} doit être égal à {1}",
|
||||||
"shouldBeDifferent": "{0} doit être différent de {1}",
|
shouldBeDifferent: "{0} doit être différent de {1}",
|
||||||
"shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
|
shouldMatchPattern: "Dois respecter le schéma: `/{0}/`",
|
||||||
"mustBeAnInteger": "Doit être un nombre entier",
|
mustBeAnInteger: "Doit être un nombre entier",
|
||||||
"notAValidOption": "N'est pas une option valide",
|
notAValidOption: "N'est pas une option valide",
|
||||||
|
|
||||||
"logoutConfirmTitle": "Déconnexion",
|
logoutConfirmTitle: "Déconnexion",
|
||||||
"logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
logoutConfirmHeader: "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
||||||
"doLogout": "Se déconnecter",
|
doLogout: "Se déconnecter",
|
||||||
"newPasswordSameAsOld": "Le nouveau mot de passe doit être différent de l'ancien",
|
newPasswordSameAsOld: "Le nouveau mot de passe doit être différent de l'ancien",
|
||||||
"passwordConfirmNotMatch": "La confirmation du mot de passe ne correspond pas"
|
passwordConfirmNotMatch: "La confirmation du mot de passe ne correspond pas"
|
||||||
/* spell-checker: enable */
|
/* spell-checker: enable */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -68,7 +68,10 @@ export declare namespace KcContext {
|
|||||||
* @param text to return
|
* @param text to return
|
||||||
* @return text if message exists for given field, else undefined
|
* @return text if message exists for given field, else undefined
|
||||||
*/
|
*/
|
||||||
printIfExists: <T extends string>(fieldName: string, text: T) => T | undefined;
|
printIfExists: <T extends string>(
|
||||||
|
fieldName: string,
|
||||||
|
text: T
|
||||||
|
) => T | undefined;
|
||||||
/**
|
/**
|
||||||
* Check if exists error message for given fields
|
* Check if exists error message for given fields
|
||||||
*
|
*
|
||||||
|
@ -4,17 +4,28 @@ import { isStorybook } from "keycloakify/lib/isStorybook";
|
|||||||
import type { ExtendKcContext } from "./getKcContextFromWindow";
|
import type { ExtendKcContext } from "./getKcContextFromWindow";
|
||||||
import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
||||||
import { symToStr } from "tsafe/symToStr";
|
import { symToStr } from "tsafe/symToStr";
|
||||||
import { kcContextMocks, kcContextCommonMock } from "keycloakify/account/kcContext/kcContextMocks";
|
import {
|
||||||
|
kcContextMocks,
|
||||||
|
kcContextCommonMock
|
||||||
|
} from "keycloakify/account/kcContext/kcContextMocks";
|
||||||
|
|
||||||
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function createGetKcContext<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(params?: {
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
mockProperties?: Record<string, string>;
|
mockProperties?: Record<string, string>;
|
||||||
}) {
|
}) {
|
||||||
const { mockData, mockProperties } = params ?? {};
|
const { mockData, mockProperties } = params ?? {};
|
||||||
|
|
||||||
function getKcContext<PageId extends ExtendKcContext<KcContextExtension>["pageId"] | undefined = undefined>(params?: {
|
function getKcContext<
|
||||||
|
PageId extends
|
||||||
|
| ExtendKcContext<KcContextExtension>["pageId"]
|
||||||
|
| undefined = undefined
|
||||||
|
>(params?: {
|
||||||
mockPageId?: PageId;
|
mockPageId?: PageId;
|
||||||
storyPartialKcContext?: DeepPartial<Extract<ExtendKcContext<KcContextExtension>, { pageId: PageId }>>;
|
storyPartialKcContext?: DeepPartial<
|
||||||
|
Extract<ExtendKcContext<KcContextExtension>, { pageId: PageId }>
|
||||||
|
>;
|
||||||
}): {
|
}): {
|
||||||
kcContext: PageId extends undefined
|
kcContext: PageId extends undefined
|
||||||
? ExtendKcContext<KcContextExtension> | undefined
|
? ExtendKcContext<KcContextExtension> | undefined
|
||||||
@ -32,34 +43,46 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
break warn_that_mock_is_enbaled;
|
break warn_that_mock_is_enbaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`%cKeycloakify: ${symToStr({ mockPageId })} set to ${mockPageId}.`, "background: red; color: yellow; font-size: medium");
|
console.log(
|
||||||
|
`%cKeycloakify: ${symToStr({
|
||||||
|
mockPageId
|
||||||
|
})} set to ${mockPageId}.`,
|
||||||
|
"background: red; color: yellow; font-size: medium"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
const kcContextDefaultMock = kcContextMocks.find(
|
||||||
|
({ pageId }) => pageId === mockPageId
|
||||||
|
);
|
||||||
|
|
||||||
const partialKcContextCustomMock = (() => {
|
const partialKcContextCustomMock = (() => {
|
||||||
const out: DeepPartial<ExtendKcContext<KcContextExtension>> = {};
|
const out: DeepPartial<ExtendKcContext<KcContextExtension>> = {};
|
||||||
|
|
||||||
const mockDataPick = mockData?.find(({ pageId }) => pageId === mockPageId);
|
const mockDataPick = mockData?.find(
|
||||||
|
({ pageId }) => pageId === mockPageId
|
||||||
|
);
|
||||||
|
|
||||||
if (mockDataPick !== undefined) {
|
if (mockDataPick !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": out,
|
target: out,
|
||||||
"source": mockDataPick
|
source: mockDataPick
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storyPartialKcContext !== undefined) {
|
if (storyPartialKcContext !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": out,
|
target: out,
|
||||||
"source": storyPartialKcContext
|
source: storyPartialKcContext
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(out).length === 0 ? undefined : out;
|
return Object.keys(out).length === 0 ? undefined : out;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (kcContextDefaultMock === undefined && partialKcContextCustomMock === undefined) {
|
if (
|
||||||
|
kcContextDefaultMock === undefined &&
|
||||||
|
partialKcContextCustomMock === undefined
|
||||||
|
) {
|
||||||
console.warn(
|
console.warn(
|
||||||
[
|
[
|
||||||
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
||||||
@ -72,21 +95,24 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
const kcContext: any = {};
|
const kcContext: any = {};
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
target: kcContext,
|
||||||
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock }
|
source:
|
||||||
|
kcContextDefaultMock !== undefined
|
||||||
|
? kcContextDefaultMock
|
||||||
|
: { pageId: mockPageId, ...kcContextCommonMock }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (partialKcContextCustomMock !== undefined) {
|
if (partialKcContextCustomMock !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
target: kcContext,
|
||||||
"source": partialKcContextCustomMock
|
source: partialKcContextCustomMock
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mockProperties !== undefined) {
|
if (mockProperties !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext.properties,
|
target: kcContext.properties,
|
||||||
"source": mockProperties
|
source: mockProperties
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,14 +120,14 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext === undefined) {
|
if (realKcContext === undefined) {
|
||||||
return { "kcContext": undefined as any };
|
return { kcContext: undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext.themeType !== "account") {
|
if (realKcContext.themeType !== "account") {
|
||||||
return { "kcContext": undefined as any };
|
return { kcContext: undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "kcContext": realKcContext as any };
|
return { kcContext: realKcContext as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { getKcContext };
|
return { getKcContext };
|
||||||
|
@ -5,7 +5,9 @@ import { createGetKcContext } from "./createGetKcContext";
|
|||||||
/** NOTE: We now recommend using createGetKcContext instead of this function to make storybook integration easier
|
/** NOTE: We now recommend using createGetKcContext instead of this function to make storybook integration easier
|
||||||
* See: https://github.com/keycloakify/keycloakify-starter/blob/main/src/keycloak-theme/account/kcContext.ts
|
* See: https://github.com/keycloakify/keycloakify-starter/blob/main/src/keycloak-theme/account/kcContext.ts
|
||||||
*/
|
*/
|
||||||
export function getKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function getKcContext<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(params?: {
|
||||||
mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
|
mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
}): { kcContext: ExtendKcContext<KcContextExtension> | undefined } {
|
}): { kcContext: ExtendKcContext<KcContextExtension> | undefined } {
|
||||||
|
@ -2,10 +2,14 @@ import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminati
|
|||||||
import { nameOfTheGlobal } from "keycloakify/bin/shared/constants";
|
import { nameOfTheGlobal } from "keycloakify/bin/shared/constants";
|
||||||
import type { KcContext } from "./KcContext";
|
import type { KcContext } from "./KcContext";
|
||||||
|
|
||||||
export type ExtendKcContext<KcContextExtension extends { pageId: string }> = [KcContextExtension] extends [never]
|
export type ExtendKcContext<KcContextExtension extends { pageId: string }> = [
|
||||||
|
KcContextExtension
|
||||||
|
] extends [never]
|
||||||
? KcContext
|
? KcContext
|
||||||
: AndByDiscriminatingKey<"pageId", KcContextExtension & KcContext.Common, KcContext>;
|
: AndByDiscriminatingKey<"pageId", KcContextExtension & KcContext.Common, KcContext>;
|
||||||
|
|
||||||
export function getKcContextFromWindow<KcContextExtension extends { pageId: string } = never>(): ExtendKcContext<KcContextExtension> | undefined {
|
export function getKcContextFromWindow<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(): ExtendKcContext<KcContextExtension> | undefined {
|
||||||
return typeof window === "undefined" ? undefined : (window as any)[nameOfTheGlobal];
|
return typeof window === "undefined" ? undefined : (window as any)[nameOfTheGlobal];
|
||||||
}
|
}
|
||||||
|
@ -7,254 +7,258 @@ import { BASE_URL } from "keycloakify/lib/BASE_URL";
|
|||||||
const resourcesPath = `${BASE_URL}${keycloak_resources}/account/resources`;
|
const resourcesPath = `${BASE_URL}${keycloak_resources}/account/resources`;
|
||||||
|
|
||||||
export const kcContextCommonMock: KcContext.Common = {
|
export const kcContextCommonMock: KcContext.Common = {
|
||||||
"themeVersion": "0.0.0",
|
themeVersion: "0.0.0",
|
||||||
"keycloakifyVersion": "0.0.0",
|
keycloakifyVersion: "0.0.0",
|
||||||
"themeType": "account",
|
themeType: "account",
|
||||||
"themeName": "my-theme-name",
|
themeName: "my-theme-name",
|
||||||
"url": {
|
url: {
|
||||||
resourcesPath,
|
resourcesPath,
|
||||||
"resourcesCommonPath": `${resourcesPath}/${resources_common}`,
|
resourcesCommonPath: `${resourcesPath}/${resources_common}`,
|
||||||
"resourceUrl": "#",
|
resourceUrl: "#",
|
||||||
"accountUrl": "#",
|
accountUrl: "#",
|
||||||
"applicationsUrl": "#",
|
applicationsUrl: "#",
|
||||||
"logoutUrl": "#",
|
logoutUrl: "#",
|
||||||
"getLogoutUrl": () => "#",
|
getLogoutUrl: () => "#",
|
||||||
"logUrl": "#",
|
logUrl: "#",
|
||||||
"passwordUrl": "#",
|
passwordUrl: "#",
|
||||||
"sessionsUrl": "#",
|
sessionsUrl: "#",
|
||||||
"socialUrl": "#",
|
socialUrl: "#",
|
||||||
"totpUrl": "#"
|
totpUrl: "#"
|
||||||
},
|
},
|
||||||
"realm": {
|
realm: {
|
||||||
"internationalizationEnabled": true,
|
internationalizationEnabled: true,
|
||||||
"userManagedAccessAllowed": true
|
userManagedAccessAllowed: true
|
||||||
},
|
},
|
||||||
"messagesPerField": {
|
messagesPerField: {
|
||||||
"printIfExists": () => {
|
printIfExists: () => {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
"existsError": () => false,
|
existsError: () => false,
|
||||||
"get": key => `Fake error for ${key}`,
|
get: key => `Fake error for ${key}`,
|
||||||
"exists": () => false
|
exists: () => false
|
||||||
},
|
},
|
||||||
"locale": {
|
locale: {
|
||||||
"supported": [
|
supported: [
|
||||||
/* spell-checker: disable */
|
/* spell-checker: disable */
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
||||||
"label": "Deutsch",
|
label: "Deutsch",
|
||||||
"languageTag": "de"
|
languageTag: "de"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
||||||
"label": "Norsk",
|
label: "Norsk",
|
||||||
"languageTag": "no"
|
languageTag: "no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
||||||
"label": "Русский",
|
label: "Русский",
|
||||||
"languageTag": "ru"
|
languageTag: "ru"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
||||||
"label": "Svenska",
|
label: "Svenska",
|
||||||
"languageTag": "sv"
|
languageTag: "sv"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
||||||
"label": "Português (Brasil)",
|
label: "Português (Brasil)",
|
||||||
"languageTag": "pt-BR"
|
languageTag: "pt-BR"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
||||||
"label": "Lietuvių",
|
label: "Lietuvių",
|
||||||
"languageTag": "lt"
|
languageTag: "lt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
||||||
"label": "English",
|
label: "English",
|
||||||
"languageTag": "en"
|
languageTag: "en"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
||||||
"label": "Italiano",
|
label: "Italiano",
|
||||||
"languageTag": "it"
|
languageTag: "it"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
||||||
"label": "Français",
|
label: "Français",
|
||||||
"languageTag": "fr"
|
languageTag: "fr"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
||||||
"label": "中文简体",
|
label: "中文简体",
|
||||||
"languageTag": "zh-CN"
|
languageTag: "zh-CN"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
||||||
"label": "Español",
|
label: "Español",
|
||||||
"languageTag": "es"
|
languageTag: "es"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
||||||
"label": "Čeština",
|
label: "Čeština",
|
||||||
"languageTag": "cs"
|
languageTag: "cs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
||||||
"label": "日本語",
|
label: "日本語",
|
||||||
"languageTag": "ja"
|
languageTag: "ja"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
||||||
"label": "Slovenčina",
|
label: "Slovenčina",
|
||||||
"languageTag": "sk"
|
languageTag: "sk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
||||||
"label": "Polski",
|
label: "Polski",
|
||||||
"languageTag": "pl"
|
languageTag: "pl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
||||||
"label": "Català",
|
label: "Català",
|
||||||
"languageTag": "ca"
|
languageTag: "ca"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
||||||
"label": "Nederlands",
|
label: "Nederlands",
|
||||||
"languageTag": "nl"
|
languageTag: "nl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
||||||
"label": "Türkçe",
|
label: "Türkçe",
|
||||||
"languageTag": "tr"
|
languageTag: "tr"
|
||||||
}
|
}
|
||||||
/* spell-checker: enable */
|
/* spell-checker: enable */
|
||||||
],
|
],
|
||||||
"currentLanguageTag": "en"
|
currentLanguageTag: "en"
|
||||||
},
|
},
|
||||||
"features": {
|
features: {
|
||||||
"authorization": true,
|
authorization: true,
|
||||||
"identityFederation": true,
|
identityFederation: true,
|
||||||
"log": true,
|
log: true,
|
||||||
"passwordUpdateSupported": true
|
passwordUpdateSupported: true
|
||||||
},
|
},
|
||||||
"referrer": undefined,
|
referrer: undefined,
|
||||||
"account": {
|
account: {
|
||||||
"firstName": "john",
|
firstName: "john",
|
||||||
"lastName": "doe",
|
lastName: "doe",
|
||||||
"email": "john.doe@code.gouv.fr",
|
email: "john.doe@code.gouv.fr",
|
||||||
"username": "doe_j"
|
username: "doe_j"
|
||||||
},
|
},
|
||||||
"properties": {
|
properties: {
|
||||||
"parent": "account-v1",
|
parent: "account-v1",
|
||||||
"kcButtonLargeClass": "btn-lg",
|
kcButtonLargeClass: "btn-lg",
|
||||||
"locales": "ar,ca,cs,da,de,en,es,fr,fi,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN",
|
locales:
|
||||||
"kcButtonPrimaryClass": "btn-primary",
|
"ar,ca,cs,da,de,en,es,fr,fi,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN",
|
||||||
"accountResourceProvider": "account-v1",
|
kcButtonPrimaryClass: "btn-primary",
|
||||||
"styles":
|
accountResourceProvider: "account-v1",
|
||||||
"css/account.css img/icon-sidebar-active.png img/logo.png resources-common/node_modules/patternfly/dist/css/patternfly.min.css resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css",
|
styles: "css/account.css img/icon-sidebar-active.png img/logo.png resources-common/node_modules/patternfly/dist/css/patternfly.min.css resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css",
|
||||||
"kcButtonClass": "btn",
|
kcButtonClass: "btn",
|
||||||
"kcButtonDefaultClass": "btn-default"
|
kcButtonDefaultClass: "btn-default"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const kcContextMocks: KcContext[] = [
|
export const kcContextMocks: KcContext[] = [
|
||||||
id<KcContext.Password>({
|
id<KcContext.Password>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "password.ftl",
|
pageId: "password.ftl",
|
||||||
"password": {
|
password: {
|
||||||
"passwordSet": true
|
passwordSet: true
|
||||||
},
|
},
|
||||||
"stateChecker": "state checker"
|
stateChecker: "state checker"
|
||||||
}),
|
}),
|
||||||
id<KcContext.Account>({
|
id<KcContext.Account>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "account.ftl",
|
pageId: "account.ftl",
|
||||||
"url": {
|
url: {
|
||||||
...kcContextCommonMock.url,
|
...kcContextCommonMock.url,
|
||||||
"referrerURI": "#",
|
referrerURI: "#",
|
||||||
"accountUrl": "#"
|
accountUrl: "#"
|
||||||
},
|
},
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"registrationEmailAsUsername": true,
|
registrationEmailAsUsername: true,
|
||||||
"editUsernameAllowed": true
|
editUsernameAllowed: true
|
||||||
},
|
},
|
||||||
"stateChecker": ""
|
stateChecker: ""
|
||||||
}),
|
}),
|
||||||
id<KcContext.Sessions>({
|
id<KcContext.Sessions>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "sessions.ftl",
|
pageId: "sessions.ftl",
|
||||||
"sessions": {
|
sessions: {
|
||||||
"sessions": [
|
sessions: [
|
||||||
{
|
{
|
||||||
"ipAddress": "127.0.0.1",
|
ipAddress: "127.0.0.1",
|
||||||
"started": new Date().toString(),
|
started: new Date().toString(),
|
||||||
"lastAccess": new Date().toString(),
|
lastAccess: new Date().toString(),
|
||||||
"expires": new Date().toString(),
|
expires: new Date().toString(),
|
||||||
"clients": ["Chrome", "Firefox"],
|
clients: ["Chrome", "Firefox"],
|
||||||
"id": "f8951177-817d-4a70-9c02-86d3c170fe51"
|
id: "f8951177-817d-4a70-9c02-86d3c170fe51"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"stateChecker": "g6WB1FaYnKotTkiy7ZrlxvFztSqS0U8jvHsOOOb2z4g"
|
stateChecker: "g6WB1FaYnKotTkiy7ZrlxvFztSqS0U8jvHsOOOb2z4g"
|
||||||
}),
|
}),
|
||||||
id<KcContext.Totp>({
|
id<KcContext.Totp>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "totp.ftl",
|
pageId: "totp.ftl",
|
||||||
"totp": {
|
totp: {
|
||||||
"enabled": true,
|
enabled: true,
|
||||||
"totpSecretEncoded": "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||||
"qrUrl": "#",
|
qrUrl: "#",
|
||||||
"totpSecretQrCode":
|
totpSecretQrCode:
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
||||||
"manualUrl": "#",
|
manualUrl: "#",
|
||||||
"totpSecret": "G4nsI8lQagRMUchH8jEG",
|
totpSecret: "G4nsI8lQagRMUchH8jEG",
|
||||||
"otpCredentials": [],
|
otpCredentials: [],
|
||||||
"supportedApplications": ["totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName"],
|
supportedApplications: [
|
||||||
"policy": {
|
"totpAppFreeOTPName",
|
||||||
"algorithm": "HmacSHA1",
|
"totpAppMicrosoftAuthenticatorName",
|
||||||
"digits": 6,
|
"totpAppGoogleName"
|
||||||
"lookAheadWindow": 1,
|
],
|
||||||
"type": "totp",
|
policy: {
|
||||||
"period": 30
|
algorithm: "HmacSHA1",
|
||||||
|
digits: 6,
|
||||||
|
lookAheadWindow: 1,
|
||||||
|
type: "totp",
|
||||||
|
period: 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mode": "qr",
|
mode: "qr",
|
||||||
"isAppInitiatedAction": false,
|
isAppInitiatedAction: false,
|
||||||
"stateChecker": ""
|
stateChecker: ""
|
||||||
}),
|
}),
|
||||||
id<KcContext.Log>({
|
id<KcContext.Log>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "log.ftl",
|
pageId: "log.ftl",
|
||||||
"log": {
|
log: {
|
||||||
"events": [
|
events: [
|
||||||
{
|
{
|
||||||
"date": "2/21/2024, 1:28:39 PM",
|
date: "2/21/2024, 1:28:39 PM",
|
||||||
"event": "login",
|
event: "login",
|
||||||
"ipAddress": "172.17.0.1",
|
ipAddress: "172.17.0.1",
|
||||||
"client": "security-admin-console",
|
client: "security-admin-console",
|
||||||
"details": [{ key: "openid-connect", value: "admin" }]
|
details: [{ key: "openid-connect", value: "admin" }]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.FederatedIdentity>({
|
id<KcContext.FederatedIdentity>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"stateChecker": "",
|
stateChecker: "",
|
||||||
"pageId": "federatedIdentity.ftl",
|
pageId: "federatedIdentity.ftl",
|
||||||
"federatedIdentity": {
|
federatedIdentity: {
|
||||||
"identities": [
|
identities: [
|
||||||
{
|
{
|
||||||
"providerId": "keycloak-oidc",
|
providerId: "keycloak-oidc",
|
||||||
"displayName": "keycloak-oidc",
|
displayName: "keycloak-oidc",
|
||||||
"userName": "John",
|
userName: "John",
|
||||||
"connected": true
|
connected: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"removeLinkPossible": true
|
removeLinkPossible: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
@ -2,19 +2,20 @@ import { createUseClassName } from "keycloakify/lib/useGetClassName";
|
|||||||
import type { ClassKey } from "keycloakify/account/TemplateProps";
|
import type { ClassKey } from "keycloakify/account/TemplateProps";
|
||||||
|
|
||||||
export const { useGetClassName } = createUseClassName<ClassKey>({
|
export const { useGetClassName } = createUseClassName<ClassKey>({
|
||||||
"defaultClasses": {
|
defaultClasses: {
|
||||||
"kcHtmlClass": undefined,
|
kcHtmlClass: undefined,
|
||||||
"kcBodyClass": undefined,
|
kcBodyClass: undefined,
|
||||||
"kcButtonClass": "btn",
|
kcButtonClass: "btn",
|
||||||
"kcContentWrapperClass": "row",
|
kcContentWrapperClass: "row",
|
||||||
"kcButtonPrimaryClass": "btn-primary",
|
kcButtonPrimaryClass: "btn-primary",
|
||||||
"kcButtonLargeClass": "btn-lg",
|
kcButtonLargeClass: "btn-lg",
|
||||||
"kcButtonDefaultClass": "btn-default",
|
kcButtonDefaultClass: "btn-default",
|
||||||
"kcFormClass": "form-horizontal",
|
kcFormClass: "form-horizontal",
|
||||||
"kcFormGroupClass": "form-group",
|
kcFormGroupClass: "form-group",
|
||||||
"kcInputWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
kcInputWrapperClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||||
"kcLabelClass": "control-label",
|
kcLabelClass: "control-label",
|
||||||
"kcInputClass": "form-control",
|
kcInputClass: "form-control",
|
||||||
"kcInputErrorMessageClass": "pf-c-form__helper-text pf-m-error required kc-feedback-text"
|
kcInputErrorMessageClass:
|
||||||
|
"pf-c-form__helper-text pf-m-error required kc-feedback-text"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,9 +9,9 @@ export default function Account(props: PageProps<Extract<KcContext, { pageId: "a
|
|||||||
|
|
||||||
const { getClassName } = useGetClassName({
|
const { getClassName } = useGetClassName({
|
||||||
doUseDefaultCss,
|
doUseDefaultCss,
|
||||||
"classes": {
|
classes: {
|
||||||
...classes,
|
...classes,
|
||||||
"kcBodyClass": clsx(classes?.kcBodyClass, "user")
|
kcBodyClass: clsx(classes?.kcBodyClass, "user")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ export default function Password(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
|
|
||||||
const { getClassName } = useGetClassName({
|
const { getClassName } = useGetClassName({
|
||||||
doUseDefaultCss,
|
doUseDefaultCss,
|
||||||
"classes": {
|
classes: {
|
||||||
...classes,
|
...classes,
|
||||||
"kcBodyClass": clsx(classes?.kcBodyClass, "password")
|
kcBodyClass: clsx(classes?.kcBodyClass, "password")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,18 +57,18 @@ export default function Password(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
{...{
|
{...{
|
||||||
kcContext: {
|
kcContext: {
|
||||||
...kcContext,
|
...kcContext,
|
||||||
"message": (() => {
|
message: (() => {
|
||||||
if (newPasswordError !== "") {
|
if (newPasswordError !== "") {
|
||||||
return {
|
return {
|
||||||
"type": "error",
|
type: "error",
|
||||||
"summary": newPasswordError
|
summary: newPasswordError
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPasswordConfirmError !== "") {
|
if (newPasswordConfirmError !== "") {
|
||||||
return {
|
return {
|
||||||
"type": "error",
|
type: "error",
|
||||||
"summary": newPasswordConfirmError
|
summary: newPasswordConfirmError
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ export default function Password(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
value={account.username ?? ""}
|
value={account.username ?? ""}
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
readOnly
|
readOnly
|
||||||
style={{ "display": "none" }}
|
style={{ display: "none" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{password.passwordSet && (
|
{password.passwordSet && (
|
||||||
|
@ -18,9 +18,9 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const algToKeyUriAlg: Record<(typeof kcContext)["totp"]["policy"]["algorithm"], string> = {
|
const algToKeyUriAlg: Record<(typeof kcContext)["totp"]["policy"]["algorithm"], string> = {
|
||||||
"HmacSHA1": "SHA1",
|
HmacSHA1: "SHA1",
|
||||||
"HmacSHA256": "SHA256",
|
HmacSHA256: "SHA256",
|
||||||
"HmacSHA512": "SHA512"
|
HmacSHA512: "SHA512"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -8,9 +8,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||||
|
|
||||||
await copyKeycloakResourcesToPublic({
|
await copyKeycloakResourcesToPublic({
|
||||||
"buildOptions": {
|
buildOptions: {
|
||||||
...buildOptions,
|
...buildOptions,
|
||||||
"publicDirPath": buildOptions.reactAppRootDirPath
|
publicDirPath: buildOptions.reactAppRootDirPath
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,22 +12,39 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
cliCommandOptions
|
cliCommandOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(chalk.cyan("Select the Keycloak version from which you want to download the builtins theme:"));
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"Select the Keycloak version from which you want to download the builtins theme:"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const { keycloakVersion } = await promptKeycloakVersion({
|
const { keycloakVersion } = await promptKeycloakVersion({
|
||||||
"startingFromMajor": undefined,
|
startingFromMajor: undefined,
|
||||||
"cacheDirPath": buildOptions.cacheDirPath
|
cacheDirPath: buildOptions.cacheDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`→ ${keycloakVersion}`);
|
console.log(`→ ${keycloakVersion}`);
|
||||||
|
|
||||||
const destDirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme");
|
const destDirPath = pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme"
|
||||||
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
[
|
[
|
||||||
`Downloading builtins theme of Keycloak ${keycloakVersion} here:`,
|
`Downloading builtins theme of Keycloak ${keycloakVersion} here:`,
|
||||||
`- ${chalk.bold(`.${pathSep}${pathJoin(pathRelative(process.cwd(), destDirPath), "base")}`)}`,
|
`- ${chalk.bold(
|
||||||
`- ${chalk.bold(`.${pathSep}${pathJoin(pathRelative(process.cwd(), destDirPath), "keycloak")}`)}`
|
`.${pathSep}${pathJoin(pathRelative(process.cwd(), destDirPath), "base")}`
|
||||||
|
)}`,
|
||||||
|
`- ${chalk.bold(
|
||||||
|
`.${pathSep}${pathJoin(
|
||||||
|
pathRelative(process.cwd(), destDirPath),
|
||||||
|
"keycloak"
|
||||||
|
)}`
|
||||||
|
)}`
|
||||||
].join("\n")
|
].join("\n")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
console.log(chalk.cyan("Theme type:"));
|
console.log(chalk.cyan("Theme type:"));
|
||||||
|
|
||||||
const { value: themeType } = await cliSelect<ThemeType>({
|
const { value: themeType } = await cliSelect<ThemeType>({
|
||||||
"values": [...themeTypes]
|
values: [...themeTypes]
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
});
|
});
|
||||||
@ -40,7 +40,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
console.log(chalk.cyan("Select the page you want to customize:"));
|
console.log(chalk.cyan("Select the page you want to customize:"));
|
||||||
|
|
||||||
const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
|
const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
|
||||||
"values": (() => {
|
values: (() => {
|
||||||
switch (themeType) {
|
switch (themeType) {
|
||||||
case "login":
|
case "login":
|
||||||
return [...loginThemePageIds];
|
return [...loginThemePageIds];
|
||||||
@ -55,14 +55,29 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
console.log(`→ ${pageId}`);
|
console.log(`→ ${pageId}`);
|
||||||
|
|
||||||
const componentPageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
|
const componentPageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(
|
||||||
|
/ftl$/,
|
||||||
|
"tsx"
|
||||||
|
);
|
||||||
|
|
||||||
const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
|
const { themeSrcDirPath } = getThemeSrcDirPath({
|
||||||
|
reactAppRootDirPath: buildOptions.reactAppRootDirPath
|
||||||
|
});
|
||||||
|
|
||||||
const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", componentPageBasename);
|
const targetFilePath = pathJoin(
|
||||||
|
themeSrcDirPath,
|
||||||
|
themeType,
|
||||||
|
"pages",
|
||||||
|
componentPageBasename
|
||||||
|
);
|
||||||
|
|
||||||
if (fs.existsSync(targetFilePath)) {
|
if (fs.existsSync(targetFilePath)) {
|
||||||
console.log(`${pageId} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`);
|
console.log(
|
||||||
|
`${pageId} is already ejected, ${pathRelative(
|
||||||
|
process.cwd(),
|
||||||
|
targetFilePath
|
||||||
|
)} already exists`
|
||||||
|
);
|
||||||
|
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
@ -71,12 +86,20 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
const targetDirPath = pathDirname(targetFilePath);
|
const targetDirPath = pathDirname(targetFilePath);
|
||||||
|
|
||||||
if (!fs.existsSync(targetDirPath)) {
|
if (!fs.existsSync(targetDirPath)) {
|
||||||
fs.mkdirSync(targetDirPath, { "recursive": true });
|
fs.mkdirSync(targetDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentPageContent = fs
|
const componentPageContent = fs
|
||||||
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "src", themeType, "pages", componentPageBasename))
|
.readFileSync(
|
||||||
|
pathJoin(
|
||||||
|
getThisCodebaseRootDirPath(),
|
||||||
|
"src",
|
||||||
|
themeType,
|
||||||
|
"pages",
|
||||||
|
componentPageBasename
|
||||||
|
)
|
||||||
|
)
|
||||||
.toString("utf8");
|
.toString("utf8");
|
||||||
|
|
||||||
fs.writeFileSync(targetFilePath, Buffer.from(componentPageContent, "utf8"));
|
fs.writeFileSync(targetFilePath, Buffer.from(componentPageContent, "utf8"));
|
||||||
@ -92,11 +115,23 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
``,
|
``,
|
||||||
`You now need to update your page router:`,
|
`You now need to update your page router:`,
|
||||||
``,
|
``,
|
||||||
`${chalk.bold(pathJoin(".", pathRelative(process.cwd(), themeSrcDirPath), themeType, "KcApp.tsx"))}:`,
|
`${chalk.bold(
|
||||||
|
pathJoin(
|
||||||
|
".",
|
||||||
|
pathRelative(process.cwd(), themeSrcDirPath),
|
||||||
|
themeType,
|
||||||
|
"KcApp.tsx"
|
||||||
|
)
|
||||||
|
)}:`,
|
||||||
chalk.grey("```"),
|
chalk.grey("```"),
|
||||||
`// ...`,
|
`// ...`,
|
||||||
``,
|
``,
|
||||||
chalk.green(`+const ${componentPageBasename.replace(/.tsx$/, "")} = lazy(() => import("./pages/${componentPageBasename}"));`),
|
chalk.green(
|
||||||
|
`+const ${componentPageBasename.replace(
|
||||||
|
/.tsx$/,
|
||||||
|
""
|
||||||
|
)} = lazy(() => import("./pages/${componentPageBasename}"));`
|
||||||
|
),
|
||||||
...[
|
...[
|
||||||
``,
|
``,
|
||||||
` export default function KcApp(props: { kcContext: KcContext; }) {`,
|
` export default function KcApp(props: { kcContext: KcContext; }) {`,
|
||||||
@ -114,7 +149,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
`+ Template={Template}`,
|
`+ Template={Template}`,
|
||||||
...(!componentPageContent.includes(userProfileFormFieldComponentName)
|
...(!componentPageContent.includes(userProfileFormFieldComponentName)
|
||||||
? []
|
? []
|
||||||
: [`+ ${userProfileFormFieldComponentName}={${userProfileFormFieldComponentName}}`]),
|
: [
|
||||||
|
`+ ${userProfileFormFieldComponentName}={${userProfileFormFieldComponentName}}`
|
||||||
|
]),
|
||||||
`+ doUseDefaultCss={true}`,
|
`+ doUseDefaultCss={true}`,
|
||||||
`+ />`,
|
`+ />`,
|
||||||
`+ );`,
|
`+ );`,
|
||||||
|
@ -14,13 +14,18 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||||
|
|
||||||
const { themeSrcDirPath } = getThemeSrcDirPath({
|
const { themeSrcDirPath } = getThemeSrcDirPath({
|
||||||
"reactAppRootDirPath": buildOptions.reactAppRootDirPath
|
reactAppRootDirPath: buildOptions.reactAppRootDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");
|
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");
|
||||||
|
|
||||||
if (fs.existsSync(emailThemeSrcDirPath)) {
|
if (fs.existsSync(emailThemeSrcDirPath)) {
|
||||||
console.warn(`There is already a ${pathRelative(process.cwd(), emailThemeSrcDirPath)} directory in your project. Aborting.`);
|
console.warn(
|
||||||
|
`There is already a ${pathRelative(
|
||||||
|
process.cwd(),
|
||||||
|
emailThemeSrcDirPath
|
||||||
|
)} directory in your project. Aborting.`
|
||||||
|
);
|
||||||
|
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
@ -29,33 +34,50 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
const { keycloakVersion } = await promptKeycloakVersion({
|
const { keycloakVersion } = await promptKeycloakVersion({
|
||||||
// NOTE: This is arbitrary
|
// NOTE: This is arbitrary
|
||||||
"startingFromMajor": 17,
|
startingFromMajor: 17,
|
||||||
"cacheDirPath": buildOptions.cacheDirPath
|
cacheDirPath: buildOptions.cacheDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
const builtinKeycloakThemeTmpDirPath = pathJoin(buildOptions.cacheDirPath, "initialize-email-theme_tmp");
|
const builtinKeycloakThemeTmpDirPath = pathJoin(
|
||||||
|
buildOptions.cacheDirPath,
|
||||||
|
"initialize-email-theme_tmp"
|
||||||
|
);
|
||||||
|
|
||||||
rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
|
rmSync(builtinKeycloakThemeTmpDirPath, {
|
||||||
|
recursive: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
await downloadKeycloakDefaultTheme({
|
await downloadKeycloakDefaultTheme({
|
||||||
keycloakVersion,
|
keycloakVersion,
|
||||||
"destDirPath": builtinKeycloakThemeTmpDirPath,
|
destDirPath: builtinKeycloakThemeTmpDirPath,
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
|
srcDirPath: pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
|
||||||
"destDirPath": emailThemeSrcDirPath
|
destDirPath: emailThemeSrcDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const themePropertyFilePath = pathJoin(emailThemeSrcDirPath, "theme.properties");
|
const themePropertyFilePath = pathJoin(emailThemeSrcDirPath, "theme.properties");
|
||||||
|
|
||||||
fs.writeFileSync(themePropertyFilePath, Buffer.from(`parent=base\n${fs.readFileSync(themePropertyFilePath).toString("utf8")}`, "utf8"));
|
fs.writeFileSync(
|
||||||
|
themePropertyFilePath,
|
||||||
|
Buffer.from(
|
||||||
|
`parent=base\n${fs.readFileSync(themePropertyFilePath).toString("utf8")}`,
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`The \`${pathJoin(".", pathRelative(process.cwd(), emailThemeSrcDirPath))}\` directory have been created.`);
|
console.log(
|
||||||
|
`The \`${pathJoin(
|
||||||
|
".",
|
||||||
|
pathRelative(process.cwd(), emailThemeSrcDirPath)
|
||||||
|
)}\` directory have been created.`
|
||||||
|
);
|
||||||
console.log("You can delete any file you don't modify.");
|
console.log("You can delete any file you don't modify.");
|
||||||
|
|
||||||
rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true });
|
rmSync(builtinKeycloakThemeTmpDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
import type {
|
||||||
|
KeycloakAccountV1Version,
|
||||||
|
KeycloakThemeAdditionalInfoExtensionVersion
|
||||||
|
} from "./extensionVersions";
|
||||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||||
import { transformCodebase } from "../../tools/transformCodebase";
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
import * as fs from "fs/promises";
|
import * as fs from "fs/promises";
|
||||||
import { accountV1ThemeName } from "../../shared/constants";
|
import { accountV1ThemeName } from "../../shared/constants";
|
||||||
import { generatePom, BuildOptionsLike as BuildOptionsLike_generatePom } from "./generatePom";
|
import {
|
||||||
|
generatePom,
|
||||||
|
BuildOptionsLike as BuildOptionsLike_generatePom
|
||||||
|
} from "./generatePom";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { isInside } from "../../tools/isInside";
|
import { isInside } from "../../tools/isInside";
|
||||||
import child_process from "child_process";
|
import child_process from "child_process";
|
||||||
@ -29,26 +35,53 @@ export async function buildJar(params: {
|
|||||||
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
||||||
buildOptions: BuildOptionsLike;
|
buildOptions: BuildOptionsLike;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { jarFileBasename, keycloakAccountV1Version, keycloakThemeAdditionalInfoExtensionVersion, buildOptions } = params;
|
const {
|
||||||
|
jarFileBasename,
|
||||||
|
keycloakAccountV1Version,
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
buildOptions
|
||||||
|
} = params;
|
||||||
|
|
||||||
const keycloakifyBuildTmpDirPath = pathJoin(buildOptions.cacheDirPath, jarFileBasename.replace(".jar", ""));
|
const keycloakifyBuildTmpDirPath = pathJoin(
|
||||||
|
buildOptions.cacheDirPath,
|
||||||
|
jarFileBasename.replace(".jar", "")
|
||||||
|
);
|
||||||
|
|
||||||
rmSync(keycloakifyBuildTmpDirPath, { "recursive": true, "force": true });
|
rmSync(keycloakifyBuildTmpDirPath, { recursive: true, force: true });
|
||||||
|
|
||||||
{
|
{
|
||||||
const metaInfKeycloakThemesJsonRelativePath = getMetaInfKeycloakThemesJsonFilePath({ "keycloakifyBuildDirPath": "" });
|
const metaInfKeycloakThemesJsonRelativePath =
|
||||||
|
getMetaInfKeycloakThemesJsonFilePath({
|
||||||
|
keycloakifyBuildDirPath: ""
|
||||||
|
});
|
||||||
|
|
||||||
const { transformCodebase_common } = (() => {
|
const { transformCodebase_common } = (() => {
|
||||||
const includingAccountV1ThemeNames = [...buildOptions.themeNames, accountV1ThemeName];
|
const includingAccountV1ThemeNames = [
|
||||||
|
...buildOptions.themeNames,
|
||||||
|
accountV1ThemeName
|
||||||
|
];
|
||||||
|
|
||||||
const transformCodebase_common: Param0<typeof transformCodebase>["transformSourceCode"] = ({ fileRelativePath, sourceCode }) => {
|
const transformCodebase_common: Param0<
|
||||||
|
typeof transformCodebase
|
||||||
|
>["transformSourceCode"] = ({ fileRelativePath, sourceCode }) => {
|
||||||
if (metaInfKeycloakThemesJsonRelativePath === fileRelativePath) {
|
if (metaInfKeycloakThemesJsonRelativePath === fileRelativePath) {
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const themeName of includingAccountV1ThemeNames) {
|
for (const themeName of includingAccountV1ThemeNames) {
|
||||||
if (isInside({ "dirPath": pathJoin("src", "main", "resources", "theme", themeName), "filePath": fileRelativePath })) {
|
if (
|
||||||
return { "modifiedSourceCode": sourceCode };
|
isInside({
|
||||||
|
dirPath: pathJoin(
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
themeName
|
||||||
|
),
|
||||||
|
filePath: fileRelativePath
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,36 +93,70 @@ export async function buildJar(params: {
|
|||||||
|
|
||||||
const { transformCodebase_patchForUsingBuiltinAccountV1 } = (() => {
|
const { transformCodebase_patchForUsingBuiltinAccountV1 } = (() => {
|
||||||
if (keycloakAccountV1Version !== null) {
|
if (keycloakAccountV1Version !== null) {
|
||||||
return { "transformCodebase_patchForUsingBuiltinAccountV1": undefined };
|
return {
|
||||||
|
transformCodebase_patchForUsingBuiltinAccountV1: undefined
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const themePropertiesFileRelativePathSet = new Set(
|
const themePropertiesFileRelativePathSet = new Set(
|
||||||
...buildOptions.themeNames.map(themeName => pathJoin("src", "main", "resources", "theme", themeName, "account", "theme.properties"))
|
...buildOptions.themeNames.map(themeName =>
|
||||||
|
pathJoin(
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
themeName,
|
||||||
|
"account",
|
||||||
|
"theme.properties"
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const accountV1RelativeDirPath = pathJoin("src", "main", "resources", "theme", accountV1ThemeName);
|
const accountV1RelativeDirPath = pathJoin(
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
accountV1ThemeName
|
||||||
|
);
|
||||||
|
|
||||||
const transformCodebase_patchForUsingBuiltinAccountV1: Param0<typeof transformCodebase>["transformSourceCode"] = ({
|
const transformCodebase_patchForUsingBuiltinAccountV1: Param0<
|
||||||
fileRelativePath,
|
typeof transformCodebase
|
||||||
sourceCode
|
>["transformSourceCode"] = ({ fileRelativePath, sourceCode }) => {
|
||||||
}) => {
|
if (
|
||||||
if (isInside({ "dirPath": accountV1RelativeDirPath, "filePath": fileRelativePath })) {
|
isInside({
|
||||||
|
dirPath: accountV1RelativeDirPath,
|
||||||
|
filePath: fileRelativePath
|
||||||
|
})
|
||||||
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileRelativePath === metaInfKeycloakThemesJsonRelativePath) {
|
if (fileRelativePath === metaInfKeycloakThemesJsonRelativePath) {
|
||||||
const keycloakThemesJsonParsed = JSON.parse(sourceCode.toString("utf8")) as {
|
const keycloakThemesJsonParsed = JSON.parse(
|
||||||
|
sourceCode.toString("utf8")
|
||||||
|
) as {
|
||||||
themes: { name: string; types: string[] }[];
|
themes: { name: string; types: string[] }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
keycloakThemesJsonParsed.themes = keycloakThemesJsonParsed.themes.filter(({ name }) => name !== accountV1ThemeName);
|
keycloakThemesJsonParsed.themes =
|
||||||
|
keycloakThemesJsonParsed.themes.filter(
|
||||||
|
({ name }) => name !== accountV1ThemeName
|
||||||
|
);
|
||||||
|
|
||||||
return { "modifiedSourceCode": Buffer.from(JSON.stringify(keycloakThemesJsonParsed, null, 2), "utf8") };
|
return {
|
||||||
|
modifiedSourceCode: Buffer.from(
|
||||||
|
JSON.stringify(keycloakThemesJsonParsed, null, 2),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (themePropertiesFileRelativePathSet.has(fileRelativePath)) {
|
if (themePropertiesFileRelativePathSet.has(fileRelativePath)) {
|
||||||
const modifiedSourceCode = Buffer.from(
|
const modifiedSourceCode = Buffer.from(
|
||||||
sourceCode.toString("utf8").replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
|
sourceCode
|
||||||
|
.toString("utf8")
|
||||||
|
.replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
|
||||||
"utf8"
|
"utf8"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -99,16 +166,16 @@ export async function buildJar(params: {
|
|||||||
return { modifiedSourceCode };
|
return { modifiedSourceCode };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
};
|
};
|
||||||
|
|
||||||
return { transformCodebase_patchForUsingBuiltinAccountV1 };
|
return { transformCodebase_patchForUsingBuiltinAccountV1 };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": buildOptions.keycloakifyBuildDirPath,
|
srcDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||||
"destDirPath": keycloakifyBuildTmpDirPath,
|
destDirPath: keycloakifyBuildTmpDirPath,
|
||||||
"transformSourceCode": params => {
|
transformSourceCode: params => {
|
||||||
const resultCommon = transformCodebase_common(params);
|
const resultCommon = transformCodebase_common(params);
|
||||||
|
|
||||||
if (resultCommon === undefined) {
|
if (resultCommon === undefined) {
|
||||||
@ -125,7 +192,7 @@ export async function buildJar(params: {
|
|||||||
|
|
||||||
return transformCodebase_patchForUsingBuiltinAccountV1?.({
|
return transformCodebase_patchForUsingBuiltinAccountV1?.({
|
||||||
...params,
|
...params,
|
||||||
"sourceCode": modifiedSourceCode
|
sourceCode: modifiedSourceCode
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -152,7 +219,16 @@ export async function buildJar(params: {
|
|||||||
|
|
||||||
(["register.ftl", "login-update-profile.ftl"] as const).forEach(pageId =>
|
(["register.ftl", "login-update-profile.ftl"] as const).forEach(pageId =>
|
||||||
buildOptions.themeNames.map(themeName => {
|
buildOptions.themeNames.map(themeName => {
|
||||||
const ftlFilePath = pathJoin(keycloakifyBuildTmpDirPath, "src", "main", "resources", "theme", themeName, "login", pageId);
|
const ftlFilePath = pathJoin(
|
||||||
|
keycloakifyBuildTmpDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
themeName,
|
||||||
|
"login",
|
||||||
|
pageId
|
||||||
|
);
|
||||||
|
|
||||||
const ftlFileContent = readFileSync(ftlFilePath).toString("utf8");
|
const ftlFileContent = readFileSync(ftlFilePath).toString("utf8");
|
||||||
|
|
||||||
@ -173,7 +249,10 @@ export async function buildJar(params: {
|
|||||||
|
|
||||||
assert(modifiedFtlFileContent !== ftlFileContent);
|
assert(modifiedFtlFileContent !== ftlFileContent);
|
||||||
|
|
||||||
fs.writeFile(pathJoin(pathDirname(ftlFilePath), realPageId), Buffer.from(modifiedFtlFileContent, "utf8"));
|
fs.writeFile(
|
||||||
|
pathJoin(pathDirname(ftlFilePath), realPageId),
|
||||||
|
Buffer.from(modifiedFtlFileContent, "utf8")
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -185,35 +264,46 @@ export async function buildJar(params: {
|
|||||||
keycloakThemeAdditionalInfoExtensionVersion
|
keycloakThemeAdditionalInfoExtensionVersion
|
||||||
});
|
});
|
||||||
|
|
||||||
await fs.writeFile(pathJoin(keycloakifyBuildTmpDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8"));
|
await fs.writeFile(
|
||||||
|
pathJoin(keycloakifyBuildTmpDirPath, "pom.xml"),
|
||||||
|
Buffer.from(pomFileCode, "utf8")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) =>
|
await new Promise<void>((resolve, reject) =>
|
||||||
child_process.exec("mvn clean install", { "cwd": keycloakifyBuildTmpDirPath }, error => {
|
child_process.exec(
|
||||||
if (error !== null) {
|
"mvn clean install",
|
||||||
console.error(
|
{ cwd: keycloakifyBuildTmpDirPath },
|
||||||
`Build jar failed: ${JSON.stringify(
|
error => {
|
||||||
{
|
if (error !== null) {
|
||||||
jarFileBasename,
|
console.error(
|
||||||
keycloakAccountV1Version,
|
`Build jar failed: ${JSON.stringify(
|
||||||
keycloakThemeAdditionalInfoExtensionVersion
|
{
|
||||||
},
|
jarFileBasename,
|
||||||
null,
|
keycloakAccountV1Version,
|
||||||
2
|
keycloakThemeAdditionalInfoExtensionVersion
|
||||||
)}`
|
},
|
||||||
);
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
reject(error);
|
reject(error);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
resolve();
|
)
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await fs.rename(
|
await fs.rename(
|
||||||
pathJoin(keycloakifyBuildTmpDirPath, "target", `${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`),
|
pathJoin(
|
||||||
|
keycloakifyBuildTmpDirPath,
|
||||||
|
"target",
|
||||||
|
`${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`
|
||||||
|
),
|
||||||
pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)
|
pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)
|
||||||
);
|
);
|
||||||
|
|
||||||
rmSync(keycloakifyBuildTmpDirPath, { "recursive": true });
|
rmSync(keycloakifyBuildTmpDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { exclude } from "tsafe/exclude";
|
import { exclude } from "tsafe/exclude";
|
||||||
import { keycloakAccountV1Versions, keycloakThemeAdditionalInfoExtensionVersions } from "./extensionVersions";
|
import {
|
||||||
|
keycloakAccountV1Versions,
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersions
|
||||||
|
} from "./extensionVersions";
|
||||||
import { getKeycloakVersionRangeForJar } from "./getKeycloakVersionRangeForJar";
|
import { getKeycloakVersionRangeForJar } from "./getKeycloakVersionRangeForJar";
|
||||||
import { buildJar, BuildOptionsLike as BuildOptionsLike_buildJar } from "./buildJar";
|
import { buildJar, BuildOptionsLike as BuildOptionsLike_buildJar } from "./buildJar";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
@ -14,11 +17,13 @@ export type BuildOptionsLike = BuildOptionsLike_buildJar & {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export async function buildJars(params: { buildOptions: BuildOptionsLike }): Promise<void> {
|
export async function buildJars(params: {
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): Promise<void> {
|
||||||
const { buildOptions } = params;
|
const { buildOptions } = params;
|
||||||
|
|
||||||
const doesImplementAccountTheme = readMetaInfKeycloakThemes({
|
const doesImplementAccountTheme = readMetaInfKeycloakThemes({
|
||||||
"keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath
|
keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath
|
||||||
}).themes.some(({ name }) => name === accountV1ThemeName);
|
}).themes.some(({ name }) => name === accountV1ThemeName);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@ -36,24 +41,38 @@ export async function buildJars(params: { buildOptions: BuildOptionsLike }): Pro
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { keycloakThemeAdditionalInfoExtensionVersion, keycloakVersionRange };
|
|
||||||
})
|
|
||||||
.filter(exclude(undefined))
|
|
||||||
.map(({ keycloakThemeAdditionalInfoExtensionVersion, keycloakVersionRange }) => {
|
|
||||||
const { jarFileBasename } = getJarFileBasename({ keycloakVersionRange });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
keycloakThemeAdditionalInfoExtensionVersion,
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
jarFileBasename
|
keycloakVersionRange
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.map(({ keycloakThemeAdditionalInfoExtensionVersion, jarFileBasename }) =>
|
.filter(exclude(undefined))
|
||||||
buildJar({
|
.map(
|
||||||
jarFileBasename,
|
({
|
||||||
keycloakAccountV1Version,
|
|
||||||
keycloakThemeAdditionalInfoExtensionVersion,
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
buildOptions
|
keycloakVersionRange
|
||||||
})
|
}) => {
|
||||||
|
const { jarFileBasename } = getJarFileBasename({
|
||||||
|
keycloakVersionRange
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
jarFileBasename
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
({
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
jarFileBasename
|
||||||
|
}) =>
|
||||||
|
buildJar({
|
||||||
|
jarFileBasename,
|
||||||
|
keycloakAccountV1Version,
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
buildOptions
|
||||||
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.flat()
|
.flat()
|
||||||
|
@ -13,4 +13,5 @@ export const keycloakThemeAdditionalInfoExtensionVersions = [null, "1.1.5"] as c
|
|||||||
* https://central.sonatype.com/artifact/dev.jcputney/keycloak-theme-additional-info-extension
|
* https://central.sonatype.com/artifact/dev.jcputney/keycloak-theme-additional-info-extension
|
||||||
* https://github.com/jcputney/keycloak-theme-additional-info-extension
|
* https://github.com/jcputney/keycloak-theme-additional-info-extension
|
||||||
* */
|
* */
|
||||||
export type KeycloakThemeAdditionalInfoExtensionVersion = (typeof keycloakThemeAdditionalInfoExtensionVersions)[number];
|
export type KeycloakThemeAdditionalInfoExtensionVersion =
|
||||||
|
(typeof keycloakThemeAdditionalInfoExtensionVersions)[number];
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
import type {
|
||||||
|
KeycloakAccountV1Version,
|
||||||
|
KeycloakThemeAdditionalInfoExtensionVersion
|
||||||
|
} from "./extensionVersions";
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
@ -15,7 +18,11 @@ export function generatePom(params: {
|
|||||||
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
||||||
buildOptions: BuildOptionsLike;
|
buildOptions: BuildOptionsLike;
|
||||||
}) {
|
}) {
|
||||||
const { keycloakAccountV1Version, keycloakThemeAdditionalInfoExtensionVersion, buildOptions } = params;
|
const {
|
||||||
|
keycloakAccountV1Version,
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
buildOptions
|
||||||
|
} = params;
|
||||||
|
|
||||||
const { pomFileCode } = (function generatePomFileCode(): {
|
const { pomFileCode } = (function generatePomFileCode(): {
|
||||||
pomFileCode: string;
|
pomFileCode: string;
|
||||||
@ -35,7 +42,8 @@ export function generatePom(params: {
|
|||||||
` <properties>`,
|
` <properties>`,
|
||||||
` <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>`,
|
` <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>`,
|
||||||
` </properties>`,
|
` </properties>`,
|
||||||
...(keycloakAccountV1Version !== null && keycloakThemeAdditionalInfoExtensionVersion !== null
|
...(keycloakAccountV1Version !== null &&
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion !== null
|
||||||
? [
|
? [
|
||||||
` <build>`,
|
` <build>`,
|
||||||
` <plugins>`,
|
` <plugins>`,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
import type {
|
||||||
|
KeycloakAccountV1Version,
|
||||||
|
KeycloakThemeAdditionalInfoExtensionVersion
|
||||||
|
} from "./extensionVersions";
|
||||||
import type { KeycloakVersionRange } from "../../shared/KeycloakVersionRange";
|
import type { KeycloakVersionRange } from "../../shared/KeycloakVersionRange";
|
||||||
|
|
||||||
export function getKeycloakVersionRangeForJar(params: {
|
export function getKeycloakVersionRangeForJar(params: {
|
||||||
@ -7,7 +10,11 @@ export function getKeycloakVersionRangeForJar(params: {
|
|||||||
keycloakAccountV1Version: KeycloakAccountV1Version;
|
keycloakAccountV1Version: KeycloakAccountV1Version;
|
||||||
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
||||||
}): KeycloakVersionRange | undefined {
|
}): KeycloakVersionRange | undefined {
|
||||||
const { keycloakAccountV1Version, keycloakThemeAdditionalInfoExtensionVersion, doesImplementAccountTheme } = params;
|
const {
|
||||||
|
keycloakAccountV1Version,
|
||||||
|
keycloakThemeAdditionalInfoExtensionVersion,
|
||||||
|
doesImplementAccountTheme
|
||||||
|
} = params;
|
||||||
|
|
||||||
if (doesImplementAccountTheme) {
|
if (doesImplementAccountTheme) {
|
||||||
const keycloakVersionRange = (() => {
|
const keycloakVersionRange = (() => {
|
||||||
@ -19,7 +26,9 @@ export function getKeycloakVersionRangeForJar(params: {
|
|||||||
case "1.1.5":
|
case "1.1.5":
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(false);
|
assert<
|
||||||
|
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
|
||||||
|
>(false);
|
||||||
case "0.3":
|
case "0.3":
|
||||||
switch (keycloakThemeAdditionalInfoExtensionVersion) {
|
switch (keycloakThemeAdditionalInfoExtensionVersion) {
|
||||||
case null:
|
case null:
|
||||||
@ -27,7 +36,9 @@ export function getKeycloakVersionRangeForJar(params: {
|
|||||||
case "1.1.5":
|
case "1.1.5":
|
||||||
return "23" as const;
|
return "23" as const;
|
||||||
}
|
}
|
||||||
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(false);
|
assert<
|
||||||
|
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
|
||||||
|
>(false);
|
||||||
case "0.4":
|
case "0.4":
|
||||||
switch (keycloakThemeAdditionalInfoExtensionVersion) {
|
switch (keycloakThemeAdditionalInfoExtensionVersion) {
|
||||||
case null:
|
case null:
|
||||||
@ -35,11 +46,18 @@ export function getKeycloakVersionRangeForJar(params: {
|
|||||||
case "1.1.5":
|
case "1.1.5":
|
||||||
return "24-and-above" as const;
|
return "24-and-above" as const;
|
||||||
}
|
}
|
||||||
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(false);
|
assert<
|
||||||
|
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
|
||||||
|
>(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithAccountTheme | undefined>>();
|
assert<
|
||||||
|
Equals<
|
||||||
|
typeof keycloakVersionRange,
|
||||||
|
KeycloakVersionRange.WithAccountTheme | undefined
|
||||||
|
>
|
||||||
|
>();
|
||||||
|
|
||||||
return keycloakVersionRange;
|
return keycloakVersionRange;
|
||||||
} else {
|
} else {
|
||||||
@ -54,10 +72,17 @@ export function getKeycloakVersionRangeForJar(params: {
|
|||||||
case "1.1.5":
|
case "1.1.5":
|
||||||
return "22-and-above";
|
return "22-and-above";
|
||||||
}
|
}
|
||||||
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(false);
|
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(
|
||||||
|
false
|
||||||
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithoutAccountTheme | undefined>>();
|
assert<
|
||||||
|
Equals<
|
||||||
|
typeof keycloakVersionRange,
|
||||||
|
KeycloakVersionRange.WithoutAccountTheme | undefined
|
||||||
|
>
|
||||||
|
>();
|
||||||
|
|
||||||
return keycloakVersionRange;
|
return keycloakVersionRange;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,12 @@ import * as fs from "fs";
|
|||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, resources_common } from "../../shared/constants";
|
import {
|
||||||
|
type ThemeType,
|
||||||
|
nameOfTheGlobal,
|
||||||
|
basenameOfTheKeycloakifyResourcesDir,
|
||||||
|
resources_common
|
||||||
|
} from "../../shared/constants";
|
||||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
@ -28,7 +33,15 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
themeType: ThemeType;
|
themeType: ThemeType;
|
||||||
fieldNames: string[];
|
fieldNames: string[];
|
||||||
}) {
|
}) {
|
||||||
const { themeName, cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType, fieldNames } = params;
|
const {
|
||||||
|
themeName,
|
||||||
|
cssGlobalsToDefine,
|
||||||
|
indexHtmlCode,
|
||||||
|
buildOptions,
|
||||||
|
keycloakifyVersion,
|
||||||
|
themeType,
|
||||||
|
fieldNames
|
||||||
|
} = params;
|
||||||
|
|
||||||
const $ = cheerio.load(indexHtmlCode);
|
const $ = cheerio.load(indexHtmlCode);
|
||||||
|
|
||||||
@ -38,7 +51,10 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
|
|
||||||
assert(jsCode !== null);
|
assert(jsCode !== null);
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportsInJsCode({ jsCode, buildOptions });
|
const { fixedJsCode } = replaceImportsInJsCode({
|
||||||
|
jsCode,
|
||||||
|
buildOptions
|
||||||
|
});
|
||||||
|
|
||||||
$(element).text(fixedJsCode);
|
$(element).text(fixedJsCode);
|
||||||
});
|
});
|
||||||
@ -72,7 +88,9 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
$(element).attr(
|
$(element).attr(
|
||||||
attrName,
|
attrName,
|
||||||
href.replace(
|
href.replace(
|
||||||
new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`),
|
new RegExp(
|
||||||
|
`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`
|
||||||
|
),
|
||||||
`\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
`\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -98,20 +116,33 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||||
const ftlObjectToJsCodeDeclaringAnObject = fs
|
const ftlObjectToJsCodeDeclaringAnObject = fs
|
||||||
.readFileSync(
|
.readFileSync(
|
||||||
pathJoin(getThisCodebaseRootDirPath(), "src", "bin", "keycloakify", "generateFtl", "ftl_object_to_js_code_declaring_an_object.ftl")
|
pathJoin(
|
||||||
|
getThisCodebaseRootDirPath(),
|
||||||
|
"src",
|
||||||
|
"bin",
|
||||||
|
"keycloakify",
|
||||||
|
"generateFtl",
|
||||||
|
"ftl_object_to_js_code_declaring_an_object.ftl"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.toString("utf8")
|
.toString("utf8")
|
||||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1]
|
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1]
|
||||||
.replace("FIELD_NAMES_eKsIY4ZsZ4xeM", fieldNames.map(name => `"${name}"`).join(", "))
|
.replace(
|
||||||
|
"FIELD_NAMES_eKsIY4ZsZ4xeM",
|
||||||
|
fieldNames.map(name => `"${name}"`).join(", ")
|
||||||
|
)
|
||||||
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
||||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
|
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
|
||||||
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
|
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
|
||||||
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", themeName)
|
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", themeName)
|
||||||
.replace("RESOURCES_COMMON_cLsLsMrtDkpVv", resources_common);
|
.replace("RESOURCES_COMMON_cLsLsMrtDkpVv", resources_common);
|
||||||
|
|
||||||
const ftlObjectToJsCodeDeclaringAnObjectPlaceholder = '{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }';
|
const ftlObjectToJsCodeDeclaringAnObjectPlaceholder =
|
||||||
|
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }';
|
||||||
|
|
||||||
$("head").prepend(`<script>\nwindow.${nameOfTheGlobal}=${ftlObjectToJsCodeDeclaringAnObjectPlaceholder}</script>`);
|
$("head").prepend(
|
||||||
|
`<script>\nwindow.${nameOfTheGlobal}=${ftlObjectToJsCodeDeclaringAnObjectPlaceholder}</script>`
|
||||||
|
);
|
||||||
|
|
||||||
// Remove part of the document marked as ignored.
|
// Remove part of the document marked as ignored.
|
||||||
{
|
{
|
||||||
@ -119,7 +150,9 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
|
|
||||||
startTags.each((...[, startTag]) => {
|
startTags.each((...[, startTag]) => {
|
||||||
const $startTag = $(startTag);
|
const $startTag = $(startTag);
|
||||||
const $endTag = $startTag.nextAll('meta[name="keycloakify-ignore-end"]').first();
|
const $endTag = $startTag
|
||||||
|
.nextAll('meta[name="keycloakify-ignore-end"]')
|
||||||
|
.first();
|
||||||
|
|
||||||
if ($endTag.length) {
|
if ($endTag.length) {
|
||||||
let currentNode = $startTag.next();
|
let currentNode = $startTag.next();
|
||||||
@ -146,9 +179,13 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
let ftlCode = $.html();
|
let ftlCode = $.html();
|
||||||
|
|
||||||
Object.entries({
|
Object.entries({
|
||||||
[ftlObjectToJsCodeDeclaringAnObjectPlaceholder]: ftlObjectToJsCodeDeclaringAnObject,
|
[ftlObjectToJsCodeDeclaringAnObjectPlaceholder]:
|
||||||
"PAGE_ID_xIgLsPgGId9D8e": pageId
|
ftlObjectToJsCodeDeclaringAnObject,
|
||||||
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
|
PAGE_ID_xIgLsPgGId9D8e: pageId
|
||||||
|
}).map(
|
||||||
|
([searchValue, replaceValue]) =>
|
||||||
|
(ftlCode = ftlCode.replace(searchValue, replaceValue))
|
||||||
|
);
|
||||||
|
|
||||||
return { ftlCode };
|
return { ftlCode };
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,11 @@ import * as fs from "fs";
|
|||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
import { resources_common, lastKeycloakVersionWithAccountV1, accountV1ThemeName } from "../../shared/constants";
|
import {
|
||||||
|
resources_common,
|
||||||
|
lastKeycloakVersionWithAccountV1,
|
||||||
|
accountV1ThemeName
|
||||||
|
} from "../../shared/constants";
|
||||||
import { downloadKeycloakDefaultTheme } from "../../shared/downloadKeycloakDefaultTheme";
|
import { downloadKeycloakDefaultTheme } from "../../shared/downloadKeycloakDefaultTheme";
|
||||||
import { transformCodebase } from "../../tools/transformCodebase";
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
import { rmSync } from "../../tools/fs.rmSync";
|
import { rmSync } from "../../tools/fs.rmSync";
|
||||||
@ -18,32 +22,53 @@ assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
|||||||
export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike }) {
|
export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike }) {
|
||||||
const { buildOptions } = params;
|
const { buildOptions } = params;
|
||||||
|
|
||||||
const builtinKeycloakThemeTmpDirPath = pathJoin(buildOptions.cacheDirPath, "bringInAccountV1_tmp");
|
const builtinKeycloakThemeTmpDirPath = pathJoin(
|
||||||
|
buildOptions.cacheDirPath,
|
||||||
|
"bringInAccountV1_tmp"
|
||||||
|
);
|
||||||
|
|
||||||
await downloadKeycloakDefaultTheme({
|
await downloadKeycloakDefaultTheme({
|
||||||
"destDirPath": builtinKeycloakThemeTmpDirPath,
|
destDirPath: builtinKeycloakThemeTmpDirPath,
|
||||||
"keycloakVersion": lastKeycloakVersionWithAccountV1,
|
keycloakVersion: lastKeycloakVersionWithAccountV1,
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
const accountV1DirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", accountV1ThemeName, "account");
|
const accountV1DirPath = pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
accountV1ThemeName,
|
||||||
|
"account"
|
||||||
|
);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "account"),
|
srcDirPath: pathJoin(builtinKeycloakThemeTmpDirPath, "base", "account"),
|
||||||
"destDirPath": accountV1DirPath
|
destDirPath: accountV1DirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "keycloak", "account", "resources"),
|
srcDirPath: pathJoin(
|
||||||
"destDirPath": pathJoin(accountV1DirPath, "resources")
|
builtinKeycloakThemeTmpDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"account",
|
||||||
|
"resources"
|
||||||
|
),
|
||||||
|
destDirPath: pathJoin(accountV1DirPath, "resources")
|
||||||
});
|
});
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "keycloak", "common", "resources"),
|
srcDirPath: pathJoin(
|
||||||
"destDirPath": pathJoin(accountV1DirPath, "resources", resources_common)
|
builtinKeycloakThemeTmpDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"common",
|
||||||
|
"resources"
|
||||||
|
),
|
||||||
|
destDirPath: pathJoin(accountV1DirPath, "resources", resources_common)
|
||||||
});
|
});
|
||||||
|
|
||||||
rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true });
|
rmSync(builtinKeycloakThemeTmpDirPath, { recursive: true });
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
pathJoin(accountV1DirPath, "theme.properties"),
|
pathJoin(accountV1DirPath, "theme.properties"),
|
||||||
@ -58,8 +83,13 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike
|
|||||||
"css/account.css",
|
"css/account.css",
|
||||||
"img/icon-sidebar-active.png",
|
"img/icon-sidebar-active.png",
|
||||||
"img/logo.png",
|
"img/logo.png",
|
||||||
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(
|
...[
|
||||||
fileBasename => `${resources_common}/node_modules/patternfly/dist/css/${fileBasename}`
|
"patternfly.min.css",
|
||||||
|
"patternfly-additions.min.css",
|
||||||
|
"patternfly-additions.min.css"
|
||||||
|
].map(
|
||||||
|
fileBasename =>
|
||||||
|
`${resources_common}/node_modules/patternfly/dist/css/${fileBasename}`
|
||||||
)
|
)
|
||||||
].join(" "),
|
].join(" "),
|
||||||
"",
|
"",
|
||||||
|
@ -16,8 +16,8 @@ export function generateMessageProperties(params: {
|
|||||||
const { themeSrcDirPath, themeType } = params;
|
const { themeSrcDirPath, themeType } = params;
|
||||||
|
|
||||||
let files = crawl({
|
let files = crawl({
|
||||||
"dirPath": pathJoin(themeSrcDirPath, themeType),
|
dirPath: pathJoin(themeSrcDirPath, themeType),
|
||||||
"returnedPathsType": "absolute"
|
returnedPathsType: "absolute"
|
||||||
});
|
});
|
||||||
|
|
||||||
files = files.filter(file => {
|
files = files.filter(file => {
|
||||||
@ -34,7 +34,9 @@ export function generateMessageProperties(params: {
|
|||||||
|
|
||||||
files = files.sort((a, b) => a.length - b.length);
|
files = files.sort((a, b) => a.length - b.length);
|
||||||
|
|
||||||
files = files.filter(file => readFileSync(file).toString("utf8").includes("createUseI18n"));
|
files = files.filter(file =>
|
||||||
|
readFileSync(file).toString("utf8").includes("createUseI18n")
|
||||||
|
);
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
@ -43,18 +45,25 @@ export function generateMessageProperties(params: {
|
|||||||
const extraMessages = files
|
const extraMessages = files
|
||||||
.map(file => {
|
.map(file => {
|
||||||
const root = recast.parse(readFileSync(file).toString("utf8"), {
|
const root = recast.parse(readFileSync(file).toString("utf8"), {
|
||||||
"parser": {
|
parser: {
|
||||||
"parse": (code: string) => babelParser.parse(code, { "sourceType": "module", "plugins": ["typescript"] }),
|
parse: (code: string) =>
|
||||||
"generator": babelGenerate,
|
babelParser.parse(code, {
|
||||||
"types": babelTypes
|
sourceType: "module",
|
||||||
|
plugins: ["typescript"]
|
||||||
|
}),
|
||||||
|
generator: babelGenerate,
|
||||||
|
types: babelTypes
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const codes: string[] = [];
|
const codes: string[] = [];
|
||||||
|
|
||||||
recast.visit(root, {
|
recast.visit(root, {
|
||||||
"visitCallExpression": function (path) {
|
visitCallExpression: function (path) {
|
||||||
if (path.node.callee.type === "Identifier" && path.node.callee.name === "createUseI18n") {
|
if (
|
||||||
|
path.node.callee.type === "Identifier" &&
|
||||||
|
path.node.callee.name === "createUseI18n"
|
||||||
|
) {
|
||||||
codes.push(babelGenerate(path.node.arguments[0] as any).code);
|
codes.push(babelGenerate(path.node.arguments[0] as any).code);
|
||||||
}
|
}
|
||||||
this.traverse(path);
|
this.traverse(path);
|
||||||
@ -65,7 +74,9 @@ export function generateMessageProperties(params: {
|
|||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
.map(code => {
|
.map(code => {
|
||||||
let extraMessages: { [languageTag: string]: Record<string, string> } = {};
|
let extraMessages: {
|
||||||
|
[languageTag: string]: Record<string, string>;
|
||||||
|
} = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
eval(`${symToStr({ extraMessages })} = ${code}`);
|
eval(`${symToStr({ extraMessages })} = ${code}`);
|
||||||
@ -140,7 +151,14 @@ export function generateMessageProperties(params: {
|
|||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
languageTag,
|
languageTag,
|
||||||
"propertiesFileSource": ["# This file was generated by keycloakify", "", "parent=base", "", propertiesFileSource, ""].join("\n")
|
propertiesFileSource: [
|
||||||
|
"# This file was generated by keycloakify",
|
||||||
|
"",
|
||||||
|
"parent=base",
|
||||||
|
"",
|
||||||
|
propertiesFileSource,
|
||||||
|
""
|
||||||
|
].join("\n")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +175,12 @@ function toUTF16(codePoint: number): string {
|
|||||||
codePoint -= 0x10000;
|
codePoint -= 0x10000;
|
||||||
let highSurrogate = (codePoint >> 10) + 0xd800;
|
let highSurrogate = (codePoint >> 10) + 0xd800;
|
||||||
let lowSurrogate = (codePoint % 0x400) + 0xdc00;
|
let lowSurrogate = (codePoint % 0x400) + 0xdc00;
|
||||||
return "\\u" + highSurrogate.toString(16).padStart(4, "0") + "\\u" + lowSurrogate.toString(16).padStart(4, "0");
|
return (
|
||||||
|
"\\u" +
|
||||||
|
highSurrogate.toString(16).padStart(4, "0") +
|
||||||
|
"\\u" +
|
||||||
|
lowSurrogate.toString(16).padStart(4, "0")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@ export type BuildOptionsLike = BuildOptionsLike_generateSrcMainResourcesForMainT
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export async function generateSrcMainResources(params: { buildOptions: BuildOptionsLike }): Promise<void> {
|
export async function generateSrcMainResources(params: {
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): Promise<void> {
|
||||||
const { buildOptions } = params;
|
const { buildOptions } = params;
|
||||||
|
|
||||||
const [themeName, ...themeVariantNames] = buildOptions.themeNames;
|
const [themeName, ...themeVariantNames] = buildOptions.themeNames;
|
||||||
|
@ -24,7 +24,10 @@ import { bringInAccountV1 } from "./bringInAccountV1";
|
|||||||
import { getThemeSrcDirPath } from "../../shared/getThemeSrcDirPath";
|
import { getThemeSrcDirPath } from "../../shared/getThemeSrcDirPath";
|
||||||
import { rmSync } from "../../tools/fs.rmSync";
|
import { rmSync } from "../../tools/fs.rmSync";
|
||||||
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
|
||||||
import { writeMetaInfKeycloakThemes, type MetaInfKeycloakTheme } from "../../shared/metaInfKeycloakThemes";
|
import {
|
||||||
|
writeMetaInfKeycloakThemes,
|
||||||
|
type MetaInfKeycloakTheme
|
||||||
|
} from "../../shared/metaInfKeycloakThemes";
|
||||||
import { objectEntries } from "tsafe/objectEntries";
|
import { objectEntries } from "tsafe/objectEntries";
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
@ -43,22 +46,35 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export async function generateSrcMainResourcesForMainTheme(params: { themeName: string; buildOptions: BuildOptionsLike }): Promise<void> {
|
export async function generateSrcMainResourcesForMainTheme(params: {
|
||||||
|
themeName: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): Promise<void> {
|
||||||
const { themeName, buildOptions } = params;
|
const { themeName, buildOptions } = params;
|
||||||
|
|
||||||
const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
|
const { themeSrcDirPath } = getThemeSrcDirPath({
|
||||||
|
reactAppRootDirPath: buildOptions.reactAppRootDirPath
|
||||||
|
});
|
||||||
|
|
||||||
const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
|
const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
|
||||||
const { themeType } = params;
|
const { themeType } = params;
|
||||||
return pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", themeName, themeType);
|
return pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
themeName,
|
||||||
|
themeType
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cssGlobalsToDefine: Record<string, string> = {};
|
const cssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
const implementedThemeTypes: Record<ThemeType | "email", boolean> = {
|
const implementedThemeTypes: Record<ThemeType | "email", boolean> = {
|
||||||
"login": false,
|
login: false,
|
||||||
"account": false,
|
account: false,
|
||||||
"email": false
|
email: false
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const themeType of ["login", "account"] as const) {
|
for (const themeType of ["login", "account"] as const) {
|
||||||
@ -71,18 +87,22 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
const themeTypeDirPath = getThemeTypeDirPath({ themeType });
|
const themeTypeDirPath = getThemeTypeDirPath({ themeType });
|
||||||
|
|
||||||
apply_replacers_and_move_to_theme_resources: {
|
apply_replacers_and_move_to_theme_resources: {
|
||||||
const destDirPath = pathJoin(themeTypeDirPath, "resources", basenameOfTheKeycloakifyResourcesDir);
|
const destDirPath = pathJoin(
|
||||||
|
themeTypeDirPath,
|
||||||
|
"resources",
|
||||||
|
basenameOfTheKeycloakifyResourcesDir
|
||||||
|
);
|
||||||
|
|
||||||
// NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
|
// NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
|
||||||
rmSync(destDirPath, { "recursive": true, "force": true });
|
rmSync(destDirPath, { recursive: true, force: true });
|
||||||
|
|
||||||
if (themeType === "account" && implementedThemeTypes.login) {
|
if (themeType === "account" && implementedThemeTypes.login) {
|
||||||
// NOTE: We prevent doing it twice, it has been done for the login theme.
|
// NOTE: We prevent doing it twice, it has been done for the login theme.
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(
|
srcDirPath: pathJoin(
|
||||||
getThemeTypeDirPath({
|
getThemeTypeDirPath({
|
||||||
"themeType": "login"
|
themeType: "login"
|
||||||
}),
|
}),
|
||||||
"resources",
|
"resources",
|
||||||
basenameOfTheKeycloakifyResourcesDir
|
basenameOfTheKeycloakifyResourcesDir
|
||||||
@ -94,14 +114,17 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
}
|
}
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": buildOptions.reactAppBuildDirPath,
|
srcDirPath: buildOptions.reactAppBuildDirPath,
|
||||||
destDirPath,
|
destDirPath,
|
||||||
"transformSourceCode": ({ filePath, sourceCode }) => {
|
transformSourceCode: ({ filePath, sourceCode }) => {
|
||||||
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
||||||
// This should not happen if users follow the new instruction setup but we keep it for retrocompatibility.
|
// This should not happen if users follow the new instruction setup but we keep it for retrocompatibility.
|
||||||
if (
|
if (
|
||||||
isInside({
|
isInside({
|
||||||
"dirPath": pathJoin(buildOptions.reactAppBuildDirPath, keycloak_resources),
|
dirPath: pathJoin(
|
||||||
|
buildOptions.reactAppBuildDirPath,
|
||||||
|
keycloak_resources
|
||||||
|
),
|
||||||
filePath
|
filePath
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
@ -109,39 +132,50 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (/\.css?$/i.test(filePath)) {
|
if (/\.css?$/i.test(filePath)) {
|
||||||
const { cssGlobalsToDefine: cssGlobalsToDefineForThisFile, fixedCssCode } = replaceImportsInCssCode({
|
const {
|
||||||
"cssCode": sourceCode.toString("utf8")
|
cssGlobalsToDefine: cssGlobalsToDefineForThisFile,
|
||||||
|
fixedCssCode
|
||||||
|
} = replaceImportsInCssCode({
|
||||||
|
cssCode: sourceCode.toString("utf8")
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.entries(cssGlobalsToDefineForThisFile).forEach(([key, value]) => {
|
Object.entries(cssGlobalsToDefineForThisFile).forEach(
|
||||||
cssGlobalsToDefine[key] = value;
|
([key, value]) => {
|
||||||
});
|
cssGlobalsToDefine[key] = value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
return {
|
||||||
|
modifiedSourceCode: Buffer.from(fixedCssCode, "utf8")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/\.js?$/i.test(filePath)) {
|
if (/\.js?$/i.test(filePath)) {
|
||||||
const { fixedJsCode } = replaceImportsInJsCode({
|
const { fixedJsCode } = replaceImportsInJsCode({
|
||||||
"jsCode": sourceCode.toString("utf8"),
|
jsCode: sourceCode.toString("utf8"),
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
return {
|
||||||
|
modifiedSourceCode: Buffer.from(fixedJsCode, "utf8")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||||
themeName,
|
themeName,
|
||||||
"indexHtmlCode": fs.readFileSync(pathJoin(buildOptions.reactAppBuildDirPath, "index.html")).toString("utf8"),
|
indexHtmlCode: fs
|
||||||
|
.readFileSync(pathJoin(buildOptions.reactAppBuildDirPath, "index.html"))
|
||||||
|
.toString("utf8"),
|
||||||
cssGlobalsToDefine,
|
cssGlobalsToDefine,
|
||||||
buildOptions,
|
buildOptions,
|
||||||
"keycloakifyVersion": readThisNpmPackageVersion(),
|
keycloakifyVersion: readThisNpmPackageVersion(),
|
||||||
themeType,
|
themeType,
|
||||||
"fieldNames": readFieldNameUsage({
|
fieldNames: readFieldNameUsage({
|
||||||
themeSrcDirPath,
|
themeSrcDirPath,
|
||||||
themeType
|
themeType
|
||||||
})
|
})
|
||||||
@ -163,9 +197,12 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
].forEach(pageId => {
|
].forEach(pageId => {
|
||||||
const { ftlCode } = generateFtlFilesCode({ pageId });
|
const { ftlCode } = generateFtlFilesCode({ pageId });
|
||||||
|
|
||||||
fs.mkdirSync(themeTypeDirPath, { "recursive": true });
|
fs.mkdirSync(themeTypeDirPath, { recursive: true });
|
||||||
|
|
||||||
fs.writeFileSync(pathJoin(themeTypeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
|
fs.writeFileSync(
|
||||||
|
pathJoin(themeTypeDirPath, pageId),
|
||||||
|
Buffer.from(ftlCode, "utf8")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
generateMessageProperties({
|
generateMessageProperties({
|
||||||
@ -174,15 +211,23 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
}).forEach(({ languageTag, propertiesFileSource }) => {
|
}).forEach(({ languageTag, propertiesFileSource }) => {
|
||||||
const messagesDirPath = pathJoin(themeTypeDirPath, "messages");
|
const messagesDirPath = pathJoin(themeTypeDirPath, "messages");
|
||||||
|
|
||||||
fs.mkdirSync(pathJoin(themeTypeDirPath, "messages"), { "recursive": true });
|
fs.mkdirSync(pathJoin(themeTypeDirPath, "messages"), {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
|
||||||
const propertiesFilePath = pathJoin(messagesDirPath, `messages_${languageTag}.properties`);
|
const propertiesFilePath = pathJoin(
|
||||||
|
messagesDirPath,
|
||||||
|
`messages_${languageTag}.properties`
|
||||||
|
);
|
||||||
|
|
||||||
fs.writeFileSync(propertiesFilePath, Buffer.from(propertiesFileSource, "utf8"));
|
fs.writeFileSync(
|
||||||
|
propertiesFilePath,
|
||||||
|
Buffer.from(propertiesFileSource, "utf8")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await downloadKeycloakStaticResources({
|
await downloadKeycloakStaticResources({
|
||||||
"keycloakVersion": (() => {
|
keycloakVersion: (() => {
|
||||||
switch (themeType) {
|
switch (themeType) {
|
||||||
case "account":
|
case "account":
|
||||||
return lastKeycloakVersionWithAccountV1;
|
return lastKeycloakVersionWithAccountV1;
|
||||||
@ -190,7 +235,7 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
"themeDirPath": pathResolve(pathJoin(themeTypeDirPath, "..")),
|
themeDirPath: pathResolve(pathJoin(themeTypeDirPath, "..")),
|
||||||
themeType,
|
themeType,
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
@ -225,8 +270,8 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
implementedThemeTypes.email = true;
|
implementedThemeTypes.email = true;
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": emailThemeSrcDirPath,
|
srcDirPath: emailThemeSrcDirPath,
|
||||||
"destDirPath": getThemeTypeDirPath({ "themeType": "email" })
|
destDirPath: getThemeTypeDirPath({ themeType: "email" })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,24 +282,24 @@ export async function generateSrcMainResourcesForMainTheme(params: { themeName:
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const metaInfKeycloakThemes: MetaInfKeycloakTheme = { "themes": [] };
|
const metaInfKeycloakThemes: MetaInfKeycloakTheme = { themes: [] };
|
||||||
|
|
||||||
metaInfKeycloakThemes.themes.push({
|
metaInfKeycloakThemes.themes.push({
|
||||||
"name": themeName,
|
name: themeName,
|
||||||
"types": objectEntries(implementedThemeTypes)
|
types: objectEntries(implementedThemeTypes)
|
||||||
.filter(([, isImplemented]) => isImplemented)
|
.filter(([, isImplemented]) => isImplemented)
|
||||||
.map(([themeType]) => themeType)
|
.map(([themeType]) => themeType)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (implementedThemeTypes.account) {
|
if (implementedThemeTypes.account) {
|
||||||
metaInfKeycloakThemes.themes.push({
|
metaInfKeycloakThemes.themes.push({
|
||||||
"name": accountV1ThemeName,
|
name: accountV1ThemeName,
|
||||||
"types": ["account"]
|
types: ["account"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
writeMetaInfKeycloakThemes({
|
writeMetaInfKeycloakThemes({
|
||||||
"keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath,
|
keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||||
metaInfKeycloakThemes
|
metaInfKeycloakThemes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
|
import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
|
||||||
import { transformCodebase } from "../../tools/transformCodebase";
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
import type { BuildOptions } from "../../shared/buildOptions";
|
import type { BuildOptions } from "../../shared/buildOptions";
|
||||||
import { readMetaInfKeycloakThemes, writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
import {
|
||||||
|
readMetaInfKeycloakThemes,
|
||||||
|
writeMetaInfKeycloakThemes
|
||||||
|
} from "../../shared/metaInfKeycloakThemes";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
@ -10,20 +13,37 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export function generateSrcMainResourcesForThemeVariant(params: { themeName: string; themeVariantName: string; buildOptions: BuildOptionsLike }) {
|
export function generateSrcMainResourcesForThemeVariant(params: {
|
||||||
|
themeName: string;
|
||||||
|
themeVariantName: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}) {
|
||||||
const { themeName, themeVariantName, buildOptions } = params;
|
const { themeName, themeVariantName, buildOptions } = params;
|
||||||
|
|
||||||
const mainThemeDirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", themeName);
|
const mainThemeDirPath = pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"theme",
|
||||||
|
themeName
|
||||||
|
);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": mainThemeDirPath,
|
srcDirPath: mainThemeDirPath,
|
||||||
"destDirPath": pathJoin(mainThemeDirPath, "..", themeVariantName),
|
destDirPath: pathJoin(mainThemeDirPath, "..", themeVariantName),
|
||||||
"transformSourceCode": ({ fileRelativePath, sourceCode }) => {
|
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||||
if (pathExtname(fileRelativePath) === ".ftl" && fileRelativePath.split(pathSep).length === 2) {
|
if (
|
||||||
|
pathExtname(fileRelativePath) === ".ftl" &&
|
||||||
|
fileRelativePath.split(pathSep).length === 2
|
||||||
|
) {
|
||||||
const modifiedSourceCode = Buffer.from(
|
const modifiedSourceCode = Buffer.from(
|
||||||
Buffer.from(sourceCode)
|
Buffer.from(sourceCode)
|
||||||
.toString("utf-8")
|
.toString("utf-8")
|
||||||
.replace(`out["themeName"] = "${themeName}";`, `out["themeName"] = "${themeVariantName}";`),
|
.replace(
|
||||||
|
`out["themeName"] = "${themeName}";`,
|
||||||
|
`out["themeName"] = "${themeVariantName}";`
|
||||||
|
),
|
||||||
"utf8"
|
"utf8"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -32,25 +52,29 @@ export function generateSrcMainResourcesForThemeVariant(params: { themeName: str
|
|||||||
return { modifiedSourceCode };
|
return { modifiedSourceCode };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const updatedMetaInfKeycloakThemes = readMetaInfKeycloakThemes({ "keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath });
|
const updatedMetaInfKeycloakThemes = readMetaInfKeycloakThemes({
|
||||||
|
keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath
|
||||||
|
});
|
||||||
|
|
||||||
updatedMetaInfKeycloakThemes.themes.push({
|
updatedMetaInfKeycloakThemes.themes.push({
|
||||||
"name": themeVariantName,
|
name: themeVariantName,
|
||||||
"types": (() => {
|
types: (() => {
|
||||||
const theme = updatedMetaInfKeycloakThemes.themes.find(({ name }) => name === themeName);
|
const theme = updatedMetaInfKeycloakThemes.themes.find(
|
||||||
|
({ name }) => name === themeName
|
||||||
|
);
|
||||||
assert(theme !== undefined);
|
assert(theme !== undefined);
|
||||||
return theme.types;
|
return theme.types;
|
||||||
})()
|
})()
|
||||||
});
|
});
|
||||||
|
|
||||||
writeMetaInfKeycloakThemes({
|
writeMetaInfKeycloakThemes({
|
||||||
"keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath,
|
keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||||
"metaInfKeycloakThemes": updatedMetaInfKeycloakThemes
|
metaInfKeycloakThemes: updatedMetaInfKeycloakThemes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,26 @@ import { id } from "tsafe/id";
|
|||||||
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { type ThemeType, accountThemePageIds, loginThemePageIds } from "../../shared/constants";
|
import {
|
||||||
|
type ThemeType,
|
||||||
|
accountThemePageIds,
|
||||||
|
loginThemePageIds
|
||||||
|
} from "../../shared/constants";
|
||||||
|
|
||||||
export function readExtraPagesNames(params: { themeSrcDirPath: string; themeType: ThemeType }): string[] {
|
export function readExtraPagesNames(params: {
|
||||||
|
themeSrcDirPath: string;
|
||||||
|
themeType: ThemeType;
|
||||||
|
}): string[] {
|
||||||
const { themeSrcDirPath, themeType } = params;
|
const { themeSrcDirPath, themeType } = params;
|
||||||
|
|
||||||
const filePaths = crawl({
|
const filePaths = crawl({
|
||||||
"dirPath": pathJoin(themeSrcDirPath, themeType),
|
dirPath: pathJoin(themeSrcDirPath, themeType),
|
||||||
"returnedPathsType": "absolute"
|
returnedPathsType: "absolute"
|
||||||
}).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
|
}).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
|
||||||
|
|
||||||
const candidateFilePaths = filePaths.filter(filePath => /kcContext\.[^.]+$/.test(filePath));
|
const candidateFilePaths = filePaths.filter(filePath =>
|
||||||
|
/kcContext\.[^.]+$/.test(filePath)
|
||||||
|
);
|
||||||
|
|
||||||
if (candidateFilePaths.length === 0) {
|
if (candidateFilePaths.length === 0) {
|
||||||
candidateFilePaths.push(...filePaths);
|
candidateFilePaths.push(...filePaths);
|
||||||
@ -24,7 +33,12 @@ export function readExtraPagesNames(params: { themeSrcDirPath: string; themeType
|
|||||||
for (const candidateFilPath of candidateFilePaths) {
|
for (const candidateFilPath of candidateFilePaths) {
|
||||||
const rawSourceFile = fs.readFileSync(candidateFilPath).toString("utf8");
|
const rawSourceFile = fs.readFileSync(candidateFilPath).toString("utf8");
|
||||||
|
|
||||||
extraPages.push(...Array.from(rawSourceFile.matchAll(/["']?pageId["']?\s*:\s*["']([^.]+.ftl)["']/g), m => m[1]));
|
extraPages.push(
|
||||||
|
...Array.from(
|
||||||
|
rawSourceFile.matchAll(/["']?pageId["']?\s*:\s*["']([^.]+.ftl)["']/g),
|
||||||
|
m => m[1]
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return extraPages.reduce(...removeDuplicates<string>()).filter(pageId => {
|
return extraPages.reduce(...removeDuplicates<string>()).filter(pageId => {
|
||||||
|
@ -5,13 +5,22 @@ import type { ThemeType } from "../../shared/constants";
|
|||||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||||
|
|
||||||
/** Assumes the theme type exists */
|
/** Assumes the theme type exists */
|
||||||
export function readFieldNameUsage(params: { themeSrcDirPath: string; themeType: ThemeType }): string[] {
|
export function readFieldNameUsage(params: {
|
||||||
|
themeSrcDirPath: string;
|
||||||
|
themeType: ThemeType;
|
||||||
|
}): string[] {
|
||||||
const { themeSrcDirPath, themeType } = params;
|
const { themeSrcDirPath, themeType } = params;
|
||||||
|
|
||||||
const fieldNames = new Set<string>();
|
const fieldNames = new Set<string>();
|
||||||
|
|
||||||
for (const srcDirPath of [pathJoin(getThisCodebaseRootDirPath(), "src", themeType), pathJoin(themeSrcDirPath, themeType)]) {
|
for (const srcDirPath of [
|
||||||
const filePaths = crawl({ "dirPath": srcDirPath, "returnedPathsType": "absolute" }).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
|
pathJoin(getThisCodebaseRootDirPath(), "src", themeType),
|
||||||
|
pathJoin(themeSrcDirPath, themeType)
|
||||||
|
]) {
|
||||||
|
const filePaths = crawl({
|
||||||
|
dirPath: srcDirPath,
|
||||||
|
returnedPathsType: "absolute"
|
||||||
|
}).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
|
||||||
|
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
const rawSourceFile = fs.readFileSync(filePath).toString("utf8");
|
const rawSourceFile = fs.readFileSync(filePath).toString("utf8");
|
||||||
@ -20,7 +29,13 @@ export function readFieldNameUsage(params: { themeSrcDirPath: string; themeType:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const functionName of ["printIfExists", "existsError", "get", "exists", "getFirstError"] as const) {
|
for (const functionName of [
|
||||||
|
"printIfExists",
|
||||||
|
"existsError",
|
||||||
|
"get",
|
||||||
|
"exists",
|
||||||
|
"getFirstError"
|
||||||
|
] as const) {
|
||||||
if (!rawSourceFile.includes(functionName)) {
|
if (!rawSourceFile.includes(functionName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -40,9 +55,21 @@ export function readFieldNameUsage(params: { themeSrcDirPath: string; themeType:
|
|||||||
return part
|
return part
|
||||||
.split(",")
|
.split(",")
|
||||||
.map(a => a.trim())
|
.map(a => a.trim())
|
||||||
.filter((...[, i]) => (functionName !== "printIfExists" ? true : i === 0))
|
.filter((...[, i]) =>
|
||||||
.filter(a => a.startsWith('"') || a.startsWith("'") || a.startsWith("`"))
|
functionName !== "printIfExists" ? true : i === 0
|
||||||
.filter(a => a.endsWith('"') || a.endsWith("'") || a.endsWith("`"))
|
)
|
||||||
|
.filter(
|
||||||
|
a =>
|
||||||
|
a.startsWith('"') ||
|
||||||
|
a.startsWith("'") ||
|
||||||
|
a.startsWith("`")
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
a =>
|
||||||
|
a.endsWith('"') ||
|
||||||
|
a.endsWith("'") ||
|
||||||
|
a.endsWith("`")
|
||||||
|
)
|
||||||
.map(a => a.slice(1).slice(0, -1));
|
.map(a => a.slice(1).slice(0, -1));
|
||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
import {
|
||||||
|
join as pathJoin,
|
||||||
|
relative as pathRelative,
|
||||||
|
basename as pathBasename
|
||||||
|
} from "path";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../shared/buildOptions";
|
import type { BuildOptions } from "../shared/buildOptions";
|
||||||
import { accountV1ThemeName } from "../shared/constants";
|
import { accountV1ThemeName } from "../shared/constants";
|
||||||
@ -27,7 +31,10 @@ export function generateStartKeycloakTestingContainer(params: {
|
|||||||
const themeRelativeDirPath = pathJoin("src", "main", "resources", "theme");
|
const themeRelativeDirPath = pathJoin("src", "main", "resources", "theme");
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
pathJoin(buildOptions.keycloakifyBuildDirPath, generateStartKeycloakTestingContainer.basename),
|
pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
generateStartKeycloakTestingContainer.basename
|
||||||
|
),
|
||||||
Buffer.from(
|
Buffer.from(
|
||||||
[
|
[
|
||||||
"#!/usr/bin/env bash",
|
"#!/usr/bin/env bash",
|
||||||
@ -45,9 +52,16 @@ export function generateStartKeycloakTestingContainer(params: {
|
|||||||
"$(pwd)",
|
"$(pwd)",
|
||||||
pathRelative(buildOptions.keycloakifyBuildDirPath, jarFilePath)
|
pathRelative(buildOptions.keycloakifyBuildDirPath, jarFilePath)
|
||||||
)}":"/opt/keycloak/providers/${pathBasename(jarFilePath)}" \\`,
|
)}":"/opt/keycloak/providers/${pathBasename(jarFilePath)}" \\`,
|
||||||
[...(doesImplementAccountTheme ? [accountV1ThemeName] : []), ...buildOptions.themeNames].map(
|
[
|
||||||
|
...(doesImplementAccountTheme ? [accountV1ThemeName] : []),
|
||||||
|
...buildOptions.themeNames
|
||||||
|
].map(
|
||||||
themeName =>
|
themeName =>
|
||||||
` -v "${pathJoin("$(pwd)", themeRelativeDirPath, themeName).replace(/\\/g, "/")}":"/opt/keycloak/themes/${themeName}":rw \\`
|
` -v "${pathJoin(
|
||||||
|
"$(pwd)",
|
||||||
|
themeRelativeDirPath,
|
||||||
|
themeName
|
||||||
|
).replace(/\\/g, "/")}":"/opt/keycloak/themes/${themeName}":rw \\`
|
||||||
),
|
),
|
||||||
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
||||||
` start-dev`,
|
` start-dev`,
|
||||||
@ -55,6 +69,6 @@ export function generateStartKeycloakTestingContainer(params: {
|
|||||||
].join("\n"),
|
].join("\n"),
|
||||||
"utf8"
|
"utf8"
|
||||||
),
|
),
|
||||||
{ "mode": 0o755 }
|
{ mode: 0o755 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
let commandOutput: Buffer | undefined = undefined;
|
let commandOutput: Buffer | undefined = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
commandOutput = child_process.execSync("mvn --version", { "stdio": ["ignore", "pipe", "ignore"] });
|
commandOutput = child_process.execSync("mvn --version", {
|
||||||
|
stdio: ["ignore", "pipe", "ignore"]
|
||||||
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
if (commandOutput?.toString("utf8").includes("Apache Maven")) {
|
if (commandOutput?.toString("utf8").includes("Apache Maven")) {
|
||||||
@ -34,7 +36,11 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
console.log(`${chalk.red("Apache Maven required.")} Install it with \`${chalk.bold(installationCommand)}\` (for example)`);
|
console.log(
|
||||||
|
`${chalk.red("Apache Maven required.")} Install it with \`${chalk.bold(
|
||||||
|
installationCommand
|
||||||
|
)}\` (for example)`
|
||||||
|
);
|
||||||
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -46,7 +52,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
console.log(
|
console.log(
|
||||||
[
|
[
|
||||||
chalk.cyan(`keycloakify v${readThisNpmPackageVersion()}`),
|
chalk.cyan(`keycloakify v${readThisNpmPackageVersion()}`),
|
||||||
chalk.green(`Building the keycloak theme in .${pathSep}${pathRelative(process.cwd(), buildOptions.keycloakifyBuildDirPath)} ...`)
|
chalk.green(
|
||||||
|
`Building the keycloak theme in .${pathSep}${pathRelative(
|
||||||
|
process.cwd(),
|
||||||
|
buildOptions.keycloakifyBuildDirPath
|
||||||
|
)} ...`
|
||||||
|
)
|
||||||
].join(" ")
|
].join(" ")
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -54,10 +65,15 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
{
|
{
|
||||||
if (!fs.existsSync(buildOptions.keycloakifyBuildDirPath)) {
|
if (!fs.existsSync(buildOptions.keycloakifyBuildDirPath)) {
|
||||||
fs.mkdirSync(buildOptions.keycloakifyBuildDirPath, { "recursive": true });
|
fs.mkdirSync(buildOptions.keycloakifyBuildDirPath, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, ".gitignore"), Buffer.from("*", "utf8"));
|
fs.writeFileSync(
|
||||||
|
pathJoin(buildOptions.keycloakifyBuildDirPath, ".gitignore"),
|
||||||
|
Buffer.from("*", "utf8")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await generateSrcMainResources({ buildOptions });
|
await generateSrcMainResources({ buildOptions });
|
||||||
@ -68,10 +84,11 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
}
|
}
|
||||||
|
|
||||||
child_process.execSync("npx vite", {
|
child_process.execSync("npx vite", {
|
||||||
"cwd": buildOptions.reactAppRootDirPath,
|
cwd: buildOptions.reactAppRootDirPath,
|
||||||
"env": {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
[vitePluginSubScriptEnvNames.runPostBuildScript]: JSON.stringify(buildOptions)
|
[vitePluginSubScriptEnvNames.runPostBuildScript]:
|
||||||
|
JSON.stringify(buildOptions)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -84,5 +101,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
await buildJars({ buildOptions });
|
await buildJars({ buildOptions });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`));
|
console.log(
|
||||||
|
chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,15 @@ export function replaceImportsInCssCode(params: { cssCode: string }): {
|
|||||||
const cssGlobalsToDefine: Record<string, string> = {};
|
const cssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*?/g) ?? []).forEach(
|
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*?/g) ?? []).forEach(
|
||||||
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match)
|
match =>
|
||||||
|
(cssGlobalsToDefine[
|
||||||
|
"url" +
|
||||||
|
crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(match)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 15)
|
||||||
|
] = match)
|
||||||
);
|
);
|
||||||
|
|
||||||
let fixedCssCode = cssCode;
|
let fixedCssCode = cssCode;
|
||||||
@ -26,26 +34,37 @@ export function replaceImportsInCssCode(params: { cssCode: string }): {
|
|||||||
Object.keys(cssGlobalsToDefine).forEach(
|
Object.keys(cssGlobalsToDefine).forEach(
|
||||||
cssVariableName =>
|
cssVariableName =>
|
||||||
//NOTE: split/join pattern ~ replace all
|
//NOTE: split/join pattern ~ replace all
|
||||||
(fixedCssCode = fixedCssCode.split(cssGlobalsToDefine[cssVariableName]).join(`var(--${cssVariableName})`))
|
(fixedCssCode = fixedCssCode
|
||||||
|
.split(cssGlobalsToDefine[cssVariableName])
|
||||||
|
.join(`var(--${cssVariableName})`))
|
||||||
);
|
);
|
||||||
|
|
||||||
return { fixedCssCode, cssGlobalsToDefine };
|
return { fixedCssCode, cssGlobalsToDefine };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Record<string, string>; buildOptions: BuildOptionsLike }): {
|
export function generateCssCodeToDefineGlobals(params: {
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): {
|
||||||
cssCodeToPrependInHead: string;
|
cssCodeToPrependInHead: string;
|
||||||
} {
|
} {
|
||||||
const { cssGlobalsToDefine, buildOptions } = params;
|
const { cssGlobalsToDefine, buildOptions } = params;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"cssCodeToPrependInHead": [
|
cssCodeToPrependInHead: [
|
||||||
":root {",
|
":root {",
|
||||||
...Object.keys(cssGlobalsToDefine)
|
...Object.keys(cssGlobalsToDefine)
|
||||||
.map(cssVariableName =>
|
.map(cssVariableName =>
|
||||||
[
|
[
|
||||||
`--${cssVariableName}:`,
|
`--${cssVariableName}:`,
|
||||||
cssGlobalsToDefine[cssVariableName].replace(
|
cssGlobalsToDefine[cssVariableName].replace(
|
||||||
new RegExp(`url\\(${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`, "g"),
|
new RegExp(
|
||||||
|
`url\\(${(buildOptions.urlPathname ?? "/").replace(
|
||||||
|
/\//g,
|
||||||
|
"\\/"
|
||||||
|
)}`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
`url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
`url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
||||||
)
|
)
|
||||||
].join(" ")
|
].join(" ")
|
||||||
|
@ -8,7 +8,10 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOptions: BuildOptionsLike }): {
|
export function replaceImportsInInlineCssCode(params: {
|
||||||
|
cssCode: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): {
|
||||||
fixedCssCode: string;
|
fixedCssCode: string;
|
||||||
} {
|
} {
|
||||||
const { cssCode, buildOptions } = params;
|
const { cssCode, buildOptions } = params;
|
||||||
@ -17,7 +20,8 @@ export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOp
|
|||||||
buildOptions.urlPathname === undefined
|
buildOptions.urlPathname === undefined
|
||||||
? /url\(["']?\/([^/][^)"']+)["']?\)/g
|
? /url\(["']?\/([^/][^)"']+)["']?\)/g
|
||||||
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
|
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
|
||||||
(...[, group]) => `url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/${group})`
|
(...[, group]) =>
|
||||||
|
`url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/${group})`
|
||||||
);
|
);
|
||||||
|
|
||||||
return { fixedCssCode };
|
return { fixedCssCode };
|
||||||
|
@ -13,7 +13,10 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export function replaceImportsInJsCode(params: { jsCode: string; buildOptions: BuildOptionsLike }) {
|
export function replaceImportsInJsCode(params: {
|
||||||
|
jsCode: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}) {
|
||||||
const { jsCode, buildOptions } = params;
|
const { jsCode, buildOptions } = params;
|
||||||
|
|
||||||
const { fixedJsCode } = (() => {
|
const { fixedJsCode } = (() => {
|
||||||
@ -22,8 +25,8 @@ export function replaceImportsInJsCode(params: { jsCode: string; buildOptions: B
|
|||||||
return replaceImportsInJsCode_vite({
|
return replaceImportsInJsCode_vite({
|
||||||
jsCode,
|
jsCode,
|
||||||
buildOptions,
|
buildOptions,
|
||||||
"basenameOfAssetsFiles": readAssetsDirSync({
|
basenameOfAssetsFiles: readAssetsDirSync({
|
||||||
"assetsDirPath": params.buildOptions.assetsDirPath
|
assetsDirPath: params.buildOptions.assetsDirPath
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
case "webpack":
|
case "webpack":
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
import {
|
||||||
|
nameOfTheGlobal,
|
||||||
|
basenameOfTheKeycloakifyResourcesDir
|
||||||
|
} from "../../../shared/constants";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../../../shared/buildOptions";
|
import type { BuildOptions } from "../../../shared/buildOptions";
|
||||||
import * as nodePath from "path";
|
import * as nodePath from "path";
|
||||||
@ -20,7 +23,12 @@ export function replaceImportsInJsCode_vite(params: {
|
|||||||
}): {
|
}): {
|
||||||
fixedJsCode: string;
|
fixedJsCode: string;
|
||||||
} {
|
} {
|
||||||
const { jsCode, buildOptions, basenameOfAssetsFiles, systemType = nodePath.sep === "/" ? "posix" : "win32" } = params;
|
const {
|
||||||
|
jsCode,
|
||||||
|
buildOptions,
|
||||||
|
basenameOfAssetsFiles,
|
||||||
|
systemType = nodePath.sep === "/" ? "posix" : "win32"
|
||||||
|
} = params;
|
||||||
|
|
||||||
const { relative: pathRelative, sep: pathSep } = nodePath[systemType];
|
const { relative: pathRelative, sep: pathSep } = nodePath[systemType];
|
||||||
|
|
||||||
@ -38,22 +46,32 @@ export function replaceImportsInJsCode_vite(params: {
|
|||||||
// Replace `Hv=function(e){return"/abcde12345/"+e}` by `Hv=function(e){return"/"+e}`
|
// Replace `Hv=function(e){return"/abcde12345/"+e}` by `Hv=function(e){return"/"+e}`
|
||||||
fixedJsCode = fixedJsCode.replace(
|
fixedJsCode = fixedJsCode.replace(
|
||||||
new RegExp(
|
new RegExp(
|
||||||
`([\\w\\$][\\w\\d\\$]*)=function\\(([\\w\\$][\\w\\d\\$]*)\\)\\{return"${replaceAll(buildOptions.urlPathname, "/", "\\/")}"\\+\\2\\}`,
|
`([\\w\\$][\\w\\d\\$]*)=function\\(([\\w\\$][\\w\\d\\$]*)\\)\\{return"${replaceAll(
|
||||||
|
buildOptions.urlPathname,
|
||||||
|
"/",
|
||||||
|
"\\/"
|
||||||
|
)}"\\+\\2\\}`,
|
||||||
"g"
|
"g"
|
||||||
),
|
),
|
||||||
(...[, funcName, paramName]) => `${funcName}=function(${paramName}){return"/"+${paramName}}`
|
(...[, funcName, paramName]) =>
|
||||||
|
`${funcName}=function(${paramName}){return"/"+${paramName}}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
replace_javascript_relatives_import_paths: {
|
replace_javascript_relatives_import_paths: {
|
||||||
// Example: "assets/ or "foo/bar/"
|
// Example: "assets/ or "foo/bar/"
|
||||||
const staticDir = (() => {
|
const staticDir = (() => {
|
||||||
let out = pathRelative(buildOptions.reactAppBuildDirPath, buildOptions.assetsDirPath);
|
let out = pathRelative(
|
||||||
|
buildOptions.reactAppBuildDirPath,
|
||||||
|
buildOptions.assetsDirPath
|
||||||
|
);
|
||||||
|
|
||||||
out = replaceAll(out, pathSep, "/") + "/";
|
out = replaceAll(out, pathSep, "/") + "/";
|
||||||
|
|
||||||
if (out === "/") {
|
if (out === "/") {
|
||||||
throw new Error(`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`);
|
throw new Error(
|
||||||
|
`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
import {
|
||||||
|
nameOfTheGlobal,
|
||||||
|
basenameOfTheKeycloakifyResourcesDir
|
||||||
|
} from "../../../shared/constants";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../../../shared/buildOptions";
|
import type { BuildOptions } from "../../../shared/buildOptions";
|
||||||
import * as nodePath from "path";
|
import * as nodePath from "path";
|
||||||
@ -12,10 +15,18 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOptions: BuildOptionsLike; systemType?: "posix" | "win32" }): {
|
export function replaceImportsInJsCode_webpack(params: {
|
||||||
|
jsCode: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
systemType?: "posix" | "win32";
|
||||||
|
}): {
|
||||||
fixedJsCode: string;
|
fixedJsCode: string;
|
||||||
} {
|
} {
|
||||||
const { jsCode, buildOptions, systemType = nodePath.sep === "/" ? "posix" : "win32" } = params;
|
const {
|
||||||
|
jsCode,
|
||||||
|
buildOptions,
|
||||||
|
systemType = nodePath.sep === "/" ? "posix" : "win32"
|
||||||
|
} = params;
|
||||||
|
|
||||||
const { relative: pathRelative, sep: pathSep } = nodePath[systemType];
|
const { relative: pathRelative, sep: pathSep } = nodePath[systemType];
|
||||||
|
|
||||||
@ -24,29 +35,51 @@ export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOp
|
|||||||
if (buildOptions.urlPathname !== undefined) {
|
if (buildOptions.urlPathname !== undefined) {
|
||||||
// "__esModule",{value:!0})},n.p="/foo-bar/",function(){if("undefined" -> ... n.p="/" ...
|
// "__esModule",{value:!0})},n.p="/foo-bar/",function(){if("undefined" -> ... n.p="/" ...
|
||||||
fixedJsCode = fixedJsCode.replace(
|
fixedJsCode = fixedJsCode.replace(
|
||||||
new RegExp(`,([a-zA-Z]\\.[a-zA-Z])="${replaceAll(buildOptions.urlPathname, "/", "\\/")}",`, "g"),
|
new RegExp(
|
||||||
|
`,([a-zA-Z]\\.[a-zA-Z])="${replaceAll(
|
||||||
|
buildOptions.urlPathname,
|
||||||
|
"/",
|
||||||
|
"\\/"
|
||||||
|
)}",`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
(...[, assignTo]) => `,${assignTo}="/",`
|
(...[, assignTo]) => `,${assignTo}="/",`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example: "static/ or "foo/bar/"
|
// Example: "static/ or "foo/bar/"
|
||||||
const staticDir = (() => {
|
const staticDir = (() => {
|
||||||
let out = pathRelative(buildOptions.reactAppBuildDirPath, buildOptions.assetsDirPath);
|
let out = pathRelative(
|
||||||
|
buildOptions.reactAppBuildDirPath,
|
||||||
|
buildOptions.assetsDirPath
|
||||||
|
);
|
||||||
|
|
||||||
out = replaceAll(out, pathSep, "/") + "/";
|
out = replaceAll(out, pathSep, "/") + "/";
|
||||||
|
|
||||||
if (out === "/") {
|
if (out === "/") {
|
||||||
throw new Error(`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`);
|
throw new Error(
|
||||||
|
`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
|
const getReplaceArgs = (
|
||||||
new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=(function\\(([a-z]+)\\){return|([a-z]+)=>)"${staticDir.replace(/\//g, "\\/")}${language}\\/"`, "g"),
|
language: "js" | "css"
|
||||||
|
): Parameters<typeof String.prototype.replace> => [
|
||||||
|
new RegExp(
|
||||||
|
`([a-zA-Z_]+)\\.([a-zA-Z]+)=(function\\(([a-z]+)\\){return|([a-z]+)=>)"${staticDir.replace(
|
||||||
|
/\//g,
|
||||||
|
"\\/"
|
||||||
|
)}${language}\\/"`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
(...[, n, u, matchedFunction, eForFunction]) => {
|
(...[, n, u, matchedFunction, eForFunction]) => {
|
||||||
const isArrowFunction = matchedFunction.includes("=>");
|
const isArrowFunction = matchedFunction.includes("=>");
|
||||||
const e = isArrowFunction ? matchedFunction.replace("=>", "").trim() : eForFunction;
|
const e = isArrowFunction
|
||||||
|
? matchedFunction.replace("=>", "").trim()
|
||||||
|
: eForFunction;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
${n}[(function(){
|
${n}[(function(){
|
||||||
@ -58,7 +91,9 @@ export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOp
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return "${u}";
|
return "${u}";
|
||||||
})()] = ${isArrowFunction ? `${e} =>` : `function(${e}) { return `} "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}${language}/"`
|
})()] = ${
|
||||||
|
isArrowFunction ? `${e} =>` : `function(${e}) { return `
|
||||||
|
} "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}${language}/"`
|
||||||
.replace(/\s+/g, " ")
|
.replace(/\s+/g, " ")
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
@ -68,7 +103,10 @@ export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOp
|
|||||||
.replace(...getReplaceArgs("js"))
|
.replace(...getReplaceArgs("js"))
|
||||||
.replace(...getReplaceArgs("css"))
|
.replace(...getReplaceArgs("css"))
|
||||||
.replace(
|
.replace(
|
||||||
new RegExp(`[a-zA-Z]+\\.[a-zA-Z]+\\+"${staticDir.replace(/\//g, "\\/")}`, "g"),
|
new RegExp(
|
||||||
|
`[a-zA-Z]+\\.[a-zA-Z]+\\+"${staticDir.replace(/\//g, "\\/")}`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
`window.${nameOfTheGlobal}.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}`
|
`window.${nameOfTheGlobal}.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
109
src/bin/main.ts
109
src/bin/main.ts
@ -10,12 +10,12 @@ export type CliCommandOptions = {
|
|||||||
|
|
||||||
const program = termost<CliCommandOptions>(
|
const program = termost<CliCommandOptions>(
|
||||||
{
|
{
|
||||||
"name": "keycloakify",
|
name: "keycloakify",
|
||||||
"description": "Keycloakify CLI",
|
description: "Keycloakify CLI",
|
||||||
"version": readThisNpmPackageVersion()
|
version: readThisNpmPackageVersion()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"onException": error => {
|
onException: error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -25,8 +25,8 @@ const program = termost<CliCommandOptions>(
|
|||||||
const optionsKeys: string[] = [];
|
const optionsKeys: string[] = [];
|
||||||
|
|
||||||
program.option({
|
program.option({
|
||||||
"key": "reactAppRootDirPath",
|
key: "reactAppRootDirPath",
|
||||||
"name": (() => {
|
name: (() => {
|
||||||
const long = "project";
|
const long = "project";
|
||||||
const short = "p";
|
const short = "p";
|
||||||
|
|
||||||
@ -34,19 +34,25 @@ program.option({
|
|||||||
|
|
||||||
return { long, short };
|
return { long, short };
|
||||||
})(),
|
})(),
|
||||||
"description": [
|
description: [
|
||||||
`For monorepos, path to the keycloakify project.`,
|
`For monorepos, path to the keycloakify project.`,
|
||||||
"Example: `npx keycloakify build --project packages/keycloak-theme`",
|
"Example: `npx keycloakify build --project packages/keycloak-theme`",
|
||||||
"https://docs.keycloakify.dev/build-options#project-or-p-cli-option"
|
"https://docs.keycloakify.dev/build-options#project-or-p-cli-option"
|
||||||
].join(" "),
|
].join(" "),
|
||||||
"defaultValue": undefined
|
defaultValue: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
function skip(_context: any, argv: { options: Record<string, unknown> }) {
|
function skip(_context: any, argv: { options: Record<string, unknown> }) {
|
||||||
const unrecognizedOptionKey = Object.keys(argv.options).find(key => !optionsKeys.includes(key));
|
const unrecognizedOptionKey = Object.keys(argv.options).find(
|
||||||
|
key => !optionsKeys.includes(key)
|
||||||
|
);
|
||||||
|
|
||||||
if (unrecognizedOptionKey !== undefined) {
|
if (unrecognizedOptionKey !== undefined) {
|
||||||
console.error(`keycloakify: Unrecognized option: ${unrecognizedOptionKey.length === 1 ? "-" : "--"}${unrecognizedOptionKey}`);
|
console.error(
|
||||||
|
`keycloakify: Unrecognized option: ${
|
||||||
|
unrecognizedOptionKey.length === 1 ? "-" : "--"
|
||||||
|
}${unrecognizedOptionKey}`
|
||||||
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +61,12 @@ function skip(_context: any, argv: { options: Record<string, unknown> }) {
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
"name": "build",
|
name: "build",
|
||||||
"description": "Build the theme (default subcommand)."
|
description: "Build the theme (default subcommand)."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./keycloakify");
|
const { command } = await import("./keycloakify");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -73,48 +79,55 @@ program
|
|||||||
keycloakVersion: string | undefined;
|
keycloakVersion: string | undefined;
|
||||||
realmJsonFilePath: string | undefined;
|
realmJsonFilePath: string | undefined;
|
||||||
}>({
|
}>({
|
||||||
"name": "start-keycloak",
|
name: "start-keycloak",
|
||||||
"description": "Spin up a pre configured Docker image of Keycloak to test your theme."
|
description:
|
||||||
|
"Spin up a pre configured Docker image of Keycloak to test your theme."
|
||||||
})
|
})
|
||||||
.option({
|
.option({
|
||||||
"key": "port",
|
key: "port",
|
||||||
"name": (() => {
|
name: (() => {
|
||||||
const name = "port";
|
const name = "port";
|
||||||
|
|
||||||
optionsKeys.push(name);
|
optionsKeys.push(name);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
})(),
|
})(),
|
||||||
"description": ["Keycloak server port.", "Example `--port 8085`"].join(" "),
|
description: ["Keycloak server port.", "Example `--port 8085`"].join(" "),
|
||||||
"defaultValue": 8080
|
defaultValue: 8080
|
||||||
})
|
})
|
||||||
.option({
|
.option({
|
||||||
"key": "keycloakVersion",
|
key: "keycloakVersion",
|
||||||
"name": (() => {
|
name: (() => {
|
||||||
const name = "keycloak-version";
|
const name = "keycloak-version";
|
||||||
|
|
||||||
optionsKeys.push(name);
|
optionsKeys.push(name);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
})(),
|
})(),
|
||||||
"description": ["Use a specific version of Keycloak.", "Example `--keycloak-version 21.1.1`"].join(" "),
|
description: [
|
||||||
"defaultValue": undefined
|
"Use a specific version of Keycloak.",
|
||||||
|
"Example `--keycloak-version 21.1.1`"
|
||||||
|
].join(" "),
|
||||||
|
defaultValue: undefined
|
||||||
})
|
})
|
||||||
.option({
|
.option({
|
||||||
"key": "realmJsonFilePath",
|
key: "realmJsonFilePath",
|
||||||
"name": (() => {
|
name: (() => {
|
||||||
const name = "import";
|
const name = "import";
|
||||||
|
|
||||||
optionsKeys.push(name);
|
optionsKeys.push(name);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
})(),
|
})(),
|
||||||
"defaultValue": undefined,
|
defaultValue: undefined,
|
||||||
"description": ["Import your own realm configuration file", "Example `--import path/to/myrealm-realm.json`"].join(" ")
|
description: [
|
||||||
|
"Import your own realm configuration file",
|
||||||
|
"Example `--import path/to/myrealm-realm.json`"
|
||||||
|
].join(" ")
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./start-keycloak");
|
const { command } = await import("./start-keycloak");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -123,12 +136,12 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
"name": "download-keycloak-default-theme",
|
name: "download-keycloak-default-theme",
|
||||||
"description": "Download the built-in Keycloak theme."
|
description: "Download the built-in Keycloak theme."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./download-keycloak-default-theme");
|
const { command } = await import("./download-keycloak-default-theme");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -137,12 +150,12 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
"name": "eject-page",
|
name: "eject-page",
|
||||||
"description": "Eject a Keycloak page."
|
description: "Eject a Keycloak page."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./eject-page");
|
const { command } = await import("./eject-page");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -151,12 +164,12 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
"name": "initialize-email-theme",
|
name: "initialize-email-theme",
|
||||||
"description": "Initialize an email theme."
|
description: "Initialize an email theme."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./initialize-email-theme");
|
const { command } = await import("./initialize-email-theme");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -165,12 +178,13 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
"name": "copy-keycloak-resources-to-public",
|
name: "copy-keycloak-resources-to-public",
|
||||||
"description": "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory."
|
description:
|
||||||
|
"(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory."
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
"handler": async cliCommandOptions => {
|
handler: async cliCommandOptions => {
|
||||||
const { command } = await import("./copy-keycloak-resources-to-public");
|
const { command } = await import("./copy-keycloak-resources-to-public");
|
||||||
|
|
||||||
await command({ cliCommandOptions });
|
await command({ cliCommandOptions });
|
||||||
@ -181,10 +195,17 @@ program
|
|||||||
{
|
{
|
||||||
const [, , ...rest] = process.argv;
|
const [, , ...rest] = process.argv;
|
||||||
|
|
||||||
if (rest.length === 0 || (rest[0].startsWith("-") && rest[0] !== "--help" && rest[0] !== "-h")) {
|
if (
|
||||||
const { status } = child_process.spawnSync("npx", ["keycloakify", "build", ...rest], {
|
rest.length === 0 ||
|
||||||
"stdio": "inherit"
|
(rest[0].startsWith("-") && rest[0] !== "--help" && rest[0] !== "-h")
|
||||||
});
|
) {
|
||||||
|
const { status } = child_process.spawnSync(
|
||||||
|
"npx",
|
||||||
|
["keycloakify", "build", ...rest],
|
||||||
|
{
|
||||||
|
stdio: "inherit"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
process.exit(status ?? 1);
|
process.exit(status ?? 1);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
export type KeycloakVersionRange = KeycloakVersionRange.WithAccountTheme | KeycloakVersionRange.WithoutAccountTheme;
|
export type KeycloakVersionRange =
|
||||||
|
| KeycloakVersionRange.WithAccountTheme
|
||||||
|
| KeycloakVersionRange.WithoutAccountTheme;
|
||||||
|
|
||||||
export namespace KeycloakVersionRange {
|
export namespace KeycloakVersionRange {
|
||||||
export type WithoutAccountTheme = "21-and-below" | "22-and-above";
|
export type WithoutAccountTheme = "21-and-below" | "22-and-above";
|
||||||
|
@ -49,7 +49,9 @@ export type ResolvedViteConfig = {
|
|||||||
userProvidedBuildOptions: UserProvidedBuildOptions;
|
userProvidedBuildOptions: UserProvidedBuildOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions }): BuildOptions {
|
export function readBuildOptions(params: {
|
||||||
|
cliCommandOptions: CliCommandOptions;
|
||||||
|
}): BuildOptions {
|
||||||
const { cliCommandOptions } = params;
|
const { cliCommandOptions } = params;
|
||||||
|
|
||||||
const reactAppRootDirPath = (() => {
|
const reactAppRootDirPath = (() => {
|
||||||
@ -58,29 +60,39 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": cliCommandOptions.reactAppRootDirPath,
|
pathIsh: cliCommandOptions.reactAppRootDirPath,
|
||||||
"cwd": process.cwd()
|
cwd: process.cwd()
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const { resolvedViteConfig } = (() => {
|
const { resolvedViteConfig } = (() => {
|
||||||
if (fs.readdirSync(reactAppRootDirPath).find(fileBasename => fileBasename.startsWith("vite.config")) === undefined) {
|
if (
|
||||||
return { "resolvedViteConfig": undefined };
|
fs
|
||||||
|
.readdirSync(reactAppRootDirPath)
|
||||||
|
.find(fileBasename => fileBasename.startsWith("vite.config")) ===
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
|
return { resolvedViteConfig: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = child_process
|
const output = child_process
|
||||||
.execSync("npx vite", {
|
.execSync("npx vite", {
|
||||||
"cwd": reactAppRootDirPath,
|
cwd: reactAppRootDirPath,
|
||||||
"env": {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
[vitePluginSubScriptEnvNames.resolveViteConfig]: "true"
|
[vitePluginSubScriptEnvNames.resolveViteConfig]: "true"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.toString("utf8");
|
.toString("utf8");
|
||||||
|
|
||||||
assert(output.includes(vitePluginSubScriptEnvNames.resolveViteConfig), "Seems like the Keycloakify's Vite plugin is not installed.");
|
assert(
|
||||||
|
output.includes(vitePluginSubScriptEnvNames.resolveViteConfig),
|
||||||
|
"Seems like the Keycloakify's Vite plugin is not installed."
|
||||||
|
);
|
||||||
|
|
||||||
const resolvedViteConfigStr = output.split(vitePluginSubScriptEnvNames.resolveViteConfig).reverse()[0];
|
const resolvedViteConfigStr = output
|
||||||
|
.split(vitePluginSubScriptEnvNames.resolveViteConfig)
|
||||||
|
.reverse()[0];
|
||||||
|
|
||||||
const resolvedViteConfig: ResolvedViteConfig = JSON.parse(resolvedViteConfigStr);
|
const resolvedViteConfig: ResolvedViteConfig = JSON.parse(resolvedViteConfigStr);
|
||||||
|
|
||||||
@ -92,22 +104,24 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
name: string;
|
name: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
homepage?: string;
|
homepage?: string;
|
||||||
keycloakify?: UserProvidedBuildOptions & { reactAppBuildDirPath?: string };
|
keycloakify?: UserProvidedBuildOptions & {
|
||||||
|
reactAppBuildDirPath?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const zParsedPackageJson = z.object({
|
const zParsedPackageJson = z.object({
|
||||||
"name": z.string(),
|
name: z.string(),
|
||||||
"version": z.string().optional(),
|
version: z.string().optional(),
|
||||||
"homepage": z.string().optional(),
|
homepage: z.string().optional(),
|
||||||
"keycloakify": z
|
keycloakify: z
|
||||||
.object({
|
.object({
|
||||||
"extraThemeProperties": z.array(z.string()).optional(),
|
extraThemeProperties: z.array(z.string()).optional(),
|
||||||
"artifactId": z.string().optional(),
|
artifactId: z.string().optional(),
|
||||||
"groupId": z.string().optional(),
|
groupId: z.string().optional(),
|
||||||
"loginThemeResourcesFromKeycloakVersion": z.string().optional(),
|
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
||||||
"reactAppBuildDirPath": z.string().optional(),
|
reactAppBuildDirPath: z.string().optional(),
|
||||||
"keycloakifyBuildDirPath": z.string().optional(),
|
keycloakifyBuildDirPath: z.string().optional(),
|
||||||
"themeName": z.union([z.string(), z.array(z.string())]).optional()
|
themeName: z.union([z.string(), z.array(z.string())]).optional()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
@ -119,7 +133,13 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
assert<Expected extends Got ? true : false>();
|
assert<Expected extends Got ? true : false>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return zParsedPackageJson.parse(JSON.parse(fs.readFileSync(pathJoin(reactAppRootDirPath, "package.json")).toString("utf8")));
|
return zParsedPackageJson.parse(
|
||||||
|
JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(pathJoin(reactAppRootDirPath, "package.json"))
|
||||||
|
.toString("utf8")
|
||||||
|
)
|
||||||
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const userProvidedBuildOptions: UserProvidedBuildOptions = {
|
const userProvidedBuildOptions: UserProvidedBuildOptions = {
|
||||||
@ -152,8 +172,8 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) {
|
if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": parsedPackageJson.keycloakify.reactAppBuildDirPath,
|
pathIsh: parsedPackageJson.keycloakify.reactAppBuildDirPath,
|
||||||
"cwd": reactAppRootDirPath
|
cwd: reactAppRootDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,15 +185,16 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({
|
const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({
|
||||||
reactAppRootDirPath,
|
reactAppRootDirPath,
|
||||||
"dependencyExpected": "keycloakify"
|
dependencyExpected: "keycloakify"
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"bundler": resolvedViteConfig !== undefined ? "vite" : "webpack",
|
bundler: resolvedViteConfig !== undefined ? "vite" : "webpack",
|
||||||
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
|
themeVersion:
|
||||||
|
process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
|
||||||
themeNames,
|
themeNames,
|
||||||
"extraThemeProperties": userProvidedBuildOptions.extraThemeProperties,
|
extraThemeProperties: userProvidedBuildOptions.extraThemeProperties,
|
||||||
"groupId": (() => {
|
groupId: (() => {
|
||||||
const fallbackGroupId = `${themeNames[0]}.keycloak`;
|
const fallbackGroupId = `${themeNames[0]}.keycloak`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -188,24 +209,30 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
.join(".") ?? fallbackGroupId) + ".keycloak"
|
.join(".") ?? fallbackGroupId) + ".keycloak"
|
||||||
);
|
);
|
||||||
})(),
|
})(),
|
||||||
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? userProvidedBuildOptions.artifactId ?? `${themeNames[0]}-keycloak-theme`,
|
artifactId:
|
||||||
"loginThemeResourcesFromKeycloakVersion": userProvidedBuildOptions.loginThemeResourcesFromKeycloakVersion ?? "24.0.4",
|
process.env.KEYCLOAKIFY_ARTIFACT_ID ??
|
||||||
|
userProvidedBuildOptions.artifactId ??
|
||||||
|
`${themeNames[0]}-keycloak-theme`,
|
||||||
|
loginThemeResourcesFromKeycloakVersion:
|
||||||
|
userProvidedBuildOptions.loginThemeResourcesFromKeycloakVersion ?? "24.0.4",
|
||||||
reactAppRootDirPath,
|
reactAppRootDirPath,
|
||||||
reactAppBuildDirPath,
|
reactAppBuildDirPath,
|
||||||
"keycloakifyBuildDirPath": (() => {
|
keycloakifyBuildDirPath: (() => {
|
||||||
if (userProvidedBuildOptions.keycloakifyBuildDirPath !== undefined) {
|
if (userProvidedBuildOptions.keycloakifyBuildDirPath !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": userProvidedBuildOptions.keycloakifyBuildDirPath,
|
pathIsh: userProvidedBuildOptions.keycloakifyBuildDirPath,
|
||||||
"cwd": reactAppRootDirPath
|
cwd: reactAppRootDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathJoin(
|
return pathJoin(
|
||||||
reactAppRootDirPath,
|
reactAppRootDirPath,
|
||||||
resolvedViteConfig?.buildDir === undefined ? "build_keycloak" : `${resolvedViteConfig.buildDir}_keycloak`
|
resolvedViteConfig?.buildDir === undefined
|
||||||
|
? "build_keycloak"
|
||||||
|
: `${resolvedViteConfig.buildDir}_keycloak`
|
||||||
);
|
);
|
||||||
})(),
|
})(),
|
||||||
"publicDirPath": (() => {
|
publicDirPath: (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (resolvedViteConfig !== undefined) {
|
||||||
break webpack;
|
break webpack;
|
||||||
@ -213,8 +240,8 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
if (process.env.PUBLIC_DIR_PATH !== undefined) {
|
if (process.env.PUBLIC_DIR_PATH !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": process.env.PUBLIC_DIR_PATH,
|
pathIsh: process.env.PUBLIC_DIR_PATH,
|
||||||
"cwd": reactAppRootDirPath
|
cwd: reactAppRootDirPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,13 +250,13 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
return pathJoin(reactAppRootDirPath, resolvedViteConfig.publicDir);
|
return pathJoin(reactAppRootDirPath, resolvedViteConfig.publicDir);
|
||||||
})(),
|
})(),
|
||||||
"cacheDirPath": (() => {
|
cacheDirPath: (() => {
|
||||||
const cacheDirPath = pathJoin(
|
const cacheDirPath = pathJoin(
|
||||||
(() => {
|
(() => {
|
||||||
if (process.env.XDG_CACHE_HOME !== undefined) {
|
if (process.env.XDG_CACHE_HOME !== undefined) {
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": process.env.XDG_CACHE_HOME,
|
pathIsh: process.env.XDG_CACHE_HOME,
|
||||||
"cwd": process.cwd()
|
cwd: process.cwd()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +267,7 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
return cacheDirPath;
|
return cacheDirPath;
|
||||||
})(),
|
})(),
|
||||||
"urlPathname": (() => {
|
urlPathname: (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (resolvedViteConfig !== undefined) {
|
||||||
break webpack;
|
break webpack;
|
||||||
@ -264,7 +291,7 @@ export function readBuildOptions(params: { cliCommandOptions: CliCommandOptions
|
|||||||
|
|
||||||
return resolvedViteConfig.urlPathname;
|
return resolvedViteConfig.urlPathname;
|
||||||
})(),
|
})(),
|
||||||
"assetsDirPath": (() => {
|
assetsDirPath: (() => {
|
||||||
webpack: {
|
webpack: {
|
||||||
if (resolvedViteConfig !== undefined) {
|
if (resolvedViteConfig !== undefined) {
|
||||||
break webpack;
|
break webpack;
|
||||||
|
@ -10,8 +10,8 @@ export const accountV1ThemeName = "account-v1";
|
|||||||
export type ThemeType = (typeof themeTypes)[number];
|
export type ThemeType = (typeof themeTypes)[number];
|
||||||
|
|
||||||
export const vitePluginSubScriptEnvNames = {
|
export const vitePluginSubScriptEnvNames = {
|
||||||
"runPostBuildScript": "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
|
runPostBuildScript: "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
|
||||||
"resolveViteConfig": "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
|
resolveViteConfig: "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const skipBuildJarsEnvName = "KEYCLOAKIFY_SKIP_BUILD_JAR";
|
export const skipBuildJarsEnvName = "KEYCLOAKIFY_SKIP_BUILD_JAR";
|
||||||
|
@ -3,7 +3,11 @@ import {
|
|||||||
type BuildOptionsLike as BuildOptionsLike_downloadKeycloakStaticResources
|
type BuildOptionsLike as BuildOptionsLike_downloadKeycloakStaticResources
|
||||||
} from "./downloadKeycloakStaticResources";
|
} from "./downloadKeycloakStaticResources";
|
||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
import { join as pathJoin, relative as pathRelative } from "path";
|
||||||
import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "../shared/constants";
|
import {
|
||||||
|
themeTypes,
|
||||||
|
keycloak_resources,
|
||||||
|
lastKeycloakVersionWithAccountV1
|
||||||
|
} from "../shared/constants";
|
||||||
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
@ -17,7 +21,9 @@ export type BuildOptionsLike = BuildOptionsLike_downloadKeycloakStaticResources
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export async function copyKeycloakResourcesToPublic(params: { buildOptions: BuildOptionsLike }) {
|
export async function copyKeycloakResourcesToPublic(params: {
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}) {
|
||||||
const { buildOptions } = params;
|
const { buildOptions } = params;
|
||||||
|
|
||||||
const destDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
|
const destDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
|
||||||
@ -27,11 +33,14 @@ export async function copyKeycloakResourcesToPublic(params: { buildOptions: Buil
|
|||||||
const keycloakifyBuildinfoRaw = JSON.stringify(
|
const keycloakifyBuildinfoRaw = JSON.stringify(
|
||||||
{
|
{
|
||||||
destDirPath,
|
destDirPath,
|
||||||
"keycloakifyVersion": readThisNpmPackageVersion(),
|
keycloakifyVersion: readThisNpmPackageVersion(),
|
||||||
"buildOptions": {
|
buildOptions: {
|
||||||
"loginThemeResourcesFromKeycloakVersion": readThisNpmPackageVersion(),
|
loginThemeResourcesFromKeycloakVersion: readThisNpmPackageVersion(),
|
||||||
"cacheDirPath": pathRelative(destDirPath, buildOptions.cacheDirPath),
|
cacheDirPath: pathRelative(destDirPath, buildOptions.cacheDirPath),
|
||||||
"npmWorkspaceRootDirPath": pathRelative(destDirPath, buildOptions.npmWorkspaceRootDirPath)
|
npmWorkspaceRootDirPath: pathRelative(
|
||||||
|
destDirPath,
|
||||||
|
buildOptions.npmWorkspaceRootDirPath
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
@ -43,7 +52,9 @@ export async function copyKeycloakResourcesToPublic(params: { buildOptions: Buil
|
|||||||
break skip_if_already_done;
|
break skip_if_already_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keycloakifyBuildinfoRaw_previousRun = fs.readFileSync(keycloakifyBuildinfoFilePath).toString("utf8");
|
const keycloakifyBuildinfoRaw_previousRun = fs
|
||||||
|
.readFileSync(keycloakifyBuildinfoFilePath)
|
||||||
|
.toString("utf8");
|
||||||
|
|
||||||
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
|
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
|
||||||
break skip_if_already_done;
|
break skip_if_already_done;
|
||||||
@ -52,15 +63,15 @@ export async function copyKeycloakResourcesToPublic(params: { buildOptions: Buil
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmSync(destDirPath, { "force": true, "recursive": true });
|
rmSync(destDirPath, { force: true, recursive: true });
|
||||||
|
|
||||||
fs.mkdirSync(destDirPath, { "recursive": true });
|
fs.mkdirSync(destDirPath, { recursive: true });
|
||||||
|
|
||||||
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
|
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
|
||||||
|
|
||||||
for (const themeType of themeTypes) {
|
for (const themeType of themeTypes) {
|
||||||
await downloadKeycloakStaticResources({
|
await downloadKeycloakStaticResources({
|
||||||
"keycloakVersion": (() => {
|
keycloakVersion: (() => {
|
||||||
switch (themeType) {
|
switch (themeType) {
|
||||||
case "login":
|
case "login":
|
||||||
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
return buildOptions.loginThemeResourcesFromKeycloakVersion;
|
||||||
@ -69,7 +80,7 @@ export async function copyKeycloakResourcesToPublic(params: { buildOptions: Buil
|
|||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
themeType,
|
themeType,
|
||||||
"themeDirPath": destDirPath,
|
themeDirPath: destDirPath,
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -86,5 +97,8 @@ export async function copyKeycloakResourcesToPublic(params: { buildOptions: Buil
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
fs.writeFileSync(keycloakifyBuildinfoFilePath, Buffer.from(keycloakifyBuildinfoRaw, "utf8"));
|
fs.writeFileSync(
|
||||||
|
keycloakifyBuildinfoFilePath,
|
||||||
|
Buffer.from(keycloakifyBuildinfoRaw, "utf8")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -28,22 +28,29 @@ export async function downloadAndUnzip(params: {
|
|||||||
};
|
};
|
||||||
buildOptions: BuildOptionsLike;
|
buildOptions: BuildOptionsLike;
|
||||||
}) {
|
}) {
|
||||||
const { url, destDirPath, specificDirsToExtract, preCacheTransform, buildOptions } = params;
|
const { url, destDirPath, specificDirsToExtract, preCacheTransform, buildOptions } =
|
||||||
|
params;
|
||||||
|
|
||||||
const { extractDirPath, zipFilePath } = (() => {
|
const { extractDirPath, zipFilePath } = (() => {
|
||||||
const zipFileBasenameWithoutExt = generateFileNameFromURL({
|
const zipFileBasenameWithoutExt = generateFileNameFromURL({
|
||||||
url,
|
url,
|
||||||
"preCacheTransform":
|
preCacheTransform:
|
||||||
preCacheTransform === undefined
|
preCacheTransform === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
"actionCacheId": preCacheTransform.actionCacheId,
|
actionCacheId: preCacheTransform.actionCacheId,
|
||||||
"actionFootprint": preCacheTransform.action.toString()
|
actionFootprint: preCacheTransform.action.toString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const zipFilePath = pathJoin(buildOptions.cacheDirPath, `${zipFileBasenameWithoutExt}.zip`);
|
const zipFilePath = pathJoin(
|
||||||
const extractDirPath = pathJoin(buildOptions.cacheDirPath, `tmp_unzip_${zipFileBasenameWithoutExt}`);
|
buildOptions.cacheDirPath,
|
||||||
|
`${zipFileBasenameWithoutExt}.zip`
|
||||||
|
);
|
||||||
|
const extractDirPath = pathJoin(
|
||||||
|
buildOptions.cacheDirPath,
|
||||||
|
`tmp_unzip_${zipFileBasenameWithoutExt}`
|
||||||
|
);
|
||||||
|
|
||||||
return { zipFilePath, extractDirPath };
|
return { zipFilePath, extractDirPath };
|
||||||
})();
|
})();
|
||||||
@ -55,28 +62,30 @@ export async function downloadAndUnzip(params: {
|
|||||||
|
|
||||||
const { response, isFromRemoteCache } = await (async () => {
|
const { response, isFromRemoteCache } = await (async () => {
|
||||||
const proxyFetchOptions = await getProxyFetchOptions({
|
const proxyFetchOptions = await getProxyFetchOptions({
|
||||||
"npmWorkspaceRootDirPath": buildOptions.npmWorkspaceRootDirPath
|
npmWorkspaceRootDirPath: buildOptions.npmWorkspaceRootDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://github.com/keycloakify/keycloakify/releases/download/v0.0.1/${pathBasename(zipFilePath)}`,
|
`https://github.com/keycloakify/keycloakify/releases/download/v0.0.1/${pathBasename(
|
||||||
|
zipFilePath
|
||||||
|
)}`,
|
||||||
proxyFetchOptions
|
proxyFetchOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return {
|
return {
|
||||||
response,
|
response,
|
||||||
"isFromRemoteCache": true
|
isFromRemoteCache: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"response": await fetch(url, proxyFetchOptions),
|
response: await fetch(url, proxyFetchOptions),
|
||||||
"isFromRemoteCache": false
|
isFromRemoteCache: false
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
await mkdir(pathDirname(zipFilePath), { "recursive": true });
|
await mkdir(pathDirname(zipFilePath), { recursive: true });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The correct way to fix this is to upgrade node-fetch beyond 3.2.5
|
* The correct way to fix this is to upgrade node-fetch beyond 3.2.5
|
||||||
@ -102,10 +111,13 @@ export async function downloadAndUnzip(params: {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await preCacheTransform?.action({
|
await preCacheTransform?.action({
|
||||||
"destDirPath": extractDirPath
|
destDirPath: extractDirPath
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await Promise.all([rm(extractDirPath, { "recursive": true }), unlink(zipFilePath)]);
|
await Promise.all([
|
||||||
|
rm(extractDirPath, { recursive: true }),
|
||||||
|
unlink(zipFilePath)
|
||||||
|
]);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -114,10 +126,11 @@ export async function downloadAndUnzip(params: {
|
|||||||
|
|
||||||
await zip(extractDirPath, zipFilePath);
|
await zip(extractDirPath, zipFilePath);
|
||||||
|
|
||||||
await rm(extractDirPath, { "recursive": true });
|
await rm(extractDirPath, { recursive: true });
|
||||||
|
|
||||||
upload_to_remote_cache_if_admin: {
|
upload_to_remote_cache_if_admin: {
|
||||||
const githubToken = process.env["KEYCLOAKIFY_ADMIN_GITHUB_PERSONAL_ACCESS_TOKEN"];
|
const githubToken =
|
||||||
|
process.env["KEYCLOAKIFY_ADMIN_GITHUB_PERSONAL_ACCESS_TOKEN"];
|
||||||
|
|
||||||
if (!githubToken) {
|
if (!githubToken) {
|
||||||
break upload_to_remote_cache_if_admin;
|
break upload_to_remote_cache_if_admin;
|
||||||
@ -145,7 +158,9 @@ export async function downloadAndUnzip(params: {
|
|||||||
githubToken
|
githubToken
|
||||||
]);
|
]);
|
||||||
} catch {
|
} catch {
|
||||||
console.log("upload failed, asset probably already exists in remote cache");
|
console.log(
|
||||||
|
"upload failed, asset probably already exists in remote cache"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,11 +168,11 @@ export async function downloadAndUnzip(params: {
|
|||||||
await unzip(zipFilePath, extractDirPath);
|
await unzip(zipFilePath, extractDirPath);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": extractDirPath,
|
srcDirPath: extractDirPath,
|
||||||
"destDirPath": destDirPath
|
destDirPath: destDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
await rm(extractDirPath, { "recursive": true });
|
await rm(extractDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateFileNameFromURL(params: {
|
function generateFileNameFromURL(params: {
|
||||||
@ -194,9 +209,15 @@ function generateFileNameFromURL(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize actionCacheId the same way as other components
|
// Sanitize actionCacheId the same way as other components
|
||||||
const sanitizedActionCacheId = preCacheTransform.actionCacheId.replace(/[^a-zA-Z0-9-_]/g, "_");
|
const sanitizedActionCacheId = preCacheTransform.actionCacheId.replace(
|
||||||
|
/[^a-zA-Z0-9-_]/g,
|
||||||
|
"_"
|
||||||
|
);
|
||||||
|
|
||||||
fileName += `_${sanitizedActionCacheId}_${createHash("sha256").update(preCacheTransform.actionFootprint).digest("hex").substring(0, 5)}`;
|
fileName += `_${sanitizedActionCacheId}_${createHash("sha256")
|
||||||
|
.update(preCacheTransform.actionFootprint)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 5)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileName;
|
return fileName;
|
||||||
|
@ -15,25 +15,38 @@ export type BuildOptionsLike = {
|
|||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: string; destDirPath: string; buildOptions: BuildOptionsLike }) {
|
export async function downloadKeycloakDefaultTheme(params: {
|
||||||
|
keycloakVersion: string;
|
||||||
|
destDirPath: string;
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}) {
|
||||||
const { keycloakVersion, destDirPath, buildOptions } = params;
|
const { keycloakVersion, destDirPath, buildOptions } = params;
|
||||||
|
|
||||||
await downloadAndUnzip({
|
await downloadAndUnzip({
|
||||||
destDirPath,
|
destDirPath,
|
||||||
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
url: `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
||||||
"specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
|
specificDirsToExtract: ["", "-community"].map(
|
||||||
|
ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`
|
||||||
|
),
|
||||||
buildOptions,
|
buildOptions,
|
||||||
"preCacheTransform": {
|
preCacheTransform: {
|
||||||
"actionCacheId": "npm install and build",
|
actionCacheId: "npm install and build",
|
||||||
"action": async ({ destDirPath }) => {
|
action: async ({ destDirPath }) => {
|
||||||
install_common_node_modules: {
|
install_common_node_modules: {
|
||||||
const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
|
const commonResourcesDirPath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"common",
|
||||||
|
"resources"
|
||||||
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(commonResourcesDirPath)) {
|
if (!fs.existsSync(commonResourcesDirPath)) {
|
||||||
break install_common_node_modules;
|
break install_common_node_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(pathJoin(commonResourcesDirPath, "package.json"))) {
|
if (
|
||||||
|
!fs.existsSync(pathJoin(commonResourcesDirPath, "package.json"))
|
||||||
|
) {
|
||||||
break install_common_node_modules;
|
break install_common_node_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,36 +55,67 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
}
|
}
|
||||||
|
|
||||||
child_process.execSync("npm install --omit=dev", {
|
child_process.execSync("npm install --omit=dev", {
|
||||||
"cwd": commonResourcesDirPath,
|
cwd: commonResourcesDirPath,
|
||||||
"stdio": "ignore"
|
stdio: "ignore"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
repatriate_common_resources_from_base_login_theme: {
|
repatriate_common_resources_from_base_login_theme: {
|
||||||
const baseLoginThemeResourceDir = pathJoin(destDirPath, "base", "login", "resources");
|
const baseLoginThemeResourceDir = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"base",
|
||||||
|
"login",
|
||||||
|
"resources"
|
||||||
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(baseLoginThemeResourceDir)) {
|
if (!fs.existsSync(baseLoginThemeResourceDir)) {
|
||||||
break repatriate_common_resources_from_base_login_theme;
|
break repatriate_common_resources_from_base_login_theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": baseLoginThemeResourceDir,
|
srcDirPath: baseLoginThemeResourceDir,
|
||||||
"destDirPath": pathJoin(destDirPath, "keycloak", "login", "resources")
|
destDirPath: pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"login",
|
||||||
|
"resources"
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
install_and_move_to_common_resources_generated_in_keycloak_v2: {
|
install_and_move_to_common_resources_generated_in_keycloak_v2: {
|
||||||
if (!fs.readFileSync(pathJoin(destDirPath, "keycloak", "login", "theme.properties")).toString("utf8").includes("web_modules")) {
|
if (
|
||||||
|
!fs
|
||||||
|
.readFileSync(
|
||||||
|
pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"login",
|
||||||
|
"theme.properties"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toString("utf8")
|
||||||
|
.includes("web_modules")
|
||||||
|
) {
|
||||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountV2DirSrcDirPath = pathJoin(destDirPath, "keycloak.v2", "account", "src");
|
const accountV2DirSrcDirPath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak.v2",
|
||||||
|
"account",
|
||||||
|
"src"
|
||||||
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(accountV2DirSrcDirPath)) {
|
if (!fs.existsSync(accountV2DirSrcDirPath)) {
|
||||||
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
const packageManager = fs.existsSync(
|
||||||
|
pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")
|
||||||
|
)
|
||||||
|
? "pnpm"
|
||||||
|
: "npm";
|
||||||
|
|
||||||
if (packageManager === "pnpm") {
|
if (packageManager === "pnpm") {
|
||||||
try {
|
try {
|
||||||
@ -82,9 +126,15 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
child_process.execSync(`${packageManager} install`, {
|
||||||
|
cwd: accountV2DirSrcDirPath,
|
||||||
|
stdio: "ignore"
|
||||||
|
});
|
||||||
|
|
||||||
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
|
const packageJsonFilePath = pathJoin(
|
||||||
|
accountV2DirSrcDirPath,
|
||||||
|
"package.json"
|
||||||
|
);
|
||||||
|
|
||||||
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
|
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
|
||||||
|
|
||||||
@ -94,13 +144,21 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
.replace(`${packageManager} run check-types`, "true")
|
.replace(`${packageManager} run check-types`, "true")
|
||||||
.replace(`${packageManager} run babel`, "true");
|
.replace(`${packageManager} run babel`, "true");
|
||||||
|
|
||||||
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
|
fs.writeFileSync(
|
||||||
|
packageJsonFilePath,
|
||||||
|
Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8")
|
||||||
|
);
|
||||||
|
|
||||||
child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
child_process.execSync(`${packageManager} run build`, {
|
||||||
|
cwd: accountV2DirSrcDirPath,
|
||||||
|
stdio: "ignore"
|
||||||
|
});
|
||||||
|
|
||||||
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
|
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
|
||||||
|
|
||||||
fs.rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), { "recursive": true });
|
fs.rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_keycloak_v2: {
|
remove_keycloak_v2: {
|
||||||
@ -110,12 +168,18 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
break remove_keycloak_v2;
|
break remove_keycloak_v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmSync(keycloakV2DirPath, { "recursive": true });
|
rmSync(keycloakV2DirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note, this is an optimization for reducing the size of the jar
|
// Note, this is an optimization for reducing the size of the jar
|
||||||
remove_unused_node_modules: {
|
remove_unused_node_modules: {
|
||||||
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
const nodeModuleDirPath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"common",
|
||||||
|
"resources",
|
||||||
|
"node_modules"
|
||||||
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(nodeModuleDirPath)) {
|
if (!fs.existsSync(nodeModuleDirPath)) {
|
||||||
break remove_unused_node_modules;
|
break remove_unused_node_modules;
|
||||||
@ -167,18 +231,26 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
];
|
];
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": nodeModuleDirPath,
|
srcDirPath: nodeModuleDirPath,
|
||||||
"destDirPath": nodeModuleDirPath,
|
destDirPath: nodeModuleDirPath,
|
||||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
transformSourceCode: ({ sourceCode, fileRelativePath }) => {
|
||||||
if (fileRelativePath.endsWith(".map")) {
|
if (fileRelativePath.endsWith(".map")) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
if (
|
||||||
|
toDeletePerfixes.find(prefix =>
|
||||||
|
fileRelativePath.startsWith(prefix)
|
||||||
|
) !== undefined
|
||||||
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileRelativePath.startsWith(pathJoin("patternfly", "dist", "fonts"))) {
|
if (
|
||||||
|
fileRelativePath.startsWith(
|
||||||
|
pathJoin("patternfly", "dist", "fonts")
|
||||||
|
)
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
!fileRelativePath.endsWith(".woff2") &&
|
!fileRelativePath.endsWith(".woff2") &&
|
||||||
!fileRelativePath.endsWith(".woff") &&
|
!fileRelativePath.endsWith(".woff") &&
|
||||||
@ -188,34 +260,50 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just like node_modules
|
// Just like node_modules
|
||||||
remove_unused_lib: {
|
remove_unused_lib: {
|
||||||
const libDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "lib");
|
const libDirPath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"common",
|
||||||
|
"resources",
|
||||||
|
"lib"
|
||||||
|
);
|
||||||
|
|
||||||
if (!fs.existsSync(libDirPath)) {
|
if (!fs.existsSync(libDirPath)) {
|
||||||
break remove_unused_lib;
|
break remove_unused_lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toDeletePerfixes = ["ui-ace", "filesaver", "fileupload", "angular", "ui-ace"];
|
const toDeletePerfixes = [
|
||||||
|
"ui-ace",
|
||||||
|
"filesaver",
|
||||||
|
"fileupload",
|
||||||
|
"angular",
|
||||||
|
"ui-ace"
|
||||||
|
];
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": libDirPath,
|
srcDirPath: libDirPath,
|
||||||
"destDirPath": libDirPath,
|
destDirPath: libDirPath,
|
||||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
transformSourceCode: ({ sourceCode, fileRelativePath }) => {
|
||||||
if (fileRelativePath.endsWith(".map")) {
|
if (fileRelativePath.endsWith(".map")) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
|
if (
|
||||||
|
toDeletePerfixes.find(prefix =>
|
||||||
|
fileRelativePath.startsWith(prefix)
|
||||||
|
) !== undefined
|
||||||
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -226,34 +314,61 @@ export async function downloadKeycloakDefaultTheme(params: { keycloakVersion: st
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css");
|
const accountCssFilePath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"account",
|
||||||
|
"resources",
|
||||||
|
"css",
|
||||||
|
"account.css"
|
||||||
|
);
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
accountCssFilePath,
|
accountCssFilePath,
|
||||||
Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8")
|
Buffer.from(
|
||||||
|
fs
|
||||||
|
.readFileSync(accountCssFilePath)
|
||||||
|
.toString("utf8")
|
||||||
|
.replace("top: -34px;", "top: -34px !important;"),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note, this is an optimization for reducing the size of the jar,
|
// Note, this is an optimization for reducing the size of the jar,
|
||||||
// For this version we know exactly which resources are used.
|
// For this version we know exactly which resources are used.
|
||||||
{
|
{
|
||||||
const nodeModulesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
const nodeModulesDirPath = pathJoin(
|
||||||
|
destDirPath,
|
||||||
|
"keycloak",
|
||||||
|
"common",
|
||||||
|
"resources",
|
||||||
|
"node_modules"
|
||||||
|
);
|
||||||
|
|
||||||
const toKeepPrefixes = [
|
const toKeepPrefixes = [
|
||||||
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(fileBasename =>
|
...[
|
||||||
|
"patternfly.min.css",
|
||||||
|
"patternfly-additions.min.css",
|
||||||
|
"patternfly-additions.min.css"
|
||||||
|
].map(fileBasename =>
|
||||||
pathJoin("patternfly", "dist", "css", fileBasename)
|
pathJoin("patternfly", "dist", "css", fileBasename)
|
||||||
),
|
),
|
||||||
pathJoin("patternfly", "dist", "fonts")
|
pathJoin("patternfly", "dist", "fonts")
|
||||||
];
|
];
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": nodeModulesDirPath,
|
srcDirPath: nodeModulesDirPath,
|
||||||
"destDirPath": nodeModulesDirPath,
|
destDirPath: nodeModulesDirPath,
|
||||||
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
transformSourceCode: ({ sourceCode, fileRelativePath }) => {
|
||||||
if (toKeepPrefixes.find(prefix => fileRelativePath.startsWith(prefix)) === undefined) {
|
if (
|
||||||
|
toKeepPrefixes.find(prefix =>
|
||||||
|
fileRelativePath.startsWith(prefix)
|
||||||
|
) === undefined
|
||||||
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return { "modifiedSourceCode": sourceCode };
|
return { modifiedSourceCode: sourceCode };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -24,26 +24,30 @@ export async function downloadKeycloakStaticResources(params: {
|
|||||||
|
|
||||||
const tmpDirPath = pathJoin(
|
const tmpDirPath = pathJoin(
|
||||||
buildOptions.cacheDirPath,
|
buildOptions.cacheDirPath,
|
||||||
`downloadKeycloakStaticResources_tmp_${crypto.createHash("sha256").update(`${themeType}-${keycloakVersion}`).digest("hex").slice(0, 8)}`
|
`downloadKeycloakStaticResources_tmp_${crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(`${themeType}-${keycloakVersion}`)
|
||||||
|
.digest("hex")
|
||||||
|
.slice(0, 8)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
await downloadKeycloakDefaultTheme({
|
await downloadKeycloakDefaultTheme({
|
||||||
keycloakVersion,
|
keycloakVersion,
|
||||||
"destDirPath": tmpDirPath,
|
destDirPath: tmpDirPath,
|
||||||
buildOptions
|
buildOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
const resourcesPath = pathJoin(themeDirPath, themeType, "resources");
|
const resourcesPath = pathJoin(themeDirPath, themeType, "resources");
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", themeType, "resources"),
|
srcDirPath: pathJoin(tmpDirPath, "keycloak", themeType, "resources"),
|
||||||
"destDirPath": resourcesPath
|
destDirPath: resourcesPath
|
||||||
});
|
});
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
|
srcDirPath: pathJoin(tmpDirPath, "keycloak", "common", "resources"),
|
||||||
"destDirPath": pathJoin(resourcesPath, resources_common)
|
destDirPath: pathJoin(resourcesPath, resources_common)
|
||||||
});
|
});
|
||||||
|
|
||||||
rmSync(tmpDirPath, { "recursive": true });
|
rmSync(tmpDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import type { KeycloakVersionRange } from "./KeycloakVersionRange";
|
import type { KeycloakVersionRange } from "./KeycloakVersionRange";
|
||||||
|
|
||||||
export function getJarFileBasename(params: { keycloakVersionRange: KeycloakVersionRange }) {
|
export function getJarFileBasename(params: {
|
||||||
|
keycloakVersionRange: KeycloakVersionRange;
|
||||||
|
}) {
|
||||||
const { keycloakVersionRange } = params;
|
const { keycloakVersionRange } = params;
|
||||||
|
|
||||||
const jarFileBasename = `keycloak-theme-for-kc-${keycloakVersionRange}.jar`;
|
const jarFileBasename = `keycloak-theme-for-kc-${keycloakVersionRange}.jar`;
|
||||||
|
@ -12,7 +12,10 @@ export function getThemeSrcDirPath(params: { reactAppRootDirPath: string }) {
|
|||||||
|
|
||||||
const srcDirPath = pathJoin(reactAppRootDirPath, "src");
|
const srcDirPath = pathJoin(reactAppRootDirPath, "src");
|
||||||
|
|
||||||
const themeSrcDirPath: string | undefined = crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })
|
const themeSrcDirPath: string | undefined = crawl({
|
||||||
|
dirPath: srcDirPath,
|
||||||
|
returnedPathsType: "relative to dirPath"
|
||||||
|
})
|
||||||
.map(fileRelativePath => {
|
.map(fileRelativePath => {
|
||||||
for (const themeSrcDirBasename of themeSrcDirBasenames) {
|
for (const themeSrcDirBasename of themeSrcDirBasenames) {
|
||||||
const split = fileRelativePath.split(themeSrcDirBasename);
|
const split = fileRelativePath.split(themeSrcDirBasename);
|
||||||
@ -32,7 +35,7 @@ export function getThemeSrcDirPath(params: { reactAppRootDirPath: string }) {
|
|||||||
if (!fs.existsSync(pathJoin(srcDirPath, themeType))) {
|
if (!fs.existsSync(pathJoin(srcDirPath, themeType))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return { "themeSrcDirPath": srcDirPath };
|
return { themeSrcDirPath: srcDirPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -6,29 +6,56 @@ export type MetaInfKeycloakTheme = {
|
|||||||
themes: { name: string; types: (ThemeType | "email")[] }[];
|
themes: { name: string; types: (ThemeType | "email")[] }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getMetaInfKeycloakThemesJsonFilePath(params: { keycloakifyBuildDirPath: string }) {
|
export function getMetaInfKeycloakThemesJsonFilePath(params: {
|
||||||
|
keycloakifyBuildDirPath: string;
|
||||||
|
}) {
|
||||||
const { keycloakifyBuildDirPath } = params;
|
const { keycloakifyBuildDirPath } = params;
|
||||||
|
|
||||||
return pathJoin(keycloakifyBuildDirPath, "src", "main", "resources", "META-INF", "keycloak-themes.json");
|
return pathJoin(
|
||||||
|
keycloakifyBuildDirPath,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"META-INF",
|
||||||
|
"keycloak-themes.json"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readMetaInfKeycloakThemes(params: { keycloakifyBuildDirPath: string }): MetaInfKeycloakTheme {
|
export function readMetaInfKeycloakThemes(params: {
|
||||||
|
keycloakifyBuildDirPath: string;
|
||||||
|
}): MetaInfKeycloakTheme {
|
||||||
const { keycloakifyBuildDirPath } = params;
|
const { keycloakifyBuildDirPath } = params;
|
||||||
|
|
||||||
return JSON.parse(fs.readFileSync(getMetaInfKeycloakThemesJsonFilePath({ keycloakifyBuildDirPath })).toString("utf8")) as MetaInfKeycloakTheme;
|
return JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(
|
||||||
|
getMetaInfKeycloakThemesJsonFilePath({
|
||||||
|
keycloakifyBuildDirPath
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.toString("utf8")
|
||||||
|
) as MetaInfKeycloakTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeMetaInfKeycloakThemes(params: { keycloakifyBuildDirPath: string; metaInfKeycloakThemes: MetaInfKeycloakTheme }) {
|
export function writeMetaInfKeycloakThemes(params: {
|
||||||
|
keycloakifyBuildDirPath: string;
|
||||||
|
metaInfKeycloakThemes: MetaInfKeycloakTheme;
|
||||||
|
}) {
|
||||||
const { keycloakifyBuildDirPath, metaInfKeycloakThemes } = params;
|
const { keycloakifyBuildDirPath, metaInfKeycloakThemes } = params;
|
||||||
|
|
||||||
const metaInfKeycloakThemesJsonPath = getMetaInfKeycloakThemesJsonFilePath({ keycloakifyBuildDirPath });
|
const metaInfKeycloakThemesJsonPath = getMetaInfKeycloakThemesJsonFilePath({
|
||||||
|
keycloakifyBuildDirPath
|
||||||
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const dirPath = pathDirname(metaInfKeycloakThemesJsonPath);
|
const dirPath = pathDirname(metaInfKeycloakThemesJsonPath);
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
fs.mkdirSync(dirPath, { "recursive": true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(metaInfKeycloakThemesJsonPath, Buffer.from(JSON.stringify(metaInfKeycloakThemes, null, 2), "utf8"));
|
fs.writeFileSync(
|
||||||
|
metaInfKeycloakThemesJsonPath,
|
||||||
|
Buffer.from(JSON.stringify(metaInfKeycloakThemes, null, 2), "utf8")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,26 @@ import * as fs from "fs";
|
|||||||
import type { ReturnType } from "tsafe";
|
import type { ReturnType } from "tsafe";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
|
|
||||||
export async function promptKeycloakVersion(params: { startingFromMajor: number | undefined; cacheDirPath: string }) {
|
export async function promptKeycloakVersion(params: {
|
||||||
|
startingFromMajor: number | undefined;
|
||||||
|
cacheDirPath: string;
|
||||||
|
}) {
|
||||||
const { startingFromMajor, cacheDirPath } = params;
|
const { startingFromMajor, cacheDirPath } = params;
|
||||||
|
|
||||||
const { getLatestsSemVersionedTag } = (() => {
|
const { getLatestsSemVersionedTag } = (() => {
|
||||||
const { octokit } = (() => {
|
const { octokit } = (() => {
|
||||||
const githubToken = process.env.GITHUB_TOKEN;
|
const githubToken = process.env.GITHUB_TOKEN;
|
||||||
|
|
||||||
const octokit = new Octokit(githubToken === undefined ? undefined : { "auth": githubToken });
|
const octokit = new Octokit(
|
||||||
|
githubToken === undefined ? undefined : { auth: githubToken }
|
||||||
|
);
|
||||||
|
|
||||||
return { octokit };
|
return { octokit };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const { getLatestsSemVersionedTag } = getLatestsSemVersionedTagFactory({ octokit });
|
const { getLatestsSemVersionedTag } = getLatestsSemVersionedTagFactory({
|
||||||
|
octokit
|
||||||
|
});
|
||||||
|
|
||||||
return { getLatestsSemVersionedTag };
|
return { getLatestsSemVersionedTag };
|
||||||
})();
|
})();
|
||||||
@ -39,7 +46,9 @@ export async function promptKeycloakVersion(params: { startingFromMajor: number
|
|||||||
break use_cache;
|
break use_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cache: Cache = JSON.parse(fs.readFileSync(cacheFilePath).toString("utf8"));
|
const cache: Cache = JSON.parse(
|
||||||
|
fs.readFileSync(cacheFilePath).toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
if (Date.now() - cache.time > 3_600_000) {
|
if (Date.now() - cache.time > 3_600_000) {
|
||||||
fs.unlinkSync(cacheFilePath);
|
fs.unlinkSync(cacheFilePath);
|
||||||
@ -50,16 +59,16 @@ export async function promptKeycloakVersion(params: { startingFromMajor: number
|
|||||||
}
|
}
|
||||||
|
|
||||||
const semVersionedTags = await getLatestsSemVersionedTag({
|
const semVersionedTags = await getLatestsSemVersionedTag({
|
||||||
"count": 50,
|
count: 50,
|
||||||
"owner": "keycloak",
|
owner: "keycloak",
|
||||||
"repo": "keycloak"
|
repo: "keycloak"
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const dirPath = pathDirname(cacheFilePath);
|
const dirPath = pathDirname(cacheFilePath);
|
||||||
|
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
fs.mkdirSync(dirPath, { "recursive": true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +76,7 @@ export async function promptKeycloakVersion(params: { startingFromMajor: number
|
|||||||
cacheFilePath,
|
cacheFilePath,
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
id<Cache>({
|
id<Cache>({
|
||||||
"time": Date.now(),
|
time: Date.now(),
|
||||||
semVersionedTags
|
semVersionedTags
|
||||||
}),
|
}),
|
||||||
null,
|
null,
|
||||||
@ -79,23 +88,33 @@ export async function promptKeycloakVersion(params: { startingFromMajor: number
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
semVersionedTags.forEach(semVersionedTag => {
|
semVersionedTags.forEach(semVersionedTag => {
|
||||||
if (startingFromMajor !== undefined && semVersionedTag.version.major < startingFromMajor) {
|
if (
|
||||||
|
startingFromMajor !== undefined &&
|
||||||
|
semVersionedTag.version.major < startingFromMajor
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentSemVersionedTag = semVersionedTagByMajor.get(semVersionedTag.version.major);
|
const currentSemVersionedTag = semVersionedTagByMajor.get(
|
||||||
|
semVersionedTag.version.major
|
||||||
|
);
|
||||||
|
|
||||||
if (currentSemVersionedTag !== undefined && SemVer.compare(semVersionedTag.version, currentSemVersionedTag.version) === -1) {
|
if (
|
||||||
|
currentSemVersionedTag !== undefined &&
|
||||||
|
SemVer.compare(semVersionedTag.version, currentSemVersionedTag.version) === -1
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
semVersionedTagByMajor.set(semVersionedTag.version.major, semVersionedTag);
|
semVersionedTagByMajor.set(semVersionedTag.version.major, semVersionedTag);
|
||||||
});
|
});
|
||||||
|
|
||||||
const lastMajorVersions = Array.from(semVersionedTagByMajor.values()).map(({ tag }) => tag);
|
const lastMajorVersions = Array.from(semVersionedTagByMajor.values()).map(
|
||||||
|
({ tag }) => tag
|
||||||
|
);
|
||||||
|
|
||||||
const { value } = await cliSelect<string>({
|
const { value } = await cliSelect<string>({
|
||||||
"values": lastMajorVersions
|
values: lastMajorVersions
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
});
|
});
|
||||||
|
@ -407,7 +407,11 @@
|
|||||||
"otpPolicyLookAheadWindow": 1,
|
"otpPolicyLookAheadWindow": 1,
|
||||||
"otpPolicyPeriod": 30,
|
"otpPolicyPeriod": 30,
|
||||||
"otpPolicyCodeReusable": false,
|
"otpPolicyCodeReusable": false,
|
||||||
"otpSupportedApplications": ["totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName"],
|
"otpSupportedApplications": [
|
||||||
|
"totpAppFreeOTPName",
|
||||||
|
"totpAppGoogleName",
|
||||||
|
"totpAppMicrosoftAuthenticatorName"
|
||||||
|
],
|
||||||
"localizationTexts": {},
|
"localizationTexts": {},
|
||||||
"webAuthnPolicyRpEntityName": "keycloak",
|
"webAuthnPolicyRpEntityName": "keycloak",
|
||||||
"webAuthnPolicySignatureAlgorithms": ["ES256"],
|
"webAuthnPolicySignatureAlgorithms": ["ES256"],
|
||||||
@ -520,7 +524,12 @@
|
|||||||
"fullScopeAllowed": false,
|
"fullScopeAllowed": false,
|
||||||
"nodeReRegistrationTimeout": 0,
|
"nodeReRegistrationTimeout": 0,
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bb874a19-9a92-4504-a7c0-d32ef6f3d266",
|
"id": "bb874a19-9a92-4504-a7c0-d32ef6f3d266",
|
||||||
@ -562,7 +571,12 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "e19ed660-ca30-4266-be25-7ac226994f0f",
|
"id": "e19ed660-ca30-4266-be25-7ac226994f0f",
|
||||||
@ -589,7 +603,12 @@
|
|||||||
"fullScopeAllowed": false,
|
"fullScopeAllowed": false,
|
||||||
"nodeReRegistrationTimeout": 0,
|
"nodeReRegistrationTimeout": 0,
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2c000747-ea5b-4226-82a3-1fc32bc8a079",
|
"id": "2c000747-ea5b-4226-82a3-1fc32bc8a079",
|
||||||
@ -616,7 +635,12 @@
|
|||||||
"fullScopeAllowed": false,
|
"fullScopeAllowed": false,
|
||||||
"nodeReRegistrationTimeout": 0,
|
"nodeReRegistrationTimeout": 0,
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5dfb0d1a-2ab1-480f-8bab-81502f762ac0",
|
"id": "5dfb0d1a-2ab1-480f-8bab-81502f762ac0",
|
||||||
@ -761,7 +785,12 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8efd01a0-40cd-487d-818b-fa2b096170e0",
|
"id": "8efd01a0-40cd-487d-818b-fa2b096170e0",
|
||||||
@ -788,7 +817,12 @@
|
|||||||
"fullScopeAllowed": false,
|
"fullScopeAllowed": false,
|
||||||
"nodeReRegistrationTimeout": 0,
|
"nodeReRegistrationTimeout": 0,
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "199b2e1b-2cdd-4d8b-8816-4a5c79741658",
|
"id": "199b2e1b-2cdd-4d8b-8816-4a5c79741658",
|
||||||
@ -838,7 +872,12 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
"defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"],
|
||||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
"optionalClientScopes": [
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"offline_access",
|
||||||
|
"microprofile-jwt"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"clientScopes": [
|
"clientScopes": [
|
||||||
@ -1379,8 +1418,20 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultDefaultClientScopes": ["role_list", "profile", "email", "roles", "web-origins", "acr"],
|
"defaultDefaultClientScopes": [
|
||||||
"defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt"],
|
"role_list",
|
||||||
|
"profile",
|
||||||
|
"email",
|
||||||
|
"roles",
|
||||||
|
"web-origins",
|
||||||
|
"acr"
|
||||||
|
],
|
||||||
|
"defaultOptionalClientScopes": [
|
||||||
|
"offline_access",
|
||||||
|
"address",
|
||||||
|
"phone",
|
||||||
|
"microprofile-jwt"
|
||||||
|
],
|
||||||
"browserSecurityHeaders": {
|
"browserSecurityHeaders": {
|
||||||
"contentSecurityPolicyReportOnly": "",
|
"contentSecurityPolicyReportOnly": "",
|
||||||
"xContentTypeOptions": "nosniff",
|
"xContentTypeOptions": "nosniff",
|
||||||
@ -1575,7 +1626,19 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"internationalizationEnabled": true,
|
"internationalizationEnabled": true,
|
||||||
"supportedLocales": ["ar", "de", "fi", "el", "ja", "en", "fa", "it", "fr", "ca", "es"],
|
"supportedLocales": [
|
||||||
|
"ar",
|
||||||
|
"de",
|
||||||
|
"fi",
|
||||||
|
"el",
|
||||||
|
"ja",
|
||||||
|
"en",
|
||||||
|
"fa",
|
||||||
|
"it",
|
||||||
|
"fr",
|
||||||
|
"ca",
|
||||||
|
"es"
|
||||||
|
],
|
||||||
"defaultLocale": "en",
|
"defaultLocale": "en",
|
||||||
"authenticationFlows": [
|
"authenticationFlows": [
|
||||||
{
|
{
|
||||||
|
@ -2,13 +2,22 @@ import { readBuildOptions } from "../shared/buildOptions";
|
|||||||
import type { CliCommandOptions as CliCommandOptions_common } from "../main";
|
import type { CliCommandOptions as CliCommandOptions_common } from "../main";
|
||||||
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
||||||
import { readMetaInfKeycloakThemes } from "../shared/metaInfKeycloakThemes";
|
import { readMetaInfKeycloakThemes } from "../shared/metaInfKeycloakThemes";
|
||||||
import { accountV1ThemeName, skipBuildJarsEnvName, containerName } from "../shared/constants";
|
import {
|
||||||
|
accountV1ThemeName,
|
||||||
|
skipBuildJarsEnvName,
|
||||||
|
containerName
|
||||||
|
} from "../shared/constants";
|
||||||
import { SemVer } from "../tools/SemVer";
|
import { SemVer } from "../tools/SemVer";
|
||||||
import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange";
|
import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange";
|
||||||
import { getJarFileBasename } from "../shared/getJarFileBasename";
|
import { getJarFileBasename } from "../shared/getJarFileBasename";
|
||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin, relative as pathRelative, sep as pathSep, posix as pathPosix } from "path";
|
import {
|
||||||
|
join as pathJoin,
|
||||||
|
relative as pathRelative,
|
||||||
|
sep as pathSep,
|
||||||
|
posix as pathPosix
|
||||||
|
} from "path";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import chokidar from "chokidar";
|
import chokidar from "chokidar";
|
||||||
@ -30,7 +39,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
let commandOutput: Buffer | undefined = undefined;
|
let commandOutput: Buffer | undefined = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
commandOutput = child_process.execSync("docker --version", { "stdio": ["ignore", "pipe", "ignore"] });
|
commandOutput = child_process.execSync("docker --version", {
|
||||||
|
stdio: ["ignore", "pipe", "ignore"]
|
||||||
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
if (commandOutput?.toString("utf8").includes("Docker")) {
|
if (commandOutput?.toString("utf8").includes("Docker")) {
|
||||||
@ -40,7 +51,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
console.log(
|
console.log(
|
||||||
[
|
[
|
||||||
`${chalk.red("Docker required.")}`,
|
`${chalk.red("Docker required.")}`,
|
||||||
`Install it with Docker Desktop: ${chalk.bold.underline("https://www.docker.com/products/docker-desktop/")}`,
|
`Install it with Docker Desktop: ${chalk.bold.underline(
|
||||||
|
"https://www.docker.com/products/docker-desktop/"
|
||||||
|
)}`,
|
||||||
`(or any other way)`
|
`(or any other way)`
|
||||||
].join(" ")
|
].join(" ")
|
||||||
);
|
);
|
||||||
@ -52,7 +65,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
let isDockerRunning: boolean;
|
let isDockerRunning: boolean;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.execSync("docker info", { "stdio": "ignore" });
|
child_process.execSync("docker info", { stdio: "ignore" });
|
||||||
isDockerRunning = true;
|
isDockerRunning = true;
|
||||||
} catch {
|
} catch {
|
||||||
isDockerRunning = false;
|
isDockerRunning = false;
|
||||||
@ -62,7 +75,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
break exit_if_docker_not_running;
|
break exit_if_docker_not_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log([`${chalk.red("Docker daemon is not running.")}`, `Please start Docker Desktop and try again.`].join(" "));
|
console.log(
|
||||||
|
[
|
||||||
|
`${chalk.red("Docker daemon is not running.")}`,
|
||||||
|
`Please start Docker Desktop and try again.`
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -77,51 +95,60 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
[`${chalk.red("The theme has not been built.")}`, `Please run ${chalk.bold("npx vite && npx keycloakify build")} first.`].join(" ")
|
[
|
||||||
|
`${chalk.red("The theme has not been built.")}`,
|
||||||
|
`Please run ${chalk.bold("npx vite && npx keycloakify build")} first.`
|
||||||
|
].join(" ")
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
|
const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
|
||||||
"keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath
|
keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some(({ name }) => name === accountV1ThemeName);
|
const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some(
|
||||||
|
({ name }) => name === accountV1ThemeName
|
||||||
|
);
|
||||||
|
|
||||||
const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } = await (async function getKeycloakMajor(): Promise<{
|
const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } =
|
||||||
keycloakVersion: string;
|
await (async function getKeycloakMajor(): Promise<{
|
||||||
keycloakMajorNumber: number;
|
keycloakVersion: string;
|
||||||
}> {
|
keycloakMajorNumber: number;
|
||||||
if (cliCommandOptions.keycloakVersion !== undefined) {
|
}> {
|
||||||
return {
|
if (cliCommandOptions.keycloakVersion !== undefined) {
|
||||||
"keycloakVersion": cliCommandOptions.keycloakVersion,
|
return {
|
||||||
"keycloakMajorNumber": SemVer.parse(cliCommandOptions.keycloakVersion).major
|
keycloakVersion: cliCommandOptions.keycloakVersion,
|
||||||
};
|
keycloakMajorNumber: SemVer.parse(cliCommandOptions.keycloakVersion)
|
||||||
}
|
.major
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
console.log(chalk.cyan("On which version of Keycloak do you want to test your theme?"));
|
|
||||||
|
|
||||||
const { keycloakVersion } = await promptKeycloakVersion({
|
|
||||||
"startingFromMajor": 17,
|
|
||||||
"cacheDirPath": buildOptions.cacheDirPath
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`→ ${keycloakVersion}`);
|
|
||||||
|
|
||||||
const keycloakMajorNumber = SemVer.parse(keycloakVersion).major;
|
|
||||||
|
|
||||||
if (doesImplementAccountTheme && keycloakMajorNumber === 22) {
|
|
||||||
console.log(
|
console.log(
|
||||||
[
|
chalk.cyan("On which version of Keycloak do you want to test your theme?")
|
||||||
"Unfortunately, Keycloakify themes that implements an account theme do not work on Keycloak 22",
|
|
||||||
"Please select any other Keycloak version"
|
|
||||||
].join(" ")
|
|
||||||
);
|
);
|
||||||
return getKeycloakMajor();
|
|
||||||
}
|
|
||||||
|
|
||||||
return { keycloakVersion, keycloakMajorNumber };
|
const { keycloakVersion } = await promptKeycloakVersion({
|
||||||
})();
|
startingFromMajor: 17,
|
||||||
|
cacheDirPath: buildOptions.cacheDirPath
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`→ ${keycloakVersion}`);
|
||||||
|
|
||||||
|
const keycloakMajorNumber = SemVer.parse(keycloakVersion).major;
|
||||||
|
|
||||||
|
if (doesImplementAccountTheme && keycloakMajorNumber === 22) {
|
||||||
|
console.log(
|
||||||
|
[
|
||||||
|
"Unfortunately, Keycloakify themes that implements an account theme do not work on Keycloak 22",
|
||||||
|
"Please select any other Keycloak version"
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
return getKeycloakMajor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { keycloakVersion, keycloakMajorNumber };
|
||||||
|
})();
|
||||||
|
|
||||||
const keycloakVersionRange: KeycloakVersionRange = (() => {
|
const keycloakVersionRange: KeycloakVersionRange = (() => {
|
||||||
if (doesImplementAccountTheme) {
|
if (doesImplementAccountTheme) {
|
||||||
@ -139,7 +166,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
return "24-and-above" as const;
|
return "24-and-above" as const;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithAccountTheme>>();
|
assert<
|
||||||
|
Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithAccountTheme>
|
||||||
|
>();
|
||||||
|
|
||||||
return keycloakVersionRange;
|
return keycloakVersionRange;
|
||||||
} else {
|
} else {
|
||||||
@ -151,7 +180,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
return "22-and-above" as const;
|
return "22-and-above" as const;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithoutAccountTheme>>();
|
assert<
|
||||||
|
Equals<
|
||||||
|
typeof keycloakVersionRange,
|
||||||
|
KeycloakVersionRange.WithoutAccountTheme
|
||||||
|
>
|
||||||
|
>();
|
||||||
|
|
||||||
return keycloakVersionRange;
|
return keycloakVersionRange;
|
||||||
}
|
}
|
||||||
@ -163,7 +197,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
const mountTargets = buildOptions.themeNames
|
const mountTargets = buildOptions.themeNames
|
||||||
.map(themeName => {
|
.map(themeName => {
|
||||||
const themeEntry = metaInfKeycloakThemes.themes.find(({ name }) => name === themeName);
|
const themeEntry = metaInfKeycloakThemes.themes.find(
|
||||||
|
({ name }) => name === themeName
|
||||||
|
);
|
||||||
|
|
||||||
assert(themeEntry !== undefined);
|
assert(themeEntry !== undefined);
|
||||||
|
|
||||||
@ -181,10 +217,24 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
return fs
|
return fs
|
||||||
.readdirSync(localPathDirname)
|
.readdirSync(localPathDirname)
|
||||||
.filter(fileOrDirectoryBasename => !fileOrDirectoryBasename.endsWith(".properties"))
|
.filter(
|
||||||
|
fileOrDirectoryBasename =>
|
||||||
|
!fileOrDirectoryBasename.endsWith(".properties")
|
||||||
|
)
|
||||||
.map(fileOrDirectoryBasename => ({
|
.map(fileOrDirectoryBasename => ({
|
||||||
"localPath": pathJoin(localPathDirname, fileOrDirectoryBasename),
|
localPath: pathJoin(
|
||||||
"containerPath": pathPosix.join("/", "opt", "keycloak", "themes", themeName, themeType, fileOrDirectoryBasename)
|
localPathDirname,
|
||||||
|
fileOrDirectoryBasename
|
||||||
|
),
|
||||||
|
containerPath: pathPosix.join(
|
||||||
|
"/",
|
||||||
|
"opt",
|
||||||
|
"keycloak",
|
||||||
|
"themes",
|
||||||
|
themeName,
|
||||||
|
themeType,
|
||||||
|
fileOrDirectoryBasename
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
@ -192,33 +242,56 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.execSync(`docker rm --force ${containerName}`, { "stdio": "ignore" });
|
child_process.execSync(`docker rm --force ${containerName}`, {
|
||||||
|
stdio: "ignore"
|
||||||
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
const realmJsonFilePath = await (async () => {
|
const realmJsonFilePath = await (async () => {
|
||||||
if (cliCommandOptions.realmJsonFilePath !== undefined) {
|
if (cliCommandOptions.realmJsonFilePath !== undefined) {
|
||||||
console.log(chalk.green(`Using realm json file: ${cliCommandOptions.realmJsonFilePath}`));
|
console.log(
|
||||||
|
chalk.green(
|
||||||
|
`Using realm json file: ${cliCommandOptions.realmJsonFilePath}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
"pathIsh": cliCommandOptions.realmJsonFilePath,
|
pathIsh: cliCommandOptions.realmJsonFilePath,
|
||||||
"cwd": process.cwd()
|
cwd: process.cwd()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirPath = pathJoin(getThisCodebaseRootDirPath(), "src", "bin", "start-keycloak");
|
const dirPath = pathJoin(
|
||||||
|
getThisCodebaseRootDirPath(),
|
||||||
|
"src",
|
||||||
|
"bin",
|
||||||
|
"start-keycloak"
|
||||||
|
);
|
||||||
|
|
||||||
const filePath = pathJoin(dirPath, `myrealm-realm-${keycloakMajorVersionNumber}.json`);
|
const filePath = pathJoin(
|
||||||
|
dirPath,
|
||||||
|
`myrealm-realm-${keycloakMajorVersionNumber}.json`
|
||||||
|
);
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`${chalk.yellow(`Keycloakify do not have a realm configuration for Keycloak ${keycloakMajorVersionNumber} yet.`)}`);
|
console.log(
|
||||||
|
`${chalk.yellow(
|
||||||
|
`Keycloakify do not have a realm configuration for Keycloak ${keycloakMajorVersionNumber} yet.`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
console.log(chalk.cyan("Select what configuration to use:"));
|
console.log(chalk.cyan("Select what configuration to use:"));
|
||||||
|
|
||||||
const { value } = await cliSelect<string>({
|
const { value } = await cliSelect<string>({
|
||||||
"values": [...fs.readdirSync(dirPath).filter(fileBasename => fileBasename.endsWith(".json")), "none"]
|
values: [
|
||||||
|
...fs
|
||||||
|
.readdirSync(dirPath)
|
||||||
|
.filter(fileBasename => fileBasename.endsWith(".json")),
|
||||||
|
"none"
|
||||||
|
]
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
});
|
});
|
||||||
@ -238,17 +311,37 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
...["--name", containerName],
|
...["--name", containerName],
|
||||||
...["-e", "KEYCLOAK_ADMIN=admin"],
|
...["-e", "KEYCLOAK_ADMIN=admin"],
|
||||||
...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
|
...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
|
||||||
...(realmJsonFilePath === undefined ? [] : ["-v", `${realmJsonFilePath}:/opt/keycloak/data/import/myrealm-realm.json`]),
|
...(realmJsonFilePath === undefined
|
||||||
...["-v", `${pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)}:/opt/keycloak/providers/keycloak-theme.jar`],
|
? []
|
||||||
...(keycloakMajorVersionNumber <= 20 ? ["-e", "JAVA_OPTS=-Dkeycloak.profile=preview"] : []),
|
: [
|
||||||
...mountTargets.map(({ localPath, containerPath }) => ["-v", `${localPath}:${containerPath}:rw`]).flat(),
|
"-v",
|
||||||
|
`${realmJsonFilePath}:/opt/keycloak/data/import/myrealm-realm.json`
|
||||||
|
]),
|
||||||
|
...[
|
||||||
|
"-v",
|
||||||
|
`${pathJoin(
|
||||||
|
buildOptions.keycloakifyBuildDirPath,
|
||||||
|
jarFileBasename
|
||||||
|
)}:/opt/keycloak/providers/keycloak-theme.jar`
|
||||||
|
],
|
||||||
|
...(keycloakMajorVersionNumber <= 20
|
||||||
|
? ["-e", "JAVA_OPTS=-Dkeycloak.profile=preview"]
|
||||||
|
: []),
|
||||||
|
...mountTargets
|
||||||
|
.map(({ localPath, containerPath }) => [
|
||||||
|
"-v",
|
||||||
|
`${localPath}:${containerPath}:rw`
|
||||||
|
])
|
||||||
|
.flat(),
|
||||||
`quay.io/keycloak/keycloak:${keycloakVersion}`,
|
`quay.io/keycloak/keycloak:${keycloakVersion}`,
|
||||||
"start-dev",
|
"start-dev",
|
||||||
...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24 ? ["--features=declarative-user-profile"] : []),
|
...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24
|
||||||
|
? ["--features=declarative-user-profile"]
|
||||||
|
: []),
|
||||||
...(realmJsonFilePath === undefined ? [] : ["--import-realm"])
|
...(realmJsonFilePath === undefined ? [] : ["--import-realm"])
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
"cwd": buildOptions.keycloakifyBuildDirPath
|
cwd: buildOptions.keycloakifyBuildDirPath
|
||||||
}
|
}
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@ -278,17 +371,23 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
`${chalk.green("Your theme is accessible at:")}`,
|
`${chalk.green("Your theme is accessible at:")}`,
|
||||||
`${chalk.green("➜")} ${chalk.cyan.bold("https://my-theme.keycloakify.dev/")}`,
|
`${chalk.green("➜")} ${chalk.cyan.bold(
|
||||||
|
"https://my-theme.keycloakify.dev/"
|
||||||
|
)}`,
|
||||||
"",
|
"",
|
||||||
"You can login with the following credentials:",
|
"You can login with the following credentials:",
|
||||||
`- username: ${chalk.cyan.bold("testuser")}`,
|
`- username: ${chalk.cyan.bold("testuser")}`,
|
||||||
`- password: ${chalk.cyan.bold("password123")}`,
|
`- password: ${chalk.cyan.bold("password123")}`,
|
||||||
"",
|
"",
|
||||||
`Keycloak Admin console: ${chalk.cyan.bold(`http://localhost:${cliCommandOptions.port}`)}`,
|
`Keycloak Admin console: ${chalk.cyan.bold(
|
||||||
|
`http://localhost:${cliCommandOptions.port}`
|
||||||
|
)}`,
|
||||||
`- user: ${chalk.cyan.bold("admin")}`,
|
`- user: ${chalk.cyan.bold("admin")}`,
|
||||||
`- password: ${chalk.cyan.bold("admin")}`,
|
`- password: ${chalk.cyan.bold("admin")}`,
|
||||||
"",
|
"",
|
||||||
`Watching for changes in ${chalk.bold(`.${pathSep}${pathRelative(process.cwd(), srcDirPath)}`)}`
|
`Watching for changes in ${chalk.bold(
|
||||||
|
`.${pathSep}${pathRelative(process.cwd(), srcDirPath)}`
|
||||||
|
)}`
|
||||||
].join("\n")
|
].join("\n")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -297,75 +396,79 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const { waitForDebounce } = waitForDebounceFactory({ "delay": 400 });
|
const { waitForDebounce } = waitForDebounceFactory({ delay: 400 });
|
||||||
|
|
||||||
chokidar.watch([srcDirPath, getThisCodebaseRootDirPath()], { "ignoreInitial": true }).on("all", async (...[, filePath]) => {
|
chokidar
|
||||||
if (
|
.watch([srcDirPath, getThisCodebaseRootDirPath()], {
|
||||||
isInside({
|
ignoreInitial: true
|
||||||
"dirPath": pathJoin(getThisCodebaseRootDirPath(), "src", "bin"),
|
})
|
||||||
filePath
|
.on("all", async (...[, filePath]) => {
|
||||||
}) ||
|
if (
|
||||||
isInside({
|
isInside({
|
||||||
"dirPath": pathJoin(getThisCodebaseRootDirPath(), "bin"),
|
dirPath: pathJoin(getThisCodebaseRootDirPath(), "src", "bin"),
|
||||||
filePath
|
filePath
|
||||||
})
|
}) ||
|
||||||
) {
|
isInside({
|
||||||
return;
|
dirPath: pathJoin(getThisCodebaseRootDirPath(), "bin"),
|
||||||
}
|
filePath
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await waitForDebounce();
|
await waitForDebounce();
|
||||||
|
|
||||||
console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
|
console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
|
||||||
|
|
||||||
const dViteBuildDone = new Deferred<void>();
|
const dViteBuildDone = new Deferred<void>();
|
||||||
|
|
||||||
{
|
{
|
||||||
const child = child_process.spawn("npx", ["vite", "build"], {
|
const child = child_process.spawn("npx", ["vite", "build"], {
|
||||||
"cwd": buildOptions.reactAppRootDirPath,
|
cwd: buildOptions.reactAppRootDirPath,
|
||||||
"env": process.env
|
env: process.env
|
||||||
});
|
});
|
||||||
|
|
||||||
child.stdout.on("data", data => {
|
child.stdout.on("data", data => {
|
||||||
if (data.toString("utf8").includes("gzip:")) {
|
if (data.toString("utf8").includes("gzip:")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.stdout.write(data);
|
process.stdout.write(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
child.stderr.on("data", data => process.stderr.write(data));
|
child.stderr.on("data", data => process.stderr.write(data));
|
||||||
|
|
||||||
child.on("exit", code => {
|
child.on("exit", code => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
dViteBuildDone.resolve();
|
dViteBuildDone.resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await dViteBuildDone.pr;
|
await dViteBuildDone.pr;
|
||||||
|
|
||||||
{
|
{
|
||||||
const child = child_process.spawn("npx", ["keycloakify", "build"], {
|
const child = child_process.spawn("npx", ["keycloakify", "build"], {
|
||||||
"cwd": buildOptions.reactAppRootDirPath,
|
cwd: buildOptions.reactAppRootDirPath,
|
||||||
"env": {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
[skipBuildJarsEnvName]: "true"
|
[skipBuildJarsEnvName]: "true"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
child.stdout.on("data", data => process.stdout.write(data));
|
child.stdout.on("data", data => process.stdout.write(data));
|
||||||
|
|
||||||
child.stderr.on("data", data => process.stderr.write(data));
|
child.stderr.on("data", data => process.stderr.write(data));
|
||||||
|
|
||||||
child.on("exit", code => {
|
child.on("exit", code => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
console.log(chalk.yellow("Theme not updated, build failed"));
|
console.log(chalk.yellow("Theme not updated, build failed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chalk.green("Rebuild done"));
|
console.log(chalk.green("Rebuild done"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,35 +12,39 @@ export namespace SemVer {
|
|||||||
export type BumpType = (typeof bumpTypes)[number];
|
export type BumpType = (typeof bumpTypes)[number];
|
||||||
|
|
||||||
export function parse(versionStr: string): SemVer {
|
export function parse(versionStr: string): SemVer {
|
||||||
const match = versionStr.match(/^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-rc.([0-9]+))?$/);
|
const match = versionStr.match(
|
||||||
|
/^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-rc.([0-9]+))?$/
|
||||||
|
);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new Error(`${versionStr} is not a valid semantic version`);
|
throw new Error(`${versionStr} is not a valid semantic version`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const semVer: Omit<SemVer, "parsedFrom"> = {
|
const semVer: Omit<SemVer, "parsedFrom"> = {
|
||||||
"major": parseInt(match[1]),
|
major: parseInt(match[1]),
|
||||||
"minor": parseInt(match[2]),
|
minor: parseInt(match[2]),
|
||||||
"patch": (() => {
|
patch: (() => {
|
||||||
const str = match[3];
|
const str = match[3];
|
||||||
|
|
||||||
return str === undefined ? 0 : parseInt(str);
|
return str === undefined ? 0 : parseInt(str);
|
||||||
})(),
|
})(),
|
||||||
...(() => {
|
...(() => {
|
||||||
const str = match[4];
|
const str = match[4];
|
||||||
return str === undefined ? {} : { "rc": parseInt(str) };
|
return str === undefined ? {} : { rc: parseInt(str) };
|
||||||
})()
|
})()
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialStr = stringify(semVer);
|
const initialStr = stringify(semVer);
|
||||||
|
|
||||||
Object.defineProperty(semVer, "parsedFrom", {
|
Object.defineProperty(semVer, "parsedFrom", {
|
||||||
"enumerable": true,
|
enumerable: true,
|
||||||
"get": function () {
|
get: function () {
|
||||||
const currentStr = stringify(this);
|
const currentStr = stringify(this);
|
||||||
|
|
||||||
if (currentStr !== initialStr) {
|
if (currentStr !== initialStr) {
|
||||||
throw new Error(`SemVer.parsedFrom can't be read anymore, the version have been modified from ${initialStr} to ${currentStr}`);
|
throw new Error(
|
||||||
|
`SemVer.parsedFrom can't be read anymore, the version have been modified from ${initialStr} to ${currentStr}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return versionStr;
|
return versionStr;
|
||||||
@ -51,7 +55,9 @@ export namespace SemVer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function stringify(v: Omit<SemVer, "parsedFrom">): string {
|
export function stringify(v: Omit<SemVer, "parsedFrom">): string {
|
||||||
return `${v.major}.${v.minor}.${v.patch}${v.rc === undefined ? "" : `-rc.${v.rc}`}`;
|
return `${v.major}.${v.minor}.${v.patch}${
|
||||||
|
v.rc === undefined ? "" : `-rc.${v.rc}`
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,12 +86,25 @@ export namespace SemVer {
|
|||||||
console.log(compare(parse("3.0.0-rc.3"), parse("4.0.0")) === -1 )
|
console.log(compare(parse("3.0.0-rc.3"), parse("4.0.0")) === -1 )
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function bumpType(params: { versionBehind: string | SemVer; versionAhead: string | SemVer }): BumpType | "no bump" {
|
export function bumpType(params: {
|
||||||
const versionAhead = typeof params.versionAhead === "string" ? parse(params.versionAhead) : params.versionAhead;
|
versionBehind: string | SemVer;
|
||||||
const versionBehind = typeof params.versionBehind === "string" ? parse(params.versionBehind) : params.versionBehind;
|
versionAhead: string | SemVer;
|
||||||
|
}): BumpType | "no bump" {
|
||||||
|
const versionAhead =
|
||||||
|
typeof params.versionAhead === "string"
|
||||||
|
? parse(params.versionAhead)
|
||||||
|
: params.versionAhead;
|
||||||
|
const versionBehind =
|
||||||
|
typeof params.versionBehind === "string"
|
||||||
|
? parse(params.versionBehind)
|
||||||
|
: params.versionBehind;
|
||||||
|
|
||||||
if (compare(versionBehind, versionAhead) === 1) {
|
if (compare(versionBehind, versionAhead) === 1) {
|
||||||
throw new Error(`Version regression ${stringify(versionBehind)} -> ${stringify(versionAhead)}`);
|
throw new Error(
|
||||||
|
`Version regression ${stringify(versionBehind)} -> ${stringify(
|
||||||
|
versionAhead
|
||||||
|
)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const level of ["major", "minor", "patch", "rc"] as const) {
|
for (const level of ["major", "minor", "patch", "rc"] as const) {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
export function replaceAll(string: string, searchValue: string | RegExp, replaceValue: string): string {
|
export function replaceAll(
|
||||||
|
string: string,
|
||||||
|
searchValue: string | RegExp,
|
||||||
|
replaceValue: string
|
||||||
|
): string {
|
||||||
if ((string as any).replaceAll !== undefined) {
|
if ((string as any).replaceAll !== undefined) {
|
||||||
return (string as any).replaceAll(searchValue, replaceValue);
|
return (string as any).replaceAll(searchValue, replaceValue);
|
||||||
}
|
}
|
||||||
@ -24,7 +28,10 @@ export function replaceAll(string: string, searchValue: string | RegExp, replace
|
|||||||
|
|
||||||
// Convert searchValue to string if it's not a string or RegExp
|
// Convert searchValue to string if it's not a string or RegExp
|
||||||
var searchString = String(searchValue);
|
var searchString = String(searchValue);
|
||||||
var regexFromString = new RegExp(searchString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
var regexFromString = new RegExp(
|
||||||
|
searchString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
|
||||||
|
"g"
|
||||||
|
);
|
||||||
|
|
||||||
return string.replace(regexFromString, replaceValue);
|
return string.replace(regexFromString, replaceValue);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,10 @@ const crawlRec = (dirPath: string, filePaths: string[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** List all files in a given directory return paths relative to the dir_path */
|
/** List all files in a given directory return paths relative to the dir_path */
|
||||||
export function crawl(params: { dirPath: string; returnedPathsType: "absolute" | "relative to dirPath" }): string[] {
|
export function crawl(params: {
|
||||||
|
dirPath: string;
|
||||||
|
returnedPathsType: "absolute" | "relative to dirPath";
|
||||||
|
}): string[] {
|
||||||
const { dirPath, returnedPathsType } = params;
|
const { dirPath, returnedPathsType } = params;
|
||||||
|
|
||||||
const filePaths: string[] = [];
|
const filePaths: string[] = [];
|
||||||
|
@ -1,27 +1,42 @@
|
|||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
|
|
||||||
const crc32tab = [
|
const crc32tab = [
|
||||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535,
|
||||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd,
|
||||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
|
||||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
||||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac,
|
||||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
|
||||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
||||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
|
||||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
|
||||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce,
|
||||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
||||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
|
||||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739,
|
||||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268,
|
||||||
|
0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0,
|
||||||
|
0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
|
||||||
|
0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||||
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
||||||
|
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703,
|
||||||
|
0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
|
||||||
|
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||||
|
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
|
||||||
|
0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||||
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6,
|
||||||
|
0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||||
|
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
|
||||||
|
0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5,
|
||||||
|
0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
||||||
|
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -33,11 +48,13 @@ const crc32tab = [
|
|||||||
export function crc32(input: Readable | String | Buffer): Promise<number> {
|
export function crc32(input: Readable | String | Buffer): Promise<number> {
|
||||||
if (typeof input === "string") {
|
if (typeof input === "string") {
|
||||||
let crc = ~0;
|
let crc = ~0;
|
||||||
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input.charCodeAt(i)) & 0xff];
|
for (let i = 0; i < input.length; i++)
|
||||||
|
crc = (crc >>> 8) ^ crc32tab[(crc ^ input.charCodeAt(i)) & 0xff];
|
||||||
return Promise.resolve((crc ^ -1) >>> 0);
|
return Promise.resolve((crc ^ -1) >>> 0);
|
||||||
} else if (input instanceof Buffer) {
|
} else if (input instanceof Buffer) {
|
||||||
let crc = ~0;
|
let crc = ~0;
|
||||||
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input[i]) & 0xff];
|
for (let i = 0; i < input.length; i++)
|
||||||
|
crc = (crc >>> 8) ^ crc32tab[(crc ^ input[i]) & 0xff];
|
||||||
return Promise.resolve((crc ^ -1) >>> 0);
|
return Promise.resolve((crc ^ -1) >>> 0);
|
||||||
} else if (input instanceof Readable) {
|
} else if (input instanceof Readable) {
|
||||||
return new Promise<number>((resolve, reject) => {
|
return new Promise<number>((resolve, reject) => {
|
||||||
@ -46,7 +63,8 @@ export function crc32(input: Readable | String | Buffer): Promise<number> {
|
|||||||
input.on("end", () => resolve((crc ^ -1) >>> 0));
|
input.on("end", () => resolve((crc ^ -1) >>> 0));
|
||||||
input.on("error", e => reject(e));
|
input.on("error", e => reject(e));
|
||||||
input.on("data", (chunk: Buffer) => {
|
input.on("data", (chunk: Buffer) => {
|
||||||
for (let i = 0; i < chunk.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ chunk[i]) & 0xff];
|
for (let i = 0; i < chunk.length; i++)
|
||||||
|
crc = (crc >>> 8) ^ crc32tab[(crc ^ chunk[i]) & 0xff];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,7 +11,10 @@ function ensureSingleOrNone<T>(arg0: T | T[]) {
|
|||||||
if (!Array.isArray(arg0)) return arg0;
|
if (!Array.isArray(arg0)) return arg0;
|
||||||
if (arg0.length === 0) return undefined;
|
if (arg0.length === 0) return undefined;
|
||||||
if (arg0.length === 1) return arg0[0];
|
if (arg0.length === 1) return arg0[0];
|
||||||
throw new Error("Illegal configuration, expected a single value but found multiple: " + arg0.map(String).join(", "));
|
throw new Error(
|
||||||
|
"Illegal configuration, expected a single value but found multiple: " +
|
||||||
|
arg0.map(String).join(", ")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type NPMConfig = Record<string, string | string[]>;
|
type NPMConfig = Record<string, string | string[]>;
|
||||||
@ -24,10 +27,15 @@ async function getNmpConfig(params: { npmWorkspaceRootDirPath: string }) {
|
|||||||
|
|
||||||
const exec = promisify(execCallback);
|
const exec = promisify(execCallback);
|
||||||
|
|
||||||
const stdout = await exec("npm config get", { "encoding": "utf8", "cwd": npmWorkspaceRootDirPath }).then(({ stdout }) => stdout);
|
const stdout = await exec("npm config get", {
|
||||||
|
encoding: "utf8",
|
||||||
|
cwd: npmWorkspaceRootDirPath
|
||||||
|
}).then(({ stdout }) => stdout);
|
||||||
|
|
||||||
const npmConfigReducer = (cfg: NPMConfig, [key, value]: [string, string]) =>
|
const npmConfigReducer = (cfg: NPMConfig, [key, value]: [string, string]) =>
|
||||||
key in cfg ? { ...cfg, [key]: [...ensureArray(cfg[key]), value] } : { ...cfg, [key]: value };
|
key in cfg
|
||||||
|
? { ...cfg, [key]: [...ensureArray(cfg[key]), value] }
|
||||||
|
: { ...cfg, [key]: value };
|
||||||
|
|
||||||
return stdout
|
return stdout
|
||||||
.split("\n")
|
.split("\n")
|
||||||
@ -37,9 +45,14 @@ async function getNmpConfig(params: { npmWorkspaceRootDirPath: string }) {
|
|||||||
.reduce(npmConfigReducer, {} as NPMConfig);
|
.reduce(npmConfigReducer, {} as NPMConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProxyFetchOptions = Pick<FetchOptions, "proxy" | "noProxy" | "strictSSL" | "cert" | "ca">;
|
export type ProxyFetchOptions = Pick<
|
||||||
|
FetchOptions,
|
||||||
|
"proxy" | "noProxy" | "strictSSL" | "cert" | "ca"
|
||||||
|
>;
|
||||||
|
|
||||||
export async function getProxyFetchOptions(params: { npmWorkspaceRootDirPath: string }): Promise<ProxyFetchOptions> {
|
export async function getProxyFetchOptions(params: {
|
||||||
|
npmWorkspaceRootDirPath: string;
|
||||||
|
}): Promise<ProxyFetchOptions> {
|
||||||
const { npmWorkspaceRootDirPath } = params;
|
const { npmWorkspaceRootDirPath } = params;
|
||||||
|
|
||||||
const cfg = await getNmpConfig({ npmWorkspaceRootDirPath });
|
const cfg = await getNmpConfig({ npmWorkspaceRootDirPath });
|
||||||
@ -60,14 +73,24 @@ export async function getProxyFetchOptions(params: { npmWorkspaceRootDirPath: st
|
|||||||
ca.push(
|
ca.push(
|
||||||
...(await (async () => {
|
...(await (async () => {
|
||||||
function chunks<T>(arr: T[], size: number = 2) {
|
function chunks<T>(arr: T[], size: number = 2) {
|
||||||
return arr.map((_, i) => i % size == 0 && arr.slice(i, i + size)).filter(Boolean) as T[][];
|
return arr
|
||||||
|
.map((_, i) => i % size == 0 && arr.slice(i, i + size))
|
||||||
|
.filter(Boolean) as T[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
const cafileContent = await readFile(cafile, "utf-8");
|
const cafileContent = await readFile(cafile, "utf-8");
|
||||||
return chunks(cafileContent.split(/(-----END CERTIFICATE-----)/), 2).map(ca => ca.join("").replace(/^\n/, "").replace(/\n/g, "\\n"));
|
return chunks(cafileContent.split(/(-----END CERTIFICATE-----)/), 2).map(
|
||||||
|
ca => ca.join("").replace(/^\n/, "").replace(/\n/g, "\\n")
|
||||||
|
);
|
||||||
})())
|
})())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { proxy, noProxy, strictSSL, cert, "ca": ca.length === 0 ? undefined : ca };
|
return {
|
||||||
|
proxy,
|
||||||
|
noProxy,
|
||||||
|
strictSSL,
|
||||||
|
cert,
|
||||||
|
ca: ca.length === 0 ? undefined : ca
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import { isAbsolute as pathIsAbsolute, sep as pathSep, join as pathJoin, resolve as pathResolve } from "path";
|
import {
|
||||||
|
isAbsolute as pathIsAbsolute,
|
||||||
|
sep as pathSep,
|
||||||
|
join as pathJoin,
|
||||||
|
resolve as pathResolve
|
||||||
|
} from "path";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
|
|
||||||
export function getAbsoluteAndInOsFormatPath(params: { pathIsh: string; cwd: string }): string {
|
export function getAbsoluteAndInOsFormatPath(params: {
|
||||||
|
pathIsh: string;
|
||||||
|
cwd: string;
|
||||||
|
}): string {
|
||||||
const { pathIsh, cwd } = params;
|
const { pathIsh, cwd } = params;
|
||||||
|
|
||||||
let pathOut = pathIsh;
|
let pathOut = pathIsh;
|
||||||
|
@ -3,14 +3,22 @@ import { join as pathJoin, resolve as pathResolve, sep as pathSep } from "path";
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
export function getNpmWorkspaceRootDirPath(params: { reactAppRootDirPath: string; dependencyExpected: string }) {
|
export function getNpmWorkspaceRootDirPath(params: {
|
||||||
|
reactAppRootDirPath: string;
|
||||||
|
dependencyExpected: string;
|
||||||
|
}) {
|
||||||
const { reactAppRootDirPath, dependencyExpected } = params;
|
const { reactAppRootDirPath, dependencyExpected } = params;
|
||||||
|
|
||||||
const npmWorkspaceRootDirPath = (function callee(depth: number): string {
|
const npmWorkspaceRootDirPath = (function callee(depth: number): string {
|
||||||
const cwd = pathResolve(pathJoin(...[reactAppRootDirPath, ...Array(depth).fill("..")]));
|
const cwd = pathResolve(
|
||||||
|
pathJoin(...[reactAppRootDirPath, ...Array(depth).fill("..")])
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.execSync("npm config get", { cwd, "stdio": "ignore" });
|
child_process.execSync("npm config get", {
|
||||||
|
cwd,
|
||||||
|
stdio: "ignore"
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (String(error).includes("ENOWORKSPACES")) {
|
if (String(error).includes("ENOWORKSPACES")) {
|
||||||
assert(cwd !== pathSep, "NPM workspace not found");
|
assert(cwd !== pathSep, "NPM workspace not found");
|
||||||
@ -26,11 +34,16 @@ export function getNpmWorkspaceRootDirPath(params: { reactAppRootDirPath: string
|
|||||||
|
|
||||||
assert(fs.existsSync(packageJsonFilePath));
|
assert(fs.existsSync(packageJsonFilePath));
|
||||||
|
|
||||||
const parsedPackageJson = JSON.parse(fs.readFileSync(packageJsonFilePath).toString("utf8"));
|
const parsedPackageJson = JSON.parse(
|
||||||
|
fs.readFileSync(packageJsonFilePath).toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
let isExpectedDependencyFound = false;
|
let isExpectedDependencyFound = false;
|
||||||
|
|
||||||
for (const dependenciesOrDevDependencies of ["dependencies", "devDependencies"] as const) {
|
for (const dependenciesOrDevDependencies of [
|
||||||
|
"dependencies",
|
||||||
|
"devDependencies"
|
||||||
|
] as const) {
|
||||||
const dependencies = parsedPackageJson[dependenciesOrDevDependencies];
|
const dependencies = parsedPackageJson[dependenciesOrDevDependencies];
|
||||||
|
|
||||||
if (dependencies === undefined) {
|
if (dependencies === undefined) {
|
||||||
|
@ -5,7 +5,11 @@ import { SemVer } from "../SemVer";
|
|||||||
export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
|
export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
|
||||||
const { octokit } = params;
|
const { octokit } = params;
|
||||||
|
|
||||||
async function getLatestsSemVersionedTag(params: { owner: string; repo: string; count: number }): Promise<
|
async function getLatestsSemVersionedTag(params: {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
count: number;
|
||||||
|
}): Promise<
|
||||||
{
|
{
|
||||||
tag: string;
|
tag: string;
|
||||||
version: SemVer;
|
version: SemVer;
|
||||||
@ -33,7 +37,9 @@ export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
|
|||||||
semVersionedTags.push({ tag, version });
|
semVersionedTags.push({ tag, version });
|
||||||
}
|
}
|
||||||
|
|
||||||
return semVersionedTags.sort(({ version: vX }, { version: vY }) => SemVer.compare(vY, vX)).slice(0, count);
|
return semVersionedTags
|
||||||
|
.sort(({ version: vX }, { version: vY }) => SemVer.compare(vY, vX))
|
||||||
|
.slice(0, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { getLatestsSemVersionedTag };
|
return { getLatestsSemVersionedTag };
|
||||||
|
@ -5,11 +5,19 @@ const per_page = 99;
|
|||||||
export function listTagsFactory(params: { octokit: Octokit }) {
|
export function listTagsFactory(params: { octokit: Octokit }) {
|
||||||
const { octokit } = params;
|
const { octokit } = params;
|
||||||
|
|
||||||
const octokit_repo_listTags = async (params: { owner: string; repo: string; per_page: number; page: number }) => {
|
const octokit_repo_listTags = async (params: {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
per_page: number;
|
||||||
|
page: number;
|
||||||
|
}) => {
|
||||||
return octokit.repos.listTags(params);
|
return octokit.repos.listTags(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function* listTags(params: { owner: string; repo: string }): AsyncGenerator<string> {
|
async function* listTags(params: {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
}): AsyncGenerator<string> {
|
||||||
const { owner, repo } = params;
|
const { owner, repo } = params;
|
||||||
|
|
||||||
let page = 1;
|
let page = 1;
|
||||||
@ -19,7 +27,7 @@ export function listTagsFactory(params: { octokit: Octokit }) {
|
|||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
per_page,
|
per_page,
|
||||||
"page": page++
|
page: page++
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const branch of resp.data.map(({ name }) => name)) {
|
for (const branch of resp.data.map(({ name }) => name)) {
|
||||||
@ -33,7 +41,10 @@ export function listTagsFactory(params: { octokit: Octokit }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
|
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
|
||||||
async function getLatestTag(params: { owner: string; repo: string }): Promise<string | undefined> {
|
async function getLatestTag(params: {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
}): Promise<string | undefined> {
|
||||||
const { owner, repo } = params;
|
const { owner, repo } = params;
|
||||||
|
|
||||||
const itRes = await listTags({ owner, repo }).next();
|
const itRes = await listTags({ owner, repo }).next();
|
||||||
|
@ -2,10 +2,19 @@ export type PromiseSettledAndPartitioned<T> = [T[], any[]];
|
|||||||
|
|
||||||
export function partitionPromiseSettledResults<T>() {
|
export function partitionPromiseSettledResults<T>() {
|
||||||
return [
|
return [
|
||||||
([successes, failures]: PromiseSettledAndPartitioned<T>, item: PromiseSettledResult<T>) =>
|
(
|
||||||
|
[successes, failures]: PromiseSettledAndPartitioned<T>,
|
||||||
|
item: PromiseSettledResult<T>
|
||||||
|
) =>
|
||||||
item.status === "rejected"
|
item.status === "rejected"
|
||||||
? ([successes, [item.reason, ...failures]] as PromiseSettledAndPartitioned<T>)
|
? ([
|
||||||
: ([[item.value, ...successes], failures] as PromiseSettledAndPartitioned<T>),
|
successes,
|
||||||
|
[item.reason, ...failures]
|
||||||
|
] as PromiseSettledAndPartitioned<T>)
|
||||||
|
: ([
|
||||||
|
[item.value, ...successes],
|
||||||
|
failures
|
||||||
|
] as PromiseSettledAndPartitioned<T>),
|
||||||
[[], []] as PromiseSettledAndPartitioned<T>
|
[[], []] as PromiseSettledAndPartitioned<T>
|
||||||
] as const;
|
] as const;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,11 @@ import * as fs from "fs";
|
|||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
|
|
||||||
export function readThisNpmPackageVersion(): string {
|
export function readThisNpmPackageVersion(): string {
|
||||||
const version = JSON.parse(fs.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json")).toString("utf8"))["version"];
|
const version = JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json"))
|
||||||
|
.toString("utf8")
|
||||||
|
)["version"];
|
||||||
|
|
||||||
assert(typeof version === "string");
|
assert(typeof version === "string");
|
||||||
|
|
||||||
|
@ -3,7 +3,11 @@ import * as path from "path";
|
|||||||
import { crawl } from "./crawl";
|
import { crawl } from "./crawl";
|
||||||
import { rmSync } from "../tools/fs.rmSync";
|
import { rmSync } from "../tools/fs.rmSync";
|
||||||
|
|
||||||
type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string; fileRelativePath: string }) =>
|
type TransformSourceCode = (params: {
|
||||||
|
sourceCode: Buffer;
|
||||||
|
filePath: string;
|
||||||
|
fileRelativePath: string;
|
||||||
|
}) =>
|
||||||
| {
|
| {
|
||||||
modifiedSourceCode: Buffer;
|
modifiedSourceCode: Buffer;
|
||||||
newFileName?: string;
|
newFileName?: string;
|
||||||
@ -15,18 +19,27 @@ type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string; file
|
|||||||
* If source and destination are the same this function can be used to apply the transformation in place
|
* If source and destination are the same this function can be used to apply the transformation in place
|
||||||
* like filtering out some files or modifying them.
|
* like filtering out some files or modifying them.
|
||||||
* */
|
* */
|
||||||
export function transformCodebase(params: { srcDirPath: string; destDirPath: string; transformSourceCode?: TransformSourceCode }) {
|
export function transformCodebase(params: {
|
||||||
|
srcDirPath: string;
|
||||||
|
destDirPath: string;
|
||||||
|
transformSourceCode?: TransformSourceCode;
|
||||||
|
}) {
|
||||||
const { srcDirPath, transformSourceCode } = params;
|
const { srcDirPath, transformSourceCode } = params;
|
||||||
|
|
||||||
const isTargetSameAsSource = path.relative(srcDirPath, params.destDirPath) === "";
|
const isTargetSameAsSource = path.relative(srcDirPath, params.destDirPath) === "";
|
||||||
|
|
||||||
const destDirPath = isTargetSameAsSource ? path.join(srcDirPath, "..", "tmp_xOsPdkPsTdzPs34sOkHs") : params.destDirPath;
|
const destDirPath = isTargetSameAsSource
|
||||||
|
? path.join(srcDirPath, "..", "tmp_xOsPdkPsTdzPs34sOkHs")
|
||||||
|
: params.destDirPath;
|
||||||
|
|
||||||
fs.mkdirSync(destDirPath, {
|
fs.mkdirSync(destDirPath, {
|
||||||
"recursive": true
|
recursive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const fileRelativePath of crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })) {
|
for (const fileRelativePath of crawl({
|
||||||
|
dirPath: srcDirPath,
|
||||||
|
returnedPathsType: "relative to dirPath"
|
||||||
|
})) {
|
||||||
const filePath = path.join(srcDirPath, fileRelativePath);
|
const filePath = path.join(srcDirPath, fileRelativePath);
|
||||||
const destFilePath = path.join(destDirPath, fileRelativePath);
|
const destFilePath = path.join(destDirPath, fileRelativePath);
|
||||||
|
|
||||||
@ -34,7 +47,7 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
|
|||||||
// it using the lower level implementation.
|
// it using the lower level implementation.
|
||||||
if (transformSourceCode === undefined) {
|
if (transformSourceCode === undefined) {
|
||||||
fs.mkdirSync(path.dirname(destFilePath), {
|
fs.mkdirSync(path.dirname(destFilePath), {
|
||||||
"recursive": true
|
recursive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.copyFileSync(filePath, destFilePath);
|
fs.copyFileSync(filePath, destFilePath);
|
||||||
@ -43,7 +56,7 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
const transformSourceCodeResult = transformSourceCode({
|
const transformSourceCodeResult = transformSourceCode({
|
||||||
"sourceCode": fs.readFileSync(filePath),
|
sourceCode: fs.readFileSync(filePath),
|
||||||
filePath,
|
filePath,
|
||||||
fileRelativePath
|
fileRelativePath
|
||||||
});
|
});
|
||||||
@ -53,16 +66,22 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs.mkdirSync(path.dirname(destFilePath), {
|
fs.mkdirSync(path.dirname(destFilePath), {
|
||||||
"recursive": true
|
recursive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
|
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
|
||||||
|
|
||||||
fs.writeFileSync(path.join(path.dirname(destFilePath), newFileName ?? path.basename(destFilePath)), modifiedSourceCode);
|
fs.writeFileSync(
|
||||||
|
path.join(
|
||||||
|
path.dirname(destFilePath),
|
||||||
|
newFileName ?? path.basename(destFilePath)
|
||||||
|
),
|
||||||
|
modifiedSourceCode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTargetSameAsSource) {
|
if (isTargetSameAsSource) {
|
||||||
rmSync(srcDirPath, { "recursive": true });
|
rmSync(srcDirPath, { recursive: true });
|
||||||
|
|
||||||
fs.renameSync(destDirPath, srcDirPath);
|
fs.renameSync(destDirPath, srcDirPath);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,10 @@ function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) {
|
|||||||
if (args[i]) {
|
if (args[i]) {
|
||||||
// if the interpolation value has newlines, indent the interpolation values
|
// if the interpolation value has newlines, indent the interpolation values
|
||||||
// using the last known string indent
|
// using the last known string indent
|
||||||
const chunk = String(args[i]).replace(/([\r?\n])/g, "$1" + " ".repeat(lastStringLineLength));
|
const chunk = String(args[i]).replace(
|
||||||
|
/([\r?\n])/g,
|
||||||
|
"$1" + " ".repeat(lastStringLineLength)
|
||||||
|
);
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,11 @@ async function pathExists(path: string) {
|
|||||||
|
|
||||||
// Handlings of non posix path is not implemented correctly
|
// Handlings of non posix path is not implemented correctly
|
||||||
// it work by coincidence. Don't have the time to fix but it should be fixed.
|
// it work by coincidence. Don't have the time to fix but it should be fixed.
|
||||||
export async function unzip(file: string, targetFolder: string, specificDirsToExtract?: string[]) {
|
export async function unzip(
|
||||||
|
file: string,
|
||||||
|
targetFolder: string,
|
||||||
|
specificDirsToExtract?: string[]
|
||||||
|
) {
|
||||||
specificDirsToExtract = specificDirsToExtract?.map(dirPath => {
|
specificDirsToExtract = specificDirsToExtract?.map(dirPath => {
|
||||||
if (!dirPath.endsWith("/") || !dirPath.endsWith("\\")) {
|
if (!dirPath.endsWith("/") || !dirPath.endsWith("\\")) {
|
||||||
dirPath += "/";
|
dirPath += "/";
|
||||||
@ -49,7 +53,9 @@ export async function unzip(file: string, targetFolder: string, specificDirsToEx
|
|||||||
|
|
||||||
zipfile.on("entry", async entry => {
|
zipfile.on("entry", async entry => {
|
||||||
if (specificDirsToExtract !== undefined) {
|
if (specificDirsToExtract !== undefined) {
|
||||||
const dirPath = specificDirsToExtract.find(dirPath => entry.fileName.startsWith(dirPath));
|
const dirPath = specificDirsToExtract.find(dirPath =>
|
||||||
|
entry.fileName.startsWith(dirPath)
|
||||||
|
);
|
||||||
|
|
||||||
// Skip files outside of the unzipSubPath
|
// Skip files outside of the unzipSubPath
|
||||||
if (dirPath === undefined) {
|
if (dirPath === undefined) {
|
||||||
@ -85,7 +91,9 @@ export async function unzip(file: string, targetFolder: string, specificDirsToEx
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fsp.mkdir(path.dirname(target), { "recursive": true });
|
await fsp.mkdir(path.dirname(target), {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
|
||||||
await pipeline(readStream, fs.createWriteStream(target));
|
await pipeline(readStream, fs.createWriteStream(target));
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export const isStorybook = typeof window === "object" && Object.keys(window).find(key => key.startsWith("__STORYBOOK")) !== undefined;
|
export const isStorybook =
|
||||||
|
typeof window === "object" &&
|
||||||
|
Object.keys(window).find(key => key.startsWith("__STORYBOOK")) !== undefined;
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
import { clsx } from "keycloakify/tools/clsx";
|
import { clsx } from "keycloakify/tools/clsx";
|
||||||
import { useConstCallback } from "keycloakify/tools/useConstCallback";
|
import { useConstCallback } from "keycloakify/tools/useConstCallback";
|
||||||
|
|
||||||
export function createUseClassName<ClassKey extends string>(params: { defaultClasses: Record<ClassKey, string | undefined> }) {
|
export function createUseClassName<ClassKey extends string>(params: {
|
||||||
|
defaultClasses: Record<ClassKey, string | undefined>;
|
||||||
|
}) {
|
||||||
const { defaultClasses } = params;
|
const { defaultClasses } = params;
|
||||||
|
|
||||||
function useGetClassName(params: { doUseDefaultCss: boolean; classes: Partial<Record<ClassKey, string>> | undefined }) {
|
function useGetClassName(params: {
|
||||||
|
doUseDefaultCss: boolean;
|
||||||
|
classes: Partial<Record<ClassKey, string>> | undefined;
|
||||||
|
}) {
|
||||||
const { classes, doUseDefaultCss } = params;
|
const { classes, doUseDefaultCss } = params;
|
||||||
|
|
||||||
const getClassName = useConstCallback((classKey: ClassKey): string => {
|
const getClassName = useConstCallback((classKey: ClassKey): string => {
|
||||||
return clsx(classKey, doUseDefaultCss ? defaultClasses[classKey] : undefined, classes?.[classKey]);
|
return clsx(
|
||||||
|
classKey,
|
||||||
|
doUseDefaultCss ? defaultClasses[classKey] : undefined,
|
||||||
|
classes?.[classKey]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { getClassName };
|
return { getClassName };
|
||||||
|
@ -41,13 +41,13 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useSetClassName({
|
useSetClassName({
|
||||||
"qualifiedName": "html",
|
qualifiedName: "html",
|
||||||
"className": getClassName("kcHtmlClass")
|
className: getClassName("kcHtmlClass")
|
||||||
});
|
});
|
||||||
|
|
||||||
useSetClassName({
|
useSetClassName({
|
||||||
"qualifiedName": "body",
|
qualifiedName: "body",
|
||||||
"className": bodyClassName ?? getClassName("kcBodyClass")
|
className: bodyClassName ?? getClassName("kcBodyClass")
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -63,7 +63,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
|
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
|
||||||
"hrefs": !doUseDefaultCss
|
hrefs: !doUseDefaultCss
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
`${url.resourcesCommonPath}/node_modules/@patternfly/patternfly/patternfly.min.css`,
|
`${url.resourcesCommonPath}/node_modules/@patternfly/patternfly/patternfly.min.css`,
|
||||||
@ -75,17 +75,17 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
"scriptTags": [
|
scriptTags: [
|
||||||
{
|
{
|
||||||
"type": "module",
|
type: "module",
|
||||||
"src": `${url.resourcesPath}/js/menu-button-links.js`
|
src: `${url.resourcesPath}/js/menu-button-links.js`
|
||||||
},
|
},
|
||||||
...(authenticationSession === undefined
|
...(authenticationSession === undefined
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
"type": "module",
|
type: "module",
|
||||||
"textContent": [
|
textContent: [
|
||||||
`import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";`,
|
`import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";`,
|
||||||
``,
|
``,
|
||||||
`checkCookiesAndSetTimer(`,
|
`checkCookiesAndSetTimer(`,
|
||||||
@ -99,8 +99,8 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
...scripts.map(
|
...scripts.map(
|
||||||
script =>
|
script =>
|
||||||
({
|
({
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"src": script
|
src: script
|
||||||
} as const)
|
} as const)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@ -237,7 +237,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
<span
|
<span
|
||||||
className={getClassName("kcAlertTitleClass")}
|
className={getClassName("kcAlertTitleClass")}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
"__html": message.summary
|
__html: message.summary
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,10 @@ import type { ReactNode } from "react";
|
|||||||
import type { KcContext } from "./kcContext";
|
import type { KcContext } from "./kcContext";
|
||||||
import type { I18n } from "./i18n";
|
import type { I18n } from "./i18n";
|
||||||
|
|
||||||
export type TemplateProps<KcContext extends KcContext.Common, I18nExtended extends I18n> = {
|
export type TemplateProps<
|
||||||
|
KcContext extends KcContext.Common,
|
||||||
|
I18nExtended extends I18n
|
||||||
|
> = {
|
||||||
kcContext: KcContext;
|
kcContext: KcContext;
|
||||||
i18n: I18nExtended;
|
i18n: I18nExtended;
|
||||||
doUseDefaultCss: boolean;
|
doUseDefaultCss: boolean;
|
||||||
|
@ -43,7 +43,7 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
|
|||||||
onIsFormSubmittableValueChange(isFormSubmittable);
|
onIsFormSubmittableValueChange(isFormSubmittable);
|
||||||
}, [isFormSubmittable]);
|
}, [isFormSubmittable]);
|
||||||
|
|
||||||
const groupNameRef = { "current": "" };
|
const groupNameRef = { current: "" };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -62,7 +62,9 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
|
|||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={getClassName("kcFormGroupClass")}
|
className={getClassName("kcFormGroupClass")}
|
||||||
style={{ "display": attribute.name === "password-confirm" && !doMakeUserConfirmPassword ? "none" : undefined }}
|
style={{
|
||||||
|
display: attribute.name === "password-confirm" && !doMakeUserConfirmPassword ? "none" : undefined
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className={getClassName("kcLabelWrapperClass")}>
|
<div className={getClassName("kcLabelWrapperClass")}>
|
||||||
<label htmlFor={attribute.name} className={getClassName("kcLabelClass")}>
|
<label htmlFor={attribute.name} className={getClassName("kcLabelClass")}>
|
||||||
@ -342,9 +344,9 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
|
|||||||
{...Object.fromEntries(Object.entries(attribute.html5DataAnnotations ?? {}).map(([key, value]) => [`data-${key}`, value]))}
|
{...Object.fromEntries(Object.entries(attribute.html5DataAnnotations ?? {}).map(([key, value]) => [`data-${key}`, value]))}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": (() => {
|
valueOrValues: (() => {
|
||||||
if (fieldIndex !== undefined) {
|
if (fieldIndex !== undefined) {
|
||||||
assert(valueOrValues instanceof Array);
|
assert(valueOrValues instanceof Array);
|
||||||
|
|
||||||
@ -363,9 +365,9 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
|
|||||||
}
|
}
|
||||||
onBlur={() =>
|
onBlur={() =>
|
||||||
props.formValidationDispatch({
|
props.formValidationDispatch({
|
||||||
"action": "focus lost",
|
action: "focus lost",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"fieldIndex": fieldIndex
|
fieldIndex: fieldIndex
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -484,9 +486,9 @@ function AddRemoveButtonsMultiValuedAttribute(props: {
|
|||||||
className="pf-c-button pf-m-inline pf-m-link"
|
className="pf-c-button pf-m-inline pf-m-link"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
dispatchFormAction({
|
dispatchFormAction({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": values.filter((_, i) => i !== fieldIndex)
|
valueOrValues: values.filter((_, i) => i !== fieldIndex)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -501,9 +503,9 @@ function AddRemoveButtonsMultiValuedAttribute(props: {
|
|||||||
className="pf-c-button pf-m-inline pf-m-link"
|
className="pf-c-button pf-m-inline pf-m-link"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
dispatchFormAction({
|
dispatchFormAction({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": [...values, ""]
|
valueOrValues: [...values, ""]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -527,17 +529,17 @@ function InputTagSelects(props: InputFiledByTypeProps) {
|
|||||||
switch (inputType) {
|
switch (inputType) {
|
||||||
case "select-radiobuttons":
|
case "select-radiobuttons":
|
||||||
return {
|
return {
|
||||||
"inputType": "radio",
|
inputType: "radio",
|
||||||
"classDiv": getClassName("kcInputClassRadio"),
|
classDiv: getClassName("kcInputClassRadio"),
|
||||||
"classInput": getClassName("kcInputClassRadioInput"),
|
classInput: getClassName("kcInputClassRadioInput"),
|
||||||
"classLabel": getClassName("kcInputClassRadioLabel")
|
classLabel: getClassName("kcInputClassRadioLabel")
|
||||||
};
|
};
|
||||||
case "multiselect-checkboxes":
|
case "multiselect-checkboxes":
|
||||||
return {
|
return {
|
||||||
"inputType": "checkbox",
|
inputType: "checkbox",
|
||||||
"classDiv": getClassName("kcInputClassCheckbox"),
|
classDiv: getClassName("kcInputClassCheckbox"),
|
||||||
"classInput": getClassName("kcInputClassCheckboxInput"),
|
classInput: getClassName("kcInputClassCheckboxInput"),
|
||||||
"classLabel": getClassName("kcInputClassCheckboxLabel")
|
classLabel: getClassName("kcInputClassCheckboxLabel")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@ -581,9 +583,9 @@ function InputTagSelects(props: InputFiledByTypeProps) {
|
|||||||
checked={valueOrValues.includes(option)}
|
checked={valueOrValues.includes(option)}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": (() => {
|
valueOrValues: (() => {
|
||||||
const isChecked = event.target.checked;
|
const isChecked = event.target.checked;
|
||||||
|
|
||||||
if (valueOrValues instanceof Array) {
|
if (valueOrValues instanceof Array) {
|
||||||
@ -604,9 +606,9 @@ function InputTagSelects(props: InputFiledByTypeProps) {
|
|||||||
}
|
}
|
||||||
onBlur={() =>
|
onBlur={() =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "focus lost",
|
action: "focus lost",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"fieldIndex": undefined
|
fieldIndex: undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -642,16 +644,16 @@ function TextareaTag(props: InputFiledByTypeProps) {
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": event.target.value
|
valueOrValues: event.target.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onBlur={() =>
|
onBlur={() =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "focus lost",
|
action: "focus lost",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"fieldIndex": undefined
|
fieldIndex: undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -677,9 +679,9 @@ function SelectTag(props: InputFiledByTypeProps) {
|
|||||||
value={valueOrValues}
|
value={valueOrValues}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"valueOrValues": (() => {
|
valueOrValues: (() => {
|
||||||
if (isMultiple) {
|
if (isMultiple) {
|
||||||
return Array.from(event.target.selectedOptions).map(option => option.value);
|
return Array.from(event.target.selectedOptions).map(option => option.value);
|
||||||
}
|
}
|
||||||
@ -690,9 +692,9 @@ function SelectTag(props: InputFiledByTypeProps) {
|
|||||||
}
|
}
|
||||||
onBlur={() =>
|
onBlur={() =>
|
||||||
formValidationDispatch({
|
formValidationDispatch({
|
||||||
"action": "focus lost",
|
action: "focus lost",
|
||||||
"name": attribute.name,
|
name: attribute.name,
|
||||||
"fieldIndex": undefined
|
fieldIndex: undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -91,19 +91,19 @@ export function createUseI18n<ExtraMessageKey extends string = never>(extraMessa
|
|||||||
|
|
||||||
setI18n({
|
setI18n({
|
||||||
...createI18nTranslationFunctions({
|
...createI18nTranslationFunctions({
|
||||||
"fallbackMessages": {
|
fallbackMessages: {
|
||||||
...fallbackMessages,
|
...fallbackMessages,
|
||||||
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
||||||
...(extraMessages[fallbackLanguageTag] ?? {})
|
...(extraMessages[fallbackLanguageTag] ?? {})
|
||||||
} as any,
|
} as any,
|
||||||
"messages": {
|
messages: {
|
||||||
...(await getMessages(currentLanguageTag)),
|
...(await getMessages(currentLanguageTag)),
|
||||||
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
||||||
...(extraMessages[currentLanguageTag] ?? {})
|
...(extraMessages[currentLanguageTag] ?? {})
|
||||||
} as any
|
} as any
|
||||||
}),
|
}),
|
||||||
currentLanguageTag,
|
currentLanguageTag,
|
||||||
"getChangeLocalUrl": newLanguageTag => {
|
getChangeLocalUrl: newLanguageTag => {
|
||||||
const { locale } = kcContext;
|
const { locale } = kcContext;
|
||||||
|
|
||||||
assert(locale !== undefined, "Internationalization not enabled");
|
assert(locale !== undefined, "Internationalization not enabled");
|
||||||
@ -114,7 +114,7 @@ export function createUseI18n<ExtraMessageKey extends string = never>(extraMessa
|
|||||||
|
|
||||||
return targetSupportedLocale.url;
|
return targetSupportedLocale.url;
|
||||||
},
|
},
|
||||||
"labelBySupportedLanguageTag": Object.fromEntries(
|
labelBySupportedLanguageTag: Object.fromEntries(
|
||||||
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -170,7 +170,7 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return doRenderMarkdown ? (
|
return doRenderMarkdown ? (
|
||||||
<Markdown allowDangerousHtml renderers={{ "paragraph": "span" }}>
|
<Markdown allowDangerousHtml renderers={{ paragraph: "span" }}>
|
||||||
{messageWithArgsInjectedIfAny}
|
{messageWithArgsInjectedIfAny}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
) : (
|
) : (
|
||||||
@ -186,7 +186,7 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
|
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
|
||||||
|
|
||||||
const out = resolveMsg({
|
const out = resolveMsg({
|
||||||
"key": keyUnwrappedFromCurlyBraces,
|
key: keyUnwrappedFromCurlyBraces,
|
||||||
args,
|
args,
|
||||||
doRenderMarkdown
|
doRenderMarkdown
|
||||||
});
|
});
|
||||||
@ -195,38 +195,48 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"msgStr": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": false }) as string,
|
msgStr: (key, ...args) => resolveMsg({ key, args, doRenderMarkdown: false }) as string,
|
||||||
"msg": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
msg: (key, ...args) => resolveMsg({ key, args, doRenderMarkdown: true }) as JSX.Element,
|
||||||
"advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
advancedMsg: (key, ...args) =>
|
||||||
"advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": false }) as string
|
resolveMsgAdvanced({
|
||||||
|
key,
|
||||||
|
args,
|
||||||
|
doRenderMarkdown: true
|
||||||
|
}) as JSX.Element,
|
||||||
|
advancedMsgStr: (key, ...args) =>
|
||||||
|
resolveMsgAdvanced({
|
||||||
|
key,
|
||||||
|
args,
|
||||||
|
doRenderMarkdown: false
|
||||||
|
}) as string
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const keycloakifyExtraMessages = {
|
const keycloakifyExtraMessages = {
|
||||||
"en": {
|
en: {
|
||||||
"shouldBeEqual": "{0} should be equal to {1}",
|
shouldBeEqual: "{0} should be equal to {1}",
|
||||||
"shouldBeDifferent": "{0} should be different to {1}",
|
shouldBeDifferent: "{0} should be different to {1}",
|
||||||
"shouldMatchPattern": "Pattern should match: `/{0}/`",
|
shouldMatchPattern: "Pattern should match: `/{0}/`",
|
||||||
"mustBeAnInteger": "Must be an integer",
|
mustBeAnInteger: "Must be an integer",
|
||||||
"notAValidOption": "Not a valid option",
|
notAValidOption: "Not a valid option",
|
||||||
"selectAnOption": "Select an option",
|
selectAnOption: "Select an option",
|
||||||
"remove": "Remove",
|
remove: "Remove",
|
||||||
"addValue": "Add value"
|
addValue: "Add value"
|
||||||
},
|
},
|
||||||
"fr": {
|
fr: {
|
||||||
/* spell-checker: disable */
|
/* spell-checker: disable */
|
||||||
"shouldBeEqual": "{0} doit être égal à {1}",
|
shouldBeEqual: "{0} doit être égal à {1}",
|
||||||
"shouldBeDifferent": "{0} doit être différent de {1}",
|
shouldBeDifferent: "{0} doit être différent de {1}",
|
||||||
"shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
|
shouldMatchPattern: "Dois respecter le schéma: `/{0}/`",
|
||||||
"mustBeAnInteger": "Doit être un nombre entier",
|
mustBeAnInteger: "Doit être un nombre entier",
|
||||||
"notAValidOption": "N'est pas une option valide",
|
notAValidOption: "N'est pas une option valide",
|
||||||
|
|
||||||
"logoutConfirmTitle": "Déconnexion",
|
logoutConfirmTitle: "Déconnexion",
|
||||||
"logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
logoutConfirmHeader: "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
||||||
"doLogout": "Se déconnecter",
|
doLogout: "Se déconnecter",
|
||||||
"selectAnOption": "Sélectionner une option",
|
selectAnOption: "Sélectionner une option",
|
||||||
"remove": "Supprimer",
|
remove: "Supprimer",
|
||||||
"addValue": "Ajouter une valeur"
|
addValue: "Ajouter une valeur"
|
||||||
/* spell-checker: enable */
|
/* spell-checker: enable */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,10 @@ import { assert } from "tsafe/assert";
|
|||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
import type { MessageKey } from "../i18n/i18n";
|
import type { MessageKey } from "../i18n/i18n";
|
||||||
|
|
||||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
type ExtractAfterStartingWith<
|
||||||
|
Prefix extends string,
|
||||||
|
StrEnum
|
||||||
|
> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||||
|
|
||||||
/** Take theses type definition with a grain of salt.
|
/** Take theses type definition with a grain of salt.
|
||||||
* Some values might be undefined on some pages.
|
* Some values might be undefined on some pages.
|
||||||
@ -103,7 +106,10 @@ export declare namespace KcContext {
|
|||||||
* @param text to return
|
* @param text to return
|
||||||
* @return text if message exists for given field, else undefined
|
* @return text if message exists for given field, else undefined
|
||||||
*/
|
*/
|
||||||
printIfExists: <T extends string>(fieldName: string, text: T) => T | undefined;
|
printIfExists: <T extends string>(
|
||||||
|
fieldName: string,
|
||||||
|
text: T
|
||||||
|
) => T | undefined;
|
||||||
/**
|
/**
|
||||||
* Check if exists error message for given fields
|
* Check if exists error message for given fields
|
||||||
*
|
*
|
||||||
|
@ -10,15 +10,23 @@ import type { ExtendKcContext } from "./getKcContextFromWindow";
|
|||||||
import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
||||||
import { symToStr } from "tsafe/symToStr";
|
import { symToStr } from "tsafe/symToStr";
|
||||||
|
|
||||||
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function createGetKcContext<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(params?: {
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
mockProperties?: Record<string, string>;
|
mockProperties?: Record<string, string>;
|
||||||
}) {
|
}) {
|
||||||
const { mockData, mockProperties } = params ?? {};
|
const { mockData, mockProperties } = params ?? {};
|
||||||
|
|
||||||
function getKcContext<PageId extends ExtendKcContext<KcContextExtension>["pageId"] | undefined = undefined>(params?: {
|
function getKcContext<
|
||||||
|
PageId extends
|
||||||
|
| ExtendKcContext<KcContextExtension>["pageId"]
|
||||||
|
| undefined = undefined
|
||||||
|
>(params?: {
|
||||||
mockPageId?: PageId;
|
mockPageId?: PageId;
|
||||||
storyPartialKcContext?: DeepPartial<Extract<ExtendKcContext<KcContextExtension>, { pageId: PageId }>>;
|
storyPartialKcContext?: DeepPartial<
|
||||||
|
Extract<ExtendKcContext<KcContextExtension>, { pageId: PageId }>
|
||||||
|
>;
|
||||||
}): {
|
}): {
|
||||||
kcContext: PageId extends undefined
|
kcContext: PageId extends undefined
|
||||||
? ExtendKcContext<KcContextExtension> | undefined
|
? ExtendKcContext<KcContextExtension> | undefined
|
||||||
@ -36,34 +44,46 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
break warn_that_mock_is_enbaled;
|
break warn_that_mock_is_enbaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`%cKeycloakify: ${symToStr({ mockPageId })} set to ${mockPageId}.`, "background: red; color: yellow; font-size: medium");
|
console.log(
|
||||||
|
`%cKeycloakify: ${symToStr({
|
||||||
|
mockPageId
|
||||||
|
})} set to ${mockPageId}.`,
|
||||||
|
"background: red; color: yellow; font-size: medium"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
const kcContextDefaultMock = kcContextMocks.find(
|
||||||
|
({ pageId }) => pageId === mockPageId
|
||||||
|
);
|
||||||
|
|
||||||
const partialKcContextCustomMock = (() => {
|
const partialKcContextCustomMock = (() => {
|
||||||
const out: DeepPartial<ExtendKcContext<KcContextExtension>> = {};
|
const out: DeepPartial<ExtendKcContext<KcContextExtension>> = {};
|
||||||
|
|
||||||
const mockDataPick = mockData?.find(({ pageId }) => pageId === mockPageId);
|
const mockDataPick = mockData?.find(
|
||||||
|
({ pageId }) => pageId === mockPageId
|
||||||
|
);
|
||||||
|
|
||||||
if (mockDataPick !== undefined) {
|
if (mockDataPick !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": out,
|
target: out,
|
||||||
"source": mockDataPick
|
source: mockDataPick
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storyPartialKcContext !== undefined) {
|
if (storyPartialKcContext !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": out,
|
target: out,
|
||||||
"source": storyPartialKcContext
|
source: storyPartialKcContext
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(out).length === 0 ? undefined : out;
|
return Object.keys(out).length === 0 ? undefined : out;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (kcContextDefaultMock === undefined && partialKcContextCustomMock === undefined) {
|
if (
|
||||||
|
kcContextDefaultMock === undefined &&
|
||||||
|
partialKcContextCustomMock === undefined
|
||||||
|
) {
|
||||||
console.warn(
|
console.warn(
|
||||||
[
|
[
|
||||||
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
||||||
@ -76,68 +96,95 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
const kcContext: any = {};
|
const kcContext: any = {};
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
target: kcContext,
|
||||||
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock }
|
source:
|
||||||
|
kcContextDefaultMock !== undefined
|
||||||
|
? kcContextDefaultMock
|
||||||
|
: { pageId: mockPageId, ...kcContextCommonMock }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (partialKcContextCustomMock !== undefined) {
|
if (partialKcContextCustomMock !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
target: kcContext,
|
||||||
"source": partialKcContextCustomMock
|
source: partialKcContextCustomMock
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("profile" in partialKcContextCustomMock) {
|
if ("profile" in partialKcContextCustomMock) {
|
||||||
assert(kcContextDefaultMock !== undefined && "profile" in kcContextDefaultMock);
|
assert(
|
||||||
|
kcContextDefaultMock !== undefined &&
|
||||||
|
"profile" in kcContextDefaultMock
|
||||||
|
);
|
||||||
|
|
||||||
const { attributes } = kcContextDefaultMock.profile;
|
const { attributes } = kcContextDefaultMock.profile;
|
||||||
|
|
||||||
id<KcContext.Register>(kcContext).profile.attributes = [];
|
id<KcContext.Register>(kcContext).profile.attributes = [];
|
||||||
id<KcContext.Register>(kcContext).profile.attributesByName = {};
|
id<KcContext.Register>(kcContext).profile.attributesByName = {};
|
||||||
|
|
||||||
const partialAttributes = [...((partialKcContextCustomMock as DeepPartial<KcContext.Register>).profile?.attributes ?? [])].filter(
|
const partialAttributes = [
|
||||||
exclude(undefined)
|
...((
|
||||||
);
|
partialKcContextCustomMock as DeepPartial<KcContext.Register>
|
||||||
|
).profile?.attributes ?? [])
|
||||||
|
].filter(exclude(undefined));
|
||||||
|
|
||||||
attributes.forEach(attribute => {
|
attributes.forEach(attribute => {
|
||||||
const partialAttribute = partialAttributes.find(({ name }) => name === attribute.name);
|
const partialAttribute = partialAttributes.find(
|
||||||
|
({ name }) => name === attribute.name
|
||||||
|
);
|
||||||
|
|
||||||
const augmentedAttribute: Attribute = {} as any;
|
const augmentedAttribute: Attribute = {} as any;
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": augmentedAttribute,
|
target: augmentedAttribute,
|
||||||
"source": attribute
|
source: attribute
|
||||||
});
|
});
|
||||||
|
|
||||||
if (partialAttribute !== undefined) {
|
if (partialAttribute !== undefined) {
|
||||||
partialAttributes.splice(partialAttributes.indexOf(partialAttribute), 1);
|
partialAttributes.splice(
|
||||||
|
partialAttributes.indexOf(partialAttribute),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": augmentedAttribute,
|
target: augmentedAttribute,
|
||||||
"source": partialAttribute
|
source: partialAttribute
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
id<KcContext.Register>(kcContext).profile.attributes.push(augmentedAttribute);
|
id<KcContext.Register>(kcContext).profile.attributes.push(
|
||||||
id<KcContext.Register>(kcContext).profile.attributesByName[augmentedAttribute.name] = augmentedAttribute;
|
augmentedAttribute
|
||||||
|
);
|
||||||
|
id<KcContext.Register>(kcContext).profile.attributesByName[
|
||||||
|
augmentedAttribute.name
|
||||||
|
] = augmentedAttribute;
|
||||||
});
|
});
|
||||||
|
|
||||||
partialAttributes
|
partialAttributes
|
||||||
.map(partialAttribute => ({ "validators": {}, ...partialAttribute }))
|
.map(partialAttribute => ({
|
||||||
|
validators: {},
|
||||||
|
...partialAttribute
|
||||||
|
}))
|
||||||
.forEach(partialAttribute => {
|
.forEach(partialAttribute => {
|
||||||
const { name } = partialAttribute;
|
const { name } = partialAttribute;
|
||||||
|
|
||||||
assert(name !== undefined, "If you define a mock attribute it must have at least a name");
|
assert(
|
||||||
|
name !== undefined,
|
||||||
|
"If you define a mock attribute it must have at least a name"
|
||||||
|
);
|
||||||
|
|
||||||
id<KcContext.Register>(kcContext).profile.attributes.push(partialAttribute as any);
|
id<KcContext.Register>(kcContext).profile.attributes.push(
|
||||||
id<KcContext.Register>(kcContext).profile.attributesByName[name] = partialAttribute as any;
|
partialAttribute as any
|
||||||
|
);
|
||||||
|
id<KcContext.Register>(kcContext).profile.attributesByName[
|
||||||
|
name
|
||||||
|
] = partialAttribute as any;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mockProperties !== undefined) {
|
if (mockProperties !== undefined) {
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext.properties,
|
target: kcContext.properties,
|
||||||
"source": mockProperties
|
source: mockProperties
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,14 +192,14 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext === undefined) {
|
if (realKcContext === undefined) {
|
||||||
return { "kcContext": undefined as any };
|
return { kcContext: undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext.themeType !== "login") {
|
if (realKcContext.themeType !== "login") {
|
||||||
return { "kcContext": undefined as any };
|
return { kcContext: undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "kcContext": realKcContext as any };
|
return { kcContext: realKcContext as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { getKcContext };
|
return { getKcContext };
|
||||||
|
@ -5,7 +5,9 @@ import { createGetKcContext } from "./createGetKcContext";
|
|||||||
/** NOTE: We now recommend using createGetKcContext instead of this function to make storybook integration easier
|
/** NOTE: We now recommend using createGetKcContext instead of this function to make storybook integration easier
|
||||||
* See: https://github.com/keycloakify/keycloakify-starter/blob/main/src/keycloak-theme/account/kcContext.ts
|
* See: https://github.com/keycloakify/keycloakify-starter/blob/main/src/keycloak-theme/account/kcContext.ts
|
||||||
*/
|
*/
|
||||||
export function getKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function getKcContext<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(params?: {
|
||||||
mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
|
mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
}): { kcContext: ExtendKcContext<KcContextExtension> | undefined } {
|
}): { kcContext: ExtendKcContext<KcContextExtension> | undefined } {
|
||||||
|
@ -2,10 +2,14 @@ import type { KcContext } from "./KcContext";
|
|||||||
import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey";
|
import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey";
|
||||||
import { nameOfTheGlobal } from "keycloakify/bin/shared/constants";
|
import { nameOfTheGlobal } from "keycloakify/bin/shared/constants";
|
||||||
|
|
||||||
export type ExtendKcContext<KcContextExtension extends { pageId: string }> = [KcContextExtension] extends [never]
|
export type ExtendKcContext<KcContextExtension extends { pageId: string }> = [
|
||||||
|
KcContextExtension
|
||||||
|
] extends [never]
|
||||||
? KcContext
|
? KcContext
|
||||||
: AndByDiscriminatingKey<"pageId", KcContextExtension & KcContext.Common, KcContext>;
|
: AndByDiscriminatingKey<"pageId", KcContextExtension & KcContext.Common, KcContext>;
|
||||||
|
|
||||||
export function getKcContextFromWindow<KcContextExtension extends { pageId: string } = never>(): ExtendKcContext<KcContextExtension> | undefined {
|
export function getKcContextFromWindow<
|
||||||
|
KcContextExtension extends { pageId: string } = never
|
||||||
|
>(): ExtendKcContext<KcContextExtension> | undefined {
|
||||||
return typeof window === "undefined" ? undefined : (window as any)[nameOfTheGlobal];
|
return typeof window === "undefined" ? undefined : (window as any)[nameOfTheGlobal];
|
||||||
}
|
}
|
||||||
|
@ -1,324 +1,336 @@
|
|||||||
import "minimal-polyfills/Object.fromEntries";
|
import "minimal-polyfills/Object.fromEntries";
|
||||||
import type { KcContext, Attribute } from "./KcContext";
|
import type { KcContext, Attribute } from "./KcContext";
|
||||||
import { resources_common, keycloak_resources, type LoginThemePageId } from "keycloakify/bin/shared/constants";
|
import {
|
||||||
|
resources_common,
|
||||||
|
keycloak_resources,
|
||||||
|
type LoginThemePageId
|
||||||
|
} from "keycloakify/bin/shared/constants";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import { BASE_URL } from "keycloakify/lib/BASE_URL";
|
import { BASE_URL } from "keycloakify/lib/BASE_URL";
|
||||||
|
|
||||||
const attributes: Attribute[] = [
|
const attributes: Attribute[] = [
|
||||||
{
|
{
|
||||||
"validators": {
|
validators: {
|
||||||
"length": {
|
length: {
|
||||||
"ignore.empty.value": true,
|
"ignore.empty.value": true,
|
||||||
"min": "3",
|
min: "3",
|
||||||
"max": "255"
|
max: "255"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"displayName": "${username}",
|
displayName: "${username}",
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"required": true,
|
required: true,
|
||||||
"autocomplete": "username",
|
autocomplete: "username",
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"name": "username",
|
name: "username",
|
||||||
"value": "xxxx"
|
value: "xxxx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"validators": {
|
validators: {
|
||||||
"length": {
|
length: {
|
||||||
"max": "255",
|
max: "255",
|
||||||
"ignore.empty.value": true
|
"ignore.empty.value": true
|
||||||
},
|
},
|
||||||
"email": {
|
email: {
|
||||||
"ignore.empty.value": true
|
"ignore.empty.value": true
|
||||||
},
|
},
|
||||||
"pattern": {
|
pattern: {
|
||||||
"ignore.empty.value": true,
|
"ignore.empty.value": true,
|
||||||
"pattern": "gmail\\.com$"
|
pattern: "gmail\\.com$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"displayName": "${email}",
|
displayName: "${email}",
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"required": true,
|
required: true,
|
||||||
"autocomplete": "email",
|
autocomplete: "email",
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"name": "email"
|
name: "email"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"validators": {
|
validators: {
|
||||||
"length": {
|
length: {
|
||||||
"max": "255",
|
max: "255",
|
||||||
"ignore.empty.value": true
|
"ignore.empty.value": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"displayName": "${firstName}",
|
displayName: "${firstName}",
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"required": true,
|
required: true,
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"name": "firstName"
|
name: "firstName"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"validators": {
|
validators: {
|
||||||
"length": {
|
length: {
|
||||||
"max": "255",
|
max: "255",
|
||||||
"ignore.empty.value": true
|
"ignore.empty.value": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"displayName": "${lastName}",
|
displayName: "${lastName}",
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"required": true,
|
required: true,
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"name": "lastName"
|
name: "lastName"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const attributesByName = Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any;
|
const attributesByName = Object.fromEntries(
|
||||||
|
attributes.map(attribute => [attribute.name, attribute])
|
||||||
|
) as any;
|
||||||
|
|
||||||
const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
|
const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
|
||||||
|
|
||||||
export const kcContextCommonMock: KcContext.Common = {
|
export const kcContextCommonMock: KcContext.Common = {
|
||||||
"themeVersion": "0.0.0",
|
themeVersion: "0.0.0",
|
||||||
"keycloakifyVersion": "0.0.0",
|
keycloakifyVersion: "0.0.0",
|
||||||
"themeType": "login",
|
themeType: "login",
|
||||||
"themeName": "my-theme-name",
|
themeName: "my-theme-name",
|
||||||
"url": {
|
url: {
|
||||||
"loginAction": "#",
|
loginAction: "#",
|
||||||
resourcesPath,
|
resourcesPath,
|
||||||
"resourcesCommonPath": `${resourcesPath}/${resources_common}`,
|
resourcesCommonPath: `${resourcesPath}/${resources_common}`,
|
||||||
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
loginRestartFlowUrl:
|
||||||
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
"/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
"ssoLoginInOtherTabsUrl": "/auth/realms/myrealm/login-actions/switch?client_id=account&tab_id=HoAx28ja4xg"
|
loginUrl:
|
||||||
|
"/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
|
ssoLoginInOtherTabsUrl:
|
||||||
|
"/auth/realms/myrealm/login-actions/switch?client_id=account&tab_id=HoAx28ja4xg"
|
||||||
},
|
},
|
||||||
"realm": {
|
realm: {
|
||||||
"name": "myrealm",
|
name: "myrealm",
|
||||||
"displayName": "myrealm",
|
displayName: "myrealm",
|
||||||
"displayNameHtml": "myrealm",
|
displayNameHtml: "myrealm",
|
||||||
"internationalizationEnabled": true,
|
internationalizationEnabled: true,
|
||||||
"registrationEmailAsUsername": false
|
registrationEmailAsUsername: false
|
||||||
},
|
},
|
||||||
"messagesPerField": {
|
messagesPerField: {
|
||||||
"printIfExists": () => {
|
printIfExists: () => {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
"existsError": () => false,
|
existsError: () => false,
|
||||||
"get": fieldName => `Fake error for ${fieldName}`,
|
get: fieldName => `Fake error for ${fieldName}`,
|
||||||
"exists": () => false,
|
exists: () => false,
|
||||||
"getFirstError": fieldName => `Fake error for ${fieldName}`
|
getFirstError: fieldName => `Fake error for ${fieldName}`
|
||||||
},
|
},
|
||||||
"locale": {
|
locale: {
|
||||||
"supported": [
|
supported: [
|
||||||
/* spell-checker: disable */
|
/* spell-checker: disable */
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
||||||
"label": "Deutsch",
|
label: "Deutsch",
|
||||||
"languageTag": "de"
|
languageTag: "de"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
||||||
"label": "Norsk",
|
label: "Norsk",
|
||||||
"languageTag": "no"
|
languageTag: "no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
||||||
"label": "Русский",
|
label: "Русский",
|
||||||
"languageTag": "ru"
|
languageTag: "ru"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
||||||
"label": "Svenska",
|
label: "Svenska",
|
||||||
"languageTag": "sv"
|
languageTag: "sv"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
||||||
"label": "Português (Brasil)",
|
label: "Português (Brasil)",
|
||||||
"languageTag": "pt-BR"
|
languageTag: "pt-BR"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
||||||
"label": "Lietuvių",
|
label: "Lietuvių",
|
||||||
"languageTag": "lt"
|
languageTag: "lt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
||||||
"label": "English",
|
label: "English",
|
||||||
"languageTag": "en"
|
languageTag: "en"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
||||||
"label": "Italiano",
|
label: "Italiano",
|
||||||
"languageTag": "it"
|
languageTag: "it"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
||||||
"label": "Français",
|
label: "Français",
|
||||||
"languageTag": "fr"
|
languageTag: "fr"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
||||||
"label": "中文简体",
|
label: "中文简体",
|
||||||
"languageTag": "zh-CN"
|
languageTag: "zh-CN"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
||||||
"label": "Español",
|
label: "Español",
|
||||||
"languageTag": "es"
|
languageTag: "es"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
||||||
"label": "Čeština",
|
label: "Čeština",
|
||||||
"languageTag": "cs"
|
languageTag: "cs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
||||||
"label": "日本語",
|
label: "日本語",
|
||||||
"languageTag": "ja"
|
languageTag: "ja"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
||||||
"label": "Slovenčina",
|
label: "Slovenčina",
|
||||||
"languageTag": "sk"
|
languageTag: "sk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
||||||
"label": "Polski",
|
label: "Polski",
|
||||||
"languageTag": "pl"
|
languageTag: "pl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
||||||
"label": "Català",
|
label: "Català",
|
||||||
"languageTag": "ca"
|
languageTag: "ca"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
||||||
"label": "Nederlands",
|
label: "Nederlands",
|
||||||
"languageTag": "nl"
|
languageTag: "nl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
||||||
"label": "Türkçe",
|
label: "Türkçe",
|
||||||
"languageTag": "tr"
|
languageTag: "tr"
|
||||||
}
|
}
|
||||||
/* spell-checker: enable */
|
/* spell-checker: enable */
|
||||||
],
|
],
|
||||||
"currentLanguageTag": "en"
|
currentLanguageTag: "en"
|
||||||
},
|
},
|
||||||
"auth": {
|
auth: {
|
||||||
"showUsername": false,
|
showUsername: false,
|
||||||
"showResetCredentials": false,
|
showResetCredentials: false,
|
||||||
"showTryAnotherWayLink": false
|
showTryAnotherWayLink: false
|
||||||
},
|
},
|
||||||
"client": {
|
client: {
|
||||||
"clientId": "myApp",
|
clientId: "myApp",
|
||||||
"attributes": {}
|
attributes: {}
|
||||||
},
|
},
|
||||||
"scripts": [],
|
scripts: [],
|
||||||
"isAppInitiatedAction": false,
|
isAppInitiatedAction: false,
|
||||||
"properties": {}
|
properties: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loginUrl = {
|
const loginUrl = {
|
||||||
...kcContextCommonMock.url,
|
...kcContextCommonMock.url,
|
||||||
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
loginResetCredentialsUrl:
|
||||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg",
|
"/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
"oauth2DeviceVerificationAction": "/auth/realms/myrealm/device",
|
registrationUrl:
|
||||||
"oauthAction": "/auth/realms/myrealm/login-actions/consent?client_id=account&tab_id=HoAx28ja4xg"
|
"/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
|
oauth2DeviceVerificationAction: "/auth/realms/myrealm/device",
|
||||||
|
oauthAction:
|
||||||
|
"/auth/realms/myrealm/login-actions/consent?client_id=account&tab_id=HoAx28ja4xg"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const kcContextMocks = [
|
export const kcContextMocks = [
|
||||||
id<KcContext.Login>({
|
id<KcContext.Login>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login.ftl",
|
pageId: "login.ftl",
|
||||||
"url": loginUrl,
|
url: loginUrl,
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"loginWithEmailAllowed": true,
|
loginWithEmailAllowed: true,
|
||||||
"rememberMe": true,
|
rememberMe: true,
|
||||||
"password": true,
|
password: true,
|
||||||
"resetPasswordAllowed": true,
|
resetPasswordAllowed: true,
|
||||||
"registrationAllowed": true
|
registrationAllowed: true
|
||||||
},
|
},
|
||||||
"auth": kcContextCommonMock.auth!,
|
auth: kcContextCommonMock.auth!,
|
||||||
"social": {
|
social: {
|
||||||
"displayInfo": true
|
displayInfo: true
|
||||||
},
|
},
|
||||||
"usernameHidden": false,
|
usernameHidden: false,
|
||||||
"login": {},
|
login: {},
|
||||||
"registrationDisabled": false
|
registrationDisabled: false
|
||||||
}),
|
}),
|
||||||
id<KcContext.Register>({
|
id<KcContext.Register>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"url": {
|
url: {
|
||||||
...loginUrl,
|
...loginUrl,
|
||||||
"registrationAction":
|
registrationAction:
|
||||||
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
||||||
},
|
},
|
||||||
"isAppInitiatedAction": false,
|
isAppInitiatedAction: false,
|
||||||
"passwordRequired": true,
|
passwordRequired: true,
|
||||||
"recaptchaRequired": false,
|
recaptchaRequired: false,
|
||||||
"pageId": "register.ftl",
|
pageId: "register.ftl",
|
||||||
"profile": {
|
profile: {
|
||||||
attributes,
|
attributes,
|
||||||
attributesByName
|
attributesByName
|
||||||
},
|
},
|
||||||
"scripts": [
|
scripts: [
|
||||||
//"https://www.google.com/recaptcha/api.js"
|
//"https://www.google.com/recaptcha/api.js"
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
id<KcContext.Info>({
|
id<KcContext.Info>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "info.ftl",
|
pageId: "info.ftl",
|
||||||
"messageHeader": "<Message header>",
|
messageHeader: "<Message header>",
|
||||||
"requiredActions": undefined,
|
requiredActions: undefined,
|
||||||
"skipLink": false,
|
skipLink: false,
|
||||||
"actionUri": "#",
|
actionUri: "#",
|
||||||
"client": {
|
client: {
|
||||||
"clientId": "myApp",
|
clientId: "myApp",
|
||||||
"baseUrl": "#",
|
baseUrl: "#",
|
||||||
"attributes": {}
|
attributes: {}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.Error>({
|
id<KcContext.Error>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "error.ftl",
|
pageId: "error.ftl",
|
||||||
"client": {
|
client: {
|
||||||
"clientId": "myApp",
|
clientId: "myApp",
|
||||||
"baseUrl": "#",
|
baseUrl: "#",
|
||||||
"attributes": {}
|
attributes: {}
|
||||||
},
|
},
|
||||||
"message": {
|
message: {
|
||||||
"type": "error",
|
type: "error",
|
||||||
"summary": "This is the error message"
|
summary: "This is the error message"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginResetPassword>({
|
id<KcContext.LoginResetPassword>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-reset-password.ftl",
|
pageId: "login-reset-password.ftl",
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"loginWithEmailAllowed": false,
|
loginWithEmailAllowed: false,
|
||||||
"duplicateEmailsAllowed": false
|
duplicateEmailsAllowed: false
|
||||||
},
|
},
|
||||||
"url": loginUrl,
|
url: loginUrl,
|
||||||
"auth": {}
|
auth: {}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginVerifyEmail>({
|
id<KcContext.LoginVerifyEmail>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-verify-email.ftl",
|
pageId: "login-verify-email.ftl",
|
||||||
"user": {
|
user: {
|
||||||
"email": "john.doe@gmail.com"
|
email: "john.doe@gmail.com"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.Terms>({
|
id<KcContext.Terms>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "terms.ftl"
|
pageId: "terms.ftl"
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginDeviceVerifyUserCode>({
|
id<KcContext.LoginDeviceVerifyUserCode>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-oauth2-device-verify-user-code.ftl",
|
pageId: "login-oauth2-device-verify-user-code.ftl",
|
||||||
url: loginUrl
|
url: loginUrl
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginOauthGrant>({
|
id<KcContext.LoginOauthGrant>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-oauth-grant.ftl",
|
pageId: "login-oauth-grant.ftl",
|
||||||
oauth: {
|
oauth: {
|
||||||
code: "5-1N4CIzfi1aprIQjmylI-9e3spLCWW9i5d-GDcs-Sw",
|
code: "5-1N4CIzfi1aprIQjmylI-9e3spLCWW9i5d-GDcs-Sw",
|
||||||
clientScopesRequested: [
|
clientScopesRequested: [
|
||||||
@ -332,150 +344,152 @@ export const kcContextMocks = [
|
|||||||
}),
|
}),
|
||||||
id<KcContext.LoginOtp>({
|
id<KcContext.LoginOtp>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-otp.ftl",
|
pageId: "login-otp.ftl",
|
||||||
"otpLogin": {
|
otpLogin: {
|
||||||
"userOtpCredentials": [
|
userOtpCredentials: [
|
||||||
{
|
{
|
||||||
"id": "id1",
|
id: "id1",
|
||||||
"userLabel": "label1"
|
userLabel: "label1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "id2",
|
id: "id2",
|
||||||
"userLabel": "label2"
|
userLabel: "label2"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginUsername>({
|
id<KcContext.LoginUsername>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-username.ftl",
|
pageId: "login-username.ftl",
|
||||||
"url": loginUrl,
|
url: loginUrl,
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"loginWithEmailAllowed": true,
|
loginWithEmailAllowed: true,
|
||||||
"rememberMe": true,
|
rememberMe: true,
|
||||||
"password": true,
|
password: true,
|
||||||
"resetPasswordAllowed": true,
|
resetPasswordAllowed: true,
|
||||||
"registrationAllowed": true
|
registrationAllowed: true
|
||||||
},
|
},
|
||||||
"social": {
|
social: {
|
||||||
"displayInfo": true
|
displayInfo: true
|
||||||
},
|
},
|
||||||
"usernameHidden": false,
|
usernameHidden: false,
|
||||||
"login": {},
|
login: {},
|
||||||
"registrationDisabled": false
|
registrationDisabled: false
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginPassword>({
|
id<KcContext.LoginPassword>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-password.ftl",
|
pageId: "login-password.ftl",
|
||||||
"url": loginUrl,
|
url: loginUrl,
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"resetPasswordAllowed": true
|
resetPasswordAllowed: true
|
||||||
},
|
},
|
||||||
"social": {
|
social: {
|
||||||
"displayInfo": false
|
displayInfo: false
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.WebauthnAuthenticate>({
|
id<KcContext.WebauthnAuthenticate>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "webauthn-authenticate.ftl",
|
pageId: "webauthn-authenticate.ftl",
|
||||||
"url": loginUrl,
|
url: loginUrl,
|
||||||
"authenticators": {
|
authenticators: {
|
||||||
"authenticators": []
|
authenticators: []
|
||||||
},
|
},
|
||||||
"realm": {
|
realm: {
|
||||||
...kcContextCommonMock.realm,
|
...kcContextCommonMock.realm,
|
||||||
"password": true,
|
password: true,
|
||||||
"registrationAllowed": true
|
registrationAllowed: true
|
||||||
},
|
},
|
||||||
"challenge": "",
|
challenge: "",
|
||||||
"userVerification": "not specified",
|
userVerification: "not specified",
|
||||||
"rpId": "",
|
rpId: "",
|
||||||
"createTimeout": "0",
|
createTimeout: "0",
|
||||||
"isUserIdentified": "false",
|
isUserIdentified: "false",
|
||||||
"shouldDisplayAuthenticators": false,
|
shouldDisplayAuthenticators: false,
|
||||||
"social": {
|
social: {
|
||||||
"displayInfo": false
|
displayInfo: false
|
||||||
},
|
},
|
||||||
"login": {}
|
login: {}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginUpdatePassword>({
|
id<KcContext.LoginUpdatePassword>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-update-password.ftl"
|
pageId: "login-update-password.ftl"
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginUpdateProfile>({
|
id<KcContext.LoginUpdateProfile>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-update-profile.ftl",
|
pageId: "login-update-profile.ftl",
|
||||||
"profile": {
|
profile: {
|
||||||
attributes,
|
attributes,
|
||||||
attributesByName
|
attributesByName
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginIdpLinkConfirm>({
|
id<KcContext.LoginIdpLinkConfirm>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-idp-link-confirm.ftl",
|
pageId: "login-idp-link-confirm.ftl",
|
||||||
"idpAlias": "FranceConnect"
|
idpAlias: "FranceConnect"
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginIdpLinkEmail>({
|
id<KcContext.LoginIdpLinkEmail>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-idp-link-email.ftl",
|
pageId: "login-idp-link-email.ftl",
|
||||||
"idpAlias": "FranceConnect",
|
idpAlias: "FranceConnect",
|
||||||
"brokerContext": {
|
brokerContext: {
|
||||||
"username": "anUsername"
|
username: "anUsername"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginConfigTotp>({
|
id<KcContext.LoginConfigTotp>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-config-totp.ftl",
|
pageId: "login-config-totp.ftl",
|
||||||
"totp": {
|
totp: {
|
||||||
"totpSecretEncoded": "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||||
"qrUrl": "#",
|
qrUrl: "#",
|
||||||
"totpSecretQrCode":
|
totpSecretQrCode:
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
||||||
"manualUrl": "#",
|
manualUrl: "#",
|
||||||
"totpSecret": "G4nsI8lQagRMUchH8jEG",
|
totpSecret: "G4nsI8lQagRMUchH8jEG",
|
||||||
"otpCredentials": [],
|
otpCredentials: [],
|
||||||
"supportedApplications": ["FreeOTP", "Google Authenticator"],
|
supportedApplications: ["FreeOTP", "Google Authenticator"],
|
||||||
"policy": {
|
policy: {
|
||||||
"algorithm": "HmacSHA1",
|
algorithm: "HmacSHA1",
|
||||||
"digits": 6,
|
digits: 6,
|
||||||
"lookAheadWindow": 1,
|
lookAheadWindow: 1,
|
||||||
"type": "totp",
|
type: "totp",
|
||||||
"period": 30,
|
period: 30,
|
||||||
"getAlgorithmKey": () => "SHA1"
|
getAlgorithmKey: () => "SHA1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LogoutConfirm>({
|
id<KcContext.LogoutConfirm>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "logout-confirm.ftl",
|
pageId: "logout-confirm.ftl",
|
||||||
"url": {
|
url: {
|
||||||
...kcContextCommonMock.url,
|
...kcContextCommonMock.url,
|
||||||
"logoutConfirmAction": "Continuer?"
|
logoutConfirmAction: "Continuer?"
|
||||||
},
|
},
|
||||||
"client": {
|
client: {
|
||||||
"clientId": "myApp",
|
clientId: "myApp",
|
||||||
"baseUrl": "#",
|
baseUrl: "#",
|
||||||
"attributes": {}
|
attributes: {}
|
||||||
},
|
},
|
||||||
"logoutConfirm": { "code": "123", skipLink: false }
|
logoutConfirm: { code: "123", skipLink: false }
|
||||||
}),
|
}),
|
||||||
id<KcContext.IdpReviewUserProfile>({
|
id<KcContext.IdpReviewUserProfile>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "idp-review-user-profile.ftl",
|
pageId: "idp-review-user-profile.ftl",
|
||||||
"profile": {
|
profile: {
|
||||||
attributes,
|
attributes,
|
||||||
attributesByName
|
attributesByName
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.UpdateEmail>({
|
id<KcContext.UpdateEmail>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "update-email.ftl",
|
pageId: "update-email.ftl",
|
||||||
"profile": {
|
profile: {
|
||||||
"attributes": attributes.filter(attribute => attribute.name === "email"),
|
attributes: attributes.filter(attribute => attribute.name === "email"),
|
||||||
"attributesByName": Object.fromEntries(
|
attributesByName: Object.fromEntries(
|
||||||
attributes.filter(attribute => attribute.name === "email").map(attribute => [attribute.name, attribute])
|
attributes
|
||||||
|
.filter(attribute => attribute.name === "email")
|
||||||
|
.map(attribute => [attribute.name, attribute])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -502,8 +516,8 @@ export const kcContextMocks = [
|
|||||||
id<KcContext.SamlPostForm>({
|
id<KcContext.SamlPostForm>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
pageId: "saml-post-form.ftl",
|
pageId: "saml-post-form.ftl",
|
||||||
"samlPost": {
|
samlPost: {
|
||||||
"url": ""
|
url: ""
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginPageExpired>({
|
id<KcContext.LoginPageExpired>({
|
||||||
@ -514,107 +528,107 @@ export const kcContextMocks = [
|
|||||||
id<KcContext.FrontchannelLogout>({
|
id<KcContext.FrontchannelLogout>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
pageId: "frontchannel-logout.ftl",
|
pageId: "frontchannel-logout.ftl",
|
||||||
"logout": {
|
logout: {
|
||||||
"clients": [
|
clients: [
|
||||||
{
|
{
|
||||||
"name": "myApp",
|
name: "myApp",
|
||||||
"frontChannelLogoutUrl": "#"
|
frontChannelLogoutUrl: "#"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myApp2",
|
name: "myApp2",
|
||||||
"frontChannelLogoutUrl": "#"
|
frontChannelLogoutUrl: "#"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.WebauthnRegister>({
|
id<KcContext.WebauthnRegister>({
|
||||||
"pageId": "webauthn-register.ftl",
|
pageId: "webauthn-register.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"challenge": "random-challenge-string",
|
challenge: "random-challenge-string",
|
||||||
"userid": "user123",
|
userid: "user123",
|
||||||
"username": "johndoe",
|
username: "johndoe",
|
||||||
"signatureAlgorithms": ["ES256", "RS256"],
|
signatureAlgorithms: ["ES256", "RS256"],
|
||||||
"rpEntityName": "Example Corp",
|
rpEntityName: "Example Corp",
|
||||||
"rpId": "example.com",
|
rpId: "example.com",
|
||||||
"attestationConveyancePreference": "direct",
|
attestationConveyancePreference: "direct",
|
||||||
"authenticatorAttachment": "platform",
|
authenticatorAttachment: "platform",
|
||||||
"requireResidentKey": "required",
|
requireResidentKey: "required",
|
||||||
"userVerificationRequirement": "preferred",
|
userVerificationRequirement: "preferred",
|
||||||
"createTimeout": 60000,
|
createTimeout: 60000,
|
||||||
"excludeCredentialIds": "credId123,credId456",
|
excludeCredentialIds: "credId123,credId456",
|
||||||
"isSetRetry": false,
|
isSetRetry: false,
|
||||||
"isAppInitiatedAction": true
|
isAppInitiatedAction: true
|
||||||
}),
|
}),
|
||||||
id<KcContext.DeleteCredential>({
|
id<KcContext.DeleteCredential>({
|
||||||
"pageId": "delete-credential.ftl",
|
pageId: "delete-credential.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"credentialLabel": "myCredential"
|
credentialLabel: "myCredential"
|
||||||
}),
|
}),
|
||||||
id<KcContext.Code>({
|
id<KcContext.Code>({
|
||||||
"pageId": "code.ftl",
|
pageId: "code.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"code": {
|
code: {
|
||||||
"success": true,
|
success: true,
|
||||||
"code": "123456"
|
code: "123456"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.DeleteAccountConfirm>({
|
id<KcContext.DeleteAccountConfirm>({
|
||||||
"pageId": "delete-account-confirm.ftl",
|
pageId: "delete-account-confirm.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"triggered_from_aia": true
|
triggered_from_aia: true
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginRecoveryAuthnCodeConfig>({
|
id<KcContext.LoginRecoveryAuthnCodeConfig>({
|
||||||
"pageId": "login-recovery-authn-code-config.ftl",
|
pageId: "login-recovery-authn-code-config.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"recoveryAuthnCodesConfigBean": {
|
recoveryAuthnCodesConfigBean: {
|
||||||
"generatedRecoveryAuthnCodesList": ["code123", "code456", "code789"],
|
generatedRecoveryAuthnCodesList: ["code123", "code456", "code789"],
|
||||||
"generatedRecoveryAuthnCodesAsString": "code123, code456, code789",
|
generatedRecoveryAuthnCodesAsString: "code123, code456, code789",
|
||||||
"generatedAt": new Date().getTime()
|
generatedAt: new Date().getTime()
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginRecoveryAuthnCodeInput>({
|
id<KcContext.LoginRecoveryAuthnCodeInput>({
|
||||||
"pageId": "login-recovery-authn-code-input.ftl",
|
pageId: "login-recovery-authn-code-input.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"recoveryAuthnCodesInputBean": {
|
recoveryAuthnCodesInputBean: {
|
||||||
"codeNumber": 1234
|
codeNumber: 1234
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginResetOtp>({
|
id<KcContext.LoginResetOtp>({
|
||||||
"pageId": "login-reset-otp.ftl",
|
pageId: "login-reset-otp.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"configuredOtpCredentials": {
|
configuredOtpCredentials: {
|
||||||
"userOtpCredentials": [
|
userOtpCredentials: [
|
||||||
{
|
{
|
||||||
"id": "otpId1",
|
id: "otpId1",
|
||||||
"userLabel": "OTP Device 1"
|
userLabel: "OTP Device 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "otpId2",
|
id: "otpId2",
|
||||||
"userLabel": "OTP Device 2"
|
userLabel: "OTP Device 2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "otpId3",
|
id: "otpId3",
|
||||||
"userLabel": "Backup OTP"
|
userLabel: "Backup OTP"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"selectedCredentialId": "otpId2"
|
selectedCredentialId: "otpId2"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginX509Info>({
|
id<KcContext.LoginX509Info>({
|
||||||
"pageId": "login-x509-info.ftl",
|
pageId: "login-x509-info.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"x509": {
|
x509: {
|
||||||
"formData": {
|
formData: {
|
||||||
"subjectDN": "CN=John Doe, O=Example Corp, C=US",
|
subjectDN: "CN=John Doe, O=Example Corp, C=US",
|
||||||
"isUserEnabled": true,
|
isUserEnabled: true,
|
||||||
"username": "johndoe"
|
username: "johndoe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.WebauthnError>({
|
id<KcContext.WebauthnError>({
|
||||||
"pageId": "webauthn-error.ftl",
|
pageId: "webauthn-error.ftl",
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"isAppInitiatedAction": true
|
isAppInitiatedAction: true
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -33,7 +33,9 @@ export function useDownloadTerms(params: {
|
|||||||
const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
|
const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
|
||||||
|
|
||||||
const downloadTermMarkdownMemoized = useConst(() =>
|
const downloadTermMarkdownMemoized = useConst(() =>
|
||||||
memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag }))
|
memoize((currentLanguageTag: string) =>
|
||||||
|
downloadTermMarkdownConst({ currentLanguageTag })
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return { downloadTermMarkdownMemoized };
|
return { downloadTermMarkdownMemoized };
|
||||||
@ -41,9 +43,9 @@ export function useDownloadTerms(params: {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (kcContext.pageId === "terms.ftl" || kcContext.termsAcceptanceRequired) {
|
if (kcContext.pageId === "terms.ftl" || kcContext.termsAcceptanceRequired) {
|
||||||
downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then(
|
downloadTermMarkdownMemoized(
|
||||||
thermMarkdown => (evtTermsMarkdown.state = thermMarkdown)
|
kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag
|
||||||
);
|
).then(thermMarkdown => (evtTermsMarkdown.state = thermMarkdown));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
@ -2,131 +2,138 @@ import { createUseClassName } from "keycloakify/lib/useGetClassName";
|
|||||||
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
||||||
|
|
||||||
export const { useGetClassName } = createUseClassName<ClassKey>({
|
export const { useGetClassName } = createUseClassName<ClassKey>({
|
||||||
"defaultClasses": {
|
defaultClasses: {
|
||||||
"kcBodyClass": undefined,
|
kcBodyClass: undefined,
|
||||||
"kcHeaderWrapperClass": undefined,
|
kcHeaderWrapperClass: undefined,
|
||||||
"kcLocaleWrapperClass": undefined,
|
kcLocaleWrapperClass: undefined,
|
||||||
"kcInfoAreaWrapperClass": undefined,
|
kcInfoAreaWrapperClass: undefined,
|
||||||
"kcFormButtonsWrapperClass": undefined,
|
kcFormButtonsWrapperClass: undefined,
|
||||||
"kcFormOptionsWrapperClass": undefined,
|
kcFormOptionsWrapperClass: undefined,
|
||||||
"kcLocaleDropDownClass": undefined,
|
kcLocaleDropDownClass: undefined,
|
||||||
"kcLocaleListItemClass": undefined,
|
kcLocaleListItemClass: undefined,
|
||||||
"kcContentWrapperClass": undefined,
|
kcContentWrapperClass: undefined,
|
||||||
"kcCheckboxInputClass": undefined,
|
kcCheckboxInputClass: undefined,
|
||||||
|
|
||||||
"kcLogoIdP-facebook": "fa fa-facebook",
|
"kcLogoIdP-facebook": "fa fa-facebook",
|
||||||
"kcAuthenticatorOTPClass": "fa fa-mobile list-view-pf-icon-lg",
|
kcAuthenticatorOTPClass: "fa fa-mobile list-view-pf-icon-lg",
|
||||||
"kcLogoIdP-bitbucket": "fa fa-bitbucket",
|
"kcLogoIdP-bitbucket": "fa fa-bitbucket",
|
||||||
"kcAuthenticatorWebAuthnClass": "fa fa-key list-view-pf-icon-lg",
|
kcAuthenticatorWebAuthnClass: "fa fa-key list-view-pf-icon-lg",
|
||||||
"kcWebAuthnDefaultIcon": "pficon pficon-key",
|
kcWebAuthnDefaultIcon: "pficon pficon-key",
|
||||||
"kcLogoIdP-stackoverflow": "fa fa-stack-overflow",
|
"kcLogoIdP-stackoverflow": "fa fa-stack-overflow",
|
||||||
"kcSelectAuthListItemClass": "pf-l-stack__item select-auth-box-parent pf-l-split",
|
kcSelectAuthListItemClass: "pf-l-stack__item select-auth-box-parent pf-l-split",
|
||||||
"kcLogoIdP-microsoft": "fa fa-windows",
|
"kcLogoIdP-microsoft": "fa fa-windows",
|
||||||
"kcLoginOTPListItemHeaderClass": "pf-c-tile__header",
|
kcLoginOTPListItemHeaderClass: "pf-c-tile__header",
|
||||||
"kcLocaleItemClass": "pf-c-dropdown__menu-item",
|
kcLocaleItemClass: "pf-c-dropdown__menu-item",
|
||||||
"kcLoginOTPListItemIconBodyClass": "pf-c-tile__icon",
|
kcLoginOTPListItemIconBodyClass: "pf-c-tile__icon",
|
||||||
"kcInputHelperTextAfterClass": "pf-c-form__helper-text pf-c-form__helper-text-after",
|
kcInputHelperTextAfterClass:
|
||||||
"kcFormClass": "form-horizontal",
|
"pf-c-form__helper-text pf-c-form__helper-text-after",
|
||||||
"kcSelectAuthListClass": "pf-l-stack select-auth-container",
|
kcFormClass: "form-horizontal",
|
||||||
"kcInputClassRadioCheckboxLabelDisabled": "pf-m-disabled",
|
kcSelectAuthListClass: "pf-l-stack select-auth-container",
|
||||||
"kcSelectAuthListItemIconClass": "pf-l-split__item select-auth-box-icon",
|
kcInputClassRadioCheckboxLabelDisabled: "pf-m-disabled",
|
||||||
"kcRecoveryCodesWarning": "kc-recovery-codes-warning",
|
kcSelectAuthListItemIconClass: "pf-l-split__item select-auth-box-icon",
|
||||||
"kcFormSettingClass": "login-pf-settings",
|
kcRecoveryCodesWarning: "kc-recovery-codes-warning",
|
||||||
"kcWebAuthnBLE": "fa fa-bluetooth-b",
|
kcFormSettingClass: "login-pf-settings",
|
||||||
"kcInputWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
kcWebAuthnBLE: "fa fa-bluetooth-b",
|
||||||
"kcSelectAuthListItemArrowIconClass": "fa fa-angle-right fa-lg",
|
kcInputWrapperClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||||
"kcFeedbackAreaClass": "col-md-12",
|
kcSelectAuthListItemArrowIconClass: "fa fa-angle-right fa-lg",
|
||||||
"kcFormPasswordVisibilityButtonClass": "pf-c-button pf-m-control",
|
kcFeedbackAreaClass: "col-md-12",
|
||||||
|
kcFormPasswordVisibilityButtonClass: "pf-c-button pf-m-control",
|
||||||
"kcLogoIdP-google": "fa fa-google",
|
"kcLogoIdP-google": "fa fa-google",
|
||||||
"kcCheckLabelClass": "pf-c-check__label",
|
kcCheckLabelClass: "pf-c-check__label",
|
||||||
"kcSelectAuthListItemFillClass": "pf-l-split__item pf-m-fill",
|
kcSelectAuthListItemFillClass: "pf-l-split__item pf-m-fill",
|
||||||
"kcAuthenticatorDefaultClass": "fa fa-list list-view-pf-icon-lg",
|
kcAuthenticatorDefaultClass: "fa fa-list list-view-pf-icon-lg",
|
||||||
"kcLogoIdP-gitlab": "fa fa-gitlab",
|
"kcLogoIdP-gitlab": "fa fa-gitlab",
|
||||||
"kcFormAreaClass": "col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2",
|
kcFormAreaClass:
|
||||||
"kcFormButtonsClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
"col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2",
|
||||||
"kcInputClassRadioLabel": "pf-c-radio__label",
|
kcFormButtonsClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||||
"kcAuthenticatorWebAuthnPasswordlessClass": "fa fa-key list-view-pf-icon-lg",
|
kcInputClassRadioLabel: "pf-c-radio__label",
|
||||||
"kcSelectAuthListItemHeadingClass": "pf-l-stack__item select-auth-box-headline pf-c-title",
|
kcAuthenticatorWebAuthnPasswordlessClass: "fa fa-key list-view-pf-icon-lg",
|
||||||
"kcInfoAreaClass": "col-xs-12 col-sm-4 col-md-4 col-lg-5 details",
|
kcSelectAuthListItemHeadingClass:
|
||||||
"kcLogoLink": "http://www.keycloak.org",
|
"pf-l-stack__item select-auth-box-headline pf-c-title",
|
||||||
"kcContainerClass": "container-fluid",
|
kcInfoAreaClass: "col-xs-12 col-sm-4 col-md-4 col-lg-5 details",
|
||||||
"kcSelectAuthListItemTitle": "select-auth-box-paragraph",
|
kcLogoLink: "http://www.keycloak.org",
|
||||||
"kcHtmlClass": "login-pf",
|
kcContainerClass: "container-fluid",
|
||||||
"kcLoginOTPListItemTitleClass": "pf-c-tile__title",
|
kcSelectAuthListItemTitle: "select-auth-box-paragraph",
|
||||||
|
kcHtmlClass: "login-pf",
|
||||||
|
kcLoginOTPListItemTitleClass: "pf-c-tile__title",
|
||||||
"kcLogoIdP-openshift-v4": "pf-icon pf-icon-openshift",
|
"kcLogoIdP-openshift-v4": "pf-icon pf-icon-openshift",
|
||||||
"kcWebAuthnUnknownIcon": "pficon pficon-key unknown-transport-class",
|
kcWebAuthnUnknownIcon: "pficon pficon-key unknown-transport-class",
|
||||||
"kcFormSocialAccountNameClass": "kc-social-provider-name",
|
kcFormSocialAccountNameClass: "kc-social-provider-name",
|
||||||
"kcLogoIdP-openshift-v3": "pf-icon pf-icon-openshift",
|
"kcLogoIdP-openshift-v3": "pf-icon pf-icon-openshift",
|
||||||
"kcLoginOTPListInputClass": "pf-c-tile__input",
|
kcLoginOTPListInputClass: "pf-c-tile__input",
|
||||||
"kcWebAuthnUSB": "fa fa-usb",
|
kcWebAuthnUSB: "fa fa-usb",
|
||||||
"kcInputClassRadio": "pf-c-radio",
|
kcInputClassRadio: "pf-c-radio",
|
||||||
"kcWebAuthnKeyIcon": "pficon pficon-key",
|
kcWebAuthnKeyIcon: "pficon pficon-key",
|
||||||
"kcFeedbackInfoIcon": "fa fa-fw fa-info-circle",
|
kcFeedbackInfoIcon: "fa fa-fw fa-info-circle",
|
||||||
"kcCommonLogoIdP": "kc-social-provider-logo kc-social-gray",
|
kcCommonLogoIdP: "kc-social-provider-logo kc-social-gray",
|
||||||
"kcRecoveryCodesActions": "kc-recovery-codes-actions",
|
kcRecoveryCodesActions: "kc-recovery-codes-actions",
|
||||||
"kcFormGroupHeader": "pf-c-form__group",
|
kcFormGroupHeader: "pf-c-form__group",
|
||||||
"kcFormSocialAccountSectionClass": "kc-social-section kc-social-gray",
|
kcFormSocialAccountSectionClass: "kc-social-section kc-social-gray",
|
||||||
"kcLogoIdP-instagram": "fa fa-instagram",
|
"kcLogoIdP-instagram": "fa fa-instagram",
|
||||||
"kcAlertClass": "pf-c-alert pf-m-inline",
|
kcAlertClass: "pf-c-alert pf-m-inline",
|
||||||
"kcHeaderClass": "login-pf-page-header",
|
kcHeaderClass: "login-pf-page-header",
|
||||||
"kcLabelWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
kcLabelWrapperClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||||
"kcFormPasswordVisibilityIconShow": "fa fa-eye",
|
kcFormPasswordVisibilityIconShow: "fa fa-eye",
|
||||||
"kcFormSocialAccountLinkClass": "pf-c-login__main-footer-links-item-link",
|
kcFormSocialAccountLinkClass: "pf-c-login__main-footer-links-item-link",
|
||||||
"kcLocaleMainClass": "pf-c-dropdown",
|
kcLocaleMainClass: "pf-c-dropdown",
|
||||||
"kcInputGroup": "pf-c-input-group",
|
kcInputGroup: "pf-c-input-group",
|
||||||
"kcTextareaClass": "form-control",
|
kcTextareaClass: "form-control",
|
||||||
"kcButtonBlockClass": "pf-m-block",
|
kcButtonBlockClass: "pf-m-block",
|
||||||
"kcButtonClass": "pf-c-button",
|
kcButtonClass: "pf-c-button",
|
||||||
"kcWebAuthnNFC": "fa fa-wifi",
|
kcWebAuthnNFC: "fa fa-wifi",
|
||||||
"kcLocaleClass": "col-xs-12 col-sm-1",
|
kcLocaleClass: "col-xs-12 col-sm-1",
|
||||||
"kcInputClassCheckboxInput": "pf-c-check__input",
|
kcInputClassCheckboxInput: "pf-c-check__input",
|
||||||
"kcFeedbackErrorIcon": "fa fa-fw fa-exclamation-circle",
|
kcFeedbackErrorIcon: "fa fa-fw fa-exclamation-circle",
|
||||||
"kcInputLargeClass": "input-lg",
|
kcInputLargeClass: "input-lg",
|
||||||
"kcInputErrorMessageClass": "pf-c-form__helper-text pf-m-error required kc-feedback-text",
|
kcInputErrorMessageClass:
|
||||||
"kcRecoveryCodesList": "kc-recovery-codes-list",
|
"pf-c-form__helper-text pf-m-error required kc-feedback-text",
|
||||||
"kcFormSocialAccountListClass": "pf-c-login__main-footer-links kc-social-links",
|
kcRecoveryCodesList: "kc-recovery-codes-list",
|
||||||
"kcAlertTitleClass": "pf-c-alert__title kc-feedback-text",
|
kcFormSocialAccountListClass: "pf-c-login__main-footer-links kc-social-links",
|
||||||
"kcAuthenticatorPasswordClass": "fa fa-unlock list-view-pf-icon-lg",
|
kcAlertTitleClass: "pf-c-alert__title kc-feedback-text",
|
||||||
"kcCheckInputClass": "pf-c-check__input",
|
kcAuthenticatorPasswordClass: "fa fa-unlock list-view-pf-icon-lg",
|
||||||
|
kcCheckInputClass: "pf-c-check__input",
|
||||||
"kcLogoIdP-linkedin": "fa fa-linkedin",
|
"kcLogoIdP-linkedin": "fa fa-linkedin",
|
||||||
"kcLogoIdP-twitter": "fa fa-twitter",
|
"kcLogoIdP-twitter": "fa fa-twitter",
|
||||||
"kcFeedbackWarningIcon": "fa fa-fw fa-exclamation-triangle",
|
kcFeedbackWarningIcon: "fa fa-fw fa-exclamation-triangle",
|
||||||
"kcResetFlowIcon": "pficon pficon-arrow fa",
|
kcResetFlowIcon: "pficon pficon-arrow fa",
|
||||||
"kcSelectAuthListItemIconPropertyClass": "fa-2x select-auth-box-icon-properties",
|
kcSelectAuthListItemIconPropertyClass: "fa-2x select-auth-box-icon-properties",
|
||||||
"kcFeedbackSuccessIcon": "fa fa-fw fa-check-circle",
|
kcFeedbackSuccessIcon: "fa fa-fw fa-check-circle",
|
||||||
"kcLoginOTPListClass": "pf-c-tile",
|
kcLoginOTPListClass: "pf-c-tile",
|
||||||
"kcSrOnlyClass": "sr-only",
|
kcSrOnlyClass: "sr-only",
|
||||||
"kcFormSocialAccountListGridClass": "pf-l-grid kc-social-grid",
|
kcFormSocialAccountListGridClass: "pf-l-grid kc-social-grid",
|
||||||
"kcButtonDefaultClass": "btn-default",
|
kcButtonDefaultClass: "btn-default",
|
||||||
"kcFormGroupErrorClass": "has-error",
|
kcFormGroupErrorClass: "has-error",
|
||||||
"kcSelectAuthListItemDescriptionClass": "pf-l-stack__item select-auth-box-desc",
|
kcSelectAuthListItemDescriptionClass: "pf-l-stack__item select-auth-box-desc",
|
||||||
"kcSelectAuthListItemBodyClass": "pf-l-split__item pf-l-stack",
|
kcSelectAuthListItemBodyClass: "pf-l-split__item pf-l-stack",
|
||||||
"kcWebAuthnInternal": "pficon pficon-key",
|
kcWebAuthnInternal: "pficon pficon-key",
|
||||||
"kcSelectAuthListItemArrowClass": "pf-l-split__item select-auth-box-arrow",
|
kcSelectAuthListItemArrowClass: "pf-l-split__item select-auth-box-arrow",
|
||||||
"kcCheckClass": "pf-c-check",
|
kcCheckClass: "pf-c-check",
|
||||||
"kcContentClass": "col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3",
|
kcContentClass:
|
||||||
"kcLogoClass": "login-pf-brand",
|
"col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3",
|
||||||
"kcLoginOTPListItemIconClass": "fa fa-mobile",
|
kcLogoClass: "login-pf-brand",
|
||||||
"kcLoginClass": "login-pf-page",
|
kcLoginOTPListItemIconClass: "fa fa-mobile",
|
||||||
"kcSignUpClass": "login-pf-signup",
|
kcLoginClass: "login-pf-page",
|
||||||
"kcButtonLargeClass": "btn-lg",
|
kcSignUpClass: "login-pf-signup",
|
||||||
"kcFormCardClass": "card-pf",
|
kcButtonLargeClass: "btn-lg",
|
||||||
"kcLocaleListClass": "pf-c-dropdown__menu pf-m-align-right",
|
kcFormCardClass: "card-pf",
|
||||||
"kcInputClass": "pf-c-form-control",
|
kcLocaleListClass: "pf-c-dropdown__menu pf-m-align-right",
|
||||||
"kcFormGroupClass": "form-group",
|
kcInputClass: "pf-c-form-control",
|
||||||
|
kcFormGroupClass: "form-group",
|
||||||
"kcLogoIdP-paypal": "fa fa-paypal",
|
"kcLogoIdP-paypal": "fa fa-paypal",
|
||||||
"kcInputClassCheckbox": "pf-c-check",
|
kcInputClassCheckbox: "pf-c-check",
|
||||||
"kcRecoveryCodesConfirmation": "kc-recovery-codes-confirmation",
|
kcRecoveryCodesConfirmation: "kc-recovery-codes-confirmation",
|
||||||
"kcFormPasswordVisibilityIconHide": "fa fa-eye-slash",
|
kcFormPasswordVisibilityIconHide: "fa fa-eye-slash",
|
||||||
"kcInputClassRadioInput": "pf-c-radio__input",
|
kcInputClassRadioInput: "pf-c-radio__input",
|
||||||
"kcFormSocialAccountListButtonClass": "pf-c-button pf-m-control pf-m-block kc-social-item kc-social-gray",
|
kcFormSocialAccountListButtonClass:
|
||||||
"kcInputClassCheckboxLabel": "pf-c-check__label",
|
"pf-c-button pf-m-control pf-m-block kc-social-item kc-social-gray",
|
||||||
"kcFormOptionsClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
kcInputClassCheckboxLabel: "pf-c-check__label",
|
||||||
"kcFormHeaderClass": "login-pf-header",
|
kcFormOptionsClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||||
"kcFormSocialAccountGridItem": "pf-l-grid__item",
|
kcFormHeaderClass: "login-pf-header",
|
||||||
"kcButtonPrimaryClass": "pf-m-primary",
|
kcFormSocialAccountGridItem: "pf-l-grid__item",
|
||||||
"kcInputHelperTextBeforeClass": "pf-c-form__helper-text pf-c-form__helper-text-before",
|
kcButtonPrimaryClass: "pf-m-primary",
|
||||||
|
kcInputHelperTextBeforeClass:
|
||||||
|
"pf-c-form__helper-text pf-c-form__helper-text-before",
|
||||||
"kcLogoIdP-github": "fa fa-github",
|
"kcLogoIdP-github": "fa fa-github",
|
||||||
"kcLabelClass": "pf-c-form__label pf-c-form__label-text"
|
kcLabelClass: "pf-c-form__label pf-c-form__label-text"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -108,11 +108,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
const { kcContext, i18n, doMakeUserConfirmPassword } = params;
|
const { kcContext, i18n, doMakeUserConfirmPassword } = params;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
"scriptTags": Object.keys(kcContext.profile?.html5DataAnnotations ?? {})
|
scriptTags: Object.keys(kcContext.profile?.html5DataAnnotations ?? {})
|
||||||
.filter(key => key !== "kcMultivalued" && key !== "kcNumberFormat") // NOTE: Keycloakify handles it.
|
.filter(key => key !== "kcMultivalued" && key !== "kcNumberFormat") // NOTE: Keycloakify handles it.
|
||||||
.map(key => ({
|
.map(key => ({
|
||||||
"type": "module",
|
type: "module",
|
||||||
"src": `${kcContext.url.resourcesPath}/js/${key}.js`
|
src: `${kcContext.url.resourcesPath}/js/${key}.js`
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -146,15 +146,15 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
.filter(name => (name !== "username" ? true : !kcContext.realm.registrationEmailAsUsername))
|
.filter(name => (name !== "username" ? true : !kcContext.realm.registrationEmailAsUsername))
|
||||||
.map(name =>
|
.map(name =>
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
"name": name,
|
name: name,
|
||||||
"displayName": id<`\${${MessageKey}}`>(`\${${name}}`),
|
displayName: id<`\${${MessageKey}}`>(`\${${name}}`),
|
||||||
"required": true,
|
required: true,
|
||||||
"value": (kcContext.register as any).formData[name] ?? "",
|
value: (kcContext.register as any).formData[name] ?? "",
|
||||||
"html5DataAnnotations": {},
|
html5DataAnnotations: {},
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"validators": {},
|
validators: {},
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"autocomplete": (() => {
|
autocomplete: (() => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "email":
|
case "email":
|
||||||
return "email";
|
return "email";
|
||||||
@ -174,15 +174,15 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
.filter(name => (name !== "username" ? true : (kcContext.user as any).editUsernameAllowed))
|
.filter(name => (name !== "username" ? true : (kcContext.user as any).editUsernameAllowed))
|
||||||
.map(name =>
|
.map(name =>
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
"name": name,
|
name: name,
|
||||||
"displayName": id<`\${${MessageKey}}`>(`\${${name}}`),
|
displayName: id<`\${${MessageKey}}`>(`\${${name}}`),
|
||||||
"required": true,
|
required: true,
|
||||||
"value": (kcContext as any).user[name] ?? "",
|
value: (kcContext as any).user[name] ?? "",
|
||||||
"html5DataAnnotations": {},
|
html5DataAnnotations: {},
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"validators": {},
|
validators: {},
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"autocomplete": (() => {
|
autocomplete: (() => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "email":
|
case "email":
|
||||||
return "email";
|
return "email";
|
||||||
@ -200,15 +200,15 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
//NOTE: Handle legacy update-email.ftl
|
//NOTE: Handle legacy update-email.ftl
|
||||||
return [
|
return [
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
"name": "email",
|
name: "email",
|
||||||
"displayName": id<`\${${MessageKey}}`>(`\${email}`),
|
displayName: id<`\${${MessageKey}}`>(`\${email}`),
|
||||||
"required": true,
|
required: true,
|
||||||
"value": (kcContext.email as any).value ?? "",
|
value: (kcContext.email as any).value ?? "",
|
||||||
"html5DataAnnotations": {},
|
html5DataAnnotations: {},
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"validators": {},
|
validators: {},
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"autocomplete": "email"
|
autocomplete: "email"
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -228,11 +228,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
|
|
||||||
return id<Attribute>({
|
return id<Attribute>({
|
||||||
...rest,
|
...rest,
|
||||||
"group": {
|
group: {
|
||||||
"name": group,
|
name: group,
|
||||||
"displayHeader": groupDisplayHeader,
|
displayHeader: groupDisplayHeader,
|
||||||
"displayDescription": groupDisplayDescription,
|
displayDescription: groupDisplayDescription,
|
||||||
"html5DataAnnotations": {}
|
html5DataAnnotations: {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -257,28 +257,28 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
|
|
||||||
syntheticAttributes.push(
|
syntheticAttributes.push(
|
||||||
{
|
{
|
||||||
"name": "password",
|
name: "password",
|
||||||
"displayName": id<`\${${MessageKey}}`>("${password}"),
|
displayName: id<`\${${MessageKey}}`>("${password}"),
|
||||||
"required": true,
|
required: true,
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"validators": {},
|
validators: {},
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"autocomplete": "new-password",
|
autocomplete: "new-password",
|
||||||
"html5DataAnnotations": {},
|
html5DataAnnotations: {},
|
||||||
// NOTE: Compat with Keycloak version prior to 24
|
// NOTE: Compat with Keycloak version prior to 24
|
||||||
...({ "groupAnnotations": {} } as {})
|
...({ groupAnnotations: {} } as {})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "password-confirm",
|
name: "password-confirm",
|
||||||
"displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"),
|
displayName: id<`\${${MessageKey}}`>("${passwordConfirm}"),
|
||||||
"required": true,
|
required: true,
|
||||||
"readOnly": false,
|
readOnly: false,
|
||||||
"validators": {},
|
validators: {},
|
||||||
"annotations": {},
|
annotations: {},
|
||||||
"html5DataAnnotations": {},
|
html5DataAnnotations: {},
|
||||||
"autocomplete": "new-password",
|
autocomplete: "new-password",
|
||||||
// NOTE: Compat with Keycloak version prior to 24
|
// NOTE: Compat with Keycloak version prior to 24
|
||||||
...({ "groupAnnotations": {} } as {})
|
...({ groupAnnotations: {} } as {})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -288,7 +288,10 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const initialFormFieldState = (() => {
|
const initialFormFieldState = (() => {
|
||||||
const out: { attribute: Attribute; valueOrValues: string | string[] }[] = [];
|
const out: {
|
||||||
|
attribute: Attribute;
|
||||||
|
valueOrValues: string | string[];
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
for (const attribute of syntheticAttributes) {
|
for (const attribute of syntheticAttributes) {
|
||||||
handle_multi_valued_attribute: {
|
handle_multi_valued_attribute: {
|
||||||
@ -324,7 +327,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
attribute,
|
attribute,
|
||||||
"valueOrValues": values
|
valueOrValues: values
|
||||||
});
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -332,7 +335,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
attribute,
|
attribute,
|
||||||
"valueOrValues": attribute.value ?? ""
|
valueOrValues: attribute.value ?? ""
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,14 +343,14 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const initialState: internal.State = {
|
const initialState: internal.State = {
|
||||||
"formFieldStates": initialFormFieldState.map(({ attribute, valueOrValues }) => ({
|
formFieldStates: initialFormFieldState.map(({ attribute, valueOrValues }) => ({
|
||||||
attribute,
|
attribute,
|
||||||
"errors": getErrors({
|
errors: getErrors({
|
||||||
"attributeName": attribute.name,
|
attributeName: attribute.name,
|
||||||
"formFieldStates": initialFormFieldState
|
formFieldStates: initialFormFieldState
|
||||||
}),
|
}),
|
||||||
"hasLostFocusAtLeastOnce": valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
|
hasLostFocusAtLeastOnce: valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
|
||||||
"valueOrValues": valueOrValues
|
valueOrValues: valueOrValues
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -381,8 +384,8 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
formFieldState.errors = getErrors({
|
formFieldState.errors = getErrors({
|
||||||
"attributeName": formAction.name,
|
attributeName: formAction.name,
|
||||||
"formFieldStates": state.formFieldStates
|
formFieldStates: state.formFieldStates
|
||||||
});
|
});
|
||||||
|
|
||||||
update_password_confirm: {
|
update_password_confirm: {
|
||||||
@ -395,9 +398,9 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = reducer(state, {
|
state = reducer(state, {
|
||||||
"action": "update",
|
action: "update",
|
||||||
"name": "password-confirm",
|
name: "password-confirm",
|
||||||
"valueOrValues": formAction.valueOrValues
|
valueOrValues: formAction.valueOrValues
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,9 +424,9 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
|
|
||||||
const formState: FormState = useMemo(
|
const formState: FormState = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
"formFieldStates": state.formFieldStates.map(
|
formFieldStates: state.formFieldStates.map(
|
||||||
({ errors, hasLostFocusAtLeastOnce: hasLostFocusAtLeastOnceOrArr, attribute, ...valueOrValuesWrap }) => ({
|
({ errors, hasLostFocusAtLeastOnce: hasLostFocusAtLeastOnceOrArr, attribute, ...valueOrValuesWrap }) => ({
|
||||||
"displayableErrors": errors.filter(error => {
|
displayableErrors: errors.filter(error => {
|
||||||
const hasLostFocusAtLeastOnce =
|
const hasLostFocusAtLeastOnce =
|
||||||
typeof hasLostFocusAtLeastOnceOrArr === "boolean"
|
typeof hasLostFocusAtLeastOnceOrArr === "boolean"
|
||||||
? hasLostFocusAtLeastOnceOrArr
|
? hasLostFocusAtLeastOnceOrArr
|
||||||
@ -482,7 +485,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
...valueOrValuesWrap
|
...valueOrValuesWrap
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
"isFormSubmittable": state.formFieldStates.every(({ errors }) => errors.length === 0)
|
isFormSubmittable: state.formFieldStates.every(({ errors }) => errors.length === 0)
|
||||||
}),
|
}),
|
||||||
[state]
|
[state]
|
||||||
);
|
);
|
||||||
@ -501,7 +504,13 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
|
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
|
||||||
|
|
||||||
const getErrors = useConstCallback(
|
const getErrors = useConstCallback(
|
||||||
(params: { attributeName: string; formFieldStates: { attribute: Attribute; valueOrValues: string | string[] }[] }): FormFieldError[] => {
|
(params: {
|
||||||
|
attributeName: string;
|
||||||
|
formFieldStates: {
|
||||||
|
attribute: Attribute;
|
||||||
|
valueOrValues: string | string[];
|
||||||
|
}[];
|
||||||
|
}): FormFieldError[] => {
|
||||||
const { attributeName, formFieldStates } = params;
|
const { attributeName, formFieldStates } = params;
|
||||||
|
|
||||||
const formFieldState = formFieldStates.find(({ attribute }) => attribute.name === attributeName);
|
const formFieldState = formFieldStates.find(({ attribute }) => attribute.name === attributeName);
|
||||||
@ -572,10 +581,10 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
errorMessageStr,
|
errorMessageStr,
|
||||||
"errorMessage": <span key={0}>{errorMessageStr}</span>,
|
errorMessage: <span key={0}>{errorMessageStr}</span>,
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "server"
|
type: "server"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -598,19 +607,19 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
.map((...[, index]) => {
|
.map((...[, index]) => {
|
||||||
const specificValueErrors = getErrors({
|
const specificValueErrors = getErrors({
|
||||||
attributeName,
|
attributeName,
|
||||||
"formFieldStates": formFieldStates.map(formFieldState => {
|
formFieldStates: formFieldStates.map(formFieldState => {
|
||||||
if (formFieldState.attribute.name === attributeName) {
|
if (formFieldState.attribute.name === attributeName) {
|
||||||
assert(formFieldState.valueOrValues instanceof Array);
|
assert(formFieldState.valueOrValues instanceof Array);
|
||||||
return {
|
return {
|
||||||
"attribute": {
|
attribute: {
|
||||||
...attribute,
|
...attribute,
|
||||||
"annotations": {
|
annotations: {
|
||||||
...attribute.annotations,
|
...attribute.annotations,
|
||||||
"inputType": undefined
|
inputType: undefined
|
||||||
},
|
},
|
||||||
"multivalued": false
|
multivalued: false
|
||||||
},
|
},
|
||||||
"valueOrValues": formFieldState.valueOrValues[index]
|
valueOrValues: formFieldState.valueOrValues[index]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,7 +635,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((error): FormFieldError => ({ ...error, "fieldIndex": index }));
|
.map(
|
||||||
|
(error): FormFieldError => ({
|
||||||
|
...error,
|
||||||
|
fieldIndex: index
|
||||||
|
})
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.reduce((acc, errors) => [...acc, ...errors], []);
|
.reduce((acc, errors) => [...acc, ...errors], []);
|
||||||
|
|
||||||
@ -642,12 +656,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["error-user-attribute-required"] as const;
|
const msgArgs = ["error-user-attribute-required"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "other",
|
type: "other",
|
||||||
"rule": "requiredField"
|
rule: "requiredField"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -696,12 +710,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"errorMessage": <Fragment key={0}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={0}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -740,12 +754,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordMinLengthMessage", `${minLength}`] as const;
|
const msgArgs = ["invalidPasswordMinLengthMessage", `${minLength}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -768,12 +782,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordMinDigitsMessage", `${minNumberOfDigits}`] as const;
|
const msgArgs = ["invalidPasswordMinDigitsMessage", `${minNumberOfDigits}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -798,12 +812,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordMinLowerCaseCharsMessage", `${minNumberOfLowerCaseChar}`] as const;
|
const msgArgs = ["invalidPasswordMinLowerCaseCharsMessage", `${minNumberOfLowerCaseChar}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -828,12 +842,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordMinUpperCaseCharsMessage", `${minNumberOfUpperCaseChar}`] as const;
|
const msgArgs = ["invalidPasswordMinUpperCaseCharsMessage", `${minNumberOfUpperCaseChar}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -856,12 +870,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordMinSpecialCharsMessage", `${minNumberOfSpecialChar}`] as const;
|
const msgArgs = ["invalidPasswordMinSpecialCharsMessage", `${minNumberOfSpecialChar}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -906,12 +920,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordNotUsernameMessage"] as const;
|
const msgArgs = ["invalidPasswordNotUsernameMessage"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -944,12 +958,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordNotEmailMessage"] as const;
|
const msgArgs = ["invalidPasswordNotEmailMessage"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "passwordPolicy",
|
type: "passwordPolicy",
|
||||||
"name": policyName
|
name: policyName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -977,12 +991,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["invalidPasswordConfirmMessage"] as const;
|
const msgArgs = ["invalidPasswordConfirmMessage"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "other",
|
type: "other",
|
||||||
"rule": "passwordConfirmMatchesPassword"
|
rule: "passwordConfirmMatchesPassword"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1001,12 +1015,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["error-user-attribute-required"] as const;
|
const msgArgs = ["error-user-attribute-required"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "other",
|
type: "other",
|
||||||
"rule": "requiredField"
|
rule: "requiredField"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1027,17 +1041,17 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
}
|
}
|
||||||
|
|
||||||
const source: FormFieldError.Source = {
|
const source: FormFieldError.Source = {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
};
|
};
|
||||||
|
|
||||||
if (max && value.length > parseInt(`${max}`)) {
|
if (max && value.length > parseInt(`${max}`)) {
|
||||||
const msgArgs = ["error-invalid-length-too-long", `${max}`] as const;
|
const msgArgs = ["error-invalid-length-too-long", `${max}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
source
|
source
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1046,9 +1060,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["error-invalid-length-too-short", `${min}`] as const;
|
const msgArgs = ["error-invalid-length-too-short", `${min}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
source
|
source
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1076,12 +1090,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = [errorMessageKey ?? id<MessageKey>("shouldMatchPattern"), pattern] as const;
|
const msgArgs = [errorMessageKey ?? id<MessageKey>("shouldMatchPattern"), pattern] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{advancedMsg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{advancedMsg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": advancedMsgStr(...msgArgs),
|
errorMessageStr: advancedMsgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1115,12 +1129,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = [id<MessageKey>("invalidEmailMessage")] as const;
|
const msgArgs = [id<MessageKey>("invalidEmailMessage")] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1143,17 +1157,17 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const intValue = parseInt(value);
|
const intValue = parseInt(value);
|
||||||
|
|
||||||
const source: FormFieldError.Source = {
|
const source: FormFieldError.Source = {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isNaN(intValue)) {
|
if (isNaN(intValue)) {
|
||||||
const msgArgs = ["mustBeAnInteger"] as const;
|
const msgArgs = ["mustBeAnInteger"] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
source
|
source
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1164,9 +1178,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["error-number-out-of-range-too-big", `${max}`] as const;
|
const msgArgs = ["error-number-out-of-range-too-big", `${max}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
source
|
source
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1177,9 +1191,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = ["error-number-out-of-range-too-small", `${min}`] as const;
|
const msgArgs = ["error-number-out-of-range-too-small", `${min}`] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{msg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": msgStr(...msgArgs),
|
errorMessageStr: msgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
source
|
source
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1207,12 +1221,12 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|||||||
const msgArgs = [id<MessageKey>("notAValidOption")] as const;
|
const msgArgs = [id<MessageKey>("notAValidOption")] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
"errorMessage": <Fragment key={`${attributeName}-${errors.length}`}>{advancedMsg(...msgArgs)}</Fragment>,
|
errorMessage: <Fragment key={`${attributeName}-${errors.length}`}>{advancedMsg(...msgArgs)}</Fragment>,
|
||||||
"errorMessageStr": advancedMsgStr(...msgArgs),
|
errorMessageStr: advancedMsgStr(...msgArgs),
|
||||||
"fieldIndex": undefined,
|
fieldIndex: undefined,
|
||||||
"source": {
|
source: {
|
||||||
"type": "validator",
|
type: "validator",
|
||||||
"name": validatorName
|
name: validatorName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,18 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
|
|||||||
return (
|
return (
|
||||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("deleteAccountConfirm")}>
|
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("deleteAccountConfirm")}>
|
||||||
<form action={url.loginAction} className="form-vertical" method="post">
|
<form action={url.loginAction} className="form-vertical" method="post">
|
||||||
<div className="alert alert-warning" style={{ "marginTop": "0", "marginBottom": "30px" }}>
|
<div className="alert alert-warning" style={{ marginTop: "0", marginBottom: "30px" }}>
|
||||||
<span className="pficon pficon-warning-triangle-o"></span>
|
<span className="pficon pficon-warning-triangle-o"></span>
|
||||||
{msg("irreversibleAction")}
|
{msg("irreversibleAction")}
|
||||||
</div>
|
</div>
|
||||||
<p>{msg("deletingImplies")}</p>
|
<p>{msg("deletingImplies")}</p>
|
||||||
<ul style={{ "color": "#72767b", "listStyle": "disc", "listStylePosition": "inside" }}>
|
<ul
|
||||||
|
style={{
|
||||||
|
color: "#72767b",
|
||||||
|
listStyle: "disc",
|
||||||
|
listStylePosition: "inside"
|
||||||
|
}}
|
||||||
|
>
|
||||||
<li>{msg("loggingOutImmediately")}</li>
|
<li>{msg("loggingOutImmediately")}</li>
|
||||||
<li>{msg("errasingData")}</li>
|
<li>{msg("errasingData")}</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -38,7 +44,7 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
|
|||||||
{triggered_from_aia && (
|
{triggered_from_aia && (
|
||||||
<button
|
<button
|
||||||
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
|
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
|
||||||
style={{ "marginLeft": "calc(100% - 220px)" }}
|
style={{ marginLeft: "calc(100% - 220px)" }}
|
||||||
type="submit"
|
type="submit"
|
||||||
name="cancel-aia"
|
name="cancel-aia"
|
||||||
value="true"
|
value="true"
|
||||||
|
@ -27,7 +27,7 @@ export default function FrontchannelLogout(props: PageProps<Extract<KcContext, {
|
|||||||
{logout.clients.map(client => (
|
{logout.clients.map(client => (
|
||||||
<li key={client.name}>
|
<li key={client.name}>
|
||||||
{client.name}
|
{client.name}
|
||||||
<iframe src={client.frontChannelLogoutUrl} style={{ "display": "none" }} />
|
<iframe src={client.frontChannelLogoutUrl} style={{ display: "none" }} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -21,10 +21,10 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
"scriptTags": [
|
scriptTags: [
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"textContent": `
|
textContent: `
|
||||||
|
|
||||||
/* copy recovery codes */
|
/* copy recovery codes */
|
||||||
function copyRecoveryCodes() {
|
function copyRecoveryCodes() {
|
||||||
|
@ -32,18 +32,18 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
|||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
"scriptTags": [
|
scriptTags: [
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"src": `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
|
src: `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"src": `${url.resourcesPath}/js/base64url.js`
|
src: `${url.resourcesPath}/js/base64url.js`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"textContent": `
|
textContent: `
|
||||||
|
|
||||||
function webAuthnAuthenticate() {
|
function webAuthnAuthenticate() {
|
||||||
let isUserIdentified = ${isUserIdentified};
|
let isUserIdentified = ${isUserIdentified};
|
||||||
@ -206,7 +206,10 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
|||||||
className={getClassName("kcSelectAuthListItemDescriptionClass")}
|
className={getClassName("kcSelectAuthListItemDescriptionClass")}
|
||||||
>
|
>
|
||||||
{authenticator.transports.displayNameProperties
|
{authenticator.transports.displayNameProperties
|
||||||
.map((nameProperty, i, arr) => ({ nameProperty, "hasNext": i !== arr.length - 1 }))
|
.map((nameProperty, i, arr) => ({
|
||||||
|
nameProperty,
|
||||||
|
hasNext: i !== arr.length - 1
|
||||||
|
}))
|
||||||
.map(({ nameProperty, hasNext }) => (
|
.map(({ nameProperty, hasNext }) => (
|
||||||
<Fragment key={nameProperty}>
|
<Fragment key={nameProperty}>
|
||||||
<span>{msg(nameProperty)}</span>
|
<span>{msg(nameProperty)}</span>
|
||||||
|
@ -35,18 +35,18 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
|
|||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
"scriptTags": [
|
scriptTags: [
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"src": `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
|
src: `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"src": `${url.resourcesPath}/js/base64url.js`
|
src: `${url.resourcesPath}/js/base64url.js`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text/javascript",
|
type: "text/javascript",
|
||||||
"textContent": `
|
textContent: `
|
||||||
function registerSecurityKey() {
|
function registerSecurityKey() {
|
||||||
|
|
||||||
// Check if WebAuthn is supported by this browser
|
// Check if WebAuthn is supported by this browser
|
||||||
|
@ -21,5 +21,9 @@ export declare namespace AndByDiscriminatingKey {
|
|||||||
SingletonU1 extends Record<DiscriminatingKey, string>,
|
SingletonU1 extends Record<DiscriminatingKey, string>,
|
||||||
U2,
|
U2,
|
||||||
U1 extends Record<DiscriminatingKey, string>
|
U1 extends Record<DiscriminatingKey, string>
|
||||||
> = U2 extends Pick<SingletonU1, DiscriminatingKey> ? U2 & SingletonU1 : U2 extends Pick<U1, DiscriminatingKey> ? never : U2;
|
> = U2 extends Pick<SingletonU1, DiscriminatingKey>
|
||||||
|
? U2 & SingletonU1
|
||||||
|
: U2 extends Pick<U1, DiscriminatingKey>
|
||||||
|
? never
|
||||||
|
: U2;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@ if (!Array.prototype.every) {
|
|||||||
var len = O.length >>> 0;
|
var len = O.length >>> 0;
|
||||||
|
|
||||||
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
|
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
|
||||||
if (typeof callbackfn !== "function" && Object.prototype.toString.call(callbackfn) !== "[object Function]") {
|
if (
|
||||||
|
typeof callbackfn !== "function" &&
|
||||||
|
Object.prototype.toString.call(callbackfn) !== "[object Function]"
|
||||||
|
) {
|
||||||
throw new TypeError();
|
throw new TypeError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
import type { LazyExoticComponent, ComponentType } from "react";
|
import type { LazyExoticComponent, ComponentType } from "react";
|
||||||
|
|
||||||
export type LazyOrNot<Component extends ComponentType<any>> = LazyExoticComponent<Component> | Component;
|
export type LazyOrNot<Component extends ComponentType<any>> =
|
||||||
|
| LazyExoticComponent<Component>
|
||||||
|
| Component;
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { typeGuard } from "tsafe/typeGuard";
|
import { typeGuard } from "tsafe/typeGuard";
|
||||||
|
|
||||||
export type CxArg = undefined | null | string | boolean | Partial<Record<string, boolean | null | undefined>> | readonly CxArg[];
|
export type CxArg =
|
||||||
|
| undefined
|
||||||
|
| null
|
||||||
|
| string
|
||||||
|
| boolean
|
||||||
|
| Partial<Record<string, boolean | null | undefined>>
|
||||||
|
| readonly CxArg[];
|
||||||
|
|
||||||
export const clsx = (...args: CxArg[]): string => {
|
export const clsx = (...args: CxArg[]): string => {
|
||||||
const len = args.length;
|
const len = args.length;
|
||||||
|
@ -3,7 +3,10 @@ import { is } from "tsafe/is";
|
|||||||
import { deepClone } from "./deepClone";
|
import { deepClone } from "./deepClone";
|
||||||
|
|
||||||
//Warning: Be mindful that because of array this is not idempotent.
|
//Warning: Be mindful that because of array this is not idempotent.
|
||||||
export function deepAssign(params: { target: Record<string, unknown>; source: Record<string, unknown> }) {
|
export function deepAssign(params: {
|
||||||
|
target: Record<string, unknown>;
|
||||||
|
source: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
const { target } = params;
|
const { target } = params;
|
||||||
|
|
||||||
const source = deepClone(params.source);
|
const source = deepClone(params.source);
|
||||||
@ -11,12 +14,16 @@ export function deepAssign(params: { target: Record<string, unknown>; source: Re
|
|||||||
Object.keys(source).forEach(key => {
|
Object.keys(source).forEach(key => {
|
||||||
var dereferencedSource = source[key];
|
var dereferencedSource = source[key];
|
||||||
|
|
||||||
if (target[key] === undefined || dereferencedSource instanceof Function || !(dereferencedSource instanceof Object)) {
|
if (
|
||||||
|
target[key] === undefined ||
|
||||||
|
dereferencedSource instanceof Function ||
|
||||||
|
!(dereferencedSource instanceof Object)
|
||||||
|
) {
|
||||||
Object.defineProperty(target, key, {
|
Object.defineProperty(target, key, {
|
||||||
"enumerable": true,
|
enumerable: true,
|
||||||
"writable": true,
|
writable: true,
|
||||||
"configurable": true,
|
configurable: true,
|
||||||
"value": dereferencedSource
|
value: dereferencedSource
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -37,8 +44,8 @@ export function deepAssign(params: { target: Record<string, unknown>; source: Re
|
|||||||
assert(is<Record<string, unknown>>(dereferencedSource));
|
assert(is<Record<string, unknown>>(dereferencedSource));
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": dereferencedTarget,
|
target: dereferencedTarget,
|
||||||
"source": dereferencedSource
|
source: dereferencedSource
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,5 +13,7 @@ export function deepClone<T>(o: T): T {
|
|||||||
return o.map(deepClone) as any;
|
return o.map(deepClone) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.fromEntries(Object.entries(o).map(([key, value]) => [key, deepClone(value)])) as any;
|
return Object.fromEntries(
|
||||||
|
Object.entries(o).map(([key, value]) => [key, deepClone(value)])
|
||||||
|
) as any;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,10 @@ export const formatNumber = (input: string, format: string): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate the maximum size of the given pattern based on the sum of the expected digits
|
// calculate the maximum size of the given pattern based on the sum of the expected digits
|
||||||
const maxSize = digitPattern.reduce((total, p) => total + parseInt(p.replace("{", "").replace("}", "")), 0);
|
const maxSize = digitPattern.reduce(
|
||||||
|
(total, p) => total + parseInt(p.replace("{", "").replace("}", "")),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
// keep only digits
|
// keep only digits
|
||||||
let rawValue = input.replace(/\D+/g, "");
|
let rawValue = input.replace(/\D+/g, "");
|
||||||
|
@ -2,7 +2,9 @@ import { useRef, useState } from "react";
|
|||||||
import { Parameters } from "tsafe/Parameters";
|
import { Parameters } from "tsafe/Parameters";
|
||||||
|
|
||||||
/** https://stackoverflow.com/questions/65890278/why-cant-usecallback-always-return-the-same-ref */
|
/** https://stackoverflow.com/questions/65890278/why-cant-usecallback-always-return-the-same-ref */
|
||||||
export function useConstCallback<T extends ((...args: any[]) => unknown) | undefined | null>(callback: NonNullable<T>): T {
|
export function useConstCallback<
|
||||||
|
T extends ((...args: any[]) => unknown) | undefined | null
|
||||||
|
>(callback: NonNullable<T>): T {
|
||||||
const callbackRef = useRef<typeof callback>(null as any);
|
const callbackRef = useRef<typeof callback>(null as any);
|
||||||
|
|
||||||
callbackRef.current = callback;
|
callbackRef.current = callback;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user