Remove eslint and run prettier (changelog ignore)
This commit is contained in:
@ -1,5 +1,3 @@
|
||||
|
||||
export const keycloakVersions = ["11.0.3", "15.0.2"] as const;
|
||||
|
||||
export type KeycloakVersion = typeof keycloakVersions[number];
|
||||
|
||||
|
@ -1,8 +1,15 @@
|
||||
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
||||
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
||||
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 * as child_process from "child_process";
|
||||
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
||||
import {
|
||||
generateDebugFiles,
|
||||
containerLaunchScriptBasename,
|
||||
} from "./generateDebugFiles";
|
||||
import { URL } from "url";
|
||||
|
||||
type ParsedPackageJson = {
|
||||
@ -13,21 +20,34 @@ type ParsedPackageJson = {
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
|
||||
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
|
||||
const doUseExternalAssets =
|
||||
process.argv[2]?.toLowerCase() === "--external-assets";
|
||||
|
||||
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
|
||||
const parsedPackageJson: ParsedPackageJson = require(pathJoin(
|
||||
reactProjectDirPath,
|
||||
"package.json",
|
||||
));
|
||||
|
||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||
export const keycloakThemeBuildingDirPath = pathJoin(
|
||||
reactProjectDirPath,
|
||||
"build_keycloak",
|
||||
);
|
||||
|
||||
function sanitizeThemeName(name: string) {
|
||||
return name.replace(/^@(.*)/, '$1').split('/').join('-');
|
||||
return name
|
||||
.replace(/^@(.*)/, "$1")
|
||||
.split("/")
|
||||
.join("-");
|
||||
}
|
||||
|
||||
export function main() {
|
||||
console.log("🔏 Building the keycloak theme...⌚");
|
||||
|
||||
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
||||
const extraThemeProperties: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraThemeProperties"] ?? [];
|
||||
const extraPagesId: string[] =
|
||||
(parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
||||
const extraThemeProperties: string[] =
|
||||
(parsedPackageJson as any)["keycloakify"]?.["extraThemeProperties"] ??
|
||||
[];
|
||||
const themeName = sanitizeThemeName(parsedPackageJson.name);
|
||||
|
||||
generateKeycloakThemeResources({
|
||||
@ -35,105 +55,111 @@ export function main() {
|
||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||
themeName,
|
||||
...(() => {
|
||||
|
||||
const url = (() => {
|
||||
|
||||
const { homepage } = parsedPackageJson;
|
||||
|
||||
return homepage === undefined ?
|
||||
undefined :
|
||||
new URL(homepage);
|
||||
|
||||
return homepage === undefined ? undefined : new URL(homepage);
|
||||
})();
|
||||
|
||||
return {
|
||||
"urlPathname":
|
||||
url === undefined ?
|
||||
"/" :
|
||||
url.pathname.replace(/([^/])$/, "$1/"),
|
||||
"urlOrigin": !doUseExternalAssets ? undefined : (() => {
|
||||
|
||||
if (url === undefined) {
|
||||
console.error("ERROR: You must specify 'homepage' in your package.json");
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
return url.origin;
|
||||
|
||||
})()
|
||||
url === undefined
|
||||
? "/"
|
||||
: url.pathname.replace(/([^/])$/, "$1/"),
|
||||
"urlOrigin": !doUseExternalAssets
|
||||
? undefined
|
||||
: (() => {
|
||||
if (url === undefined) {
|
||||
console.error(
|
||||
"ERROR: You must specify 'homepage' in your package.json",
|
||||
);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
return url.origin;
|
||||
})(),
|
||||
};
|
||||
|
||||
})(),
|
||||
extraPagesId,
|
||||
extraThemeProperties,
|
||||
//We have to leave it at that otherwise we break our default theme.
|
||||
//We have to leave it at that otherwise we break our default theme.
|
||||
//Problem is that we can't guarantee that the the old resources common
|
||||
//will still be available on the newer keycloak version.
|
||||
"keycloakVersion": "11.0.3"
|
||||
//will still be available on the newer keycloak version.
|
||||
"keycloakVersion": "11.0.3",
|
||||
});
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
version: parsedPackageJson.version,
|
||||
themeName,
|
||||
homepage: parsedPackageJson.homepage,
|
||||
keycloakThemeBuildingDirPath
|
||||
keycloakThemeBuildingDirPath,
|
||||
});
|
||||
|
||||
child_process.execSync(
|
||||
"mvn package",
|
||||
{ "cwd": keycloakThemeBuildingDirPath }
|
||||
);
|
||||
child_process.execSync("mvn package", {
|
||||
"cwd": keycloakThemeBuildingDirPath,
|
||||
});
|
||||
|
||||
generateDebugFiles({
|
||||
keycloakThemeBuildingDirPath,
|
||||
themeName,
|
||||
"keycloakVersion": "15.0.2"
|
||||
"keycloakVersion": "15.0.2",
|
||||
});
|
||||
|
||||
console.log([
|
||||
'',
|
||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
||||
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image.`,
|
||||
'',
|
||||
'Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:',
|
||||
'',
|
||||
'value.yaml: ',
|
||||
' extraInitContainers: |',
|
||||
' - name: realm-ext-provider',
|
||||
' image: curlimages/curl',
|
||||
' imagePullPolicy: IfNotPresent',
|
||||
' command:',
|
||||
' - sh',
|
||||
' args:',
|
||||
' - -c',
|
||||
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
||||
' volumeMounts:',
|
||||
' - name: extensions',
|
||||
' mountPath: /extensions',
|
||||
' ',
|
||||
' extraVolumeMounts: |',
|
||||
' - name: extensions',
|
||||
' mountPath: /opt/jboss/keycloak/standalone/deployments',
|
||||
' extraEnv: |',
|
||||
' - name: KEYCLOAK_USER',
|
||||
' value: admin',
|
||||
' - name: KEYCLOAK_PASSWORD',
|
||||
' value: xxxxxxxxx',
|
||||
' - name: JAVA_OPTS',
|
||||
' value: -Dkeycloak.profile=preview',
|
||||
'',
|
||||
'',
|
||||
'To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:',
|
||||
'',
|
||||
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`,
|
||||
'',
|
||||
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
|
||||
`go to your realm settings, click on the theme tab then select ${themeName}.`,
|
||||
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
|
||||
'',
|
||||
'Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈',
|
||||
'',
|
||||
].join("\n"));
|
||||
|
||||
console.log(
|
||||
[
|
||||
"",
|
||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(
|
||||
reactProjectDirPath,
|
||||
jarFilePath,
|
||||
)} 🚀`,
|
||||
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image.`,
|
||||
"",
|
||||
"Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:",
|
||||
"",
|
||||
"value.yaml: ",
|
||||
" extraInitContainers: |",
|
||||
" - name: realm-ext-provider",
|
||||
" image: curlimages/curl",
|
||||
" imagePullPolicy: IfNotPresent",
|
||||
" command:",
|
||||
" - sh",
|
||||
" args:",
|
||||
" - -c",
|
||||
` - curl -L -f -S -o /extensions/${pathBasename(
|
||||
jarFilePath,
|
||||
)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
||||
" volumeMounts:",
|
||||
" - name: extensions",
|
||||
" mountPath: /extensions",
|
||||
" ",
|
||||
" extraVolumeMounts: |",
|
||||
" - name: extensions",
|
||||
" mountPath: /opt/jboss/keycloak/standalone/deployments",
|
||||
" extraEnv: |",
|
||||
" - name: KEYCLOAK_USER",
|
||||
" value: admin",
|
||||
" - name: KEYCLOAK_PASSWORD",
|
||||
" value: xxxxxxxxx",
|
||||
" - name: JAVA_OPTS",
|
||||
" value: -Dkeycloak.profile=preview",
|
||||
"",
|
||||
"",
|
||||
"To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:",
|
||||
"",
|
||||
`👉 $ ./${pathRelative(
|
||||
reactProjectDirPath,
|
||||
pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
containerLaunchScriptBasename,
|
||||
),
|
||||
)} 👈`,
|
||||
"",
|
||||
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
|
||||
`go to your realm settings, click on the theme tab then select ${themeName}.`,
|
||||
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
|
||||
"",
|
||||
"Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
|
||||
export const ftlValuesGlobalName = "kcContext";
|
||||
export const ftlValuesGlobalName = "kcContext";
|
||||
|
@ -1,18 +1,15 @@
|
||||
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname, } from "path";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
|
||||
export const containerLaunchScriptBasename = "start_keycloak_testing_container.sh";
|
||||
export const containerLaunchScriptBasename =
|
||||
"start_keycloak_testing_container.sh";
|
||||
|
||||
/** Files for being able to run a hot reload keycloak container */
|
||||
export function generateDebugFiles(
|
||||
params: {
|
||||
keycloakVersion: "11.0.3" | "15.0.2";
|
||||
themeName: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
export function generateDebugFiles(params: {
|
||||
keycloakVersion: "11.0.3" | "15.0.2";
|
||||
themeName: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}) {
|
||||
const { themeName, keycloakThemeBuildingDirPath, keycloakVersion } = params;
|
||||
|
||||
fs.writeFileSync(
|
||||
@ -29,8 +26,8 @@ export function generateDebugFiles(
|
||||
"",
|
||||
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
)
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
|
||||
const dockerImage = `${themeName}/keycloak-hot-reload`;
|
||||
@ -54,42 +51,53 @@ export function generateDebugFiles(
|
||||
" -e KEYCLOAK_USER=admin \\",
|
||||
" -e KEYCLOAK_PASSWORD=admin \\",
|
||||
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
|
||||
` -v ${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName)
|
||||
}:/opt/jboss/keycloak/themes/${themeName}:rw \\`,
|
||||
` -v ${pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"theme",
|
||||
themeName,
|
||||
)}:/opt/jboss/keycloak/themes/${themeName}:rw \\`,
|
||||
` -it ${dockerImage}:latest`,
|
||||
""
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
"utf8",
|
||||
),
|
||||
{ "mode": 0o755 }
|
||||
{ "mode": 0o755 },
|
||||
);
|
||||
|
||||
const standaloneHaFilePath = pathJoin(keycloakThemeBuildingDirPath, "configuration", `standalone-ha.xml`);
|
||||
const standaloneHaFilePath = pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"configuration",
|
||||
`standalone-ha.xml`,
|
||||
);
|
||||
|
||||
try { fs.mkdirSync(pathDirname(standaloneHaFilePath)); } catch { }
|
||||
try {
|
||||
fs.mkdirSync(pathDirname(standaloneHaFilePath));
|
||||
} catch {}
|
||||
|
||||
fs.writeFileSync(
|
||||
standaloneHaFilePath,
|
||||
fs.readFileSync(
|
||||
pathJoin(
|
||||
__dirname,
|
||||
`standalone-ha_${keycloakVersion}.xml`
|
||||
fs
|
||||
.readFileSync(
|
||||
pathJoin(__dirname, `standalone-ha_${keycloakVersion}.xml`),
|
||||
)
|
||||
)
|
||||
.toString("utf8")
|
||||
.replace(
|
||||
new RegExp([
|
||||
"<staticMaxAge>2592000</staticMaxAge>",
|
||||
"<cacheThemes>true</cacheThemes>",
|
||||
"<cacheTemplates>true</cacheTemplates>"
|
||||
].join("\\s*"), "g"
|
||||
new RegExp(
|
||||
[
|
||||
"<staticMaxAge>2592000</staticMaxAge>",
|
||||
"<cacheThemes>true</cacheThemes>",
|
||||
"<cacheTemplates>true</cacheTemplates>",
|
||||
].join("\\s*"),
|
||||
"g",
|
||||
),
|
||||
[
|
||||
"<staticMaxAge>-1</staticMaxAge>",
|
||||
"<cacheThemes>false</cacheThemes>",
|
||||
"<cacheTemplates>false</cacheTemplates>"
|
||||
].join("\n")
|
||||
)
|
||||
"<cacheTemplates>false</cacheTemplates>",
|
||||
].join("\n"),
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
export * from "./generateDebugFiles";
|
||||
export * from "./generateDebugFiles";
|
||||
|
@ -2,7 +2,7 @@ import cheerio from "cheerio";
|
||||
import {
|
||||
replaceImportsFromStaticInJsCode,
|
||||
replaceImportsInInlineCssCode,
|
||||
generateCssCodeToDefineGlobals
|
||||
generateCssCodeToDefineGlobals,
|
||||
} from "../replaceImportFromStatic";
|
||||
import fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
@ -10,62 +10,62 @@ import { objectKeys } from "tsafe/objectKeys";
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
|
||||
export const pageIds = [
|
||||
"login.ftl", "register.ftl", "register-user-profile.ftl",
|
||||
"info.ftl", "error.ftl", "login-reset-password.ftl",
|
||||
"login-verify-email.ftl", "terms.ftl", "login-otp.ftl",
|
||||
"login-update-profile.ftl", "login-idp-link-confirm.ftl"
|
||||
"login.ftl",
|
||||
"register.ftl",
|
||||
"register-user-profile.ftl",
|
||||
"info.ftl",
|
||||
"error.ftl",
|
||||
"login-reset-password.ftl",
|
||||
"login-verify-email.ftl",
|
||||
"terms.ftl",
|
||||
"login-otp.ftl",
|
||||
"login-update-profile.ftl",
|
||||
"login-idp-link-confirm.ftl",
|
||||
] as const;
|
||||
|
||||
export type PageId = typeof pageIds[number];
|
||||
|
||||
function loadAdjacentFile(fileBasename: string) {
|
||||
return fs.readFileSync(pathJoin(__dirname, fileBasename))
|
||||
.toString("utf8");
|
||||
};
|
||||
return fs.readFileSync(pathJoin(__dirname, fileBasename)).toString("utf8");
|
||||
}
|
||||
|
||||
|
||||
export function generateFtlFilesCodeFactory(
|
||||
params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
indexHtmlCode: string;
|
||||
urlPathname: string;
|
||||
urlOrigin: undefined | string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { cssGlobalsToDefine, indexHtmlCode, urlPathname, urlOrigin } = params;
|
||||
export function generateFtlFilesCodeFactory(params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
indexHtmlCode: string;
|
||||
urlPathname: string;
|
||||
urlOrigin: undefined | string;
|
||||
}) {
|
||||
const { cssGlobalsToDefine, indexHtmlCode, urlPathname, urlOrigin } =
|
||||
params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
$("script:not([src])").each((...[, element]) => {
|
||||
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": $(element).html()!,
|
||||
urlOrigin
|
||||
urlOrigin,
|
||||
});
|
||||
|
||||
$(element).text(fixedJsCode);
|
||||
|
||||
});
|
||||
|
||||
$("style").each((...[, element]) => {
|
||||
|
||||
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
||||
"cssCode": $(element).html()!,
|
||||
"urlPathname": params.urlPathname,
|
||||
urlOrigin
|
||||
urlOrigin,
|
||||
});
|
||||
|
||||
$(element).text(fixedCssCode);
|
||||
|
||||
});
|
||||
|
||||
([
|
||||
["link", "href"],
|
||||
["script", "src"],
|
||||
] as const).forEach(([selector, attrName]) =>
|
||||
(
|
||||
[
|
||||
["link", "href"],
|
||||
["script", "src"],
|
||||
] as const
|
||||
).forEach(([selector, attrName]) =>
|
||||
$(selector).each((...[, element]) => {
|
||||
|
||||
const href = $(element).attr(attrName);
|
||||
|
||||
if (href === undefined) {
|
||||
@ -74,94 +74,90 @@ export function generateFtlFilesCodeFactory(
|
||||
|
||||
$(element).attr(
|
||||
attrName,
|
||||
urlOrigin !== undefined ?
|
||||
href.replace(/^\//, `${urlOrigin}/`) :
|
||||
href.replace(
|
||||
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
|
||||
"${url.resourcesPath}/build/"
|
||||
)
|
||||
urlOrigin !== undefined
|
||||
? href.replace(/^\//, `${urlOrigin}/`)
|
||||
: href.replace(
|
||||
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
|
||||
"${url.resourcesPath}/build/",
|
||||
),
|
||||
);
|
||||
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||
const ftlPlaceholders = {
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile("common.ftl")
|
||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
||||
[
|
||||
'<#if scripts??>',
|
||||
' <#list scripts as script>',
|
||||
' <script src="${script}" type="text/javascript"></script>',
|
||||
' </#list>',
|
||||
'</#if>'
|
||||
].join("\n")
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile(
|
||||
"common.ftl",
|
||||
).match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
|
||||
"<#if scripts??>",
|
||||
" <#list scripts as script>",
|
||||
' <script src="${script}" type="text/javascript"></script>',
|
||||
" </#list>",
|
||||
"</#if>",
|
||||
].join("\n"),
|
||||
};
|
||||
|
||||
const pageSpecificCodePlaceholder = "<!-- dIddLqMeOedErIdLsPdNdI9dSl42sw -->";
|
||||
const pageSpecificCodePlaceholder =
|
||||
"<!-- dIddLqMeOedErIdLsPdNdI9dSl42sw -->";
|
||||
|
||||
$("head").prepend(
|
||||
[
|
||||
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
|
||||
'',
|
||||
'<style>',
|
||||
generateCssCodeToDefineGlobals({
|
||||
cssGlobalsToDefine,
|
||||
urlPathname
|
||||
}).cssCodeToPrependInHead,
|
||||
'</style>',
|
||||
''
|
||||
]),
|
||||
...(Object.keys(cssGlobalsToDefine).length === 0
|
||||
? []
|
||||
: [
|
||||
"",
|
||||
"<style>",
|
||||
generateCssCodeToDefineGlobals({
|
||||
cssGlobalsToDefine,
|
||||
urlPathname,
|
||||
}).cssCodeToPrependInHead,
|
||||
"</style>",
|
||||
"",
|
||||
]),
|
||||
"<script>",
|
||||
loadAdjacentFile("Object.deepAssign.js"),
|
||||
"</script>",
|
||||
'<script>',
|
||||
"<script>",
|
||||
` window.${ftlValuesGlobalName}= Object.assign(`,
|
||||
` {},`,
|
||||
` ${objectKeys(ftlPlaceholders)[0]}`,
|
||||
' );',
|
||||
'</script>',
|
||||
'',
|
||||
" );",
|
||||
"</script>",
|
||||
"",
|
||||
pageSpecificCodePlaceholder,
|
||||
'',
|
||||
objectKeys(ftlPlaceholders)[1]
|
||||
"",
|
||||
objectKeys(ftlPlaceholders)[1],
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const partiallyFixedIndexHtmlCode = $.html();
|
||||
|
||||
function generateFtlFilesCode(
|
||||
params: {
|
||||
pageId: string;
|
||||
}
|
||||
): { ftlCode: string; } {
|
||||
|
||||
function generateFtlFilesCode(params: { pageId: string }): {
|
||||
ftlCode: string;
|
||||
} {
|
||||
const { pageId } = params;
|
||||
|
||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||
|
||||
let ftlCode = $.html()
|
||||
.replace(
|
||||
pageSpecificCodePlaceholder,
|
||||
[
|
||||
'<script>',
|
||||
` Object.deepAssign(`,
|
||||
` window.${ftlValuesGlobalName},`,
|
||||
` { "pageId": "${pageId}" }`,
|
||||
' );',
|
||||
'</script>'
|
||||
].join("\n")
|
||||
);
|
||||
let ftlCode = $.html().replace(
|
||||
pageSpecificCodePlaceholder,
|
||||
[
|
||||
"<script>",
|
||||
` Object.deepAssign(`,
|
||||
` window.${ftlValuesGlobalName},`,
|
||||
` { "pageId": "${pageId}" }`,
|
||||
" );",
|
||||
"</script>",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
objectKeys(ftlPlaceholders)
|
||||
.forEach(id => ftlCode = ftlCode.replace(id, ftlPlaceholders[id]));
|
||||
objectKeys(ftlPlaceholders).forEach(
|
||||
id => (ftlCode = ftlCode.replace(id, ftlPlaceholders[id])),
|
||||
);
|
||||
|
||||
return { ftlCode };
|
||||
|
||||
}
|
||||
|
||||
return { generateFtlFilesCode };
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
export * from "./generateFtl";
|
||||
export * from "./generateFtl";
|
||||
|
@ -1,39 +1,33 @@
|
||||
|
||||
import * as url from "url";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
|
||||
|
||||
export function generateJavaStackFiles(
|
||||
params: {
|
||||
version: string;
|
||||
themeName: string;
|
||||
homepage?: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}
|
||||
): { jarFilePath: string; } {
|
||||
|
||||
const {
|
||||
themeName,
|
||||
version,
|
||||
homepage,
|
||||
keycloakThemeBuildingDirPath
|
||||
} = params;
|
||||
export function generateJavaStackFiles(params: {
|
||||
version: string;
|
||||
themeName: string;
|
||||
homepage?: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}): { jarFilePath: string } {
|
||||
const { themeName, version, homepage, keycloakThemeBuildingDirPath } =
|
||||
params;
|
||||
|
||||
{
|
||||
|
||||
const { pomFileCode } = (function generatePomFileCode(): { pomFileCode: string; } {
|
||||
|
||||
|
||||
const { pomFileCode } = (function generatePomFileCode(): {
|
||||
pomFileCode: string;
|
||||
} {
|
||||
const groupId = (() => {
|
||||
|
||||
const fallbackGroupId = `there.was.no.homepage.field.in.the.package.json.${themeName}`;
|
||||
|
||||
return (!homepage ?
|
||||
fallbackGroupId :
|
||||
url.parse(homepage).host?.replace(/:[0-9]+$/,"")?.split(".").reverse().join(".") ?? fallbackGroupId
|
||||
) + ".keycloak";
|
||||
|
||||
return (
|
||||
(!homepage
|
||||
? fallbackGroupId
|
||||
: url
|
||||
.parse(homepage)
|
||||
.host?.replace(/:[0-9]+$/, "")
|
||||
?.split(".")
|
||||
.reverse()
|
||||
.join(".") ?? fallbackGroupId) + ".keycloak"
|
||||
);
|
||||
})();
|
||||
|
||||
const artefactId = `${themeName}-keycloak-theme`;
|
||||
@ -49,51 +43,57 @@ export function generateJavaStackFiles(
|
||||
` <version>${version}</version>`,
|
||||
` <name>${artefactId}</name>`,
|
||||
` <description />`,
|
||||
`</project>`
|
||||
`</project>`,
|
||||
].join("\n");
|
||||
|
||||
return { pomFileCode };
|
||||
|
||||
})();
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, "pom.xml"),
|
||||
Buffer.from(pomFileCode, "utf8")
|
||||
Buffer.from(pomFileCode, "utf8"),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
const themeManifestFilePath = pathJoin(
|
||||
keycloakThemeBuildingDirPath, "src", "main",
|
||||
"resources", "META-INF", "keycloak-themes.json"
|
||||
keycloakThemeBuildingDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"META-INF",
|
||||
"keycloak-themes.json",
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
fs.mkdirSync(pathDirname(themeManifestFilePath));
|
||||
|
||||
} catch { }
|
||||
} catch {}
|
||||
|
||||
fs.writeFileSync(
|
||||
themeManifestFilePath,
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
"themes": [
|
||||
{
|
||||
"name": themeName,
|
||||
"types": ["login"]
|
||||
}
|
||||
]
|
||||
}, null, 2),
|
||||
"utf8"
|
||||
)
|
||||
JSON.stringify(
|
||||
{
|
||||
"themes": [
|
||||
{
|
||||
"name": themeName,
|
||||
"types": ["login"],
|
||||
},
|
||||
],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return { "jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${themeName}-${version}.jar`) };
|
||||
|
||||
return {
|
||||
"jarFilePath": pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"target",
|
||||
`${themeName}-${version}.jar`,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,178 +1,197 @@
|
||||
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import {
|
||||
replaceImportsInCssCode,
|
||||
replaceImportsFromStaticInJsCode
|
||||
replaceImportsFromStaticInJsCode,
|
||||
} from "./replaceImportFromStatic";
|
||||
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
|
||||
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
|
||||
import * as child_process from "child_process";
|
||||
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/getKcContext/kcContextMocks/urlResourcesPath";
|
||||
import {
|
||||
resourcesCommonPath,
|
||||
resourcesPath,
|
||||
subDirOfPublicDirBasename,
|
||||
} from "../../lib/getKcContext/kcContextMocks/urlResourcesPath";
|
||||
import { isInside } from "../tools/isInside";
|
||||
|
||||
|
||||
export function generateKeycloakThemeResources(
|
||||
params: {
|
||||
themeName: string;
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
urlPathname: string;
|
||||
//If urlOrigin is not undefined then it means --externals-assets
|
||||
urlOrigin: undefined | string;
|
||||
extraPagesId: string[];
|
||||
extraThemeProperties: string[];
|
||||
keycloakVersion: "11.0.3" | "15.0.2"
|
||||
}
|
||||
) {
|
||||
|
||||
const {
|
||||
themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath,
|
||||
urlPathname, urlOrigin, extraPagesId, extraThemeProperties,
|
||||
keycloakVersion
|
||||
export function generateKeycloakThemeResources(params: {
|
||||
themeName: string;
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
urlPathname: string;
|
||||
//If urlOrigin is not undefined then it means --externals-assets
|
||||
urlOrigin: undefined | string;
|
||||
extraPagesId: string[];
|
||||
extraThemeProperties: string[];
|
||||
keycloakVersion: "11.0.3" | "15.0.2";
|
||||
}) {
|
||||
const {
|
||||
themeName,
|
||||
reactAppBuildDirPath,
|
||||
keycloakThemeBuildingDirPath,
|
||||
urlPathname,
|
||||
urlOrigin,
|
||||
extraPagesId,
|
||||
extraThemeProperties,
|
||||
keycloakVersion,
|
||||
} = params;
|
||||
|
||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||
const themeDirPath = pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"theme",
|
||||
themeName,
|
||||
"login",
|
||||
);
|
||||
|
||||
let allCssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
transformCodebase({
|
||||
"destDirPath":
|
||||
urlOrigin === undefined ?
|
||||
pathJoin(themeDirPath, "resources", "build") :
|
||||
reactAppBuildDirPath,
|
||||
urlOrigin === undefined
|
||||
? pathJoin(themeDirPath, "resources", "build")
|
||||
: reactAppBuildDirPath,
|
||||
"srcDirPath": reactAppBuildDirPath,
|
||||
"transformSourceCode": ({ filePath, sourceCode }) => {
|
||||
|
||||
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
||||
if (
|
||||
urlOrigin === undefined &&
|
||||
isInside({
|
||||
"dirPath": pathJoin(reactAppBuildDirPath, subDirOfPublicDirBasename),
|
||||
filePath
|
||||
"dirPath": pathJoin(
|
||||
reactAppBuildDirPath,
|
||||
subDirOfPublicDirBasename,
|
||||
),
|
||||
filePath,
|
||||
})
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (urlOrigin === undefined && /\.css?$/i.test(filePath)) {
|
||||
|
||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
|
||||
{ "cssCode": sourceCode.toString("utf8") }
|
||||
);
|
||||
const { cssGlobalsToDefine, fixedCssCode } =
|
||||
replaceImportsInCssCode({
|
||||
"cssCode": sourceCode.toString("utf8"),
|
||||
});
|
||||
|
||||
allCssGlobalsToDefine = {
|
||||
...allCssGlobalsToDefine,
|
||||
...cssGlobalsToDefine
|
||||
...cssGlobalsToDefine,
|
||||
};
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||
|
||||
return {
|
||||
"modifiedSourceCode": Buffer.from(fixedCssCode, "utf8"),
|
||||
};
|
||||
}
|
||||
|
||||
if (/\.js?$/i.test(filePath)) {
|
||||
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": sourceCode.toString("utf8"),
|
||||
urlOrigin
|
||||
urlOrigin,
|
||||
});
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||
|
||||
return {
|
||||
"modifiedSourceCode": Buffer.from(fixedJsCode, "utf8"),
|
||||
};
|
||||
}
|
||||
|
||||
return urlOrigin === undefined ?
|
||||
{ "modifiedSourceCode": sourceCode } :
|
||||
undefined;
|
||||
|
||||
}
|
||||
return urlOrigin === undefined
|
||||
? { "modifiedSourceCode": sourceCode }
|
||||
: undefined;
|
||||
},
|
||||
});
|
||||
|
||||
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||
"indexHtmlCode": fs.readFileSync(
|
||||
pathJoin(reactAppBuildDirPath, "index.html")
|
||||
).toString("utf8"),
|
||||
"indexHtmlCode": fs
|
||||
.readFileSync(pathJoin(reactAppBuildDirPath, "index.html"))
|
||||
.toString("utf8"),
|
||||
urlPathname,
|
||||
urlOrigin
|
||||
urlOrigin,
|
||||
});
|
||||
|
||||
[...pageIds, ...extraPagesId].forEach(pageId => {
|
||||
|
||||
const { ftlCode } = generateFtlFilesCode({ pageId });
|
||||
|
||||
fs.mkdirSync(themeDirPath, { "recursive": true });
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(themeDirPath, pageId),
|
||||
Buffer.from(ftlCode, "utf8")
|
||||
Buffer.from(ftlCode, "utf8"),
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
"destDirPath": tmpDirPath
|
||||
"destDirPath": tmpDirPath,
|
||||
});
|
||||
|
||||
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
|
||||
"destDirPath": themeResourcesDirPath
|
||||
"srcDirPath": pathJoin(
|
||||
tmpDirPath,
|
||||
"keycloak",
|
||||
"login",
|
||||
"resources",
|
||||
),
|
||||
"destDirPath": themeResourcesDirPath,
|
||||
});
|
||||
|
||||
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
|
||||
const reactAppPublicDirPath = pathJoin(
|
||||
reactAppBuildDirPath,
|
||||
"..",
|
||||
"public",
|
||||
);
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": themeResourcesDirPath,
|
||||
"destDirPath": pathJoin(
|
||||
reactAppPublicDirPath,
|
||||
resourcesPath
|
||||
)
|
||||
"destDirPath": pathJoin(reactAppPublicDirPath, resourcesPath),
|
||||
});
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
|
||||
"destDirPath": pathJoin(
|
||||
reactAppPublicDirPath,
|
||||
resourcesCommonPath
|
||||
)
|
||||
"srcDirPath": pathJoin(
|
||||
tmpDirPath,
|
||||
"keycloak",
|
||||
"common",
|
||||
"resources",
|
||||
),
|
||||
"destDirPath": pathJoin(reactAppPublicDirPath, resourcesCommonPath),
|
||||
});
|
||||
|
||||
const keycloakResourcesWithinPublicDirPath =
|
||||
pathJoin(reactAppPublicDirPath, subDirOfPublicDirBasename);
|
||||
|
||||
const keycloakResourcesWithinPublicDirPath = pathJoin(
|
||||
reactAppPublicDirPath,
|
||||
subDirOfPublicDirBasename,
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
|
||||
Buffer.from([
|
||||
"This is just a test folder that helps develop",
|
||||
"the login and register page without having to yarn build"
|
||||
].join(" "))
|
||||
Buffer.from(
|
||||
[
|
||||
"This is just a test folder that helps develop",
|
||||
"the login and register page without having to yarn build",
|
||||
].join(" "),
|
||||
),
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"),
|
||||
Buffer.from("*", "utf8")
|
||||
Buffer.from("*", "utf8"),
|
||||
);
|
||||
|
||||
child_process.execSync(`rm -r ${tmpDirPath}`);
|
||||
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(themeDirPath, "theme.properties"),
|
||||
Buffer.from(
|
||||
"parent=keycloak".concat("\n\n", extraThemeProperties.join("\n\n")),
|
||||
"utf8"
|
||||
)
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,5 @@ export * from "./build-keycloak-theme";
|
||||
import { main } from "./build-keycloak-theme";
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
main();
|
||||
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
|
||||
import * as crypto from "crypto";
|
||||
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
|
||||
|
||||
export function replaceImportsFromStaticInJsCode(
|
||||
params: {
|
||||
jsCode: string;
|
||||
urlOrigin: undefined | string;
|
||||
}
|
||||
): { fixedJsCode: string; } {
|
||||
|
||||
export function replaceImportsFromStaticInJsCode(params: {
|
||||
jsCode: string;
|
||||
urlOrigin: undefined | string;
|
||||
}): { fixedJsCode: string } {
|
||||
/*
|
||||
NOTE:
|
||||
|
||||
@ -23,114 +19,104 @@ export function replaceImportsFromStaticInJsCode(
|
||||
|
||||
const { jsCode, urlOrigin } = params;
|
||||
|
||||
const fixedJsCode =
|
||||
jsCode
|
||||
.replace(
|
||||
/([a-z]+\.[a-z]+)\+"static\//g,
|
||||
(...[, group]) =>
|
||||
urlOrigin === undefined ?
|
||||
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` :
|
||||
`("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`
|
||||
)
|
||||
.replace(
|
||||
/".chunk.css",([a-z])+=([a-z]+\.[a-z]+)\+([a-z]+),/,
|
||||
(...[, group1, group2, group3]) =>
|
||||
urlOrigin === undefined ?
|
||||
`".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},` :
|
||||
`".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group2} + ${group3},`
|
||||
);
|
||||
const fixedJsCode = jsCode
|
||||
.replace(/([a-z]+\.[a-z]+)\+"static\//g, (...[, group]) =>
|
||||
urlOrigin === undefined
|
||||
? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
|
||||
: `("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`,
|
||||
)
|
||||
.replace(
|
||||
/".chunk.css",([a-z])+=([a-z]+\.[a-z]+)\+([a-z]+),/,
|
||||
(...[, group1, group2, group3]) =>
|
||||
urlOrigin === undefined
|
||||
? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
|
||||
: `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group2} + ${group3},`,
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
|
||||
}
|
||||
|
||||
export function replaceImportsInInlineCssCode(
|
||||
params: {
|
||||
cssCode: string;
|
||||
urlPathname: string;
|
||||
urlOrigin: undefined | string;
|
||||
}
|
||||
): { fixedCssCode: string; } {
|
||||
|
||||
export function replaceImportsInInlineCssCode(params: {
|
||||
cssCode: string;
|
||||
urlPathname: string;
|
||||
urlOrigin: undefined | string;
|
||||
}): { fixedCssCode: string } {
|
||||
const { cssCode, urlPathname, urlOrigin } = params;
|
||||
|
||||
const fixedCssCode = cssCode.replace(
|
||||
urlPathname === "/" ?
|
||||
/url\(\/([^/][^)]+)\)/g :
|
||||
new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
|
||||
(...[, group]) => `url(${urlOrigin === undefined ?
|
||||
"${url.resourcesPath}/build/" + group :
|
||||
params.urlOrigin + urlPathname + group})`
|
||||
urlPathname === "/"
|
||||
? /url\(\/([^/][^)]+)\)/g
|
||||
: new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
|
||||
(...[, group]) =>
|
||||
`url(${
|
||||
urlOrigin === undefined
|
||||
? "${url.resourcesPath}/build/" + group
|
||||
: params.urlOrigin + urlPathname + group
|
||||
})`,
|
||||
);
|
||||
|
||||
return { fixedCssCode };
|
||||
|
||||
}
|
||||
|
||||
export function replaceImportsInCssCode(
|
||||
params: {
|
||||
cssCode: string;
|
||||
}
|
||||
): {
|
||||
export function replaceImportsInCssCode(params: { cssCode: string }): {
|
||||
fixedCssCode: string;
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
} {
|
||||
|
||||
const { cssCode } = params;
|
||||
|
||||
const cssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
|
||||
.forEach(match =>
|
||||
cssGlobalsToDefine[
|
||||
"url" + crypto
|
||||
.createHash("sha256")
|
||||
.update(match)
|
||||
.digest("hex")
|
||||
.substring(0, 15)
|
||||
] = match
|
||||
);
|
||||
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? []).forEach(
|
||||
match =>
|
||||
(cssGlobalsToDefine[
|
||||
"url" +
|
||||
crypto
|
||||
.createHash("sha256")
|
||||
.update(match)
|
||||
.digest("hex")
|
||||
.substring(0, 15)
|
||||
] = match),
|
||||
);
|
||||
|
||||
let fixedCssCode = cssCode;
|
||||
|
||||
Object.keys(cssGlobalsToDefine).forEach(
|
||||
cssVariableName =>
|
||||
//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 };
|
||||
|
||||
}
|
||||
|
||||
export function generateCssCodeToDefineGlobals(
|
||||
params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
urlPathname: string;
|
||||
}
|
||||
): {
|
||||
export function generateCssCodeToDefineGlobals(params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
urlPathname: string;
|
||||
}): {
|
||||
cssCodeToPrependInHead: string;
|
||||
} {
|
||||
|
||||
const { cssGlobalsToDefine, urlPathname } = params;
|
||||
|
||||
return {
|
||||
"cssCodeToPrependInHead": [
|
||||
":root {",
|
||||
...Object.keys(cssGlobalsToDefine)
|
||||
.map(cssVariableName => [
|
||||
`--${cssVariableName}:`,
|
||||
cssGlobalsToDefine[cssVariableName]
|
||||
.replace(new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"), "url(${url.resourcesPath}/build/")
|
||||
].join(" "))
|
||||
.map(cssVariableName =>
|
||||
[
|
||||
`--${cssVariableName}:`,
|
||||
cssGlobalsToDefine[cssVariableName].replace(
|
||||
new RegExp(
|
||||
`url\\(${urlPathname.replace(/\//g, "\\/")}`,
|
||||
"g",
|
||||
),
|
||||
"url(${url.resourcesPath}/build/",
|
||||
),
|
||||
].join(" "),
|
||||
)
|
||||
.map(line => ` ${line};`),
|
||||
"}"
|
||||
].join("\n")
|
||||
"}",
|
||||
].join("\n"),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -2,52 +2,49 @@
|
||||
|
||||
import { keycloakThemeBuildingDirPath } from "./build-keycloak-theme";
|
||||
import { join as pathJoin } from "path";
|
||||
import { downloadAndUnzip } from "./tools/downloadAndUnzip"
|
||||
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
||||
import type { KeycloakVersion } from "./KeycloakVersion";
|
||||
|
||||
export function downloadBuiltinKeycloakTheme(
|
||||
params: {
|
||||
keycloakVersion: KeycloakVersion;
|
||||
destDirPath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
export function downloadBuiltinKeycloakTheme(params: {
|
||||
keycloakVersion: KeycloakVersion;
|
||||
destDirPath: string;
|
||||
}) {
|
||||
const { keycloakVersion, destDirPath } = params;
|
||||
|
||||
for (const ext of ["", "-community"]) {
|
||||
|
||||
downloadAndUnzip({
|
||||
"destDirPath": destDirPath,
|
||||
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
||||
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`
|
||||
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
const keycloakVersion = (() => {
|
||||
|
||||
const keycloakVersion = process.argv[2] as (KeycloakVersion | undefined);
|
||||
const keycloakVersion = process.argv[2] as KeycloakVersion | undefined;
|
||||
|
||||
if (keycloakVersion === undefined) {
|
||||
return "15.0.2";
|
||||
}
|
||||
|
||||
return keycloakVersion;
|
||||
|
||||
})();
|
||||
|
||||
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
|
||||
const destDirPath = pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"theme",
|
||||
);
|
||||
|
||||
console.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
||||
console.log(
|
||||
`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`,
|
||||
);
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
destDirPath
|
||||
destDirPath,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ import { keycloakVersions } from "./KeycloakVersion";
|
||||
const propertiesParser = require("properties-parser");
|
||||
|
||||
for (const keycloakVersion of keycloakVersions) {
|
||||
|
||||
console.log({ keycloakVersion });
|
||||
|
||||
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
|
||||
@ -20,20 +19,21 @@ for (const keycloakVersion of keycloakVersions) {
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
"destDirPath": tmpDirPath
|
||||
"destDirPath": tmpDirPath,
|
||||
});
|
||||
|
||||
type Dictionary = { [idiomId: string]: string };
|
||||
|
||||
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } = {};
|
||||
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } =
|
||||
{};
|
||||
|
||||
{
|
||||
|
||||
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
|
||||
|
||||
crawl(baseThemeDirPath).forEach(filePath => {
|
||||
|
||||
const match = filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/);
|
||||
const match = filePath.match(
|
||||
/^([^/]+)\/messages\/messages_([^.]+)\.properties$/,
|
||||
);
|
||||
|
||||
if (match === null) {
|
||||
return;
|
||||
@ -45,43 +45,58 @@ for (const keycloakVersion of keycloakVersions) {
|
||||
Object.fromEntries(
|
||||
Object.entries(
|
||||
propertiesParser.parse(
|
||||
fs.readFileSync(
|
||||
pathJoin(baseThemeDirPath, filePath)
|
||||
)
|
||||
.toString("utf8")
|
||||
)
|
||||
).map(([key, value]: any) => [key, value.replace(/''/g, "'")])
|
||||
fs
|
||||
.readFileSync(
|
||||
pathJoin(baseThemeDirPath, filePath),
|
||||
)
|
||||
.toString("utf8"),
|
||||
),
|
||||
).map(([key, value]: any) => [
|
||||
key,
|
||||
value.replace(/''/g, "'"),
|
||||
]),
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
rm_r(tmpDirPath);
|
||||
|
||||
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_kcMessages", keycloakVersion);
|
||||
const targetDirPath = pathJoin(
|
||||
getProjectRoot(),
|
||||
"src",
|
||||
"lib",
|
||||
"i18n",
|
||||
"generated_kcMessages",
|
||||
keycloakVersion,
|
||||
);
|
||||
|
||||
fs.mkdirSync(targetDirPath, { "recursive": true });
|
||||
|
||||
Object.keys(record).forEach(pageType => {
|
||||
|
||||
const filePath = pathJoin(targetDirPath, `${pageType}.ts`);
|
||||
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
Buffer.from(
|
||||
[
|
||||
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
|
||||
'//PLEASE DO NOT EDIT MANUALLY',
|
||||
'',
|
||||
'/* spell-checker: disable */',
|
||||
`export const kcMessages= ${JSON.stringify(record[pageType], null, 2)};`,
|
||||
'/* spell-checker: enable */'
|
||||
].join("\n"), "utf8")
|
||||
`//This code was automatically generated by running ${pathRelative(
|
||||
getProjectRoot(),
|
||||
__filename,
|
||||
)}`,
|
||||
"//PLEASE DO NOT EDIT MANUALLY",
|
||||
"",
|
||||
"/* spell-checker: disable */",
|
||||
`export const kcMessages= ${JSON.stringify(
|
||||
record[pageType],
|
||||
null,
|
||||
2,
|
||||
)};`,
|
||||
"/* spell-checker: enable */",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
|
||||
console.log(`${filePath} wrote`);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { execSync } from "child_process";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import * as fs from "fs";
|
||||
@ -12,7 +11,9 @@ fs.writeFileSync(
|
||||
(() => {
|
||||
const packageJsonParsed = JSON.parse(
|
||||
fs
|
||||
.readFileSync(pathJoin(keycloakifyDirPath, "package.json"))
|
||||
.readFileSync(
|
||||
pathJoin(keycloakifyDirPath, "package.json"),
|
||||
)
|
||||
.toString("utf8"),
|
||||
);
|
||||
|
||||
@ -31,7 +32,13 @@ fs.writeFileSync(
|
||||
|
||||
const commonThirdPartyDeps = (() => {
|
||||
const namespaceModuleNames = ["@emotion"];
|
||||
const standaloneModuleNames = ["react", "@types/react", "powerhooks", "tss-react", "evt"];
|
||||
const standaloneModuleNames = [
|
||||
"react",
|
||||
"@types/react",
|
||||
"powerhooks",
|
||||
"tss-react",
|
||||
"evt",
|
||||
];
|
||||
|
||||
return [
|
||||
...namespaceModuleNames
|
||||
@ -69,7 +76,9 @@ const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
|
||||
...(targetModuleName !== undefined ? [targetModuleName] : []),
|
||||
].join(" ");
|
||||
|
||||
console.log(`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`);
|
||||
console.log(
|
||||
`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`,
|
||||
);
|
||||
|
||||
execSync(cmd, {
|
||||
cwd,
|
||||
@ -128,4 +137,4 @@ testAppNames.forEach(testAppName =>
|
||||
"cwd": getTestAppPath(testAppName),
|
||||
"targetModuleName": "keycloakify",
|
||||
}),
|
||||
);
|
||||
);
|
||||
|
@ -3,35 +3,25 @@ import * as path from "path";
|
||||
|
||||
/** List all files in a given directory return paths relative to the dir_path */
|
||||
export const crawl = (() => {
|
||||
|
||||
const crawlRec = (dir_path: string, paths: string[]) => {
|
||||
|
||||
for (const file_name of fs.readdirSync(dir_path)) {
|
||||
|
||||
const file_path = path.join(dir_path, file_name);
|
||||
|
||||
if (fs.lstatSync(file_path).isDirectory()) {
|
||||
|
||||
crawlRec(file_path, paths);
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
paths.push(file_path);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return function crawl(dir_path: string): string[] {
|
||||
|
||||
const paths: string[] = [];
|
||||
|
||||
crawlRec(dir_path, paths);
|
||||
|
||||
return paths.map(file_path => path.relative(dir_path, file_path));
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
};
|
||||
})();
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { basename as pathBasename, join as pathJoin } from "path";
|
||||
import { execSync } from "child_process";
|
||||
import fs from "fs";
|
||||
@ -6,14 +5,11 @@ import { transformCodebase } from "../tools/transformCodebase";
|
||||
import { rm_rf, rm, rm_r } from "./rm";
|
||||
|
||||
/** assert url ends with .zip */
|
||||
export function downloadAndUnzip(
|
||||
params: {
|
||||
url: string;
|
||||
destDirPath: string;
|
||||
pathOfDirToExtractInArchive?: string;
|
||||
}
|
||||
) {
|
||||
|
||||
export function downloadAndUnzip(params: {
|
||||
url: string;
|
||||
destDirPath: string;
|
||||
pathOfDirToExtractInArchive?: string;
|
||||
}) {
|
||||
const { url, destDirPath, pathOfDirToExtractInArchive } = params;
|
||||
|
||||
const tmpDirPath = pathJoin(destDirPath, "..", "tmp_xxKdOxnEdx");
|
||||
@ -25,23 +21,23 @@ export function downloadAndUnzip(
|
||||
execSync(`wget ${url}`, { "cwd": tmpDirPath });
|
||||
|
||||
execSync(
|
||||
`unzip ${pathBasename(url)
|
||||
}${pathOfDirToExtractInArchive === undefined ?
|
||||
"" : ` "${pathOfDirToExtractInArchive}/*"`
|
||||
`unzip ${pathBasename(url)}${
|
||||
pathOfDirToExtractInArchive === undefined
|
||||
? ""
|
||||
: ` "${pathOfDirToExtractInArchive}/*"`
|
||||
}`,
|
||||
{ "cwd": tmpDirPath }
|
||||
{ "cwd": tmpDirPath },
|
||||
);
|
||||
|
||||
rm(pathBasename(url), { "cwd": tmpDirPath });
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathOfDirToExtractInArchive === undefined ?
|
||||
tmpDirPath :
|
||||
pathJoin(tmpDirPath, pathOfDirToExtractInArchive)
|
||||
,
|
||||
"srcDirPath":
|
||||
pathOfDirToExtractInArchive === undefined
|
||||
? tmpDirPath
|
||||
: pathJoin(tmpDirPath, pathOfDirToExtractInArchive),
|
||||
destDirPath,
|
||||
});
|
||||
|
||||
rm_r(tmpDirPath);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,4 +16,4 @@ export function getProjectRoot(): string {
|
||||
}
|
||||
|
||||
return (result = getProjectRootRec(__dirname));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { getProjectRoot } from "./getProjectRoot";
|
||||
import { join as pathJoin } from "path";
|
||||
import child_process from "child_process";
|
||||
|
||||
import { getProjectRoot } from "./getProjectRoot";
|
||||
import { join as pathJoin } from "path";
|
||||
import child_process from "child_process";
|
||||
|
||||
Object.entries<string>(require(pathJoin(getProjectRoot(), "package.json"))["bin"])
|
||||
.forEach(([, scriptPath]) => child_process.execSync(`chmod +x ${scriptPath}`, { "cwd": getProjectRoot() }));
|
||||
|
||||
Object.entries<string>(
|
||||
require(pathJoin(getProjectRoot(), "package.json"))["bin"],
|
||||
).forEach(([, scriptPath]) =>
|
||||
child_process.execSync(`chmod +x ${scriptPath}`, {
|
||||
"cwd": getProjectRoot(),
|
||||
}),
|
||||
);
|
||||
|
@ -1,14 +1,7 @@
|
||||
import { relative as pathRelative } from "path";
|
||||
|
||||
export function isInside(
|
||||
params: {
|
||||
dirPath: string;
|
||||
filePath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
export function isInside(params: { dirPath: string; filePath: string }) {
|
||||
const { dirPath, filePath } = params;
|
||||
|
||||
return !pathRelative(dirPath, filePath).startsWith("..");
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,38 @@
|
||||
|
||||
import { execSync } from "child_process";
|
||||
|
||||
function rmInternal(
|
||||
params: {
|
||||
pathToRemove: string;
|
||||
args: string | undefined;
|
||||
cwd: string | undefined;
|
||||
}
|
||||
) {
|
||||
function rmInternal(params: {
|
||||
pathToRemove: string;
|
||||
args: string | undefined;
|
||||
cwd: string | undefined;
|
||||
}) {
|
||||
const { pathToRemove, args, cwd } = params;
|
||||
|
||||
const { pathToRemove, args, cwd } = params;
|
||||
|
||||
execSync(
|
||||
`rm ${args ? `-${args} ` : ""}${pathToRemove.replace(/\ /g, "\\ ")}`,
|
||||
cwd !== undefined ? { cwd } : undefined
|
||||
);
|
||||
execSync(
|
||||
`rm ${args ? `-${args} ` : ""}${pathToRemove.replace(/ /g, "\\ ")}`,
|
||||
cwd !== undefined ? { cwd } : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
export function rm(pathToRemove: string, options?: { cwd: string; }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": undefined,
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
export function rm(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": undefined,
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
}
|
||||
|
||||
export function rm_r(pathToRemove: string, options?: { cwd: string; }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "r",
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
export function rm_r(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "r",
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
}
|
||||
|
||||
export function rm_rf(pathToRemove: string, options?: { cwd: string; }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "rf",
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
export function rm_rf(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "rf",
|
||||
"cwd": options?.cwd,
|
||||
});
|
||||
}
|
||||
|
@ -1,69 +1,56 @@
|
||||
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { crawl } from "./crawl";
|
||||
import { id } from "tsafe/id";
|
||||
import { id } from "tsafe/id";
|
||||
|
||||
type TransformSourceCode =
|
||||
(params: {
|
||||
sourceCode: Buffer;
|
||||
filePath: string;
|
||||
}) => {
|
||||
modifiedSourceCode: Buffer;
|
||||
newFileName?: string;
|
||||
} | undefined;
|
||||
type TransformSourceCode = (params: {
|
||||
sourceCode: Buffer;
|
||||
filePath: string;
|
||||
}) =>
|
||||
| {
|
||||
modifiedSourceCode: Buffer;
|
||||
newFileName?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
/** Apply a transformation function to every file of directory */
|
||||
export function transformCodebase(
|
||||
params: {
|
||||
srcDirPath: string;
|
||||
destDirPath: string;
|
||||
transformSourceCode?: TransformSourceCode;
|
||||
}
|
||||
) {
|
||||
|
||||
const {
|
||||
srcDirPath,
|
||||
destDirPath,
|
||||
transformSourceCode = id<TransformSourceCode>(({ sourceCode }) => ({ "modifiedSourceCode": sourceCode }))
|
||||
export function transformCodebase(params: {
|
||||
srcDirPath: string;
|
||||
destDirPath: string;
|
||||
transformSourceCode?: TransformSourceCode;
|
||||
}) {
|
||||
const {
|
||||
srcDirPath,
|
||||
destDirPath,
|
||||
transformSourceCode = id<TransformSourceCode>(({ sourceCode }) => ({
|
||||
"modifiedSourceCode": sourceCode,
|
||||
})),
|
||||
} = params;
|
||||
|
||||
for (const file_relative_path of crawl(srcDirPath)) {
|
||||
|
||||
const filePath = path.join(srcDirPath, file_relative_path);
|
||||
|
||||
const transformSourceCodeResult = transformSourceCode({
|
||||
"sourceCode": fs.readFileSync(filePath),
|
||||
"filePath": path.join(srcDirPath, file_relative_path)
|
||||
"filePath": path.join(srcDirPath, file_relative_path),
|
||||
});
|
||||
|
||||
if (transformSourceCodeResult === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.mkdirSync(
|
||||
path.dirname(
|
||||
path.join(
|
||||
destDirPath,
|
||||
file_relative_path
|
||||
)
|
||||
),
|
||||
{ "recursive": true }
|
||||
);
|
||||
fs.mkdirSync(path.dirname(path.join(destDirPath, file_relative_path)), {
|
||||
"recursive": true,
|
||||
});
|
||||
|
||||
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(
|
||||
path.dirname(path.join(destDirPath, file_relative_path)),
|
||||
newFileName ?? path.basename(file_relative_path)
|
||||
newFileName ?? path.basename(file_relative_path),
|
||||
),
|
||||
modifiedSourceCode
|
||||
modifiedSourceCode,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user