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-19 04:02:36 +02:00
|
|
|
import {
|
|
|
|
loginThemePageIds,
|
|
|
|
accountThemePageIds,
|
|
|
|
type LoginThemePageId,
|
|
|
|
type AccountThemePageId,
|
|
|
|
themeTypes,
|
|
|
|
type ThemeType
|
|
|
|
} from "./shared/constants";
|
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 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";
|
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-06-09 09:15:16 +02:00
|
|
|
const buildContext = getBuildContext({
|
2024-05-15 05:14:01 +02:00
|
|
|
cliCommandOptions
|
2024-02-11 16:17:38 +01:00
|
|
|
});
|
2023-09-03 23:26:34 +02:00
|
|
|
|
2024-05-19 10:23:38 +02:00
|
|
|
console.log(chalk.cyan("Theme type:"));
|
2024-05-15 05:14:01 +02:00
|
|
|
|
2023-03-20 00:03:15 +01:00
|
|
|
const { value: themeType } = await cliSelect<ThemeType>({
|
2024-05-20 15:48:51 +02:00
|
|
|
values: [...themeTypes]
|
2023-03-19 15:52:41 +01:00
|
|
|
}).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
|
|
|
|
2024-06-06 06:12:05 +02: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":
|
2024-06-06 06:12:05 +02:00
|
|
|
return [
|
|
|
|
templateValue,
|
|
|
|
userProfileFormFieldsValue,
|
|
|
|
...loginThemePageIds
|
|
|
|
];
|
2023-03-20 00:03:15 +01:00
|
|
|
case "account":
|
2024-06-06 06:12:05 +02:00
|
|
|
return [templateValue, ...accountThemePageIds];
|
2023-03-20 00:03:15 +01:00
|
|
|
}
|
|
|
|
assert<Equals<typeof themeType, never>>(false);
|
|
|
|
})()
|
|
|
|
}).catch(() => {
|
|
|
|
process.exit(-1);
|
|
|
|
});
|
|
|
|
|
2024-06-06 06:12:05 +02:00
|
|
|
console.log(`→ ${pageIdOrComponent}`);
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2024-06-06 06:12:05 +02:00
|
|
|
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(
|
2024-06-16 01:29:15 +02:00
|
|
|
buildContext.themeSrcDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
themeType,
|
2024-06-06 06:12:05 +02:00
|
|
|
pagesOrDot,
|
|
|
|
componentBasename
|
2024-05-20 15:48:51 +02:00
|
|
|
);
|
2023-03-19 15:52:41 +01:00
|
|
|
|
2024-05-18 03:41:12 +02:00
|
|
|
if (fs.existsSync(targetFilePath)) {
|
2024-05-20 15:48:51 +02:00
|
|
|
console.log(
|
2024-06-06 06:12:05 +02:00
|
|
|
`${pageIdOrComponent} is already ejected, ${pathRelative(
|
2024-05-20 15:48:51 +02:00
|
|
|
process.cwd(),
|
|
|
|
targetFilePath
|
|
|
|
)} already exists`
|
|
|
|
);
|
2023-03-19 15:52:41 +01:00
|
|
|
|
|
|
|
process.exit(-1);
|
|
|
|
}
|
|
|
|
|
2024-06-06 06:12:05 +02:00
|
|
|
const componentCode = fs
|
2024-05-20 15:48:51 +02:00
|
|
|
.readFileSync(
|
|
|
|
pathJoin(
|
|
|
|
getThisCodebaseRootDirPath(),
|
|
|
|
"src",
|
|
|
|
themeType,
|
2024-06-06 06:12:05 +02:00
|
|
|
pagesOrDot,
|
|
|
|
componentBasename
|
2024-05-20 15:48:51 +02:00
|
|
|
)
|
|
|
|
)
|
2024-05-18 04:14:36 +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 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-06 06:12:05 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-06-16 01:29:15 +02:00
|
|
|
const kcAppTsxPath = pathJoin(
|
|
|
|
buildContext.themeSrcDirPath,
|
|
|
|
themeType,
|
|
|
|
"KcPage.tsx"
|
|
|
|
);
|
2024-06-06 06:12:05 +02:00
|
|
|
|
|
|
|
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"
|
2024-06-06 06:12:05 +02:00
|
|
|
)
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.writeFileSync(kcAppTsxPath, Buffer.from(modifiedKcAppTsxCode, "utf8"));
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
`${chalk.green("✓")} ${chalk.bold(
|
|
|
|
pathJoin(".", pathRelative(process.cwd(), kcAppTsxPath))
|
|
|
|
)} Updated`
|
|
|
|
);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2024-05-18 04:14:36 +02:00
|
|
|
|
|
|
|
const userProfileFormFieldComponentName = "UserProfileFormFields";
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
[
|
|
|
|
``,
|
|
|
|
`You now need to update your page router:`,
|
|
|
|
``,
|
2024-05-20 15:48:51 +02:00
|
|
|
`${chalk.bold(
|
|
|
|
pathJoin(
|
|
|
|
".",
|
2024-06-16 01:29:15 +02:00
|
|
|
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-18 04:14:36 +02:00
|
|
|
`// ...`,
|
|
|
|
``,
|
2024-05-20 15:48:51 +02:00
|
|
|
chalk.green(
|
2024-06-06 06:12:05 +02:00
|
|
|
`+const ${componentBasename.replace(
|
2024-05-20 15:48:51 +02:00
|
|
|
/.tsx$/,
|
|
|
|
""
|
2024-06-06 06:12:05 +02:00
|
|
|
)} = 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) {`,
|
|
|
|
` // ...`,
|
2024-06-06 06:12:05 +02:00
|
|
|
`+ 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}`,
|
2024-06-06 06:12:05 +02:00
|
|
|
...(!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("```")
|
2024-05-18 04:14:36 +02:00
|
|
|
].join("\n")
|
|
|
|
);
|
2024-05-15 05:14:01 +02:00
|
|
|
}
|