keycloak_theme/src/bin/eject-page.ts

258 lines
8.0 KiB
TypeScript
Raw Normal View History

#!/usr/bin/env node
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
import cliSelect from "cli-select";
2024-05-19 04:02:36 +02:00
import {
loginThemePageIds,
accountThemePageIds,
type LoginThemePageId,
type AccountThemePageId,
themeTypes,
type ThemeType
} from "./shared/constants";
import { capitalize } from "tsafe/capitalize";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
2023-03-20 00:03:15 +01:00
import { assert, Equals } from "tsafe/assert";
import type { CliCommandOptions } from "./main";
2024-06-09 09:15:16 +02:00
import { getBuildContext } from "./shared/buildContext";
2024-05-19 10:23:38 +02:00
import chalk from "chalk";
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
const { cliCommandOptions } = params;
2023-03-20 00:03:15 +01:00
2024-06-09 09:15:16 +02:00
const buildContext = getBuildContext({
cliCommandOptions
});
2023-09-03 23:26:34 +02:00
2024-05-19 10:23:38 +02:00
console.log(chalk.cyan("Theme type:"));
2023-03-20 00:03:15 +01:00
const { value: themeType } = await cliSelect<ThemeType>({
2024-05-20 15:48:51 +02:00
values: [...themeTypes]
}).catch(() => {
process.exit(-1);
});
2024-05-19 10:23:38 +02:00
console.log(`${themeType}`);
console.log(chalk.cyan("Select the page you want to customize:"));
2023-03-20 00:03:15 +01:00
const templateValue = "Template.tsx (Layout common to every page)";
const userProfileFormFieldsValue =
"UserProfileFormFields.tsx (Renders the form of the register.ftl, login-update-profile.ftl, update-email.ftl and idp-review-user-profile.ftl)";
const { value: pageIdOrComponent } = await cliSelect<
| LoginThemePageId
| AccountThemePageId
| typeof templateValue
| typeof userProfileFormFieldsValue
>({
2024-05-20 15:48:51 +02:00
values: (() => {
2023-03-20 00:03:15 +01:00
switch (themeType) {
case "login":
return [
templateValue,
userProfileFormFieldsValue,
...loginThemePageIds
];
2023-03-20 00:03:15 +01:00
case "account":
return [templateValue, ...accountThemePageIds];
2023-03-20 00:03:15 +01:00
}
assert<Equals<typeof themeType, never>>(false);
})()
}).catch(() => {
process.exit(-1);
});
console.log(`${pageIdOrComponent}`);
const componentBasename = (() => {
if (pageIdOrComponent === templateValue) {
return "Template.tsx";
}
if (pageIdOrComponent === userProfileFormFieldsValue) {
return "UserProfileFormFields.tsx";
}
return capitalize(kebabCaseToCamelCase(pageIdOrComponent)).replace(/ftl$/, "tsx");
})();
const pagesOrDot = (() => {
if (
pageIdOrComponent === templateValue ||
pageIdOrComponent === userProfileFormFieldsValue
) {
return ".";
}
return "pages";
})();
2024-05-20 15:48:51 +02:00
const targetFilePath = pathJoin(
buildContext.themeSrcDirPath,
2024-05-20 15:48:51 +02:00
themeType,
pagesOrDot,
componentBasename
2024-05-20 15:48:51 +02:00
);
if (fs.existsSync(targetFilePath)) {
2024-05-20 15:48:51 +02:00
console.log(
`${pageIdOrComponent} is already ejected, ${pathRelative(
2024-05-20 15:48:51 +02:00
process.cwd(),
targetFilePath
)} already exists`
);
process.exit(-1);
}
const componentCode = fs
2024-05-20 15:48:51 +02:00
.readFileSync(
pathJoin(
getThisCodebaseRootDirPath(),
"src",
themeType,
pagesOrDot,
componentBasename
2024-05-20 15:48:51 +02:00
)
)
.toString("utf8");
2024-06-06 09:13:40 +02:00
{
const targetDirPath = pathDirname(targetFilePath);
if (!fs.existsSync(targetDirPath)) {
fs.mkdirSync(targetDirPath, { recursive: true });
}
}
fs.writeFileSync(targetFilePath, Buffer.from(componentCode, "utf8"));
console.log(
`${chalk.green("✓")} ${chalk.bold(
pathJoin(".", pathRelative(process.cwd(), targetFilePath))
)} copy pasted from the Keycloakify source code into your project`
);
edit_KcApp: {
if (
pageIdOrComponent !== templateValue &&
pageIdOrComponent !== userProfileFormFieldsValue
) {
break edit_KcApp;
}
const kcAppTsxPath = pathJoin(
buildContext.themeSrcDirPath,
themeType,
"KcPage.tsx"
);
const kcAppTsxCode = fs.readFileSync(kcAppTsxPath).toString("utf8");
const modifiedKcAppTsxCode = (() => {
switch (pageIdOrComponent) {
case templateValue:
return kcAppTsxCode.replace(
`keycloakify/${themeType}/Template`,
"./Template"
);
case userProfileFormFieldsValue:
return kcAppTsxCode.replace(
`keycloakify/login/UserProfileFormFields`,
"./UserProfileFormFields"
);
}
assert<Equals<typeof pageIdOrComponent, never>>(false);
})();
if (kcAppTsxCode === modifiedKcAppTsxCode) {
console.log(
chalk.red(
2024-06-09 11:53:25 +02:00
"Unable to automatically update KcPage.tsx, please update it manually"
)
);
return;
}
fs.writeFileSync(kcAppTsxPath, Buffer.from(modifiedKcAppTsxCode, "utf8"));
console.log(
`${chalk.green("✓")} ${chalk.bold(
pathJoin(".", pathRelative(process.cwd(), kcAppTsxPath))
)} Updated`
);
return;
}
const userProfileFormFieldComponentName = "UserProfileFormFields";
console.log(
[
``,
`You now need to update your page router:`,
``,
2024-05-20 15:48:51 +02:00
`${chalk.bold(
pathJoin(
".",
pathRelative(process.cwd(), buildContext.themeSrcDirPath),
2024-05-20 15:48:51 +02:00
themeType,
2024-06-09 11:53:25 +02:00
"KcPage.tsx"
2024-05-20 15:48:51 +02:00
)
)}:`,
2024-05-19 10:23:38 +02:00
chalk.grey("```"),
`// ...`,
``,
2024-05-20 15:48:51 +02:00
chalk.green(
`+const ${componentBasename.replace(
2024-05-20 15:48:51 +02:00
/.tsx$/,
""
)} = lazy(() => import("./pages/${componentBasename}"));`
2024-05-20 15:48:51 +02:00
),
2024-05-19 10:23:38 +02:00
...[
``,
2024-06-09 11:53:25 +02:00
` export default function KcPage(props: { kcContext: KcContext; }) {`,
2024-05-19 10:23:38 +02:00
``,
` // ...`,
``,
` return (`,
` <Suspense>`,
` {(() => {`,
` switch (kcContext.pageId) {`,
` // ...`,
`+ case "${pageIdOrComponent}": return (`,
2024-06-09 09:50:02 +02:00
`+ <${componentBasename}`,
2024-05-19 10:23:38 +02:00
`+ {...{ kcContext, i18n, classes }}`,
`+ Template={Template}`,
2024-06-09 09:50:02 +02:00
`+ doUseDefaultCss={true}`,
...(!componentCode.includes(userProfileFormFieldComponentName)
2024-05-19 10:23:38 +02:00
? []
2024-05-20 15:48:51 +02:00
: [
2024-06-09 09:50:02 +02:00
`+ ${userProfileFormFieldComponentName}={${userProfileFormFieldComponentName}}`,
`+ doMakeUserConfirmPassword={doMakeUserConfirmPassword}`
2024-05-20 15:48:51 +02:00
]),
2024-05-19 10:23:38 +02:00
`+ />`,
`+ );`,
` default: return <Fallback /* .. */ />;`,
` }`,
` })()}`,
` </Suspense>`,
` );`,
` }`
].map(line => {
if (line.startsWith("+")) {
return chalk.green(line);
}
if (line.startsWith("-")) {
return chalk.red(line);
}
return chalk.grey(line);
}),
chalk.grey("```")
].join("\n")
);
}