2023-03-19 15:52:41 +01:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
2024-02-11 20:15:18 +01:00
|
|
|
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
2023-03-19 15:52:41 +01:00
|
|
|
import cliSelect from "cli-select";
|
2024-05-15 05:14:01 +02:00
|
|
|
import { loginThemePageIds, accountThemePageIds, type LoginThemePageId, type AccountThemePageId } from "./shared/pageIds";
|
2023-03-19 15:52:41 +01:00
|
|
|
import { capitalize } from "tsafe/capitalize";
|
2024-05-18 03:41:12 +02:00
|
|
|
import * as fs from "fs";
|
|
|
|
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
|
2023-03-19 15:52:41 +01:00
|
|
|
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
2023-03-20 00:03:15 +01:00
|
|
|
import { assert, Equals } from "tsafe/assert";
|
2024-05-15 05:14:01 +02:00
|
|
|
import { getThemeSrcDirPath } from "./shared/getThemeSrcDirPath";
|
|
|
|
import { themeTypes, type ThemeType } from "./shared/constants";
|
|
|
|
import type { CliCommandOptions } from "./main";
|
|
|
|
import { readBuildOptions } from "./shared/buildOptions";
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2024-05-15 05:14:01 +02:00
|
|
|
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
|
|
|
const { cliCommandOptions } = params;
|
2023-03-20 00:03:15 +01:00
|
|
|
|
2024-05-15 05:14:01 +02:00
|
|
|
const buildOptions = readBuildOptions({
|
|
|
|
cliCommandOptions
|
2024-02-11 16:17:38 +01:00
|
|
|
});
|
2023-09-03 23:26:34 +02:00
|
|
|
|
2024-05-18 04:14:36 +02:00
|
|
|
console.log("Theme type:");
|
2024-05-15 05:14:01 +02:00
|
|
|
|
2023-03-20 00:03:15 +01:00
|
|
|
const { value: themeType } = await cliSelect<ThemeType>({
|
|
|
|
"values": [...themeTypes]
|
2023-03-19 15:52:41 +01:00
|
|
|
}).catch(() => {
|
|
|
|
console.log("Aborting");
|
|
|
|
|
|
|
|
process.exit(-1);
|
|
|
|
});
|
|
|
|
|
2024-05-18 04:14:36 +02:00
|
|
|
console.log("Select the page you want to customize:");
|
2023-03-20 00:03:15 +01:00
|
|
|
|
|
|
|
const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
|
|
|
|
"values": (() => {
|
|
|
|
switch (themeType) {
|
|
|
|
case "login":
|
|
|
|
return [...loginThemePageIds];
|
|
|
|
case "account":
|
|
|
|
return [...accountThemePageIds];
|
|
|
|
}
|
|
|
|
assert<Equals<typeof themeType, never>>(false);
|
|
|
|
})()
|
|
|
|
}).catch(() => {
|
|
|
|
console.log("Aborting");
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2023-03-20 00:03:15 +01:00
|
|
|
process.exit(-1);
|
|
|
|
});
|
|
|
|
|
2024-05-18 04:14:36 +02:00
|
|
|
const componentPageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2024-05-15 05:14:01 +02:00
|
|
|
const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
|
2023-03-25 04:56:17 +01:00
|
|
|
|
2024-05-18 04:14:36 +02:00
|
|
|
const targetFilePath = pathJoin(themeSrcDirPath, themeType, "pages", componentPageBasename);
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2024-05-18 03:41:12 +02:00
|
|
|
if (fs.existsSync(targetFilePath)) {
|
2023-03-19 15:52:41 +01:00
|
|
|
console.log(`${pageId} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`);
|
|
|
|
|
|
|
|
process.exit(-1);
|
|
|
|
}
|
|
|
|
|
2024-05-18 03:41:12 +02:00
|
|
|
{
|
|
|
|
const targetDirPath = pathDirname(targetFilePath);
|
|
|
|
|
|
|
|
if (!fs.existsSync(targetDirPath)) {
|
|
|
|
fs.mkdirSync(targetDirPath, { "recursive": true });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-18 04:14:36 +02:00
|
|
|
const componentPageContent = fs
|
|
|
|
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "src", themeType, "pages", componentPageBasename))
|
|
|
|
.toString("utf8");
|
|
|
|
|
|
|
|
fs.writeFileSync(targetFilePath, Buffer.from(componentPageContent, "utf8"));
|
|
|
|
|
|
|
|
const userProfileFormFieldComponentName = "UserProfileFormFields";
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
[
|
|
|
|
``,
|
|
|
|
`\`${pathJoin(".", pathRelative(process.cwd(), targetFilePath))}\` copy pasted from the Keycloakify source code into your project.`,
|
|
|
|
``,
|
|
|
|
`You now need to update your page router:`,
|
|
|
|
``,
|
|
|
|
`\`${pathJoin(".", pathRelative(process.cwd(), themeSrcDirPath), themeType, "KcApp.tsx")}\`:`,
|
|
|
|
"```",
|
|
|
|
`// ...`,
|
|
|
|
``,
|
|
|
|
`+const ${componentPageBasename.replace(/.tsx$/, "")} = lazy(() => import("./pages/${componentPageBasename}"));`,
|
|
|
|
``,
|
|
|
|
` export default function KcApp(props: { kcContext: KcContext; }) {`,
|
|
|
|
``,
|
|
|
|
` // ...`,
|
|
|
|
``,
|
|
|
|
` return (`,
|
|
|
|
` <Suspense>`,
|
|
|
|
` {(() => {`,
|
|
|
|
` switch (kcContext.pageId) {`,
|
|
|
|
` // ...`,
|
|
|
|
` case "${pageId}": return (`,
|
|
|
|
`+ <Login`,
|
|
|
|
`+ {...{ kcContext, i18n, classes }}`,
|
|
|
|
`+ Template={Template}`,
|
|
|
|
...(!componentPageContent.includes(userProfileFormFieldComponentName)
|
|
|
|
? []
|
|
|
|
: [`+ ${userProfileFormFieldComponentName}={${userProfileFormFieldComponentName}}`]),
|
|
|
|
`+ doUseDefaultCss={true}`,
|
|
|
|
`+ />`,
|
|
|
|
`+ );`,
|
|
|
|
` default: return <Fallback /* .. */ />;`,
|
|
|
|
` }`,
|
|
|
|
` })()}`,
|
|
|
|
` </Suspense>`,
|
|
|
|
` );`,
|
|
|
|
` }`,
|
|
|
|
"```"
|
|
|
|
].join("\n")
|
|
|
|
);
|
2024-05-15 05:14:01 +02:00
|
|
|
}
|