Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
9246a54c68 | |||
eaf9a5373a | |||
5b261c9916 | |||
73d155b3bb | |||
1f8b245499 |
@ -140,24 +140,6 @@
|
||||
"contributions": [
|
||||
"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,
|
||||
|
33
README.md
33
README.md
@ -39,15 +39,9 @@
|
||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
||||
</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 👼
|
||||
|
||||
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.
|
||||
|
||||
[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.
|
||||
- Custom theme building for your brand using Keycloakify.
|
||||
|
||||
<div align="center">
|
||||
|
||||

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

|
||||
|
||||
</div>
|
||||
|
||||
<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/>
|
||||
<i>5% of your annual subscription will be donated to us, and you'll get 5% off too.</i>
|
||||
</p>
|
||||
@ -104,8 +90,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
</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://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>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -124,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.
|
||||
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.)
|
||||
[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).
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "7.11.11",
|
||||
"version": "7.11.1",
|
||||
"description": "Create Keycloak themes using React",
|
||||
"repository": {
|
||||
"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 type { Equals } from "tsafe";
|
||||
|
||||
@ -7,7 +7,6 @@ export type KcContext = KcContext.Password | KcContext.Account;
|
||||
export declare namespace KcContext {
|
||||
export type Common = {
|
||||
keycloakifyVersion: string;
|
||||
themeType: "account";
|
||||
locale?: {
|
||||
supported: {
|
||||
url: string;
|
||||
@ -85,15 +84,4 @@ export declare namespace KcContext {
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
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>();
|
||||
assert<Equals<KcContext["pageId"], AccountThemePageId>>();
|
||||
|
@ -7,6 +7,8 @@ import { pathBasename } from "keycloakify/tools/pathBasename";
|
||||
import { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
||||
import { symToStr } from "tsafe/symToStr";
|
||||
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?: {
|
||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||
@ -85,7 +87,7 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
||||
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 };
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
||||
|
||||
export const kcContextCommonMock: KcContext.Common = {
|
||||
"keycloakifyVersion": "0.0.0",
|
||||
"themeType": "account",
|
||||
"url": {
|
||||
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
||||
"resourcesCommonPath": pathJoin(PUBLIC_URL, resourcesCommonDirPathRelativeToPublicDir),
|
||||
|
@ -121,7 +121,6 @@
|
||||
|
||||
out["keycloakifyVersion"] = "KEYCLOAKIFY_VERSION_xEdKd3xEdr";
|
||||
out["themeVersion"] = "KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx";
|
||||
out["themeType"] = "KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr";
|
||||
out["pageId"] = "${pageId}";
|
||||
|
||||
return out;
|
||||
@ -337,17 +336,6 @@
|
||||
|
||||
</#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>
|
||||
<#return '"' + object?js_string + '"'>;
|
||||
<#recover>
|
||||
|
@ -55,9 +55,8 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
buildOptions: BuildOptionsLike;
|
||||
keycloakifyVersion: string;
|
||||
themeType: ThemeType;
|
||||
}) {
|
||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion, themeType } = params;
|
||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion } = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
@ -133,8 +132,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
buildOptions.customUserAttributes.length === 0 ? "" : ", " + buildOptions.customUserAttributes.map(name => `"${name}"`).join(", ")
|
||||
)
|
||||
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType),
|
||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion),
|
||||
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
|
||||
"<#if scripts??>",
|
||||
" <#list scripts as script>",
|
||||
|
@ -141,8 +141,7 @@ export async function generateTheme(params: {
|
||||
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||
buildOptions,
|
||||
keycloakifyVersion,
|
||||
themeType
|
||||
keycloakifyVersion
|
||||
});
|
||||
|
||||
return generateFtlFilesCode;
|
||||
|
@ -58,7 +58,7 @@ export async function main() {
|
||||
case "keycloakify":
|
||||
logger.log("🫶 Let keycloakify do its thang");
|
||||
await jar({
|
||||
"rootPath": buildOptions.keycloakifyBuildDirPath,
|
||||
"rootPath": pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources"),
|
||||
"version": buildOptions.themeVersion,
|
||||
"groupId": buildOptions.groupId,
|
||||
"artifactId": buildOptions.artifactId,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { dirname, relative, sep, join } from "path";
|
||||
import { dirname, relative, sep } from "path";
|
||||
import { createWriteStream } from "fs";
|
||||
|
||||
import walk from "./walk";
|
||||
@ -48,12 +48,8 @@ export async function jarStream({ groupId, artifactId, version, asyncPathGenerat
|
||||
for await (const entry of asyncPathGeneratorFn()) {
|
||||
if ("buffer" in entry) {
|
||||
zipFile.addBuffer(entry.buffer, entry.zipPath);
|
||||
} else if ("fsPath" in entry) {
|
||||
if (entry.fsPath.endsWith(sep)) {
|
||||
zipFile.addEmptyDirectory(entry.zipPath);
|
||||
} else {
|
||||
zipFile.addFile(entry.fsPath, entry.zipPath);
|
||||
}
|
||||
} else if ("fsPath" in entry && !entry.fsPath.endsWith(sep)) {
|
||||
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
|
||||
* 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 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) {
|
||||
await mkdir(dirname(targetPath), { recursive: true });
|
||||
|
||||
const asyncPathGeneratorFn = async function* (): ZipEntryGenerator {
|
||||
const resourcesPath = join(rootPath, "src", "main", "resources");
|
||||
for await (const fsPath of walk(resourcesPath)) {
|
||||
const zipPath = relative(resourcesPath, fsPath).split(sep).join("/");
|
||||
for await (const fsPath of walk(rootPath)) {
|
||||
const zipPath = relative(rootPath, fsPath).split(sep).join("/");
|
||||
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 });
|
||||
|
@ -9,7 +9,7 @@ function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) {
|
||||
if (strings[i]) {
|
||||
chunks.push(strings[i]);
|
||||
// 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 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 type { Equals } from "tsafe";
|
||||
import type { MessageKey } from "../i18n/i18n";
|
||||
@ -38,7 +38,6 @@ export type KcContext =
|
||||
export declare namespace KcContext {
|
||||
export type Common = {
|
||||
keycloakifyVersion: string;
|
||||
themeType: "login";
|
||||
url: {
|
||||
loginAction: string;
|
||||
resourcesPath: string;
|
||||
@ -93,7 +92,7 @@ export declare namespace KcContext {
|
||||
url: string;
|
||||
SAMLRequest?: string;
|
||||
SAMLResponse?: string;
|
||||
relayState?: string;
|
||||
RelayState?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@ -565,5 +564,3 @@ export declare namespace Validators {
|
||||
assert<Equals<OnlyInGot, 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 { resourcesCommonDirPathRelativeToPublicDir } from "keycloakify/bin/mockTestingResourcesPath";
|
||||
import { symToStr } from "tsafe/symToStr";
|
||||
import { loginThemePageIds } from "keycloakify/bin/keycloakify/generateFtl/pageId";
|
||||
|
||||
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
|
||||
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
|
||||
@ -144,7 +145,7 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
|
||||
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 };
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,6 @@ const attributesByName = Object.fromEntries(attributes.map(attribute => [attribu
|
||||
|
||||
export const kcContextCommonMock: KcContext.Common = {
|
||||
"keycloakifyVersion": "0.0.0",
|
||||
"themeType": "login",
|
||||
"url": {
|
||||
"loginAction": "#",
|
||||
"resourcesPath": pathJoin(PUBLIC_URL, resourcesDirPathRelativeToPublicDir),
|
||||
@ -527,7 +526,7 @@ export const kcContextMocks = [
|
||||
...kcContextCommonMock,
|
||||
pageId: "saml-post-form.ftl",
|
||||
"samlPost": {
|
||||
"url": ""
|
||||
"url": "https://saml-post-url"
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginPageExpired>({
|
||||
|
@ -11,7 +11,7 @@ export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { p
|
||||
|
||||
return (
|
||||
<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">
|
||||
{msg("emailVerifyInstruction2")}
|
||||
<br />
|
||||
|
@ -20,7 +20,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const [isFormSubmittable, setIsFormSubmittable] = useState(false);
|
||||
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||
|
||||
return (
|
||||
<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">
|
||||
<UserProfileFormFields
|
||||
kcContext={kcContext}
|
||||
onIsFormSubmittableValueChange={setIsFormSubmittable}
|
||||
onIsFormSubmittableValueChange={setIsFomSubmittable}
|
||||
i18n={i18n}
|
||||
getClassName={getClassName}
|
||||
/>
|
||||
@ -62,7 +62,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
||||
)}
|
||||
type="submit"
|
||||
value={msgStr("doRegister")}
|
||||
disabled={!isFormSubmittable}
|
||||
disabled={!isFomSubmittable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -10,28 +9,14 @@ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageI
|
||||
|
||||
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 (
|
||||
<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>
|
||||
<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.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>
|
||||
<p>{msg("saml.post-form.js-disabled")}</p>
|
||||
<input type="submit" value={msgStr("doContinue")} />
|
||||
|
@ -3,7 +3,7 @@ import { fromBuffer, Entry, ZipFile } from "yauzl";
|
||||
import { it, describe, assert, afterAll } from "vitest";
|
||||
import { Readable } from "stream";
|
||||
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 { createReadStream } from "fs";
|
||||
import walk from "keycloakify/bin/tools/walk";
|
||||
@ -98,17 +98,12 @@ describe("jar", () => {
|
||||
|
||||
it("creates a jar from _real_ files without error", async () => {
|
||||
const tmp = await mkdtemp(path.join(tmpdir(), "kc-jar-test-"));
|
||||
|
||||
tmpDirs.push(tmp);
|
||||
|
||||
const rootPath = path.join(tmp, "root");
|
||||
const resourcesPath = path.join(tmp, "root", "src", "main", "resources");
|
||||
const rootPath = path.join(tmp, "src");
|
||||
const targetPath = path.join(tmp, "jar.jar");
|
||||
await mkdir(rootPath);
|
||||
|
||||
await mkdir(resourcesPath, { recursive: true });
|
||||
await writeFile(path.join(rootPath, "pom.xml"), "foo", "utf-8");
|
||||
|
||||
await cp(path.dirname(__dirname), resourcesPath, { recursive: true });
|
||||
await cp(path.dirname(__dirname), rootPath, { recursive: true });
|
||||
|
||||
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/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)) {
|
||||
const rel = path.relative(resourcesPath, fsPath).replace(path.sep === "/" ? /\//g : /\\/g, "/");
|
||||
assert.isOk(zipPaths.includes(rel), `missing '${rel}' (${rel}, '${zipPaths.join("', '")}')`);
|
||||
const rel = path.relative(rootPath, fsPath).replace(path.sep === "/" ? /\//g : /\\/g, "/");
|
||||
assert.isOk(zipPaths.includes(rel), `missing ${rel} (${rel}, ${zipPaths.join(", ")})`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user