Compare commits

...

21 Commits

Author SHA1 Message Date
ad275e4c34 Update changelog v0.3.8 2021-03-22 22:36:17 +00:00
060b9fe0de Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 23:34:13 +01:00
17b24d14ed Make standalone mode the default 2021-03-22 23:34:07 +01:00
2d278b0680 Update changelog v0.3.7 2021-03-22 19:57:34 +00:00
fb5975e4f1 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 20:54:35 +01:00
24fccaf513 (test) external asset mode by default 2021-03-22 20:54:28 +01:00
293953aa1b Update changelog v0.3.6 2021-03-22 19:02:53 +00:00
1049e312f9 Fix previous release 2021-03-22 20:00:58 +01:00
a2db250600 Update changelog v0.3.5 2021-03-22 18:43:18 +00:00
cf7fe8c337 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 19:41:08 +01:00
f5350097bf Bump version (changelog ignore) 2021-03-22 19:40:58 +01:00
1cb5dd461b support homepage with urlPath 2021-03-22 19:40:38 +01:00
845599a5e8 Update changelog v0.3.4 2021-03-22 06:23:40 +00:00
0cc02c292f Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 07:21:36 +01:00
1919702326 Bugfix: Import assets from CSS 2021-03-22 07:21:31 +01:00
0c0052e1cd Update changelog v0.3.3 2021-03-22 04:27:01 +00:00
78622770ec Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 05:21:22 +01:00
7b86727394 Fix submit not receving correct text 2021-03-22 05:21:05 +01:00
0965f8648e Update changelog v0.3.2 2021-03-21 21:31:01 +00:00
98974b4367 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-21 22:25:49 +01:00
597bcadd9e Fix broken previous release 2021-03-21 22:25:47 +01:00
11 changed files with 189 additions and 57 deletions

@ -1,3 +1,31 @@
### **0.3.8** (2021-03-22)
- Make standalone mode the default
### **0.3.7** (2021-03-22)
- (test) external asset mode by default
### **0.3.6** (2021-03-22)
- Fix previous release
### **0.3.5** (2021-03-22)
- support homepage with urlPath
### **0.3.4** (2021-03-22)
- Bugfix: Import assets from CSS
### **0.3.3** (2021-03-22)
- Fix submit not receving correct text
### **0.3.2** (2021-03-21)
- Fix broken previous release
### **0.3.1** (2021-03-21) ### **0.3.1** (2021-03-21)
- kcHeaderClass can be updated after initial mount - kcHeaderClass can be updated after initial mount

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "0.3.1", "version": "0.3.8",
"description": "Keycloak theme generator for Reacts app", "description": "Keycloak theme generator for Reacts app",
"repository": { "repository": {
"type": "git", "type": "git",

@ -11,9 +11,9 @@ import { objectKeys } from "evt/tools/typeSafety/objectKeys";
export const pageIds = ["login.ftl", "register.ftl", "info.ftl", "error.ftl", "login-reset-password.ftl", "login-verify-email.ftl"] as const; export const pageIds = ["login.ftl", "register.ftl", "info.ftl", "error.ftl", "login-reset-password.ftl", "login-verify-email.ftl"] as const;
export type PageId = typeof pageIds[number]; export type PageId = typeof pageIds[number];
function loadAdjacentFile(fileBasename: string){ function loadAdjacentFile(fileBasename: string) {
return fs.readFileSync(pathJoin(__dirname, fileBasename)) return fs.readFileSync(pathJoin(__dirname, fileBasename))
.toString("utf8"); .toString("utf8");
}; };
@ -29,15 +29,25 @@ function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
} }
} }
export type Mode = {
type: "standalone";
urlPathname: string;
} | {
type: "external assets";
urlPathname: string;
urlOrigin: string;
}
export function generateFtlFilesCodeFactory( export function generateFtlFilesCodeFactory(
params: { params: {
ftlValuesGlobalName: string; ftlValuesGlobalName: string;
cssGlobalsToDefine: Record<string, string>; cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string; indexHtmlCode: string;
mode: Mode;
} }
) { ) {
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params; const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, mode } = params;
const $ = cheerio.load(indexHtmlCode); const $ = cheerio.load(indexHtmlCode);
@ -45,7 +55,8 @@ export function generateFtlFilesCodeFactory(
const { fixedJsCode } = replaceImportFromStaticInJsCode({ const { fixedJsCode } = replaceImportFromStaticInJsCode({
ftlValuesGlobalName, ftlValuesGlobalName,
"jsCode": $(element).html()! "jsCode": $(element).html()!,
mode
}); });
$(element).text(fixedJsCode); $(element).text(fixedJsCode);
@ -60,11 +71,28 @@ export function generateFtlFilesCodeFactory(
const href = $(element).attr(attrName); const href = $(element).attr(attrName);
if (!href?.startsWith("/")) { if (href === undefined) {
return; return;
} }
$(element).attr(attrName, "${url.resourcesPath}/build" + href); switch (mode.type) {
case "external assets":
$(element).attr(
attrName,
href.replace(/^\//, `${mode.urlOrigin}/`)
);
break;
case "standalone":
$(element).attr(
attrName,
href.replace(
new RegExp(`^${mode.urlPathname.replace(/\//g, "\\/")}`),
"${url.resourcesPath}/build/"
)
);
break;
}
}) })
); );
@ -89,9 +117,10 @@ export function generateFtlFilesCodeFactory(
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [ ...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
'', '',
'<style>', '<style>',
generateCssCodeToDefineGlobals( generateCssCodeToDefineGlobals({
{ cssGlobalsToDefine } cssGlobalsToDefine,
).cssCodeToPrependInHead, "urlPathname": mode.urlPathname
}).cssCodeToPrependInHead,
'</style>', '</style>',
'' ''
]), ]),

