Compare commits
5 Commits
v7.12.6-rc
...
v7.11.1
Author | SHA1 | Date | |
---|---|---|---|
9246a54c68 | |||
eaf9a5373a | |||
5b261c9916 | |||
73d155b3bb | |||
1f8b245499 |
@ -140,24 +140,6 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "satanshiro",
|
|
||||||
"name": "satanshiro",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/38865738?v=4",
|
|
||||||
"profile": "https://github.com/satanshiro",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "kpoelhekke",
|
|
||||||
"name": "Koen Poelhekke",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/1632377?v=4",
|
|
||||||
"profile": "https://poelhekke.dev",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
42
README.md
42
README.md
@ -20,7 +20,7 @@
|
|||||||
<a href="https://github.com/thomasdarimont/awesome-keycloak">
|
<a href="https://github.com/thomasdarimont/awesome-keycloak">
|
||||||
<img src="https://awesome.re/mentioned-badge.svg"/>
|
<img src="https://awesome.re/mentioned-badge.svg"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/kYFZG7fQmn">
|
<a href="https://discord.gg/rBzsYtUn">
|
||||||
<img src="https://img.shields.io/discord/1097708346976505977"/>
|
<img src="https://img.shields.io/discord/1097708346976505977"/>
|
||||||
</a>
|
</a>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -35,19 +35,13 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>This build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
|
<i>Ultimately this build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
|
||||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Whether or not React is your preferred framework, Keycloakify
|
|
||||||
> offers a solid option for building Keycloak themes.
|
|
||||||
> It's not just a convenient way to create a Keycloak theme
|
|
||||||
> when using React; it's a well-regarded solution that many
|
|
||||||
> developers appreciate.
|
|
||||||
|
|
||||||
## Sponsor 👼
|
## Sponsor 👼
|
||||||
|
|
||||||
We are exclusively sponsored by [Cloud IAM](https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github), a French company offering Keycloak as a service.
|
We are exclusively sponsored by [Cloud IAM](https://www.cloud-iam.com), a French company offering Keycloak as a service.
|
||||||
Their dedicated support helps us continue the development and maintenance of this project.
|
Their dedicated support helps us continue the development and maintenance of this project.
|
||||||
|
|
||||||
[Cloud IAM](https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github) provides the following services:
|
[Cloud IAM](https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github) provides the following services:
|
||||||
@ -55,20 +49,12 @@ Their dedicated support helps us continue the development and maintenance of thi
|
|||||||
- Simplify and secure your Keycloak Identity and Access Management. Keycloak as a Service.
|
- Simplify and secure your Keycloak Identity and Access Management. Keycloak as a Service.
|
||||||
- Custom theme building for your brand using Keycloakify.
|
- Custom theme building for your brand using Keycloakify.
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>Checkout <a href="https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github">Cloud IAM</a> and use promo code <code>keycloakify5</code></i>
|
<a href="https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github">
|
||||||
|
<img src="https://user-images.githubusercontent.com/6702424/233476937-e37b1dc6-5a1c-4a0d-ba02-61c2ce62ffb6.png" alt="Cloud IAM Logo" width="350"/>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
<i>Use promo code <code>keycloakify5</code> </i>
|
||||||
<br/>
|
<br/>
|
||||||
<i>5% of your annual subscription will be donated to us, and you'll get 5% off too.</i>
|
<i>5% of your annual subscription will be donated to us, and you'll get 5% off too.</i>
|
||||||
</p>
|
</p>
|
||||||
@ -104,8 +90,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://www.gravitysoftware.be"><img src="https://avatars.githubusercontent.com/u/1140574?v=4?s=100" width="100px;" alt="Thomas Silvestre"/><br /><sub><b>Thomas Silvestre</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=thosil" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://www.gravitysoftware.be"><img src="https://avatars.githubusercontent.com/u/1140574?v=4?s=100" width="100px;" alt="Thomas Silvestre"/><br /><sub><b>Thomas Silvestre</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=thosil" title="Code">💻</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/satanshiro"><img src="https://avatars.githubusercontent.com/u/38865738?v=4?s=100" width="100px;" alt="satanshiro"/><br /><sub><b>satanshiro</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=satanshiro" title="Code">💻</a></td>
|
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://poelhekke.dev"><img src="https://avatars.githubusercontent.com/u/1632377?v=4?s=100" width="100px;" alt="Koen Poelhekke"/><br /><sub><b>Koen Poelhekke</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kpoelhekke" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -117,11 +101,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
|
|
||||||
# Changelog highlights
|
# Changelog highlights
|
||||||
|
|
||||||
## 7.12
|
|
||||||
|
|
||||||
- You can now pack multiple themes variant in a single `.jar` bundle. In vanilla Keycloak themes you have the ability to extend a base theme.
|
|
||||||
There is now an idiomatic way of achieving the same result. [Learn more](https://docs.keycloakify.dev/build-options#keycloakify.extrathemenames).
|
|
||||||
|
|
||||||
## 7.9
|
## 7.9
|
||||||
|
|
||||||
- Separate script for copying the default theme static assets to the public directory.
|
- Separate script for copying the default theme static assets to the public directory.
|
||||||
@ -129,9 +108,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
You are now expected to have a `"prepare": "copy-keycloak-resources-to-public",` in your package.json scripts.
|
You are now expected to have a `"prepare": "copy-keycloak-resources-to-public",` in your package.json scripts.
|
||||||
This script will create `public/keycloak-assets` when you run `yarn install` (If you are using another package manager
|
This script will create `public/keycloak-assets` when you run `yarn install` (If you are using another package manager
|
||||||
like `pnpm` makes sure that `"prepare"` is actually ran.)
|
like `pnpm` makes sure that `"prepare"` is actually ran.)
|
||||||
[See the updated starter](https://github.com/keycloakify/keycloakify-starter/blob/94532fcf10bf8b19e0873be8575fd28a8958a806/package.json#L11). `public/keycloak-assets` shouldn't be tracked by GIT and is automatically ignored.
|
[See the updated starter](https://github.com/keycloakify/keycloakify-starter/blob/94532fcf10bf8b19e0873be8575fd28a8958a806/package.json#L11).
|
||||||
|
`public/keycloak-assets` shouldn't be tracked by GIT and is automatically ignored.
|
||||||
|
|
||||||
## 7.7
|
## 7.7
|
||||||
|
|
||||||
- Better storybook support, see [the starter project](https://github.com/keycloakify/keycloakify-starter).
|
- Better storybook support, see [the starter project](https://github.com/keycloakify/keycloakify-starter).
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "7.12.6-rc.0",
|
"version": "7.11.1",
|
||||||
"description": "Create Keycloak themes using React",
|
"description": "Create Keycloak themes using React",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { AccountThemePageId, ThemeType } from "keycloakify/bin/keycloakify/generateFtl";
|
import type { AccountThemePageId } from "keycloakify/bin/keycloakify/generateFtl";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
|
|
||||||
@ -7,8 +7,6 @@ export type KcContext = KcContext.Password | KcContext.Account;
|
|||||||
export declare namespace KcContext {
|
export declare namespace KcContext {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
keycloakifyVersion: string;
|
keycloakifyVersion: string;
|
||||||
themeType: "account";
|
|
||||||
themeName: string;
|
|
||||||
locale?: {
|
locale?: {
|
||||||
supported: {
|
supported: {
|
||||||
url: string;
|
url: string;
|
||||||
@ -52,34 +50,9 @@ export declare namespace KcContext {
|
|||||||
name: string; // Client id
|
name: string; // Client id
|
||||||
};
|
};
|
||||||
messagesPerField: {
|
messagesPerField: {
|
||||||
/**
|
printIfExists: <T>(fieldName: string, x: T) => T | undefined;
|
||||||
* Return text if message for given field exists. Useful eg. to add css styles for fields with message.
|
|
||||||
*
|
|
||||||
* @param fieldName to check for
|
|
||||||
* @param text to return
|
|
||||||
* @return text if message exists for given field, else undefined
|
|
||||||
*/
|
|
||||||
printIfExists: <T extends string>(fieldName: string, text: T) => T | undefined;
|
|
||||||
/**
|
|
||||||
* Check if exists error message for given fields
|
|
||||||
*
|
|
||||||
* @param fields
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
existsError: (fieldName: string) => boolean;
|
existsError: (fieldName: string) => boolean;
|
||||||
/**
|
|
||||||
* Get message for given field.
|
|
||||||
*
|
|
||||||
* @param fieldName
|
|
||||||
* @return message text or empty string
|
|
||||||
*/
|
|
||||||
get: (fieldName: string) => string;
|
get: (fieldName: string) => string;
|
||||||
/**
|
|
||||||
* Check if message for given field exists
|
|
||||||
*
|
|
||||||
* @param field
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
exists: (fieldName: string) => boolean;
|
exists: (fieldName: string) => boolean;
|
||||||
};
|
};
|
||||||
account: {
|
account: {
|
||||||
@ -111,15 +84,4 @@ export declare namespace KcContext {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
assert<Equals<KcContext["pageId"], AccountThemePageId>>();
|
||||||
type Got = KcContext["pageId"];
|
|
||||||
type Expected = AccountThemePageId;
|
|
||||||
|
|
||||||
type OnlyInGot = Exclude<Got, Expected>;
|
|
||||||
type OnlyInExpected = Exclude<Expected, Got>;
|
|
||||||
|
|
||||||
assert<Equals<OnlyInGot, never>>();
|
|
||||||
assert<Equals<OnlyInExpected, never>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert<KcContext["themeType"] extends ThemeType ? true : false>();
|
|
||||||
|
@ -7,6 +7,8 @@ import { pathBasename } from "keycloakify/tools/pathBasename";
|
|||||||
import { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
import { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
||||||
import { symToStr } from "tsafe/symToStr";
|
import { symToStr } from "tsafe/symToStr";
|
||||||
import { kcContextMocks, kcContextCommonMock } from "keycloakify/account/kcContext/kcContextMocks";
|
import { kcContextMocks, kcContextCommonMock } from "keycloakify/account/kcContext/kcContextMocks";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { accountThemePageIds } from "keycloakify/bin/keycloakify/generateFtl/pageId";
|
||||||
|
|
||||||
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
@ -85,7 +87,7 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
return { "kcContext": undefined as any };
|
return { "kcContext": undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext.themeType !== "account") {
|
if (id<readonly string[]>(accountThemePageIds).indexOf(realKcContext.pageId) < 0 && !("account" in realKcContext)) {
|
||||||
return { "kcContext": undefined as any };
|
return { "kcContext": undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@ const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
|||||||
|
|
||||||
export const kcContextCommonMock: KcContext.Common = {
|
export const kcContextCommonMock: KcContext.Common = {
|
||||||
"keycloakifyVersion": "0.0.0",
|
"keycloakifyVersion": "0.0.0",
|
||||||
"themeType": "account",
|
|
||||||
"themeName": "my-theme-name",
|
|
||||||
"url": {
|
"url": {
|
||||||
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
||||||
"resourcesCommonPath": pathJoin(PUBLIC_URL, resourcesCommonDirPathRelativeToPublicDir),
|
"resourcesCommonPath": pathJoin(PUBLIC_URL, resourcesCommonDirPathRelativeToPublicDir),
|
||||||
|
@ -16,7 +16,6 @@ export namespace BuildOptions {
|
|||||||
isSilent: boolean;
|
isSilent: boolean;
|
||||||
themeVersion: string;
|
themeVersion: string;
|
||||||
themeName: string;
|
themeName: string;
|
||||||
extraThemeNames: string[];
|
|
||||||
extraLoginPages: string[] | undefined;
|
extraLoginPages: string[] | undefined;
|
||||||
extraAccountPages: string[] | undefined;
|
extraAccountPages: string[] | undefined;
|
||||||
extraThemeProperties?: string[];
|
extraThemeProperties?: string[];
|
||||||
@ -109,17 +108,8 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
|
|||||||
const common: BuildOptions.Common = (() => {
|
const common: BuildOptions.Common = (() => {
|
||||||
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
|
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
|
||||||
|
|
||||||
const {
|
const { extraPages, extraLoginPages, extraAccountPages, extraThemeProperties, groupId, artifactId, bundler, keycloakVersionDefaultAssets } =
|
||||||
extraPages,
|
keycloakify ?? {};
|
||||||
extraLoginPages,
|
|
||||||
extraAccountPages,
|
|
||||||
extraThemeProperties,
|
|
||||||
groupId,
|
|
||||||
artifactId,
|
|
||||||
bundler,
|
|
||||||
keycloakVersionDefaultAssets,
|
|
||||||
extraThemeNames = []
|
|
||||||
} = keycloakify ?? {};
|
|
||||||
|
|
||||||
const themeName =
|
const themeName =
|
||||||
keycloakify.themeName ??
|
keycloakify.themeName ??
|
||||||
@ -130,7 +120,6 @@ export function readBuildOptions(params: { projectDirPath: string; processArgv:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
themeName,
|
themeName,
|
||||||
extraThemeNames,
|
|
||||||
"bundler": (() => {
|
"bundler": (() => {
|
||||||
const { KEYCLOAKIFY_BUNDLER } = process.env;
|
const { KEYCLOAKIFY_BUNDLER } = process.env;
|
||||||
|
|
||||||
|
@ -28,373 +28,85 @@
|
|||||||
<#recover>
|
<#recover>
|
||||||
</#attempt>
|
</#attempt>
|
||||||
|
|
||||||
"printIfExists": function (fieldName, text) {
|
"printIfExists": function (fieldName, x) {
|
||||||
|
<#if !messagesPerField?? >
|
||||||
<#if !messagesPerField?? || !(messagesPerField?is_hash)>
|
return undefined;
|
||||||
throw new Error("You're not supposed to use messagesPerField.printIfExists in this page");
|
<#else>
|
||||||
</#if>
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
<#list fieldNames as fieldName>
|
<#attempt>
|
||||||
if(fieldName === "${fieldName}" ){
|
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
||||||
|
return <#if messagesPerField.existsError('username', 'password')>x<#else>undefined</#if>;
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/359 Compat with Keycloak prior v12 -->
|
|
||||||
<#if !messagesPerField.existsError??>
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistMessageForUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('username')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if !doExists>
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
return <#if doExistMessageForUsernameOrPassword>text<#else>undefined</#if>;
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#local doExistMessageForField = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistMessageForField>text<#else>undefined</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = messagesPerField.existsError('username', 'password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if doExistErrorOnUsernameOrPassword>
|
|
||||||
return text;
|
|
||||||
<#else>
|
<#else>
|
||||||
|
return <#if messagesPerField.existsError('${fieldName}')>x<#else>undefined</#if>;
|
||||||
<#local doExistMessageForField = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistMessageForField>text<#else>undefined</#if>;
|
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
<#recover>
|
||||||
<#else>
|
</#attempt>
|
||||||
|
}
|
||||||
<#local doExistMessageForField = "">
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
<#attempt>
|
</#if>
|
||||||
<#local doExistMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistMessageForField>text<#else>undefined</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
|
|
||||||
throw new Error("There is no " + fieldName + " field. See: https://docs.keycloakify.dev/build-options#keycloakify.customuserattributes");
|
|
||||||
},
|
},
|
||||||
"existsError": function (fieldName) {
|
"existsError": function (fieldName) {
|
||||||
|
<#if !messagesPerField?? >
|
||||||
<#if !messagesPerField?? || !(messagesPerField?is_hash)>
|
return false;
|
||||||
throw new Error("You're not supposed to use messagesPerField.printIfExists in this page");
|
<#else>
|
||||||
</#if>
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
<#list fieldNames as fieldName>
|
<#attempt>
|
||||||
if(fieldName === "${fieldName}" ){
|
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
||||||
|
return <#if messagesPerField.existsError('username', 'password')>true<#else>false</#if>;
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/359 Compat with Keycloak prior v12 -->
|
<#else>
|
||||||
<#if !messagesPerField.existsError??>
|
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistMessageForUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('username')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if !doExistMessageForUsernameOrPassword>
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
</#if>
|
||||||
|
<#recover>
|
||||||
return <#if doExistMessageForUsernameOrPassword>true<#else>false</#if>;
|
</#attempt>
|
||||||
|
}
|
||||||
<#else>
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
<#local doExistMessageForField = "">
|
</#if>
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistMessageForField>true<#else>false</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = messagesPerField.existsError('username', 'password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistErrorOnUsernameOrPassword>true<#else>false</#if>;
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#local doExistErrorMessageForField = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorMessageForField = messagesPerField.existsError('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistErrorMessageForField>true<#else>false</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
|
|
||||||
throw new Error("There is no " + fieldName + " field. See: https://docs.keycloakify.dev/build-options#keycloakify.customuserattributes");
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"get": function (fieldName) {
|
"get": function (fieldName) {
|
||||||
|
<#if !messagesPerField?? >
|
||||||
|
return '';
|
||||||
<#if !messagesPerField?? || !(messagesPerField?is_hash)>
|
<#else>
|
||||||
throw new Error("You're not supposed to use messagesPerField.get in this page");
|
<#list fieldNames as fieldName>
|
||||||
</#if>
|
if(fieldName === "${fieldName}" ){
|
||||||
|
<#attempt>
|
||||||
<#list fieldNames as fieldName>
|
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
||||||
if(fieldName === "${fieldName}" ){
|
<#if messagesPerField.existsError('username', 'password')>
|
||||||
|
return 'Invalid username or password.';
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/359 Compat with Keycloak prior v12 -->
|
</#if>
|
||||||
<#if !messagesPerField.existsError??>
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistMessageForUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('username')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if !doExistMessageForUsernameOrPassword>
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#if !doExistMessageForUsernameOrPassword>
|
|
||||||
return "";
|
|
||||||
<#else>
|
<#else>
|
||||||
<#attempt>
|
<#if messagesPerField.existsError('${fieldName}')>
|
||||||
return "${kcSanitize(msg('invalidUserMessage'))?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
return "Invalid username or password.";
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
return "invalid field";
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
// CONITNUE HERE!!!!!
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = messagesPerField.existsError('username', 'password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if doExistErrorOnUsernameOrPassword>
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${kcSanitize(msg('invalidUserMessage'))?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
return "Invalid username or password.";
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
||||||
<#recover>
|
</#if>
|
||||||
return "";
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
<#recover>
|
||||||
<#else>
|
</#attempt>
|
||||||
|
}
|
||||||
<#attempt>
|
</#list>
|
||||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
throw new Error("There is no " + fieldName + " field");
|
||||||
<#recover>
|
</#if>
|
||||||
return "invalid field";
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
|
|
||||||
throw new Error("There is no " + fieldName + " field. See: https://docs.keycloakify.dev/build-options#keycloakify.customuserattributes");
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"exists": function (fieldName) {
|
"exists": function (fieldName) {
|
||||||
|
<#if !messagesPerField?? >
|
||||||
<#if !messagesPerField?? || !(messagesPerField?is_hash)>
|
return false;
|
||||||
throw new Error("You're not supposed to use messagesPerField.exists in this page");
|
<#else>
|
||||||
</#if>
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
<#list fieldNames as fieldName>
|
<#attempt>
|
||||||
if(fieldName === "${fieldName}" ){
|
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
||||||
|
return <#if messagesPerField.exists('username') || messagesPerField.exists('password')>true<#else>false</#if>;
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/359 Compat with Keycloak prior v12 -->
|
<#else>
|
||||||
<#if !messagesPerField.existsError??>
|
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistMessageForUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('username')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if !doExistMessageForUsernameOrPassword>
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = messagesPerField.exists('password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
</#if>
|
||||||
|
<#recover>
|
||||||
return <#if doExistMessageForUsernameOrPassword>true<#else>false</#if>;
|
</#attempt>
|
||||||
|
}
|
||||||
<#else>
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
<#local doExistMessageForField = "">
|
</#if>
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistMessageForField>true<#else>false</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#-- https://github.com/keycloakify/keycloakify/pull/218 -->
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = messagesPerField.existsError('username', 'password')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorOnUsernameOrPassword = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistErrorOnUsernameOrPassword>true<#else>false</#if>;
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
|
|
||||||
<#local doExistErrorMessageForField = "">
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#local doExistErrorMessageForField = messagesPerField.exists('${fieldName}')>
|
|
||||||
<#recover>
|
|
||||||
<#local doExistErrorMessageForField = true>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return <#if doExistErrorMessageForField>true<#else>false</#if>;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
|
|
||||||
throw new Error("There is no " + fieldName + " field. See: https://docs.keycloakify.dev/build-options#keycloakify.customuserattributes");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -409,8 +121,6 @@
|
|||||||
|
|
||||||
out["keycloakifyVersion"] = "KEYCLOAKIFY_VERSION_xEdKd3xEdr";
|
out["keycloakifyVersion"] = "KEYCLOAKIFY_VERSION_xEdKd3xEdr";
|
||||||
out["themeVersion"] = "KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx";
|
out["themeVersion"] = "KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx";
|
||||||
out["themeType"] = "KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr";
|
|
||||||
out["themeName"] = "KEYCLOAKIFY_THEME_NAME_cXxKd3xEer";
|
|
||||||
out["pageId"] = "${pageId}";
|
out["pageId"] = "${pageId}";
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@ -459,10 +169,9 @@
|
|||||||
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
|
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
|
||||||
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
|
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
|
||||||
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
|
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
|
||||||
<#-- https://github.com/keycloakify/keycloakify/issues/357 -->
|
|
||||||
key == "loginAction" &&
|
key == "loginAction" &&
|
||||||
are_same_path(path, ["url"]) &&
|
are_same_path(path, ["url"]) &&
|
||||||
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl"]?seq_contains(pageId) &&
|
["saml-post-form.ftl", "error.ftl", "info.ftl"]?seq_contains(pageId) &&
|
||||||
!(auth?has_content && auth.showTryAnotherWayLink())
|
!(auth?has_content && auth.showTryAnotherWayLink())
|
||||||
) || (
|
) || (
|
||||||
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
|
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
|
||||||
@ -627,17 +336,6 @@
|
|||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#local isDate = "">
|
|
||||||
<#attempt>
|
|
||||||
<#local isDate = object?is_date_like>
|
|
||||||
<#recover>
|
|
||||||
<#return "ABORT: Can't test if it's a date">
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
<#if isDate>
|
|
||||||
<#return '"' + object?datetime?iso_utc + '"'>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
<#return '"' + object?js_string + '"'>;
|
<#return '"' + object?js_string + '"'>;
|
||||||
<#recover>
|
<#recover>
|
||||||
|
@ -17,7 +17,6 @@ export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.Ex
|
|||||||
|
|
||||||
export namespace BuildOptionsLike {
|
export namespace BuildOptionsLike {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
themeName: string;
|
|
||||||
customUserAttributes: string[];
|
customUserAttributes: string[];
|
||||||
themeVersion: string;
|
themeVersion: string;
|
||||||
};
|
};
|
||||||
@ -56,9 +55,8 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
cssGlobalsToDefine: Record<string, string>;
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
buildOptions: BuildOptionsLike;
|
buildOptions: BuildOptionsLike;
|
||||||
keycloakifyVersion: string;
|
keycloakifyVersion: string;
|
||||||
themeType: ThemeType;
|
|
||||||
}) {
|
}) {
|
||||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType } = params;
|
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion } = params;
|
||||||
|
|
||||||
const $ = cheerio.load(indexHtmlCode);
|
const $ = cheerio.load(indexHtmlCode);
|
||||||
|
|
||||||
@ -134,9 +132,7 @@ export function generateFtlFilesCodeFactory(params: {
|
|||||||
buildOptions.customUserAttributes.length === 0 ? "" : ", " + buildOptions.customUserAttributes.map(name => `"${name}"`).join(", ")
|
buildOptions.customUserAttributes.length === 0 ? "" : ", " + buildOptions.customUserAttributes.map(name => `"${name}"`).join(", ")
|
||||||
)
|
)
|
||||||
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
||||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
|
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion),
|
||||||
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
|
|
||||||
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", buildOptions.themeName),
|
|
||||||
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
|
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
|
||||||
"<#if scripts??>",
|
"<#if scripts??>",
|
||||||
" <#list scripts as script>",
|
" <#list scripts as script>",
|
||||||
|
@ -7,7 +7,6 @@ import type { BuildOptions } from "./BuildOptions";
|
|||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
themeName: string;
|
themeName: string;
|
||||||
extraThemeNames: string[];
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
artifactId?: string;
|
artifactId?: string;
|
||||||
themeVersion: string;
|
themeVersion: string;
|
||||||
@ -27,7 +26,7 @@ export function generateJavaStackFiles(params: {
|
|||||||
jarFilePath: string;
|
jarFilePath: string;
|
||||||
} {
|
} {
|
||||||
const {
|
const {
|
||||||
buildOptions: { groupId, themeName, extraThemeNames, themeVersion, artifactId },
|
buildOptions: { groupId, themeName, themeVersion, artifactId },
|
||||||
keycloakThemeBuildingDirPath,
|
keycloakThemeBuildingDirPath,
|
||||||
doBundlesEmailTemplate
|
doBundlesEmailTemplate
|
||||||
} = params;
|
} = params;
|
||||||
@ -68,10 +67,12 @@ export function generateJavaStackFiles(params: {
|
|||||||
Buffer.from(
|
Buffer.from(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
"themes": [themeName, ...extraThemeNames].map(themeName => ({
|
"themes": [
|
||||||
"name": themeName,
|
{
|
||||||
"types": [...themeTypes, ...(doBundlesEmailTemplate ? ["email"] : [])]
|
"name": themeName,
|
||||||
}))
|
"types": [...themeTypes, ...(doBundlesEmailTemplate ? ["email"] : [])]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
2
|
2
|
||||||
|
@ -6,7 +6,6 @@ import type { BuildOptions } from "./BuildOptions";
|
|||||||
|
|
||||||
export type BuildOptionsLike = {
|
export type BuildOptionsLike = {
|
||||||
themeName: string;
|
themeName: string;
|
||||||
extraThemeNames: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -28,9 +27,11 @@ export function generateStartKeycloakTestingContainer(params: {
|
|||||||
const {
|
const {
|
||||||
keycloakThemeBuildingDirPath,
|
keycloakThemeBuildingDirPath,
|
||||||
keycloakVersion,
|
keycloakVersion,
|
||||||
buildOptions: { themeName, extraThemeNames }
|
buildOptions: { themeName }
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
|
const keycloakThemePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName).replace(/\\/g, "/");
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
|
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
|
||||||
|
|
||||||
@ -48,13 +49,7 @@ export function generateStartKeycloakTestingContainer(params: {
|
|||||||
" -e KEYCLOAK_ADMIN=admin \\",
|
" -e KEYCLOAK_ADMIN=admin \\",
|
||||||
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
|
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
|
||||||
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
|
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
|
||||||
...[themeName, ...extraThemeNames].map(
|
` -v "${keycloakThemePath}":"/opt/keycloak/themes/${themeName}":rw \\`,
|
||||||
themeName =>
|
|
||||||
` -v "${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName).replace(
|
|
||||||
/\\/g,
|
|
||||||
"/"
|
|
||||||
)}":"/opt/keycloak/themes/${themeName}":rw \\`
|
|
||||||
),
|
|
||||||
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
||||||
` start-dev`,
|
` start-dev`,
|
||||||
""
|
""
|
||||||
|
@ -141,8 +141,7 @@ export async function generateTheme(params: {
|
|||||||
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
||||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||||
buildOptions,
|
buildOptions,
|
||||||
keycloakifyVersion,
|
keycloakifyVersion
|
||||||
themeType
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return generateFtlFilesCode;
|
return generateFtlFilesCode;
|
||||||
|
@ -23,38 +23,27 @@ export async function main() {
|
|||||||
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
||||||
logger.log("🔏 Building the keycloak theme...⌚");
|
logger.log("🔏 Building the keycloak theme...⌚");
|
||||||
|
|
||||||
let doBundlesEmailTemplate: boolean | undefined;
|
const { doBundlesEmailTemplate } = await generateTheme({
|
||||||
|
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||||
|
"emailThemeSrcDirPath": (() => {
|
||||||
|
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath({ projectDirPath });
|
||||||
|
|
||||||
for (const themeName of [buildOptions.themeName, ...buildOptions.extraThemeNames]) {
|
if (emailThemeSrcDirPath === undefined || !fs.existsSync(emailThemeSrcDirPath)) {
|
||||||
const { doBundlesEmailTemplate: doBundlesEmailTemplate_ } = await generateTheme({
|
return;
|
||||||
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
}
|
||||||
"emailThemeSrcDirPath": (() => {
|
|
||||||
const { emailThemeSrcDirPath } = getEmailThemeSrcDirPath({ projectDirPath });
|
|
||||||
|
|
||||||
if (emailThemeSrcDirPath === undefined || !fs.existsSync(emailThemeSrcDirPath)) {
|
return emailThemeSrcDirPath;
|
||||||
return;
|
})(),
|
||||||
}
|
"reactAppBuildDirPath": buildOptions.reactAppBuildDirPath,
|
||||||
|
buildOptions,
|
||||||
|
"keycloakifyVersion": (() => {
|
||||||
|
const version = JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["version"];
|
||||||
|
|
||||||
return emailThemeSrcDirPath;
|
assert(typeof version === "string");
|
||||||
})(),
|
|
||||||
"reactAppBuildDirPath": buildOptions.reactAppBuildDirPath,
|
|
||||||
"buildOptions": {
|
|
||||||
...buildOptions,
|
|
||||||
"themeName": themeName
|
|
||||||
},
|
|
||||||
"keycloakifyVersion": (() => {
|
|
||||||
const version = JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["version"];
|
|
||||||
|
|
||||||
assert(typeof version === "string");
|
return version;
|
||||||
|
})()
|
||||||
return version;
|
});
|
||||||
})()
|
|
||||||
});
|
|
||||||
|
|
||||||
doBundlesEmailTemplate ??= doBundlesEmailTemplate_;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(doBundlesEmailTemplate !== undefined);
|
|
||||||
|
|
||||||
const { jarFilePath } = generateJavaStackFiles({
|
const { jarFilePath } = generateJavaStackFiles({
|
||||||
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
keycloakThemeBuildingDirPath: buildOptions.keycloakifyBuildDirPath,
|
||||||
@ -69,7 +58,7 @@ export async function main() {
|
|||||||
case "keycloakify":
|
case "keycloakify":
|
||||||
logger.log("🫶 Let keycloakify do its thang");
|
logger.log("🫶 Let keycloakify do its thang");
|
||||||
await jar({
|
await jar({
|
||||||
"rootPath": buildOptions.keycloakifyBuildDirPath,
|
"rootPath": pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources"),
|
||||||
"version": buildOptions.themeVersion,
|
"version": buildOptions.themeVersion,
|
||||||
"groupId": buildOptions.groupId,
|
"groupId": buildOptions.groupId,
|
||||||
"artifactId": buildOptions.artifactId,
|
"artifactId": buildOptions.artifactId,
|
||||||
|
@ -25,7 +25,6 @@ export type ParsedPackageJson = {
|
|||||||
keycloakifyBuildDirPath?: string;
|
keycloakifyBuildDirPath?: string;
|
||||||
customUserAttributes?: string[];
|
customUserAttributes?: string[];
|
||||||
themeName?: string;
|
themeName?: string;
|
||||||
extraThemeNames?: string[];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,8 +46,7 @@ export const zParsedPackageJson = z.object({
|
|||||||
"reactAppBuildDirPath": z.string().optional(),
|
"reactAppBuildDirPath": z.string().optional(),
|
||||||
"keycloakifyBuildDirPath": z.string().optional(),
|
"keycloakifyBuildDirPath": z.string().optional(),
|
||||||
"customUserAttributes": z.array(z.string()).optional(),
|
"customUserAttributes": z.array(z.string()).optional(),
|
||||||
"themeName": z.string().optional(),
|
"themeName": z.string().optional()
|
||||||
"extraThemeNames": z.array(z.string()).optional()
|
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { dirname, relative, sep, join } from "path";
|
import { dirname, relative, sep } from "path";
|
||||||
import { createWriteStream } from "fs";
|
import { createWriteStream } from "fs";
|
||||||
|
|
||||||
import walk from "./walk";
|
import walk from "./walk";
|
||||||
@ -48,12 +48,8 @@ export async function jarStream({ groupId, artifactId, version, asyncPathGenerat
|
|||||||
for await (const entry of asyncPathGeneratorFn()) {
|
for await (const entry of asyncPathGeneratorFn()) {
|
||||||
if ("buffer" in entry) {
|
if ("buffer" in entry) {
|
||||||
zipFile.addBuffer(entry.buffer, entry.zipPath);
|
zipFile.addBuffer(entry.buffer, entry.zipPath);
|
||||||
} else if ("fsPath" in entry) {
|
} else if ("fsPath" in entry && !entry.fsPath.endsWith(sep)) {
|
||||||
if (entry.fsPath.endsWith(sep)) {
|
zipFile.addFile(entry.fsPath, entry.zipPath);
|
||||||
zipFile.addEmptyDirectory(entry.zipPath);
|
|
||||||
} else {
|
|
||||||
zipFile.addFile(entry.fsPath, entry.zipPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,23 +65,15 @@ export async function jarStream({ groupId, artifactId, version, asyncPathGenerat
|
|||||||
* Create a jar archive, using the resources found at `rootPath` (a directory) and write the
|
* Create a jar archive, using the resources found at `rootPath` (a directory) and write the
|
||||||
* archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
|
* archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
|
||||||
* the contents of the pom.properties file which is going to be added to the archive.
|
* the contents of the pom.properties file which is going to be added to the archive.
|
||||||
* The root directory is expectedto have a conventional maven/gradle folder structure with a
|
|
||||||
* single `pom.xml` file at the root and a `src/main/resources` directory containing all
|
|
||||||
* application resources.
|
|
||||||
*/
|
*/
|
||||||
export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
|
export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
|
||||||
await mkdir(dirname(targetPath), { recursive: true });
|
await mkdir(dirname(targetPath), { recursive: true });
|
||||||
|
|
||||||
const asyncPathGeneratorFn = async function* (): ZipEntryGenerator {
|
const asyncPathGeneratorFn = async function* (): ZipEntryGenerator {
|
||||||
const resourcesPath = join(rootPath, "src", "main", "resources");
|
for await (const fsPath of walk(rootPath)) {
|
||||||
for await (const fsPath of walk(resourcesPath)) {
|
const zipPath = relative(rootPath, fsPath).split(sep).join("/");
|
||||||
const zipPath = relative(resourcesPath, fsPath).split(sep).join("/");
|
|
||||||
yield { fsPath, zipPath };
|
yield { fsPath, zipPath };
|
||||||
}
|
}
|
||||||
yield {
|
|
||||||
fsPath: join(rootPath, "pom.xml"),
|
|
||||||
zipPath: `META-INF/maven/${groupId}/${artifactId}/pom.xml`
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const zipFile = await jarStream({ groupId, artifactId, version, asyncPathGeneratorFn });
|
const zipFile = await jarStream({ groupId, artifactId, version, asyncPathGeneratorFn });
|
||||||
|
@ -9,7 +9,7 @@ function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) {
|
|||||||
if (strings[i]) {
|
if (strings[i]) {
|
||||||
chunks.push(strings[i]);
|
chunks.push(strings[i]);
|
||||||
// remember last indent of the string portion
|
// remember last indent of the string portion
|
||||||
lastStringLineLength = strings[i].split("\n").slice(-1)[0]?.length ?? 0;
|
lastStringLineLength = strings[i].split("\n").at(-1)?.length ?? 0;
|
||||||
}
|
}
|
||||||
if (args[i]) {
|
if (args[i]) {
|
||||||
// if the interpolation value has newlines, indent the interpolation values
|
// if the interpolation value has newlines, indent the interpolation values
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { LoginThemePageId, ThemeType } from "keycloakify/bin/keycloakify/generateFtl";
|
import type { LoginThemePageId } from "keycloakify/bin/keycloakify/generateFtl";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
import type { MessageKey } from "../i18n/i18n";
|
import type { MessageKey } from "../i18n/i18n";
|
||||||
@ -38,8 +38,6 @@ export type KcContext =
|
|||||||
export declare namespace KcContext {
|
export declare namespace KcContext {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
keycloakifyVersion: string;
|
keycloakifyVersion: string;
|
||||||
themeType: "login";
|
|
||||||
themeName: string;
|
|
||||||
url: {
|
url: {
|
||||||
loginAction: string;
|
loginAction: string;
|
||||||
resourcesPath: string;
|
resourcesPath: string;
|
||||||
@ -81,34 +79,9 @@ export declare namespace KcContext {
|
|||||||
};
|
};
|
||||||
isAppInitiatedAction: boolean;
|
isAppInitiatedAction: boolean;
|
||||||
messagesPerField: {
|
messagesPerField: {
|
||||||
/**
|
printIfExists: <T>(fieldName: string, x: T) => T | undefined;
|
||||||
* Return text if message for given field exists. Useful eg. to add css styles for fields with message.
|
|
||||||
*
|
|
||||||
* @param fieldName to check for
|
|
||||||
* @param text to return
|
|
||||||
* @return text if message exists for given field, else undefined
|
|
||||||
*/
|
|
||||||
printIfExists: <T extends string>(fieldName: string, text: T) => T | undefined;
|
|
||||||
/**
|
|
||||||
* Check if exists error message for given fields
|
|
||||||
*
|
|
||||||
* @param fields
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
existsError: (fieldName: string) => boolean;
|
existsError: (fieldName: string) => boolean;
|
||||||
/**
|
|
||||||
* Get message for given field.
|
|
||||||
*
|
|
||||||
* @param fieldName
|
|
||||||
* @return message text or empty string
|
|
||||||
*/
|
|
||||||
get: (fieldName: string) => string;
|
get: (fieldName: string) => string;
|
||||||
/**
|
|
||||||
* Check if message for given field exists
|
|
||||||
*
|
|
||||||
* @param field
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
exists: (fieldName: string) => boolean;
|
exists: (fieldName: string) => boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -119,7 +92,7 @@ export declare namespace KcContext {
|
|||||||
url: string;
|
url: string;
|
||||||
SAMLRequest?: string;
|
SAMLRequest?: string;
|
||||||
SAMLResponse?: string;
|
SAMLResponse?: string;
|
||||||
relayState?: string;
|
RelayState?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -591,5 +564,3 @@ export declare namespace Validators {
|
|||||||
assert<Equals<OnlyInGot, never>>();
|
assert<Equals<OnlyInGot, never>>();
|
||||||
assert<Equals<OnlyInExpected, never>>();
|
assert<Equals<OnlyInExpected, never>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert<KcContext["themeType"] extends ThemeType ? true : false>();
|
|
||||||
|
@ -11,6 +11,7 @@ import { pathJoin } from "keycloakify/bin/tools/pathJoin";
|
|||||||
import { pathBasename } from "keycloakify/tools/pathBasename";
|
import { pathBasename } from "keycloakify/tools/pathBasename";
|
||||||
import { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
import { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
||||||
import { symToStr } from "tsafe/symToStr";
|
import { symToStr } from "tsafe/symToStr";
|
||||||
|
import { loginThemePageIds } from "keycloakify/bin/keycloakify/generateFtl/pageId";
|
||||||
|
|
||||||
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
||||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||||
@ -144,7 +145,7 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
|||||||
return { "kcContext": undefined as any };
|
return { "kcContext": undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realKcContext.themeType !== "login") {
|
if (id<readonly string[]>(loginThemePageIds).indexOf(realKcContext.pageId) < 0 && !("login" in realKcContext)) {
|
||||||
return { "kcContext": undefined as any };
|
return { "kcContext": undefined as any };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +104,6 @@ const attributesByName = Object.fromEntries(attributes.map(attribute => [attribu
|
|||||||
|
|
||||||
export const kcContextCommonMock: KcContext.Common = {
|
export const kcContextCommonMock: KcContext.Common = {
|
||||||
"keycloakifyVersion": "0.0.0",
|
"keycloakifyVersion": "0.0.0",
|
||||||
"themeType": "login",
|
|
||||||
"themeName": "my-theme-name",
|
|
||||||
"url": {
|
"url": {
|
||||||
"loginAction": "#",
|
"loginAction": "#",
|
||||||
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
||||||
@ -528,7 +526,7 @@ export const kcContextMocks = [
|
|||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
pageId: "saml-post-form.ftl",
|
pageId: "saml-post-form.ftl",
|
||||||
"samlPost": {
|
"samlPost": {
|
||||||
"url": ""
|
"url": "https://saml-post-url"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
id<KcContext.LoginPageExpired>({
|
id<KcContext.LoginPageExpired>({
|
||||||
|
@ -11,7 +11,7 @@ export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { p
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("emailVerifyTitle")}>
|
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("emailVerifyTitle")}>
|
||||||
<p className="instruction">{msg("emailVerifyInstruction1", user?.email ?? "")}</p>
|
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
|
||||||
<p className="instruction">
|
<p className="instruction">
|
||||||
{msg("emailVerifyInstruction2")}
|
{msg("emailVerifyInstruction2")}
|
||||||
<br />
|
<br />
|
||||||
|
@ -20,7 +20,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
|||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const [isFormSubmittable, setIsFormSubmittable] = useState(false);
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
@ -32,7 +32,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
|||||||
<form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
|
<form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
|
||||||
<UserProfileFormFields
|
<UserProfileFormFields
|
||||||
kcContext={kcContext}
|
kcContext={kcContext}
|
||||||
onIsFormSubmittableValueChange={setIsFormSubmittable}
|
onIsFormSubmittableValueChange={setIsFomSubmittable}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
getClassName={getClassName}
|
getClassName={getClassName}
|
||||||
/>
|
/>
|
||||||
@ -62,7 +62,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
|||||||
)}
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doRegister")}
|
value={msgStr("doRegister")}
|
||||||
disabled={!isFormSubmittable}
|
disabled={!isFomSubmittable}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||||
import type { KcContext } from "../kcContext";
|
import type { KcContext } from "../kcContext";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
@ -10,28 +9,14 @@ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageI
|
|||||||
|
|
||||||
const { samlPost } = kcContext;
|
const { samlPost } = kcContext;
|
||||||
|
|
||||||
const [htmlFormElement, setHtmlFormElement] = useState<HTMLFormElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (htmlFormElement === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storybook
|
|
||||||
if (samlPost.url === "") {
|
|
||||||
alert("In a real Keycloak the user would be redirected immediately");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlFormElement.submit();
|
|
||||||
}, [htmlFormElement]);
|
|
||||||
return (
|
return (
|
||||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("saml.post-form.title")}>
|
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("saml.post-form.title")}>
|
||||||
|
<script dangerouslySetInnerHTML={{ "__html": `window.onload = function() {document.forms[0].submit()};` }} />
|
||||||
<p>{msg("saml.post-form.message")}</p>
|
<p>{msg("saml.post-form.message")}</p>
|
||||||
<form name="saml-post-binding" method="post" action={samlPost.url} ref={setHtmlFormElement}>
|
<form name="saml-post-binding" method="post" action={samlPost.url}>
|
||||||
{samlPost.SAMLRequest && <input type="hidden" name="SAMLRequest" value={samlPost.SAMLRequest} />}
|
{samlPost.SAMLRequest && <input type="hidden" name="SAMLRequest" value={samlPost.SAMLRequest} />}
|
||||||
{samlPost.SAMLResponse && <input type="hidden" name="SAMLResponse" value={samlPost.SAMLResponse} />}
|
{samlPost.SAMLResponse && <input type="hidden" name="SAMLResponse" value={samlPost.SAMLResponse} />}
|
||||||
{samlPost.relayState && <input type="hidden" name="RelayState" value={samlPost.relayState} />}
|
{samlPost.RelayState && <input type="hidden" name="RelayState" value={samlPost.RelayState} />}
|
||||||
<noscript>
|
<noscript>
|
||||||
<p>{msg("saml.post-form.js-disabled")}</p>
|
<p>{msg("saml.post-form.js-disabled")}</p>
|
||||||
<input type="submit" value={msgStr("doContinue")} />
|
<input type="submit" value={msgStr("doContinue")} />
|
||||||
|
@ -3,7 +3,7 @@ import { fromBuffer, Entry, ZipFile } from "yauzl";
|
|||||||
import { it, describe, assert, afterAll } from "vitest";
|
import { it, describe, assert, afterAll } from "vitest";
|
||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import { tmpdir } from "os";
|
import { tmpdir } from "os";
|
||||||
import { mkdtemp, cp, mkdir, rm, writeFile } from "fs/promises";
|
import { mkdtemp, cp, mkdir, rm } from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { createReadStream } from "fs";
|
import { createReadStream } from "fs";
|
||||||
import walk from "keycloakify/bin/tools/walk";
|
import walk from "keycloakify/bin/tools/walk";
|
||||||
@ -98,17 +98,12 @@ describe("jar", () => {
|
|||||||
|
|
||||||
it("creates a jar from _real_ files without error", async () => {
|
it("creates a jar from _real_ files without error", async () => {
|
||||||
const tmp = await mkdtemp(path.join(tmpdir(), "kc-jar-test-"));
|
const tmp = await mkdtemp(path.join(tmpdir(), "kc-jar-test-"));
|
||||||
|
|
||||||
tmpDirs.push(tmp);
|
tmpDirs.push(tmp);
|
||||||
|
const rootPath = path.join(tmp, "src");
|
||||||
const rootPath = path.join(tmp, "root");
|
|
||||||
const resourcesPath = path.join(tmp, "root", "src", "main", "resources");
|
|
||||||
const targetPath = path.join(tmp, "jar.jar");
|
const targetPath = path.join(tmp, "jar.jar");
|
||||||
|
await mkdir(rootPath);
|
||||||
|
|
||||||
await mkdir(resourcesPath, { recursive: true });
|
await cp(path.dirname(__dirname), rootPath, { recursive: true });
|
||||||
await writeFile(path.join(rootPath, "pom.xml"), "foo", "utf-8");
|
|
||||||
|
|
||||||
await cp(path.dirname(__dirname), resourcesPath, { recursive: true });
|
|
||||||
|
|
||||||
await jar({ ...coords, rootPath, targetPath });
|
await jar({ ...coords, rootPath, targetPath });
|
||||||
|
|
||||||
@ -119,12 +114,11 @@ describe("jar", () => {
|
|||||||
|
|
||||||
assert.isOk(entries.has("META-INF/MANIFEST.MF"));
|
assert.isOk(entries.has("META-INF/MANIFEST.MF"));
|
||||||
assert.isOk(entries.has("META-INF/maven/someGroupId/someArtifactId/pom.properties"));
|
assert.isOk(entries.has("META-INF/maven/someGroupId/someArtifactId/pom.properties"));
|
||||||
assert.isOk(entries.has("META-INF/maven/someGroupId/someArtifactId/pom.xml"));
|
|
||||||
|
|
||||||
for await (const fsPath of walk(resourcesPath)) {
|
for await (const fsPath of walk(rootPath)) {
|
||||||
if (!fsPath.endsWith(path.sep)) {
|
if (!fsPath.endsWith(path.sep)) {
|
||||||
const rel = path.relative(resourcesPath, fsPath).replace(path.sep === "/" ? /\//g : /\\/g, "/");
|
const rel = path.relative(rootPath, fsPath).replace(path.sep === "/" ? /\//g : /\\/g, "/");
|
||||||
assert.isOk(zipPaths.includes(rel), `missing '${rel}' (${rel}, '${zipPaths.join("', '")}')`);
|
assert.isOk(zipPaths.includes(rel), `missing ${rel} (${rel}, ${zipPaths.join(", ")})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user