Handle <style> tag, improve documentation
This commit is contained in:
parent
9c633a7521
commit
3aa140335f
72
README.md
72
README.md
@ -49,9 +49,12 @@ Here is `yarn add keycloakify` for you 🍸
|
|||||||
- [GitHub Actions](#github-actions)
|
- [GitHub Actions](#github-actions)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Limitations](#limitations)
|
- [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)
|
- [API Reference](#api-reference)
|
||||||
- [The build tool](#the-build-tool)
|
- [The build tool](#the-build-tool)
|
||||||
- [Implement context persistence (optional)](#implement-context-persistence-optional)
|
|
||||||
|
|
||||||
# How to use
|
# How to use
|
||||||
## Setting up the build tool
|
## 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.
|
### 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.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click to expand</summary>
|
||||||
|
|
||||||
When you run `npx build-keycloak-theme` without arguments, Keycloakify will build
|
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
|
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
|
one hosted at the url specified as `homepage`, is down the Keycloak theme will still work.
|
||||||
Keycloak theme will still work.
|
It also mean that you won't have to update your theme on your Keycloak server each time
|
||||||
In this mode (the default) every asset are served by the keycloak server. It is
|
your app is updated.
|
||||||
convergent for debugging but it production you probably want the assets to be
|
In this mode, the default, every asset are served by the keycloak server.
|
||||||
fetched from your app.
|
The drawback of this approach is that when users access the login page for the first time
|
||||||
Indeed in the default mode your users have to download again the whole app just
|
they have to download the whole app again.
|
||||||
to access the login page. You probably have [long-term asset caching](https://create-react-app.dev/docs/production-build/#static-file-caching)
|
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))
|
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
|
so it can be interesting to only serve the html from Keycloak server and everything
|
||||||
else, your JS bundles, your CSS ect point to your app.
|
else, your JS bundles, your CSS ect from the server that host your app.
|
||||||
|
|
||||||
To enable this behavior you car run:
|
To enable this behavior you car run:
|
||||||
```bash
|
```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
|
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)
|
GET request on *.woff2?. [Example with Nginx](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/nginx.conf#L18-L20)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
## Developing your login and register pages in your React app
|
## Developing your login and register pages in your React app
|
||||||
|
|
||||||
### Just changing the look
|
### Just changing the look
|
||||||
@ -214,19 +229,27 @@ NOTE: This build tool has only be tested on MacOS.
|
|||||||
|
|
||||||
# Limitations
|
# Limitations
|
||||||
|
|
||||||
In the standalone mode (when you run `npx build-keycloak-theme` without `--external-assets`) the fonts won't work if you are self
|
## `process.env.PUBLIC_URL` not supported.
|
||||||
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
|
|
||||||
|
|
||||||
## 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.
|
**If you are building the theme with `--external-assets` this limitation doesn't apply.**
|
||||||
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
|
### 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)
|
# Implement context persistence (optional)
|
||||||
|
|
||||||
@ -286,3 +309,12 @@ keycloakInstance.init({
|
|||||||
If you really want to go the extra miles and avoid having the white
|
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
|
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`.
|
[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)
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
import cheerio from "cheerio";
|
import cheerio from "cheerio";
|
||||||
import {
|
import {
|
||||||
replaceImportFromStaticInJsCode,
|
replaceImportsFromStaticInJsCode,
|
||||||
|
replaceImportsInInlineCssCode,
|
||||||
generateCssCodeToDefineGlobals
|
generateCssCodeToDefineGlobals
|
||||||
} from "../replaceImportFromStatic";
|
} from "../replaceImportFromStatic";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
@ -53,7 +54,7 @@ export function generateFtlFilesCodeFactory(
|
|||||||
|
|
||||||
$("script:not([src])").each((...[, element]) => {
|
$("script:not([src])").each((...[, element]) => {
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||||
ftlValuesGlobalName,
|
ftlValuesGlobalName,
|
||||||
"jsCode": $(element).html()!,
|
"jsCode": $(element).html()!,
|
||||||
mode
|
mode
|
||||||
@ -63,6 +64,17 @@ export function generateFtlFilesCodeFactory(
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("style").each((...[, element]) => {
|
||||||
|
|
||||||
|
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
||||||
|
"cssCode": $(element).html()!,
|
||||||
|
mode
|
||||||
|
});
|
||||||
|
|
||||||
|
$(element).text(fixedCssCode);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
([
|
([
|
||||||
["link", "href"],
|
["link", "href"],
|
||||||
["script", "src"],
|
["script", "src"],
|
||||||
|
@ -3,8 +3,8 @@ import { transformCodebase } from "../tools/transformCodebase";
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import {
|
import {
|
||||||
replaceImportFromStaticInCssCode,
|
replaceImportsInCssCode,
|
||||||
replaceImportFromStaticInJsCode
|
replaceImportsFromStaticInJsCode
|
||||||
} from "./replaceImportFromStatic";
|
} from "./replaceImportFromStatic";
|
||||||
import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl";
|
import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl";
|
||||||
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
||||||
@ -49,7 +49,7 @@ export function generateKeycloakThemeResources(
|
|||||||
|
|
||||||
if (/\.css?$/i.test(filePath)) {
|
if (/\.css?$/i.test(filePath)) {
|
||||||
|
|
||||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
|
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
|
||||||
{ "cssCode": sourceCode.toString("utf8") }
|
{ "cssCode": sourceCode.toString("utf8") }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export function generateKeycloakThemeResources(
|
|||||||
|
|
||||||
if (/\.js?$/i.test(filePath)) {
|
if (/\.js?$/i.test(filePath)) {
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||||
"jsCode": sourceCode.toString("utf8"),
|
"jsCode": sourceCode.toString("utf8"),
|
||||||
ftlValuesGlobalName,
|
ftlValuesGlobalName,
|
||||||
mode
|
mode
|
||||||
|
@ -9,7 +9,7 @@ type Mode = {
|
|||||||
urlPathname: string;
|
urlPathname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceImportFromStaticInJsCode(
|
export function replaceImportsFromStaticInJsCode(
|
||||||
params: {
|
params: {
|
||||||
ftlValuesGlobalName: string;
|
ftlValuesGlobalName: string;
|
||||||
jsCode: string;
|
jsCode: string;
|
||||||
@ -19,27 +19,48 @@ export function replaceImportFromStaticInJsCode(
|
|||||||
|
|
||||||
const { jsCode, ftlValuesGlobalName, mode } = params;
|
const { jsCode, ftlValuesGlobalName, mode } = params;
|
||||||
|
|
||||||
const fixedJsCode = (() => {
|
const fixedJsCode = jsCode.replace(
|
||||||
|
/[a-z]+\.[a-z]+\+"static\//g,
|
||||||
|
(() => {
|
||||||
switch (mode.type) {
|
switch (mode.type) {
|
||||||
case "standalone":
|
case "standalone":
|
||||||
return jsCode!.replace(
|
return `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`;
|
||||||
/[a-z]+\.[a-z]+\+"static\//g,
|
|
||||||
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
|
|
||||||
);
|
|
||||||
case "external assets":
|
case "external assets":
|
||||||
return jsCode!.replace(
|
return `"${mode.urlOrigin}${mode.urlPathname}static/`;
|
||||||
/[a-z]+\.[a-z]+\+"static\//g,
|
|
||||||
`"${mode.urlOrigin}${mode.urlPathname}static/`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})();
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
return { fixedJsCode };
|
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: {
|
params: {
|
||||||
cssCode: string;
|
cssCode: string;
|
||||||
}
|
}
|
||||||
@ -52,7 +73,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
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
replaceImportFromStaticInJsCode,
|
replaceImportsFromStaticInJsCode,
|
||||||
replaceImportFromStaticInCssCode,
|
replaceImportsInCssCode,
|
||||||
generateCssCodeToDefineGlobals
|
generateCssCodeToDefineGlobals
|
||||||
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
|
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||||
"ftlValuesGlobalName": "keycloakFtlValues",
|
"ftlValuesGlobalName": "keycloakFtlValues",
|
||||||
"jsCode": `
|
"jsCode": `
|
||||||
function f() {
|
function f() {
|
||||||
@ -25,7 +25,7 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
|||||||
|
|
||||||
console.log({ fixedJsCode });
|
console.log({ fixedJsCode });
|
||||||
|
|
||||||
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
|
const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
|
||||||
"cssCode": `
|
"cssCode": `
|
||||||
|
|
||||||
.my-div {
|
.my-div {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user