Compare commits

...

24 Commits

Author SHA1 Message Date
d96ff13a67 Update changelog v0.3.10 2021-03-26 13:05:25 +00:00
2c1351ce47 fmt (changelog ignore) 2021-03-26 14:04:45 +01:00
96cd56ec77 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 14:02:46 +01:00
e5b2096d65 Bump version (changelog ignore) 2021-03-26 14:02:40 +01:00
3aa140335f Handle <style> tag, improve documentation 2021-03-26 14:02:14 +01:00
4cafaa2492 Update changelog v0.3.9 2021-03-25 11:54:13 +00:00
9c633a7521 Update readme 2021-03-25 12:48:40 +01:00
e27845ba91 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-25 12:01:15 +01:00
2a8708a45b Document --external-assets 2021-03-25 12:01:11 +01:00
6874fa4c24 Update README.md 2021-03-24 09:12:50 +01:00
ba531a4927 Update README.md 2021-03-24 09:12:04 +01:00
20175b57cf Update README.md 2021-03-24 09:10:44 +01:00
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
9 changed files with 336 additions and 87 deletions

View File

@ -1,3 +1,31 @@
### **0.3.10** (2021-03-26)
- Handle <style> tag, improve documentation
### **0.3.9** (2021-03-25)
- Update readme
- Document --external-assets
- Update README.md
- Update README.md
- Update README.md
### **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

139
README.md
View File

