import cheerio from "cheerio"; import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode"; import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCssCode"; import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode"; import * as fs from "fs"; import { join as pathJoin } from "path"; import { objectKeys } from "tsafe/objectKeys"; import type { BuildOptions } from "../buildOptions"; import { assert } from "tsafe/assert"; import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, resources_common } from "../../constants"; export type BuildOptionsLike = { bundler: "vite" | "webpack"; themeVersion: string; urlPathname: string | undefined; reactAppBuildDirPath: string; assetsDirPath: string; }; assert(); export function generateFtlFilesCodeFactory(params: { themeName: string; indexHtmlCode: string; cssGlobalsToDefine: Record; buildOptions: BuildOptionsLike; keycloakifyVersion: string; themeType: ThemeType; fieldNames: string[]; }) { const { themeName, cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType, fieldNames } = params; const $ = cheerio.load(indexHtmlCode); fix_imports_statements: { $("script:not([src])").each((...[, element]) => { const jsCode = $(element).html(); assert(jsCode !== null); const { fixedJsCode } = replaceImportsInJsCode({ jsCode, buildOptions }); $(element).text(fixedJsCode); }); $("style").each((...[, element]) => { const cssCode = $(element).html(); assert(cssCode !== null); const { fixedCssCode } = replaceImportsInInlineCssCode({ cssCode, buildOptions }); $(element).text(fixedCssCode); }); ( [ ["link", "href"], ["script", "src"] ] as const ).forEach(([selector, attrName]) => $(selector).each((...[, element]) => { const href = $(element).attr(attrName); if (href === undefined) { return; } $(element).attr( attrName, href.replace( new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), `\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/` ) ); }) ); if (Object.keys(cssGlobalsToDefine).length !== 0) { $("head").prepend( [ "", "", "" ].join("\n") ); } } //FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later. const replaceValueBySearchValue = { '{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs .readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl")) .toString("utf8") .match(/^', " ", "" ].join("\n") }; $("head").prepend( [ "", "", objectKeys(replaceValueBySearchValue)[1] ].join("\n") ); // Remove part of the document marked as ignored. { const startTags = $('meta[name="keycloakify-ignore-start"]'); startTags.each((...[, startTag]) => { const $startTag = $(startTag); const $endTag = $startTag.nextAll('meta[name="keycloakify-ignore-end"]').first(); if ($endTag.length) { let currentNode = $startTag.next(); while (currentNode.length && !currentNode.is($endTag)) { currentNode.remove(); currentNode = $startTag.next(); } $startTag.remove(); $endTag.remove(); } }); } const partiallyFixedIndexHtmlCode = $.html(); function generateFtlFilesCode(params: { pageId: string }): { ftlCode: string; } { const { pageId } = params; const $ = cheerio.load(partiallyFixedIndexHtmlCode); let ftlCode = $.html(); Object.entries({ ...replaceValueBySearchValue, "PAGE_ID_xIgLsPgGId9D8e": pageId }).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue))); return { ftlCode }; } return { generateFtlFilesCode }; }