keycloakify
Version:
Framework to create custom Keycloak UIs
224 lines (184 loc) • 7.01 kB
text/typescript
import { getIsPrettierAvailable, runPrettier } from "../tools/runPrettier";
import * as fsPr from "fs/promises";
import { join as pathJoin, sep as pathSep } from "path";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../shared/buildContext";
import { KEYCLOAK_THEME } from "../shared/constants";
export type BuildContextLike = {
themeSrcDirPath: string;
publicDirPath: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
buildContext: BuildContextLike;
isPublic: boolean;
fileRelativePath: string;
isOwnershipAction: boolean;
extensionModuleDirPath: string;
extensionModuleName: string;
extensionModuleVersion: string;
}): Promise<Buffer> {
const {
buildContext,
extensionModuleDirPath,
isPublic,
fileRelativePath,
isOwnershipAction,
extensionModuleName,
extensionModuleVersion
} = params;
const { refSourceCode } = await (async () => {
let sourceCode: string | undefined = undefined;
const sourceCode_originalBuffer = await fsPr.readFile(
pathJoin(
extensionModuleDirPath,
KEYCLOAK_THEME,
isPublic ? "public" : ".",
fileRelativePath
)
);
let hasBeenUpdated = false;
const refSourceCode = {
get current(): string {
if (sourceCode === undefined) {
sourceCode = sourceCode_originalBuffer.toString("utf8");
}
return sourceCode;
},
set current(value: string) {
hasBeenUpdated = true;
sourceCode = value;
},
getAsBuffer: () => {
if (!hasBeenUpdated) {
return sourceCode_originalBuffer;
}
return Buffer.from(refSourceCode.current, "utf8");
}
};
return { refSourceCode };
})();
add_eslint_disable: {
if (isOwnershipAction) {
break add_eslint_disable;
}
if (!fileRelativePath.endsWith(".ts") && !fileRelativePath.endsWith(".tsx")) {
break add_eslint_disable;
}
if (refSourceCode.current.includes("/* eslint-disable */")) {
break add_eslint_disable;
}
refSourceCode.current = ["/* eslint-disable */", "", refSourceCode.current].join(
"\n"
);
}
addCommentToSourceCode({
refSourceCode,
fileRelativePath,
commentLines: (() => {
const path = fileRelativePath.split(pathSep).join("/");
return isOwnershipAction
? [
`This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
`To relinquish ownership and restore this file to its original content, run the following command:`,
``,
`$ npx keycloakify own --path "${path}" ${isPublic ? "--public " : ""}--revert`
]
: [
`WARNING: Before modifying this file, run the following command:`,
``,
`$ npx keycloakify own --path "${path}"${isPublic ? " --public" : ""}`,
``,
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
`It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`
];
})()
});
format: {
if (!(await getIsPrettierAvailable())) {
break format;
}
const sourceCode_buffer_before = refSourceCode.getAsBuffer();
const sourceCode_buffer_after = await runPrettier({
filePath: pathJoin(
isPublic
? pathJoin(buildContext.publicDirPath, KEYCLOAK_THEME)
: buildContext.themeSrcDirPath,
fileRelativePath
),
sourceCode: sourceCode_buffer_before
});
if (sourceCode_buffer_before.compare(sourceCode_buffer_after) === 0) {
break format;
}
refSourceCode.current = sourceCode_buffer_after.toString("utf8");
}
return refSourceCode.getAsBuffer();
}
function addCommentToSourceCode(params: {
refSourceCode: { current: string };
fileRelativePath: string;
commentLines: string[];
}): void {
const { refSourceCode, fileRelativePath, commentLines } = params;
const updateRef = (comment: string) => {
refSourceCode.current = [comment, ``, refSourceCode.current].join("\n");
};
for (const ext of [".ts", ".tsx", ".css", ".less", ".sass", ".js", ".jsx"]) {
if (!fileRelativePath.endsWith(ext)) {
continue;
}
updateRef([`/**`, ...commentLines.map(line => ` * ${line}`), ` */`].join("\n"));
return;
}
if (fileRelativePath.endsWith(".properties")) {
updateRef(commentLines.map(line => `# ${line}`).join("\n"));
return;
}
if (fileRelativePath.endsWith(".ftl")) {
const comment = [`<#--`, ...commentLines.map(line => ` ${line}`), `-->`].join(
"\n"
);
if (refSourceCode.current.trim().startsWith("<#ftl")) {
const [first, ...rest] = refSourceCode.current.split(">");
const last = rest.join(">");
refSourceCode.current = [`${first}>`, comment, last].join("\n");
return;
}
updateRef(comment);
return;
}
if (fileRelativePath.endsWith(".html") || fileRelativePath.endsWith(".svg")) {
const comment = [
`<!--`,
...commentLines.map(
line =>
` ${line
.replace("--path", "-t")
.replace("--revert", "-r")
.replace("Before modifying", "Before modifying or replacing")}`
),
`-->`
].join("\n");
if (
fileRelativePath.endsWith(".html") &&
refSourceCode.current.trim().startsWith("<!")
) {
const [first, ...rest] = refSourceCode.current.split(">");
const last = rest.join(">");
refSourceCode.current = [`${first}>`, comment, last].join("\n");
return;
}
if (
fileRelativePath.endsWith(".svg") &&
refSourceCode.current.trim().startsWith("<?")
) {
const [first, ...rest] = refSourceCode.current.split("?>");
const last = rest.join("?>");
refSourceCode.current = [`${first}?>`, comment, last].join("\n");
return;
}
updateRef(comment);
return;
}
}