Remove eslint and run prettier (changelog ignore)

This commit is contained in:
garronej
2021-10-11 21:35:40 +02:00
parent 9f8218efb7
commit 305ce9e44d
76 changed files with 27255 additions and 22419 deletions

View File

@ -1,5 +1,3 @@
export const keycloakVersions = ["11.0.3", "15.0.2"] as const;
export type KeycloakVersion = typeof keycloakVersions[number];

View File

@ -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"),
);
}

View File

@ -1,2 +1 @@
export const ftlValuesGlobalName = "kcContext";
export const ftlValuesGlobalName = "kcContext";

View File

@ -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"),
),
);
}
}

View File

@ -1 +1 @@
export * from "./generateDebugFiles";
export * from "./generateDebugFiles";

View File

@ -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 };
}
}

View File

@ -1 +1 @@
export * from "./generateFtl";
export * from "./generateFtl";

View File

@ -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`,
),
};
}

View File

@ -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",
),
);
}

View File

@ -4,7 +4,5 @@ export * from "./build-keycloak-theme";
import { main } from "./build-keycloak-theme";
if (require.main === module) {
main();
}
main();
}

View File

@ -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"),
};
}

View File

@ -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,
});
}

View File

@ -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`);
});
}

View File

@ -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",
}),
);
);

View File

@ -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));
}
})();
};
})();

View File

@ -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);
}
}

View File

@ -16,4 +16,4 @@ export function getProjectRoot(): string {
}
return (result = getProjectRootRec(__dirname));
}
}

View File

@ -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(),
}),
);

View File

@ -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("..");
}
}

View File

@ -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,
});
}

View File

@ -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,
);
}
}