Analyze the code to see what field names are acutally used. Deprecates the customUserAttributes option, it's no longer needed
This commit is contained in:
parent
58301e0844
commit
8c8540de5d
@ -11,7 +11,6 @@
|
||||
"scripts": {
|
||||
"prepare": "yarn generate-i18n-messages",
|
||||
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src && tsc-alias -p src/tsconfig.json && yarn grant-exec-perms && yarn copy-files dist/",
|
||||
"watch-in-starter": "yarn build && yarn link-in-starter && (concurrently \"tsc -p src -w\" \"tsc-alias -p src/tsconfig.json\" \"tsc -p src/bin -w\")",
|
||||
"generate:json-schema": "ts-node scripts/generate-json-schema.ts",
|
||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
||||
"copy-files": "copyfiles -u 1 src/**/*.ftl",
|
||||
@ -24,6 +23,7 @@
|
||||
"generate-i18n-messages": "ts-node --skipProject scripts/generate-i18n-messages.ts",
|
||||
"link-in-app": "ts-node --skipProject scripts/link-in-app.ts",
|
||||
"link-in-starter": "yarn link-in-app keycloakify-starter",
|
||||
"watch-in-starter": "yarn build && yarn link-in-starter && (concurrently \"tsc -p src -w\" \"tsc-alias -p src/tsconfig.json\" \"tsc -p src/bin -w\")",
|
||||
"copy-keycloak-resources-to-storybook-static": "PUBLIC_DIR_PATH=.storybook/static node dist/bin/copy-keycloak-resources-to-public.js",
|
||||
"storybook": "yarn build && yarn copy-keycloak-resources-to-storybook-static && start-storybook -p 6006",
|
||||
"build-storybook": "yarn build && yarn copy-keycloak-resources-to-storybook-static && build-storybook"
|
||||
|
@ -31,13 +31,3 @@ export function getThemeSrcDirPath(params: { projectDirPath: string }) {
|
||||
|
||||
return { themeSrcDirPath };
|
||||
}
|
||||
|
||||
export function getEmailThemeSrcDirPath(params: { projectDirPath: string }) {
|
||||
const { projectDirPath } = params;
|
||||
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath({ projectDirPath });
|
||||
|
||||
const emailThemeSrcDirPath = themeSrcDirPath === undefined ? undefined : pathJoin(themeSrcDirPath, "email");
|
||||
|
||||
return { emailThemeSrcDirPath };
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
import { readBuildOptions } from "./keycloakify/BuildOptions";
|
||||
import * as fs from "fs";
|
||||
import { getLogger } from "./tools/logger";
|
||||
import { getEmailThemeSrcDirPath } from "./getSrcDirPath";
|
||||
import { getThemeSrcDirPath } from "./getSrcDirPath";
|
||||
|
||||
export async function main() {
|
||||
const { isSilent } = readBuildOptions({
|
||||
@ -17,16 +17,18 @@ export async function main() {
|
||||
|
||||
const logger = getLogger({ isSilent });
|
||||
|
||||
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath({
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath({
|
||||
"projectDirPath": process.cwd()
|
||||
});
|
||||
|
||||
if (emailThemeSrcDirPath === undefined) {
|
||||
if (themeSrcDirPath === undefined) {
|
||||
logger.warn("Couldn't locate your theme source directory");
|
||||
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");
|
||||
|
||||
if (fs.existsSync(emailThemeSrcDirPath)) {
|
||||
logger.warn(`There is already a ${pathRelative(process.cwd(), emailThemeSrcDirPath)} directory in your project. Aborting.`);
|
||||
|
||||
|
@ -28,7 +28,6 @@ export namespace BuildOptions {
|
||||
reactAppBuildDirPath: string;
|
||||
/** Directory that keycloakify outputs to. Defaults to {cwd}/build_keycloak */
|
||||
keycloakifyBuildDirPath: string;
|
||||
customUserAttributes: string[];
|
||||
};
|
||||
|
||||
export type Standalone = Common & {
|
||||
@ -199,8 +198,7 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
|
||||
}
|
||||
|
||||
return keycloakifyBuildDirPath;
|
||||
})(),
|
||||
"customUserAttributes": keycloakify.customUserAttributes ?? []
|
||||
})()
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -8,13 +8,7 @@
|
||||
out["advancedMsg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
|
||||
|
||||
out["messagesPerField"]= {
|
||||
<#assign fieldNames = [
|
||||
"global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm",
|
||||
"totp", "totpSecret", "SAMLRequest", "SAMLResponse", "relayState", "device_user_code", "code",
|
||||
"password-new", "rememberMe", "login", "authenticationExecution", "cancel-aia", "clientDataJSON",
|
||||
"authenticatorData", "signature", "credentialId", "userHandle", "error", "authn_use_chk", "authenticationExecution",
|
||||
"isSetRetry", "try-again", "attestationObject", "publicKeyCredentialId", "authenticatorLabel"CUSTOM_USER_ATTRIBUTES_eKsIY4ZsZ4xeM
|
||||
]>
|
||||
<#assign fieldNames = [ FIELD_NAMES_eKsIY4ZsZ4xeM ]>
|
||||
|
||||
<#attempt>
|
||||
<#if profile?? && profile.attributes?? && profile.attributes?is_enumerable>
|
||||
|
@ -18,7 +18,6 @@ export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.Ex
|
||||
export namespace BuildOptionsLike {
|
||||
export type Common = {
|
||||
themeName: string;
|
||||
customUserAttributes: string[];
|
||||
themeVersion: string;
|
||||
};
|
||||
|
||||
@ -57,8 +56,9 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
buildOptions: BuildOptionsLike;
|
||||
keycloakifyVersion: string;
|
||||
themeType: ThemeType;
|
||||
fieldNames: string[];
|
||||
}) {
|
||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType } = params;
|
||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType, fieldNames } = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
@ -129,10 +129,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
|
||||
.toString("utf8")
|
||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1]
|
||||
.replace(
|
||||
"CUSTOM_USER_ATTRIBUTES_eKsIY4ZsZ4xeM",
|
||||
buildOptions.customUserAttributes.length === 0 ? "" : ", " + buildOptions.customUserAttributes.map(name => `"${name}"`).join(", ")
|
||||
)
|
||||
.replace("FIELD_NAMES_eKsIY4ZsZ4xeM", fieldNames.map(name => `"${name}"`).join(", "))
|
||||
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { themeTypes } from "./generateFtl/generateFtl";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { BuildOptions } from "./BuildOptions";
|
||||
import type { ThemeType } from "./generateFtl";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
themeName: string;
|
||||
@ -21,7 +21,7 @@ export type BuildOptionsLike = {
|
||||
|
||||
export function generateJavaStackFiles(params: {
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
doBundlesEmailTemplate: boolean;
|
||||
implementedThemeTypes: Record<ThemeType | "email", boolean>;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}): {
|
||||
jarFilePath: string;
|
||||
@ -29,7 +29,7 @@ export function generateJavaStackFiles(params: {
|
||||
const {
|
||||
buildOptions: { groupId, themeName, extraThemeNames, themeVersion, artifactId },
|
||||
keycloakThemeBuildingDirPath,
|
||||
doBundlesEmailTemplate
|
||||
implementedThemeTypes
|
||||
} = params;
|
||||
|
||||
{
|
||||
@ -70,7 +70,9 @@ export function generateJavaStackFiles(params: {
|
||||
{
|
||||
"themes": [themeName, ...extraThemeNames].map(themeName => ({
|
||||
"name": themeName,
|
||||
"types": [...themeTypes, ...(doBundlesEmailTemplate ? ["email"] : [])]
|
||||
"types": Object.entries(implementedThemeTypes)
|
||||
.filter(([, isImplemented]) => isImplemented)
|
||||
.map(([themeType]) => themeType)
|
||||
}))
|
||||
},
|
||||
null,
|
||||
|
@ -33,7 +33,6 @@ export function generateStartKeycloakTestingContainer(params: {
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
|
||||
|
||||
Buffer.from(
|
||||
[
|
||||
"#!/usr/bin/env bash",
|
||||
|
@ -9,6 +9,7 @@ import { isInside } from "../../tools/isInside";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { downloadKeycloakStaticResources } from "./downloadKeycloakStaticResources";
|
||||
import { readFieldNameUsage } from "./readFieldNameUsage";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
@ -19,7 +20,6 @@ export namespace BuildOptionsLike {
|
||||
extraAccountPages?: string[];
|
||||
extraThemeProperties?: string[];
|
||||
isSilent: boolean;
|
||||
customUserAttributes: string[];
|
||||
themeVersion: string;
|
||||
keycloakVersionDefaultAssets: string;
|
||||
};
|
||||
@ -53,11 +53,12 @@ assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
export async function generateTheme(params: {
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
emailThemeSrcDirPath: string | undefined;
|
||||
themeSrcDirPath: string | undefined;
|
||||
keycloakifySrcDirPath: string;
|
||||
buildOptions: BuildOptionsLike;
|
||||
keycloakifyVersion: string;
|
||||
}): Promise<{ doBundlesEmailTemplate: boolean }> {
|
||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, emailThemeSrcDirPath, buildOptions, keycloakifyVersion } = params;
|
||||
}): Promise<void> {
|
||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, themeSrcDirPath, keycloakifySrcDirPath, buildOptions, keycloakifyVersion } = params;
|
||||
|
||||
const getThemeDirPath = (themeType: ThemeType | "email") =>
|
||||
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
|
||||
@ -142,7 +143,12 @@ export async function generateTheme(params: {
|
||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||
buildOptions,
|
||||
keycloakifyVersion,
|
||||
themeType
|
||||
themeType,
|
||||
"fieldNames": readFieldNameUsage({
|
||||
keycloakifySrcDirPath,
|
||||
themeSrcDirPath,
|
||||
themeType
|
||||
})
|
||||
});
|
||||
|
||||
return generateFtlFilesCode;
|
||||
@ -220,21 +226,20 @@ export async function generateTheme(params: {
|
||||
);
|
||||
}
|
||||
|
||||
let doBundlesEmailTemplate: boolean;
|
||||
|
||||
email: {
|
||||
if (emailThemeSrcDirPath === undefined) {
|
||||
doBundlesEmailTemplate = false;
|
||||
if (themeSrcDirPath === undefined) {
|
||||
break email;
|
||||
}
|
||||
|
||||
doBundlesEmailTemplate = true;
|
||||
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");
|
||||
|
||||
if (!fs.existsSync(emailThemeSrcDirPath)) {
|
||||
break email;
|
||||
}
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": emailThemeSrcDirPath,
|
||||
"destDirPath": getThemeDirPath("email")
|
||||
});
|
||||
}
|
||||
|
||||
return { doBundlesEmailTemplate };
|
||||
}
|
||||
|
95
src/bin/keycloakify/generateTheme/readFieldNameUsage.ts
Normal file
95
src/bin/keycloakify/generateTheme/readFieldNameUsage.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { crawl } from "../../tools/crawl";
|
||||
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
||||
import { join as pathJoin } from "path";
|
||||
import * as fs from "fs";
|
||||
import type { ThemeType } from "../generateFtl";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
|
||||
export function readFieldNameUsage(params: {
|
||||
keycloakifySrcDirPath: string;
|
||||
themeSrcDirPath: string | undefined;
|
||||
themeType: ThemeType | "email";
|
||||
}): string[] {
|
||||
const { keycloakifySrcDirPath, themeSrcDirPath, themeType } = params;
|
||||
|
||||
const fieldNames: string[] = [];
|
||||
|
||||
if (themeSrcDirPath === undefined) {
|
||||
//If we can't detect the user theme directory we restore the fieldNames we had previously to prevent errors.
|
||||
fieldNames.push(
|
||||
...[
|
||||
"global",
|
||||
"userLabel",
|
||||
"username",
|
||||
"email",
|
||||
"firstName",
|
||||
"lastName",
|
||||
"password",
|
||||
"password-confirm",
|
||||
"totp",
|
||||
"totpSecret",
|
||||
"SAMLRequest",
|
||||
"SAMLResponse",
|
||||
"relayState",
|
||||
"device_user_code",
|
||||
"code",
|
||||
"password-new",
|
||||
"rememberMe",
|
||||
"login",
|
||||
"authenticationExecution",
|
||||
"cancel-aia",
|
||||
"clientDataJSON",
|
||||
"authenticatorData",
|
||||
"signature",
|
||||
"credentialId",
|
||||
"userHandle",
|
||||
"error",
|
||||
"authn_use_chk",
|
||||
"authenticationExecution",
|
||||
"isSetRetry",
|
||||
"try-again",
|
||||
"attestationObject",
|
||||
"publicKeyCredentialId",
|
||||
"authenticatorLabel"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
for (const srcDirPath of (
|
||||
[
|
||||
pathJoin(keycloakifySrcDirPath, themeType),
|
||||
(() => {
|
||||
if (themeSrcDirPath === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const srcDirPath = pathJoin(themeSrcDirPath, themeType);
|
||||
|
||||
if (!fs.existsSync(srcDirPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return srcDirPath;
|
||||
})()
|
||||
] as const
|
||||
).filter(exclude(undefined))) {
|
||||
const filePaths = crawl(srcDirPath)
|
||||
.filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath))
|
||||
.map(filePath => pathJoin(srcDirPath, filePath));
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
const rawSourceFile = fs.readFileSync(filePath).toString("utf8");
|
||||
|
||||
fieldNames.push(
|
||||
...Array.from(
|
||||
rawSourceFile.matchAll(/messagesPerField\.(?:(?:printIfExists)|(?:existsError)|(?:get)|(?:exists))\(["']([^"']+)["']/g),
|
||||
m => m[1]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const out = fieldNames.reduce(...removeDuplicates<string>());
|
||||
|
||||
return out;
|
||||
}
|
@ -9,8 +9,9 @@ import { getLogger } from "../tools/logger";
|
||||
import jar from "../tools/jar";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Equals } from "tsafe";
|
||||
import { getEmailThemeSrcDirPath } from "../getSrcDirPath";
|
||||
import { getThemeSrcDirPath } from "../getSrcDirPath";
|
||||
import { getProjectRoot } from "../tools/getProjectRoot";
|
||||
import { objectKeys } from "tsafe/objectKeys";
|
||||
|
||||
export async function main() {
|
||||
const projectDirPath = process.cwd();
|
||||
@ -23,42 +24,54 @@ export async function main() {
|
||||
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
||||
logger.log("🔏 Building the keycloak theme...⌚");
|
||||
|
||||
let doBundlesEmailTemplate: boolean | undefined;
|
||||
const keycloakifyDirPath = getProjectRoot();
|
||||
|
||||
const { themeSrcDirPath } = getThemeSrcDirPath({ projectDirPath });
|
||||
|
||||
for (const themeName of [buildOptions.themeName, ...buildOptions.extraThemeNames]) {
|
||||
const { doBundlesEmailTemplate: doBundlesEmailTemplate_ } = await generateTheme({
|
||||
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||
"emailThemeSrcDirPath": (() => {
|
||||
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath({ projectDirPath });
|
||||
|
||||
if (emailThemeSrcDirPath === undefined || !fs.existsSync(emailThemeSrcDirPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return emailThemeSrcDirPath;
|
||||
})(),
|
||||
await generateTheme({
|
||||
"keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
|
||||
themeSrcDirPath,
|
||||
"keycloakifySrcDirPath": pathJoin(keycloakifyDirPath, "src"),
|
||||
"reactAppBuildDirPath": buildOptions.reactAppBuildDirPath,
|
||||
"buildOptions": {
|
||||
...buildOptions,
|
||||
"themeName": themeName
|
||||
},
|
||||
"keycloakifyVersion": (() => {
|
||||
const version = JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["version"];
|
||||
const version = JSON.parse(fs.readFileSync(pathJoin(keycloakifyDirPath, "package.json")).toString("utf8"))["version"];
|
||||
|
||||
assert(typeof version === "string");
|
||||
|
||||
return version;
|
||||
})()
|
||||
});
|
||||
|
||||
doBundlesEmailTemplate ??= doBundlesEmailTemplate_;
|
||||
}
|
||||
|
||||
assert(doBundlesEmailTemplate !== undefined);
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||
doBundlesEmailTemplate,
|
||||
"keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
|
||||
"implementedThemeTypes": (() => {
|
||||
const implementedThemeTypes = {
|
||||
"login": false,
|
||||
"account": false,
|
||||
"email": false
|
||||
};
|
||||
|
||||
if (themeSrcDirPath === undefined) {
|
||||
implementedThemeTypes["login"] = true;
|
||||
implementedThemeTypes["account"] = true;
|
||||
return implementedThemeTypes;
|
||||
}
|
||||
|
||||
for (const themeType of objectKeys(implementedThemeTypes)) {
|
||||
if (!fs.existsSync(pathJoin(themeSrcDirPath, themeType))) {
|
||||
continue;
|
||||
}
|
||||
implementedThemeTypes[themeType] = true;
|
||||
}
|
||||
|
||||
return implementedThemeTypes;
|
||||
})(),
|
||||
buildOptions
|
||||
});
|
||||
|
||||
|
@ -23,7 +23,6 @@ export type ParsedPackageJson = {
|
||||
keycloakVersionDefaultAssets?: string;
|
||||
reactAppBuildDirPath?: string;
|
||||
keycloakifyBuildDirPath?: string;
|
||||
customUserAttributes?: string[];
|
||||
themeName?: string;
|
||||
extraThemeNames?: string[];
|
||||
};
|
||||
@ -46,7 +45,6 @@ export const zParsedPackageJson = z.object({
|
||||
"keycloakVersionDefaultAssets": z.string().optional(),
|
||||
"reactAppBuildDirPath": z.string().optional(),
|
||||
"keycloakifyBuildDirPath": z.string().optional(),
|
||||
"customUserAttributes": z.array(z.string()).optional(),
|
||||
"themeName": z.string().optional(),
|
||||
"extraThemeNames": z.array(z.string()).optional()
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user