keycloakify 11.7.4 → 11.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/bin/{33.index.js → 226.index.js} +2 -19
  2. package/bin/{911.index.js → 297.index.js} +18918 -11359
  3. package/bin/355.index.js +41 -703
  4. package/bin/363.index.js +115 -93
  5. package/bin/369.index.js +968 -0
  6. package/bin/{288.index.js → 502.index.js} +123 -227
  7. package/bin/656.index.js +111 -0
  8. package/bin/{880.index.js → 712.index.js} +227 -216
  9. package/bin/780.index.js +9 -7
  10. package/bin/932.index.js +965 -0
  11. package/bin/97.index.js +2099 -769
  12. package/bin/main.js +66 -49
  13. package/bin/shared/buildContext.d.ts +11 -3
  14. package/bin/start-keycloak/realmConfig/prepareRealmConfig.d.ts +5 -6
  15. package/bin/start-keycloak/realmConfig/realmConfig.d.ts +6 -2
  16. package/package.json +12 -17
  17. package/src/bin/initialize-account-theme/initialize-account-theme.ts +29 -27
  18. package/src/bin/initialize-email-theme.ts +103 -53
  19. package/src/bin/keycloakify/buildJars/buildJar.ts +45 -20
  20. package/src/bin/keycloakify/generateResources/generateResources.ts +263 -217
  21. package/src/bin/shared/{initializeSpa/addSyncExtensionsToPostinstallScript.ts → addSyncExtensionsToPostinstallScript.ts} +1 -1
  22. package/src/bin/shared/buildContext.ts +69 -24
  23. package/src/bin/shared/{initializeSpa/initializeSpa.ts → initializeSpa.ts} +3 -3
  24. package/src/bin/start-keycloak/realmConfig/prepareRealmConfig.ts +12 -23
  25. package/src/bin/start-keycloak/realmConfig/realmConfig.ts +14 -12
  26. package/src/bin/start-keycloak/start-keycloak.ts +55 -28
  27. package/src/bin/sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.ts +6 -0
  28. package/vite-plugin/index.js +48 -20
  29. package/bin/313.index.js +0 -377
  30. package/bin/678.index.js +0 -7565
  31. package/bin/9.index.js +0 -850
  32. package/bin/947.index.js +0 -1565
  33. package/bin/shared/initializeSpa/index.d.ts +0 -1
  34. package/bin/shared/metaInfKeycloakThemes.d.ts +0 -13
  35. package/src/bin/shared/initializeSpa/index.ts +0 -1
  36. package/src/bin/shared/metaInfKeycloakThemes.ts +0 -40
  37. /package/bin/shared/{initializeSpa/addSyncExtensionsToPostinstallScript.d.ts → addSyncExtensionsToPostinstallScript.d.ts} +0 -0
  38. /package/bin/shared/{initializeSpa/initializeSpa.d.ts → initializeSpa.d.ts} +0 -0