@ -20,8 +20,10 @@
The problem:
![keycloak_before](https://user-images.githubusercontent.com/6702424/108838381-dbbbcf80-75d3-11eb-8ae8-db41563ef9db.gif)
<p align="center">
<i>Without keycloakify:</i><br>
<img src="https://user-images.githubusercontent.com/6702424/108838381-dbbbcf80-75d3-11eb-8ae8-db41563ef9db.gif">
</p>
When we redirected to Keycloak the user suffers from a harsh context switch.
Keycloak does offer a way to customize theses pages but it requires a lot of raw HTML/CSS hacking
to reproduce the look and feel of a specific app. Not mentioning the maintenance cost of such an endeavour.
@ -29,39 +31,39 @@ to reproduce the look and feel of a specific app. Not mentioning the maintenance
Wouldn't it be great if we could just design the login and register pages as if they where part of our app?
Here is `yarn add keycloakify` for you 🍸
TODO: Insert video after.
<p align="center">
<i>With keycloakify:</i><br>
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
</p>
Tested with the following Keycloak versions:
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
**Disclaimer**: This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
(before you customize it) will always be the ones of the Keycloak v11.
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
- [Motivations](#motivations)
- [How to use](#how-to-use)
- [Setting up the build tool](#setting-up-the-build-tool)
- [Specify from where the resources should be downloaded.](#specify-from-where-the-resources-should-be-downloaded)
- [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)
- [Changing the look **and** feel](#changing-the-look-and-feel)
- [Hot reload](#hot-reload)
- [GitHub Actions](#github-actions)
- [REQUIREMENTS](#requirements)
- [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)
- [Workarounds](#workarounds)
- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [API Reference](#api-reference)
- [The build tool](#the-build-tool)
- [Implement context persistance (optional)](#implement-context-persistance-optional)
# How to use
## Setting up the build tool
Add `keycloakify` to the dev dependencies of your project `npm install --save-dev keycloakify` or `yarn add --dev keycloakify`
then configure your `package.json` build's script to build the keycloak's theme by adding `&& build-keycloak-theme`.
Typically you will get:
`package.json`
[`package.json`](https://github.com/garronej/keycloakify-demo-app/blob/main/package.json)
```json
"devDependencies": {
"homepage": "https://URL.OF/YOUR-APP"
"dependencies": {
"keycloakify": "^0.0.10"
},
"scripts": {
@ -69,14 +71,60 @@ Typically you will get:
},
```
Then run `yarn keycloak` or `npm run keycloak`, you will be provided with instructions
about how to load the theme into Keycloak.
It is mandatory that you specify the url where your app will be available
using the `homepage` field.
Once you've edited your `package.json` you can install your new
dependency with `yarn install` and build the keycloak theme with
`yarn keycloak`.
Once the build is complete instructions about how to load
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.
<details>
<summary>Click to expand</summary>
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`, 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 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
npx build-keycloak-theme --external-assets
```
(instead of `npx build-keycloak-theme`)
This is something you probably want to do in your CI pipeline. [Example](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/.github/workflows/ci.yaml#L112)
Also note that there is [a same-origin policy exception for fonts](https://en.wikipedia.org/wiki/Same-origin_policy#cite_note-3) so you must enabled
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)
</details>
## Developing your login and register pages in your React app
### Just changing the look
The fist approach is to only arr/replace the default class names by your
The first approach is to only arr/replace the default class names by your
own.
```tsx
@ -159,30 +207,52 @@ Checkout [this concrete example](https://github.com/garronej/keycloakify-demo-ap
[Here is a demo repo](https://github.com/garronej/keycloakify-demo-app) to show how to automate
the building and publishing of the theme (the .jar file).
# REQUIREMENTS
# Requirements
Tested with the following Keycloak versions:
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
(before you customize it) will always be the ones of the Keycloak v11.
This tools assumes you are bundling your app with Webpack (tested with 4.44.2) .
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
and a `static/` directory generated by webpack.
and a `build/static/` directory generated by webpack.
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3=)
- For building the theme: `mvn` (Maven) must be installed
- For development, (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
- For development (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
and `docker` up and running.
NOTE: This build tool has only be tested on MacOS.
# API Reference
# Limitations
## The build tool
## `process.env.PUBLIC_URL` not supported.
Part of the lib that runs with node, at build time.
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).
- `npx build-keycloak-theme`: 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)
## `@font-face` importing fonts from the `src/` dir
# Implement context persistance (optional)
**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)
If, before logging in, a user has selected a specific language
you don't want it to be reset to default when the user gets redirected to
@ -240,3 +310,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)

View File

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

View File

@ -2,7 +2,8 @@
import cheerio from "cheerio";
import {
replaceImportFromStaticInJsCode,
replaceImportsFromStaticInJsCode,
replaceImportsInInlineCssCode,
generateCssCodeToDefineGlobals
} from "../replaceImportFromStatic";
import fs from "fs";
@ -11,9 +12,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 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))
.toString("utf8");
};
@ -29,29 +30,51 @@ function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
}
}
export type Mode = {
type: "standalone";
urlPathname: string;
} | {
type: "external assets";
urlPathname: string;
urlOrigin: string;
}
export function generateFtlFilesCodeFactory(
params: {
ftlValuesGlobalName: string;
cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string;
mode: Mode;
}
) {
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, mode } = params;
const $ = cheerio.load(indexHtmlCode);
$("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportFromStaticInJsCode({
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
ftlValuesGlobalName,
"jsCode": $(element).html()!
"jsCode": $(element).html()!,
mode
});
$(element).text(fixedJsCode);
});
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
mode
});
$(element).text(fixedCssCode);
});
([
["link", "href"],
["script", "src"],
@ -60,11 +83,28 @@ export function generateFtlFilesCodeFactory(
const href = $(element).attr(attrName);
if (!href?.startsWith("/")) {
if (href === undefined) {
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 +129,10 @@ export function generateFtlFilesCodeFactory(
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
'',
'<style>',
generateCssCodeToDefineGlobals(
{ cssGlobalsToDefine }
).cssCodeToPrependInHead,
generateCssCodeToDefineGlobals({
cssGlobalsToDefine,
"urlPathname": mode.urlPathname
}).cssCodeToPrependInHead,
'</style>',
''
]),

View File

@ -3,26 +3,28 @@ 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 } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import * as child_process from "child_process";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
import { isInside } from "../tools/isInside";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
import { isInside } from "../tools/isInside";
export function generateKeycloakThemeResources(
params: {
themeName: string;
reactAppBuildDirPath: 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");
@ -43,30 +45,34 @@ export function generateKeycloakThemeResources(
return undefined;
}
if (mode.type === "standalone") {
if (/\.css?$/i.test(filePath)) {
if (/\.css?$/i.test(filePath)) {
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
{ "cssCode": sourceCode.toString("utf8") }
);
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
{ "cssCode": sourceCode.toString("utf8") }
);
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine
};
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...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({
"jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName
});
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName,
mode
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
}
@ -80,7 +86,8 @@ export function generateKeycloakThemeResources(
ftlValuesGlobalName,
"indexHtmlCode": fs.readFileSync(
pathJoin(reactAppBuildDirPath, "index.html")
).toString("utf8")
).toString("utf8"),
mode
});
pageIds.forEach(pageId => {

View File

@ -6,10 +6,13 @@ import type { ParsedPackageJson } from "./generateJavaStackFiles";
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 { URL } from "url";
const reactProjectDirPath = process.cwd();
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
@ -22,7 +25,51 @@ if (require.main === module) {
generateKeycloakThemeResources({
keycloakThemeBuildingDirPath,
"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({

View File

@ -1,25 +1,66 @@
import * as crypto from "crypto";
export function replaceImportFromStaticInJsCode(
type Mode = {
type: "standalone";
} | {
type: "external assets";
urlOrigin: string;
urlPathname: string;
}
export function replaceImportsFromStaticInJsCode(
params: {
ftlValuesGlobalName: string;
jsCode: string;
mode: Mode;
}
): { fixedJsCode: string; } {
const { jsCode, ftlValuesGlobalName } = params;
const { jsCode, ftlValuesGlobalName, mode } = params;
const fixedJsCode = jsCode!.replace(
/"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/build/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;
}
@ -32,7 +73,7 @@ export function replaceImportFromStaticInCssCode(
const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/url\(\/[^)]+\)[^;}]*/g) ?? [])
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
.forEach(match =>
cssGlobalsToDefine[
"url" + crypto
@ -60,12 +101,13 @@ export function replaceImportFromStaticInCssCode(
export function generateCssCodeToDefineGlobals(
params: {
cssGlobalsToDefine: Record<string, string>;
urlPathname: string;
}
): {
cssCodeToPrependInHead: string;
} {
const { cssGlobalsToDefine } = params;
const { cssGlobalsToDefine, urlPathname } = params;
return {
"cssCodeToPrependInHead": [
@ -74,7 +116,7 @@ export function generateCssCodeToDefineGlobals(
.map(cssVariableName => [
`--${cssVariableName}:`,
cssGlobalsToDefine[cssVariableName]
.replace(/url\(/g, "url(${url.resourcesPath}/build")
.replace(new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"), "url(${url.resourcesPath}/build/")
].join(" "))
.map(line => ` ${line};`),
"}"

View File

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

View File

@ -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() {
@ -19,12 +19,13 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
3: "0664cdc0"
}[e] + ".chunk.js"
}
`
`,
"mode": { "type": "standalone" }
});
console.log({ fixedJsCode });
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
"cssCode": `
.my-div {
@ -45,6 +46,6 @@ const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
console.log({ fixedCssCode, cssGlobalsToDefine });
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine });
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine, "urlPathname": "/" });
console.log({ cssCodeToPrependInHead });