diff --git a/README.md b/README.md
index eec470c1..0a62f358 100644
--- a/README.md
+++ b/README.md
@@ -49,9 +49,12 @@ Here is `yarn add keycloakify` for you 🍸
- [GitHub Actions](#github-actions)
- [Requirements](#requirements)
- [Limitations](#limitations)
+ - [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
+ - [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-thesrc-dir)
+ - [Example of setup that **won't** work](#example-of-setup-that-wont-work)
+- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [API Reference](#api-reference)
- [The build tool](#the-build-tool)
-- [Implement context persistence (optional)](#implement-context-persistence-optional)
# How to use
## Setting up the build tool
@@ -79,18 +82,27 @@ the theme into Keycloak are printed in the console.
### Specify from where the resources should be downloaded.
+*TL;DR*: Building the theme with the `--external-assets` option enables the login
+page to load faster for first time users but it also implies that:
+- If the app is down, your Keycloak login and register pages are down as well.
+- Each time the app is updated, the theme must be updated as well.
+- CORS must be enabled for fonts.
+
+
+ Click to expand
+
When you run `npx build-keycloak-theme` without arguments, Keycloakify will build
a standalone version of the Keycloak theme. That is to say even if your app, the
-one hosted at the url specified as `homepage` in your package.json, is down the
-Keycloak theme will still work.
-In this mode (the default) every asset are served by the keycloak server. It is
-convergent for debugging but it production you probably want the assets to be
-fetched from your app.
-Indeed in the default mode your users have to download again the whole app just
-to access the login page. You probably have [long-term asset caching](https://create-react-app.dev/docs/production-build/#static-file-caching)
+one hosted at the url specified as `homepage`, is down the Keycloak theme will still work.
+It also mean that you won't have to update your theme on your Keycloak server each time
+your app is updated.
+In this mode, the default, every asset are served by the keycloak server.
+The drawback of this approach is that when users access the login page for the first time
+they have to download the whole app again.
+You probably have [long-term asset caching](https://create-react-app.dev/docs/production-build/#static-file-caching)
enabled in the server that host your app ([example](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/nginx.conf#L14))
-so it's better if only the html is served by the Keycloak server and everything
-else, your JS bundles, your CSS ect point to your app.
+so it can be interesting to only serve the html from Keycloak server and everything
+else, your JS bundles, your CSS ect from the server that host your app.
To enable this behavior you car run:
```bash
@@ -104,6 +116,9 @@ Also note that there is [a same-origin policy exception for fonts](https://en.wi
CORS for fonts on the server hosting your app. Concretely this mean that your server should add a `Access-Control-Allow-Origin: *` response header to
GET request on *.woff2?. [Example with Nginx](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/nginx.conf#L18-L20)
+
+
+
## Developing your login and register pages in your React app
### Just changing the look
@@ -214,19 +229,27 @@ NOTE: This build tool has only be tested on MacOS.
# Limitations
-In the standalone mode (when you run `npx build-keycloak-theme` without `--external-assets`) the fonts won't work if you are self
-hosting them. This, for example, won’t work: [`src: url("/assets/worksans-bold-webfont.woff2") format("woff2")`](https://github.com/InseeFrLab/onyxia-ui/blob/b24df3a9b34b505ce00619bb8ec0174223ecfaca/src/app/theme/fonts.scss#L5-L6)
-you will have to [host them externally](https://github.com/InseeFrLab/onyxia-ui/blob/43bf4a508419072a4ae202698e59d20b69feb9c0/src/app/theme/fonts.scss#L8-L9)
-on a server that has CORS enabled.
-Again this apply ony if you are not building your theme with `--external-assets` which is advised against in production.
-# API Reference
+## `process.env.PUBLIC_URL` not supported.
-## The build tool
+You won't be able to [import things from your public directory in your JavaScript code](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system). (This isn't recommended anyway).
-Part of the lib that runs with node, at build time.
+## `@font-face` importing fonts from the `src/` dir
-- `npx build-keycloak-theme [--external-assets]`: Builds the theme, the CWD is assumed to be the root of your react project.
-- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
+**If you are building the theme with `--external-assets` this limitation doesn't apply.**
+### Example of setup that **won't** work
+
+- We have a `fonts/` directory in `src/`
+- We import the font like this [`src: url("/fonts/my-font.woff2") format("woff2");`(https://github.com/garronej/keycloakify-demo-app/blob/07d54a3012ef354ee12b1374c6f7ad1cb125d56b/src/fonts.scss#L4) in a `.scss` a file.
+
+### Workarounds
+
+If it is possible, use Google Fonts or any other font provider.
+
+If you want to host your font recommended approach is to move your fonts into the `public`
+directory and to place your `@font-face` statements in the `public/index.html`.
+Example [here]().
+
+You can also [use your explicit url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
# Implement context persistence (optional)
@@ -286,3 +309,12 @@ keycloakInstance.init({
If you really want to go the extra miles and avoid having the white
flash of the blank html before the js bundle have been evaluated
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
+
+# API Reference
+
+## The build tool
+
+Part of the lib that runs with node, at build time.
+
+- `npx build-keycloak-theme [--external-assets]`: Builds the theme, the CWD is assumed to be the root of your react project.
+- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
\ No newline at end of file
diff --git a/src/bin/build-keycloak-theme/generateFtl/index.ts b/src/bin/build-keycloak-theme/generateFtl/index.ts
index 9ad33323..c6e8d019 100644
--- a/src/bin/build-keycloak-theme/generateFtl/index.ts
+++ b/src/bin/build-keycloak-theme/generateFtl/index.ts
@@ -2,7 +2,8 @@
import cheerio from "cheerio";
import {
- replaceImportFromStaticInJsCode,
+ replaceImportsFromStaticInJsCode,
+ replaceImportsInInlineCssCode,
generateCssCodeToDefineGlobals
} from "../replaceImportFromStatic";
import fs from "fs";
@@ -53,7 +54,7 @@ export function generateFtlFilesCodeFactory(
$("script:not([src])").each((...[, element]) => {
- const { fixedJsCode } = replaceImportFromStaticInJsCode({
+ const { fixedJsCode } = replaceImportsFromStaticInJsCode({
ftlValuesGlobalName,
"jsCode": $(element).html()!,
mode
@@ -63,6 +64,17 @@ export function generateFtlFilesCodeFactory(
});
+ $("style").each((...[, element]) => {
+
+ const { fixedCssCode } = replaceImportsInInlineCssCode({
+ "cssCode": $(element).html()!,
+ mode
+ });
+
+ $(element).text(fixedCssCode);
+
+ });
+
([
["link", "href"],
["script", "src"],
diff --git a/src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts b/src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts
index dbf1872c..5e0ab31b 100644
--- a/src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts
+++ b/src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts
@@ -3,8 +3,8 @@ import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs";
import { join as pathJoin } from "path";
import {
- replaceImportFromStaticInCssCode,
- replaceImportFromStaticInJsCode
+ replaceImportsInCssCode,
+ replaceImportsFromStaticInJsCode
} from "./replaceImportFromStatic";
import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
@@ -49,7 +49,7 @@ export function generateKeycloakThemeResources(
if (/\.css?$/i.test(filePath)) {
- const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
+ const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
{ "cssCode": sourceCode.toString("utf8") }
);
@@ -64,7 +64,7 @@ export function generateKeycloakThemeResources(
if (/\.js?$/i.test(filePath)) {
- const { fixedJsCode } = replaceImportFromStaticInJsCode({
+ const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName,
mode
diff --git a/src/bin/build-keycloak-theme/replaceImportFromStatic.ts b/src/bin/build-keycloak-theme/replaceImportFromStatic.ts
index 463343f0..e1630e77 100644
--- a/src/bin/build-keycloak-theme/replaceImportFromStatic.ts
+++ b/src/bin/build-keycloak-theme/replaceImportFromStatic.ts
@@ -9,7 +9,7 @@ type Mode = {
urlPathname: string;
}
-export function replaceImportFromStaticInJsCode(
+export function replaceImportsFromStaticInJsCode(
params: {
ftlValuesGlobalName: string;
jsCode: string;
@@ -19,27 +19,48 @@ export function replaceImportFromStaticInJsCode(
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(
+ /[a-z]+\.[a-z]+\+"static\//g,
+ (() => {
+ switch (mode.type) {
+ case "standalone":
+ return `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`;
+ case "external assets":
+ return `"${mode.urlOrigin}${mode.urlPathname}static/`;
+ }
+ })()
+ );
return { fixedJsCode };
}
-export function replaceImportFromStaticInCssCode(
+export function replaceImportsInInlineCssCode(
+ params: {
+ cssCode: string;
+ mode: Mode;
+ }
+): { fixedCssCode: string; } {
+
+ const { cssCode, mode } = params;
+
+ const fixedCssCode = cssCode.replace(
+ /url\((\/[^/][^)]+)\)/g,
+ (...[,group])=> `url(${
+ (()=>{
+ switch(mode.type){
+ case "standalone": return "${url.resourcesPath}/build" + group;
+ case "external assets": return mode.urlOrigin + group
+ }
+ })()
+ })`
+ );
+
+ return { fixedCssCode };
+
+}
+
+export function replaceImportsInCssCode(
params: {
cssCode: string;
}
@@ -52,7 +73,7 @@ export function replaceImportFromStaticInCssCode(
const cssGlobalsToDefine: Record = {};
- new Set(cssCode.match(/url\(\/[^)]+\)[^;}]*/g) ?? [])
+ new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
.forEach(match =>
cssGlobalsToDefine[
"url" + crypto
diff --git a/src/test/replaceImportFromStatic.ts b/src/test/replaceImportFromStatic.ts
index 5121b3ac..40bb34f4 100644
--- a/src/test/replaceImportFromStatic.ts
+++ b/src/test/replaceImportFromStatic.ts
@@ -1,11 +1,11 @@
import {
- replaceImportFromStaticInJsCode,
- replaceImportFromStaticInCssCode,
+ replaceImportsFromStaticInJsCode,
+ replaceImportsInCssCode,
generateCssCodeToDefineGlobals
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
-const { fixedJsCode } = replaceImportFromStaticInJsCode({
+const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"ftlValuesGlobalName": "keycloakFtlValues",
"jsCode": `
function f() {
@@ -25,7 +25,7 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
console.log({ fixedJsCode });
-const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
+const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
"cssCode": `
.my-div {