@ -6,23 +6,25 @@ import {
replaceImportFromStaticInCssCode, replaceImportFromStaticInCssCode,
replaceImportFromStaticInJsCode replaceImportFromStaticInJsCode
} from "./replaceImportFromStatic"; } from "./replaceImportFromStatic";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl"; import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes"; import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip"; import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import * as child_process from "child_process"; import * as child_process from "child_process";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName"; import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath"; import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
import { isInside } from "../tools/isInside"; import { isInside } from "../tools/isInside";
export function generateKeycloakThemeResources( export function generateKeycloakThemeResources(
params: { params: {
themeName: string; themeName: string;
reactAppBuildDirPath: string; reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string; keycloakThemeBuildingDirPath: string;
mode: Mode;
} }
) { ) {
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath } = params; const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, mode } = params;
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login"); const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
@ -43,30 +45,34 @@ export function generateKeycloakThemeResources(
return undefined; return undefined;
} }
if (mode.type === "standalone") {
if (/\.css?$/i.test(filePath)) { if (/\.css?$/i.test(filePath)) {
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode( const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
{ "cssCode": sourceCode.toString("utf8") } { "cssCode": sourceCode.toString("utf8") }
); );
allCssGlobalsToDefine = { allCssGlobalsToDefine = {
...allCssGlobalsToDefine, ...allCssGlobalsToDefine,
...cssGlobalsToDefine ...cssGlobalsToDefine
}; };
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") }; return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
} }
if (/\.js?$/i.test(filePath)) { if (/\.js?$/i.test(filePath)) {
const { fixedJsCode } = replaceImportFromStaticInJsCode({ const { fixedJsCode } = replaceImportFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"), "jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName ftlValuesGlobalName,
}); mode
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") }; return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
} }
@ -80,7 +86,8 @@ export function generateKeycloakThemeResources(
ftlValuesGlobalName, ftlValuesGlobalName,
"indexHtmlCode": fs.readFileSync( "indexHtmlCode": fs.readFileSync(
pathJoin(reactAppBuildDirPath, "index.html") pathJoin(reactAppBuildDirPath, "index.html")
).toString("utf8") ).toString("utf8"),
mode
}); });
pageIds.forEach(pageId => { pageIds.forEach(pageId => {

@ -6,10 +6,13 @@ import type { ParsedPackageJson } 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 * as child_process from "child_process";
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles"; import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
import { URL } from "url";
const reactProjectDirPath = process.cwd(); const reactProjectDirPath = process.cwd();
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");
@ -22,7 +25,51 @@ if (require.main === module) {
generateKeycloakThemeResources({ generateKeycloakThemeResources({
keycloakThemeBuildingDirPath, keycloakThemeBuildingDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"), "reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
"themeName": parsedPackageJson.name "themeName": parsedPackageJson.name,
"mode": (() => {
const url = (() => {
const { homepage } = parsedPackageJson;
return homepage === undefined ?
undefined :
new URL(homepage);
})();
const urlPathname =
url === undefined ?
"/" :
url.pathname.replace(/([^/])$/, "$1/");
return !doUseExternalAssets ?
{
"type": "standalone",
urlPathname
} as const
:
{
"type": "external assets",
urlPathname,
"urlOrigin": (() => {
if (url === undefined) {
console.error("ERROR: You must specify 'homepage' in your package.json");
process.exit(-1);
}
return url.origin;
})()
} as const;
})()
}); });
const { jarFilePath } = generateJavaStackFiles({ const { jarFilePath } = generateJavaStackFiles({

@ -1,19 +1,39 @@
import * as crypto from "crypto"; import * as crypto from "crypto";
type Mode = {
type: "standalone";
} | {
type: "external assets";
urlOrigin: string;
urlPathname: string;
}
export function replaceImportFromStaticInJsCode( export function replaceImportFromStaticInJsCode(
params: { params: {
ftlValuesGlobalName: string; ftlValuesGlobalName: string;
jsCode: string; jsCode: string;
mode: Mode;
} }
): { fixedJsCode: string; } { ): { fixedJsCode: string; } {
const { jsCode, ftlValuesGlobalName } = params; const { jsCode, ftlValuesGlobalName, mode } = params;
const fixedJsCode = (() => {
switch (mode.type) {
case "standalone":
return jsCode!.replace(
/[a-z]+\.[a-z]+\+"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
);
case "external assets":
return jsCode!.replace(
/[a-z]+\.[a-z]+\+"static\//g,
`"${mode.urlOrigin}${mode.urlPathname}static/`
);
}
})();
const fixedJsCode = jsCode!.replace(
/"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/build/static/`
);
return { fixedJsCode }; return { fixedJsCode };
@ -32,7 +52,7 @@ export function replaceImportFromStaticInCssCode(
const cssGlobalsToDefine: Record<string, string> = {}; const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? []) new Set(cssCode.match(/url\(\/[^)]+\)[^;}]*/g) ?? [])
.forEach(match => .forEach(match =>
cssGlobalsToDefine[ cssGlobalsToDefine[
"url" + crypto "url" + crypto
@ -60,12 +80,13 @@ export function replaceImportFromStaticInCssCode(
export function generateCssCodeToDefineGlobals( export function generateCssCodeToDefineGlobals(
params: { params: {
cssGlobalsToDefine: Record<string, string>; cssGlobalsToDefine: Record<string, string>;
urlPathname: string;
} }
): { ): {
cssCodeToPrependInHead: string; cssCodeToPrependInHead: string;
} { } {
const { cssGlobalsToDefine } = params; const { cssGlobalsToDefine, urlPathname } = params;
return { return {
"cssCodeToPrependInHead": [ "cssCodeToPrependInHead": [
@ -73,12 +94,8 @@ export function generateCssCodeToDefineGlobals(
...Object.keys(cssGlobalsToDefine) ...Object.keys(cssGlobalsToDefine)
.map(cssVariableName => [ .map(cssVariableName => [
`--${cssVariableName}:`, `--${cssVariableName}:`,
[ cssGlobalsToDefine[cssVariableName]
"url(", .replace(new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"), "url(${url.resourcesPath}/build/")
"${url.resourcesPath}/build" +
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
")"
].join("")
].join(" ")) ].join(" "))
.map(line => ` ${line};`), .map(line => ` ${line};`),
"}" "}"

@ -66,7 +66,7 @@ export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: Kc
props.kcButtonBlockClass, props.kcButtonLargeClass props.kcButtonBlockClass, props.kcButtonLargeClass
)} )}
type="submit" type="submit"
defaultValue={msgStr("doSubmit")} value={msgStr("doSubmit")}
/> />
</div> </div>
</div> </div>

@ -113,7 +113,7 @@ export const Register = memo(({ kcContext, ...props }: { kcContext: KcContext.Re
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}> <div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
<input className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)} type="submit" <input className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)} type="submit"
defaultValue={msgStr("doRegister")} /> value={msgStr("doRegister")} />
</div> </div>
</div> </div>
</form > </form >

@ -27,7 +27,6 @@ export type TemplateProps = {
infoNode?: ReactNode; infoNode?: ReactNode;
} & { kcContext: KcContext.Template; } & KcTemplateProps; } & { kcContext: KcContext.Template; } & KcTemplateProps;
export const Template = memo((props: TemplateProps) => { export const Template = memo((props: TemplateProps) => {
const { const {
@ -137,7 +136,7 @@ export const Template = memo((props: TemplateProps) => {
}; };
}, [props.kcHeaderClass]); }, [props.kcHtmlClass]);
if (!isExtraCssLoaded) { if (!isExtraCssLoaded) {
return null; return null;

@ -1,7 +1,7 @@
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { generateKeycloakThemeResources } from "../bin/build-keycloak-theme/generateKeycloakThemeResources"; import { generateKeycloakThemeResources } from "../bin/build-keycloak-theme/generateKeycloakThemeResources";
import { import {
setupSampleReactProject, setupSampleReactProject,
sampleReactProjectDirPath sampleReactProjectDirPath
} from "./setupSampleReactProject"; } from "./setupSampleReactProject";
@ -9,8 +9,12 @@ import {
setupSampleReactProject(); setupSampleReactProject();
generateKeycloakThemeResources({ generateKeycloakThemeResources({
"themeName": "onyxia-ui", "themeName": "keycloakify-demo-app",
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"), "reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme") "keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
"mode": {
"type": "standalone",
"urlPathname": "/keycloakify-demo-app/"
}
}); });

@ -19,7 +19,8 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
3: "0664cdc0" 3: "0664cdc0"
}[e] + ".chunk.js" }[e] + ".chunk.js"
} }
` `,
"mode": { "type": "standalone" }
}); });
console.log({ fixedJsCode }); console.log({ fixedJsCode });
@ -45,6 +46,6 @@ const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
console.log({ fixedCssCode, cssGlobalsToDefine }); console.log({ fixedCssCode, cssGlobalsToDefine });
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine }); const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine, "urlPathname": "/" });
console.log({ cssCodeToPrependInHead }); console.log({ cssCodeToPrependInHead });