@@ -45,12 +45,16 @@ export type BuildContext = {
45
45
  environmentVariables: { name: string; default: string }[];
46
46
  themeSrcDirPath: string;
47
47
  implementedThemeTypes: {
48
- login: { isImplemented: boolean };
49
- email: { isImplemented: boolean };
48
+ login:
49
+ | { isImplemented: true }
50
+ | { isImplemented: false; isImplemented_native: boolean };
51
+ email: { isImplemented: false; isImplemented_native: boolean };
50
52
  account:
51
- | { isImplemented: false }
53
+ | { isImplemented: false; isImplemented_native: boolean }
52
54
  | { isImplemented: true; type: "Single-Page" | "Multi-Page" };
53
- admin: { isImplemented: boolean };
55
+ admin:
56
+ | { isImplemented: true }
57
+ | { isImplemented: false; isImplemented_native: boolean };
54
58
  };
55
59
  packageJsonFilePath: string;
56
60
  bundler: "vite" | "webpack";
@@ -434,27 +438,68 @@ export function getBuildContext(params: {
434
438
  assert<Equals<typeof bundler, never>>(false);
435
439
  })();
436
440
 
437
- const implementedThemeTypes: BuildContext["implementedThemeTypes"] = {
438
- login: {
439
- isImplemented: fs.existsSync(pathJoin(themeSrcDirPath, "login"))
440
- },
441
- email: {
442
- isImplemented: fs.existsSync(pathJoin(themeSrcDirPath, "email"))
443
- },
444
- account: (() => {
445
- if (buildOptions.accountThemeImplementation === "none") {
446
- return { isImplemented: false };
447
- }
441
+ const implementedThemeTypes: BuildContext["implementedThemeTypes"] = (() => {
442
+ const getIsNative = (dirPath: string) =>
443
+ fs.existsSync(pathJoin(dirPath, "theme.properties"));
448
444
 
449
- return {
450
- isImplemented: true,
451
- type: buildOptions.accountThemeImplementation
452
- };
453
- })(),
454
- admin: {
455
- isImplemented: fs.existsSync(pathJoin(themeSrcDirPath, "admin"))
456
- }
457
- };
445
+ return {
446
+ login: (() => {
447
+ const dirPath = pathJoin(themeSrcDirPath, "login");
448
+
449
+ if (!fs.existsSync(dirPath)) {
450
+ return { isImplemented: false, isImplemented_native: false };
451
+ }
452
+
453
+ if (getIsNative(dirPath)) {
454
+ return { isImplemented: false, isImplemented_native: true };
455
+ }
456
+
457
+ return { isImplemented: true };
458
+ })(),
459
+ email: (() => {
460
+ const dirPath = pathJoin(themeSrcDirPath, "email");
461
+
462
+ if (!fs.existsSync(dirPath) || !getIsNative(dirPath)) {
463
+ return { isImplemented: false, isImplemented_native: false };
464
+ }
465
+
466
+ return { isImplemented: false, isImplemented_native: true };
467
+ })(),
468
+ account: (() => {
469
+ const dirPath = pathJoin(themeSrcDirPath, "account");
470
+
471
+ if (!fs.existsSync(dirPath)) {
472
+ return { isImplemented: false, isImplemented_native: false };
473
+ }
474
+
475
+ if (getIsNative(dirPath)) {
476
+ return { isImplemented: false, isImplemented_native: true };
477
+ }
478
+
479
+ if (buildOptions.accountThemeImplementation === "none") {
480
+ return { isImplemented: false, isImplemented_native: false };
481
+ }
482
+
483
+ return {
484
+ isImplemented: true,
485
+ type: buildOptions.accountThemeImplementation
486
+ };
487
+ })(),
488
+ admin: (() => {
489
+ const dirPath = pathJoin(themeSrcDirPath, "admin");
490
+
491
+ if (!fs.existsSync(dirPath)) {
492
+ return { isImplemented: false, isImplemented_native: false };
493
+ }
494
+
495
+ if (getIsNative(dirPath)) {
496
+ return { isImplemented: false, isImplemented_native: true };
497
+ }
498
+
499
+ return { isImplemented: true };
500
+ })()
501
+ };
502
+ })();
458
503
 
459
504
  if (
460
505
  implementedThemeTypes.account.isImplemented &&
@@ -1,5 +1,5 @@
1
1
  import { dirname as pathDirname, join as pathJoin, relative as pathRelative } from "path";
2
- import type { BuildContext } from "../buildContext";
2
+ import type { BuildContext } from "./buildContext";
3
3
  import * as fs from "fs";
4
4
  import { assert, is, type Equals } from "tsafe/assert";
5
5
  import { id } from "tsafe/id";
@@ -7,8 +7,8 @@ import {
7
7
  addSyncExtensionsToPostinstallScript,
8
8
  type BuildContextLike as BuildContextLike_addSyncExtensionsToPostinstallScript
9
9
  } from "./addSyncExtensionsToPostinstallScript";
10
- import { getIsPrettierAvailable, runPrettier } from "../../tools/runPrettier";
11
- import { npmInstall } from "../../tools/npmInstall";
10
+ import { getIsPrettierAvailable, runPrettier } from "../tools/runPrettier";
11
+ import { npmInstall } from "../tools/npmInstall";
12
12
  import * as child_process from "child_process";
13
13
  import { z } from "zod";
14
14
  import chalk from "chalk";
@@ -1,28 +1,20 @@
1
1
  import { assert } from "tsafe/assert";
2
2
  import type { ParsedRealmJson } from "./ParsedRealmJson";
3
3
  import { getDefaultConfig } from "./defaultConfig";
4
- import type { BuildContext } from "../../shared/buildContext";
5
- import { objectKeys } from "tsafe/objectKeys";
6
- import { TEST_APP_URL } from "../../shared/constants";
4
+ import { TEST_APP_URL, type ThemeType, THEME_TYPES } from "../../shared/constants";
7
5
  import { sameFactory } from "evt/tools/inDepth/same";
8
6
 
9
- export type BuildContextLike = {
10
- themeNames: BuildContext["themeNames"];
11
- implementedThemeTypes: BuildContext["implementedThemeTypes"];
12
- };
13
-
14
- assert<BuildContext extends BuildContextLike ? true : false>;
15
-
16
7
  export function prepareRealmConfig(params: {
17
8
  parsedRealmJson: ParsedRealmJson;
18
9
  keycloakMajorVersionNumber: number;
19
- buildContext: BuildContextLike;
10
+ parsedKeycloakThemesJsonEntry: { name: string; types: (ThemeType | "email")[] };
20
11
  }): {
21
12
  realmName: string;
22
13
  clientName: string;
23
14
  username: string;
24
15
  } {
25
- const { parsedRealmJson, keycloakMajorVersionNumber, buildContext } = params;
16
+ const { parsedRealmJson, keycloakMajorVersionNumber, parsedKeycloakThemesJsonEntry } =
17
+ params;
26
18
 
27
19
  const { username } = addOrEditTestUser({
28
20
  parsedRealmJson,
@@ -38,8 +30,7 @@ export function prepareRealmConfig(params: {
38
30
 
39
31
  enableCustomThemes({
40
32
  parsedRealmJson,
41
- themeName: buildContext.themeNames[0],
42
- implementedThemeTypes: buildContext.implementedThemeTypes
33
+ parsedKeycloakThemesJsonEntry
43
34
  });
44
35
 
45
36
  enable_custom_events_listeners: {
@@ -63,17 +54,15 @@ export function prepareRealmConfig(params: {
63
54
 
64
55
  function enableCustomThemes(params: {
65
56
  parsedRealmJson: ParsedRealmJson;
66
- themeName: string;
67
- implementedThemeTypes: BuildContextLike["implementedThemeTypes"];
57
+ parsedKeycloakThemesJsonEntry: { name: string; types: (ThemeType | "email")[] };
68
58
  }) {
69
- const { parsedRealmJson, themeName, implementedThemeTypes } = params;
70
-
71
- for (const themeType of objectKeys(implementedThemeTypes)) {
72
- if (!implementedThemeTypes[themeType].isImplemented) {
73
- continue;
74
- }
59
+ const { parsedRealmJson, parsedKeycloakThemesJsonEntry } = params;
75
60
 
76
- parsedRealmJson[`${themeType}Theme` as const] = themeName;
61
+ for (const themeType of [...THEME_TYPES, "email"] as const) {
62
+ parsedRealmJson[`${themeType}Theme` as const] =
63
+ !parsedKeycloakThemesJsonEntry.types.includes(themeType)
64
+ ? ""
65
+ : parsedKeycloakThemesJsonEntry.name;
77
66
  }
78
67
  }
79
68
 
@@ -1,10 +1,7 @@
1
1
  import type { BuildContext } from "../../shared/buildContext";
2
2
  import { assert } from "tsafe/assert";
3
3
  import { getDefaultConfig } from "./defaultConfig";
4
- import {
5
- prepareRealmConfig,
6
- type BuildContextLike as BuildContextLike_prepareRealmConfig
7
- } from "./prepareRealmConfig";
4
+ import { prepareRealmConfig } from "./prepareRealmConfig";
8
5
  import * as fs from "fs";
9
6
  import {
10
7
  join as pathJoin,
@@ -24,18 +21,19 @@ import {
24
21
  } from "./dumpContainerConfig";
25
22
  import * as runExclusive from "run-exclusive";
26
23
  import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
24
+ import type { ThemeType } from "../../shared/constants";
27
25
  import chalk from "chalk";
28
26
 
29
- export type BuildContextLike = BuildContextLike_dumpContainerConfig &
30
- BuildContextLike_prepareRealmConfig & {
31
- projectDirPath: string;
32
- };
27
+ export type BuildContextLike = BuildContextLike_dumpContainerConfig & {
28
+ projectDirPath: string;
29
+ };
33
30
 
34
31
  assert<BuildContext extends BuildContextLike ? true : false>;
35
32
 
36
33
  export async function getRealmConfig(params: {
37
34
  keycloakMajorVersionNumber: number;
38
35
  realmJsonFilePath_userProvided: string | undefined;
36
+ parsedKeycloakThemesJsonEntry: { name: string; types: (ThemeType | "email")[] };
39
37
  buildContext: BuildContextLike;
40
38
  }): Promise<{
41
39
  realmJsonFilePath: string;
@@ -44,8 +42,12 @@ export async function getRealmConfig(params: {
44
42
  username: string;
45
43
  onRealmConfigChange: () => Promise<void>;
46
44
  }> {
47
- const { keycloakMajorVersionNumber, realmJsonFilePath_userProvided, buildContext } =
48
- params;
45
+ const {
46
+ keycloakMajorVersionNumber,
47
+ realmJsonFilePath_userProvided,
48
+ parsedKeycloakThemesJsonEntry,
49
+ buildContext
50
+ } = params;
49
51
 
50
52
  const realmJsonFilePath = pathJoin(
51
53
  buildContext.projectDirPath,
@@ -71,8 +73,8 @@ export async function getRealmConfig(params: {
71
73
 
72
74
  const { clientName, realmName, username } = prepareRealmConfig({
73
75
  parsedRealmJson,
74
- buildContext,
75
- keycloakMajorVersionNumber
76
+ keycloakMajorVersionNumber,
77
+ parsedKeycloakThemesJsonEntry
76
78
  });
77
79
 
78
80
  {
@@ -4,7 +4,8 @@ import {
4
4
  CONTAINER_NAME,
5
5
  KEYCLOAKIFY_SPA_DEV_SERVER_PORT,
6
6
  KEYCLOAKIFY_LOGIN_JAR_BASENAME,
7
- TEST_APP_URL
7
+ TEST_APP_URL,
8
+ ThemeType
8
9
  } from "../shared/constants";
9
10
  import { SemVer } from "../tools/SemVer";
10
11
  import { assert, type Equals } from "tsafe/assert";
@@ -34,6 +35,7 @@ import { startViteDevServer } from "./startViteDevServer";
34
35
  import { getSupportedKeycloakMajorVersions } from "./realmConfig/defaultConfig";
35
36
  import { getSupportedDockerImageTags } from "./getSupportedDockerImageTags";
36
37
  import { getRealmConfig } from "./realmConfig";
38
+ import { id } from "tsafe/id";
37
39
 
38
40
  export async function command(params: {
39
41
  buildContext: BuildContext;
@@ -270,32 +272,6 @@ export async function command(params: {
270
272
  return wrap.majorVersionNumber;
271
273
  })();
272
274
 
273
- const { clientName, onRealmConfigChange, realmJsonFilePath, realmName, username } =
274
- await getRealmConfig({
275
- keycloakMajorVersionNumber,
276
- realmJsonFilePath_userProvided: await (async () => {
277
- if (cliCommandOptions.realmJsonFilePath !== undefined) {
278
- return getAbsoluteAndInOsFormatPath({
279
- pathIsh: cliCommandOptions.realmJsonFilePath,
280
- cwd: process.cwd()
281
- });
282
- }
283
-
284
- if (buildContext.startKeycloakOptions.realmJsonFilePath !== undefined) {
285
- assert(
286
- await existsAsync(
287
- buildContext.startKeycloakOptions.realmJsonFilePath
288
- ),
289
- `${pathRelative(process.cwd(), buildContext.startKeycloakOptions.realmJsonFilePath)} does not exist`
290
- );
291
- return buildContext.startKeycloakOptions.realmJsonFilePath;
292
- }
293
-
294
- return undefined;
295
- })(),
296
- buildContext
297
- });
298
-
299
275
  {
300
276
  const { isAppBuildSuccess } = await appBuild({
301
277
  buildContext
@@ -376,10 +352,24 @@ export async function command(params: {
376
352
  ))
377
353
  ];
378
354
 
355
+ let parsedKeycloakThemesJson = id<
356
+ { themes: { name: string; types: (ThemeType | "email")[] }[] } | undefined
357
+ >(undefined);
358
+
379
359
  async function extractThemeResourcesFromJar() {
380
360
  await extractArchive({
381
361
  archiveFilePath: jarFilePath,
382
- onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) => {
362
+ onArchiveFile: async ({ relativeFilePathInArchive, writeFile, readFile }) => {
363
+ if (
364
+ relativeFilePathInArchive ===
365
+ pathJoin("META-INF", "keycloak-themes.json") &&
366
+ parsedKeycloakThemesJson === undefined
367
+ ) {
368
+ parsedKeycloakThemesJson = JSON.parse(
369
+ (await readFile()).toString("utf8")
370
+ );
371
+ }
372
+
383
373
  if (isInside({ dirPath: "theme", filePath: relativeFilePathInArchive })) {
384
374
  await writeFile({
385
375
  filePath: pathJoin(
@@ -401,6 +391,43 @@ export async function command(params: {
401
391
 
402
392
  await extractThemeResourcesFromJar();
403
393
 
394
+ assert(parsedKeycloakThemesJson !== undefined);
395
+
396
+ const { clientName, onRealmConfigChange, realmJsonFilePath, realmName, username } =
397
+ await getRealmConfig({
398
+ keycloakMajorVersionNumber,
399
+ parsedKeycloakThemesJsonEntry: (() => {
400
+ const entry = parsedKeycloakThemesJson.themes.find(
401
+ ({ name }) => name === buildContext.themeNames[0]
402
+ );
403
+
404
+ assert(entry !== undefined);
405
+
406
+ return entry;
407
+ })(),
408
+ realmJsonFilePath_userProvided: await (async () => {
409
+ if (cliCommandOptions.realmJsonFilePath !== undefined) {
410
+ return getAbsoluteAndInOsFormatPath({
411
+ pathIsh: cliCommandOptions.realmJsonFilePath,
412
+ cwd: process.cwd()
413
+ });
414
+ }
415
+
416
+ if (buildContext.startKeycloakOptions.realmJsonFilePath !== undefined) {
417
+ assert(
418
+ await existsAsync(
419
+ buildContext.startKeycloakOptions.realmJsonFilePath
420
+ ),
421
+ `${pathRelative(process.cwd(), buildContext.startKeycloakOptions.realmJsonFilePath)} does not exist`
422
+ );
423
+ return buildContext.startKeycloakOptions.realmJsonFilePath;
424
+ }
425
+
426
+ return undefined;
427
+ })(),
428
+ buildContext
429
+ });
430
+
404
431
  const jarFilePath_cacheDir = pathJoin(
405
432
  buildContext.cacheDirPath,
406
433
  pathBasename(jarFilePath)
@@ -99,6 +99,12 @@ function addCommentToSourceCode(params: {
99
99
  return toResult(commentLines.map(line => `# ${line}`).join("\n"));
100
100
  }
101
101
 
102
+ if (fileRelativePath.endsWith(".ftl")) {
103
+ return toResult(
104
+ [`<#--`, ...commentLines.map(line => ` ${line}`), `-->`].join("\n")
105
+ );
106
+ }
107
+
102
108
  if (fileRelativePath.endsWith(".html") || fileRelativePath.endsWith(".svg")) {
103
109
  const comment = [
104
110
  `<!--`,
@@ -4524,26 +4524,54 @@ function getBuildContext(params) {
4524
4524
  }
4525
4525
  (0,assert/* assert */.h)(false);
4526
4526
  })();
4527
- const implementedThemeTypes = {
4528
- login: {
4529
- isImplemented: external_fs_.existsSync((0,external_path_.join)(themeSrcDirPath, "login"))
4530
- },
4531
- email: {
4532
- isImplemented: external_fs_.existsSync((0,external_path_.join)(themeSrcDirPath, "email"))
4533
- },
4534
- account: (() => {
4535
- if (buildOptions.accountThemeImplementation === "none") {
4536
- return { isImplemented: false };
4537
- }
4538
- return {
4539
- isImplemented: true,
4540
- type: buildOptions.accountThemeImplementation
4541
- };
4542
- })(),
4543
- admin: {
4544
- isImplemented: external_fs_.existsSync((0,external_path_.join)(themeSrcDirPath, "admin"))
4545
- }
4546
- };
4527
+ const implementedThemeTypes = (() => {
4528
+ const getIsNative = (dirPath) => external_fs_.existsSync((0,external_path_.join)(dirPath, "theme.properties"));
4529
+ return {
4530
+ login: (() => {
4531
+ const dirPath = (0,external_path_.join)(themeSrcDirPath, "login");
4532
+ if (!external_fs_.existsSync(dirPath)) {
4533
+ return { isImplemented: false, isImplemented_native: false };
4534
+ }
4535
+ if (getIsNative(dirPath)) {
4536
+ return { isImplemented: false, isImplemented_native: true };
4537
+ }
4538
+ return { isImplemented: true };
4539
+ })(),
4540
+ email: (() => {
4541
+ const dirPath = (0,external_path_.join)(themeSrcDirPath, "email");
4542
+ if (!external_fs_.existsSync(dirPath) || !getIsNative(dirPath)) {
4543
+ return { isImplemented: false, isImplemented_native: false };
4544
+ }
4545
+ return { isImplemented: false, isImplemented_native: true };
4546
+ })(),
4547
+ account: (() => {
4548
+ const dirPath = (0,external_path_.join)(themeSrcDirPath, "account");
4549
+ if (!external_fs_.existsSync(dirPath)) {
4550
+ return { isImplemented: false, isImplemented_native: false };
4551
+ }
4552
+ if (getIsNative(dirPath)) {
4553
+ return { isImplemented: false, isImplemented_native: true };
4554
+ }
4555
+ if (buildOptions.accountThemeImplementation === "none") {
4556
+ return { isImplemented: false, isImplemented_native: false };
4557
+ }
4558
+ return {
4559
+ isImplemented: true,
4560
+ type: buildOptions.accountThemeImplementation
4561
+ };
4562
+ })(),
4563
+ admin: (() => {
4564
+ const dirPath = (0,external_path_.join)(themeSrcDirPath, "admin");
4565
+ if (!external_fs_.existsSync(dirPath)) {
4566
+ return { isImplemented: false, isImplemented_native: false };
4567
+ }
4568
+ if (getIsNative(dirPath)) {
4569
+ return { isImplemented: false, isImplemented_native: true };
4570
+ }
4571
+ return { isImplemented: true };
4572
+ })()
4573
+ };
4574
+ })();
4547
4575
  if (implementedThemeTypes.account.isImplemented &&
4548
4576
  !external_fs_.existsSync((0,external_path_.join)(themeSrcDirPath, "account"))) {
4549
4577
  console.error(source_default().red([