keycloakify 10.0.0-rc.17 → 10.0.0-rc.18

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 (205) hide show
  1. package/PUBLIC_URL.js.map +1 -1
  2. package/account/Template.js +5 -5
  3. package/account/Template.js.map +1 -1
  4. package/account/i18n/i18n.js +37 -29
  5. package/account/i18n/i18n.js.map +1 -1
  6. package/account/kcContext/KcContext.js.map +1 -1
  7. package/account/kcContext/createGetKcContext.js +20 -15
  8. package/account/kcContext/createGetKcContext.js.map +1 -1
  9. package/account/kcContext/getKcContext.js.map +1 -1
  10. package/account/kcContext/getKcContextFromWindow.d.ts +3 -1
  11. package/account/kcContext/getKcContextFromWindow.js.map +1 -1
  12. package/account/kcContext/kcContextMocks.js +148 -144
  13. package/account/kcContext/kcContextMocks.js.map +1 -1
  14. package/account/lib/useGetClassName.js +14 -14
  15. package/account/lib/useGetClassName.js.map +1 -1
  16. package/account/pages/Account.js +1 -1
  17. package/account/pages/Account.js.map +1 -1
  18. package/account/pages/Password.js +7 -7
  19. package/account/pages/Password.js.map +1 -1
  20. package/account/pages/Totp.js +4 -4
  21. package/account/pages/Totp.js.map +1 -1
  22. package/bin/main.js +2066 -2165
  23. package/bin/shared/constants.d.ts +1 -0
  24. package/bin/shared/constants.js +4 -3
  25. package/bin/shared/constants.js.map +1 -1
  26. package/lib/isStorybook.js +2 -1
  27. package/lib/isStorybook.js.map +1 -1
  28. package/lib/useGetClassName.js.map +1 -1
  29. package/login/Template.js +13 -13
  30. package/login/Template.js.map +1 -1
  31. package/login/UserProfileFormFields.js +43 -41
  32. package/login/UserProfileFormFields.js.map +1 -1
  33. package/login/i18n/baseMessages/ca.d.ts +1 -1
  34. package/login/i18n/baseMessages/ca.js +1 -1
  35. package/login/i18n/baseMessages/el.d.ts +0 -1
  36. package/login/i18n/baseMessages/el.js +0 -1
  37. package/login/i18n/baseMessages/el.js.map +1 -1
  38. package/login/i18n/baseMessages/en.d.ts +1 -1
  39. package/login/i18n/baseMessages/en.js +1 -1
  40. package/login/i18n/baseMessages/es.d.ts +1 -1
  41. package/login/i18n/baseMessages/es.js +1 -1
  42. package/login/i18n/baseMessages/fa.d.ts +0 -1
  43. package/login/i18n/baseMessages/fa.js +0 -1
  44. package/login/i18n/baseMessages/fa.js.map +1 -1
  45. package/login/i18n/baseMessages/hu.d.ts +1 -1
  46. package/login/i18n/baseMessages/hu.js +1 -1
  47. package/login/i18n/baseMessages/index.d.ts +1 -2
  48. package/login/i18n/baseMessages/zh-CN.d.ts +1 -1
  49. package/login/i18n/baseMessages/zh-CN.js +1 -1
  50. package/login/i18n/i18n.js +39 -31
  51. package/login/i18n/i18n.js.map +1 -1
  52. package/login/kcContext/KcContext.js.map +1 -1
  53. package/login/kcContext/createGetKcContext.js +30 -22
  54. package/login/kcContext/createGetKcContext.js.map +1 -1
  55. package/login/kcContext/getKcContext.js.map +1 -1
  56. package/login/kcContext/getKcContextFromWindow.d.ts +3 -1
  57. package/login/kcContext/getKcContextFromWindow.js.map +1 -1
  58. package/login/kcContext/kcContextMocks.js +233 -231
  59. package/login/kcContext/kcContextMocks.js.map +1 -1
  60. package/login/lib/useDownloadTerms.js.map +1 -1
  61. package/login/lib/useGetClassName.js +112 -112
  62. package/login/lib/useGetClassName.js.map +1 -1
  63. package/login/lib/useUserProfileForm.js +181 -181
  64. package/login/lib/useUserProfileForm.js.map +1 -1
  65. package/login/pages/DeleteAccountConfirm.js +5 -1
  66. package/login/pages/DeleteAccountConfirm.js.map +1 -1
  67. package/login/pages/FrontchannelLogout.js +1 -1
  68. package/login/pages/FrontchannelLogout.js.map +1 -1
  69. package/login/pages/Login.js.map +1 -1
  70. package/login/pages/LoginRecoveryAuthnCodeConfig.js +3 -3
  71. package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
  72. package/login/pages/LoginResetPassword.js.map +1 -1
  73. package/login/pages/LoginUsername.js.map +1 -1
  74. package/login/pages/WebauthnAuthenticate.js +11 -8
  75. package/login/pages/WebauthnAuthenticate.js.map +1 -1
  76. package/login/pages/WebauthnRegister.js +7 -7
  77. package/login/pages/WebauthnRegister.js.map +1 -1
  78. package/package.json +230 -226
  79. package/src/PUBLIC_URL.ts +4 -1
  80. package/src/account/Template.tsx +5 -5
  81. package/src/account/TemplateProps.ts +4 -1
  82. package/src/account/i18n/i18n.tsx +40 -30
  83. package/src/account/kcContext/KcContext.ts +4 -1
  84. package/src/account/kcContext/createGetKcContext.ts +48 -22
  85. package/src/account/kcContext/getKcContext.ts +3 -1
  86. package/src/account/kcContext/getKcContextFromWindow.ts +6 -2
  87. package/src/account/kcContext/kcContextMocks.ts +164 -160
  88. package/src/account/lib/useGetClassName.ts +15 -14
  89. package/src/account/pages/Account.tsx +2 -2
  90. package/src/account/pages/Password.tsx +8 -8
  91. package/src/account/pages/Totp.tsx +4 -6
  92. package/src/bin/copy-keycloak-resources-to-public.ts +2 -2
  93. package/src/bin/download-keycloak-default-theme.ts +30 -8
  94. package/src/bin/eject-page.ts +48 -11
  95. package/src/bin/initialize-email-theme.ts +25 -17
  96. package/src/bin/keycloakify/buildJars/buildJar.ts +179 -104
  97. package/src/bin/keycloakify/buildJars/buildJars.ts +35 -16
  98. package/src/bin/keycloakify/buildJars/extensionVersions.ts +2 -1
  99. package/src/bin/keycloakify/buildJars/generatePom.ts +11 -3
  100. package/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts +33 -8
  101. package/src/bin/keycloakify/generateFtl/generateFtl.ts +49 -12
  102. package/src/bin/keycloakify/generateSrcMainResources/bringInAccountV1.ts +29 -18
  103. package/src/bin/keycloakify/generateSrcMainResources/generateMessageProperties.ts +35 -12
  104. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResources.ts +3 -1
  105. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForMainTheme.ts +86 -41
  106. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForThemeVariant.ts +39 -15
  107. package/src/bin/keycloakify/generateSrcMainResources/readExtraPageNames.ts +21 -7
  108. package/src/bin/keycloakify/generateSrcMainResources/readFieldNameUsage.ts +34 -7
  109. package/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +19 -5
  110. package/src/bin/keycloakify/keycloakify.ts +28 -9
  111. package/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts +24 -5
  112. package/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts +6 -2
  113. package/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts +6 -3
  114. package/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts +24 -6
  115. package/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts +49 -11
  116. package/src/bin/main.ts +78 -41
  117. package/src/bin/shared/KeycloakVersionRange.ts +3 -1
  118. package/src/bin/shared/buildOptions.ts +70 -43
  119. package/src/bin/shared/constants.ts +4 -2
  120. package/src/bin/shared/copyKeycloakResourcesToPublic.ts +27 -13
  121. package/src/bin/shared/downloadKeycloakDefaultTheme.ts +161 -218
  122. package/src/bin/shared/downloadKeycloakStaticResources.ts +25 -21
  123. package/src/bin/shared/getJarFileBasename.ts +3 -1
  124. package/src/bin/shared/getThemeSrcDirPath.ts +5 -2
  125. package/src/bin/shared/metaInfKeycloakThemes.ts +35 -8
  126. package/src/bin/shared/promptKeycloakVersion.ts +33 -14
  127. package/src/bin/start-keycloak/index.ts +1 -0
  128. package/src/bin/start-keycloak/myrealm-realm-23.json +2142 -0
  129. package/src/bin/start-keycloak/myrealm-realm-24.json +2318 -0
  130. package/src/bin/start-keycloak/start-keycloak.ts +467 -0
  131. package/src/bin/tools/SemVer.ts +32 -13
  132. package/src/bin/tools/String.prototype.replaceAll.ts +9 -2
  133. package/src/bin/tools/crawl.ts +4 -1
  134. package/src/bin/tools/crc32.ts +42 -24
  135. package/src/bin/tools/downloadAndExtractArchive/downloadAndExtractArchive.ts +262 -0
  136. package/src/bin/tools/downloadAndExtractArchive/fetchProxyOptions.ts +96 -0
  137. package/src/bin/tools/downloadAndExtractArchive/index.ts +1 -0
  138. package/src/bin/tools/extractArchive.ts +120 -0
  139. package/src/bin/tools/fetchProxyOptions.ts +31 -8
  140. package/src/bin/tools/getAbsoluteAndInOsFormatPath.ts +10 -2
  141. package/src/bin/tools/getNpmWorkspaceRootDirPath.ts +18 -5
  142. package/src/bin/tools/octokit-addons/getLatestsSemVersionedTag.ts +8 -2
  143. package/src/bin/tools/octokit-addons/listTags.ts +15 -4
  144. package/src/bin/tools/partitionPromiseSettledResults.ts +12 -3
  145. package/src/bin/tools/readThisNpmPackageVersion.ts +5 -1
  146. package/src/bin/tools/transformCodebase.ts +29 -10
  147. package/src/bin/tools/trimIndent.ts +4 -1
  148. package/src/lib/isStorybook.ts +3 -1
  149. package/src/lib/useGetClassName.ts +12 -3
  150. package/src/login/Template.tsx +14 -14
  151. package/src/login/TemplateProps.ts +4 -1
  152. package/src/login/UserProfileFormFields.tsx +44 -42
  153. package/src/login/i18n/baseMessages/ca.ts +1 -1
  154. package/src/login/i18n/baseMessages/el.ts +0 -1
  155. package/src/login/i18n/baseMessages/en.ts +1 -1
  156. package/src/login/i18n/baseMessages/es.ts +1 -1
  157. package/src/login/i18n/baseMessages/fa.ts +0 -1
  158. package/src/login/i18n/baseMessages/hu.ts +1 -1
  159. package/src/login/i18n/baseMessages/zh-CN.ts +1 -1
  160. package/src/login/i18n/i18n.tsx +42 -32
  161. package/src/login/kcContext/KcContext.ts +8 -2
  162. package/src/login/kcContext/createGetKcContext.ts +84 -37
  163. package/src/login/kcContext/getKcContext.ts +3 -1
  164. package/src/login/kcContext/getKcContextFromWindow.ts +6 -2
  165. package/src/login/kcContext/kcContextMocks.ts +339 -325
  166. package/src/login/lib/useDownloadTerms.ts +6 -4
  167. package/src/login/lib/useGetClassName.ts +119 -112
  168. package/src/login/lib/useUserProfileForm.tsx +219 -205
  169. package/src/login/pages/DeleteAccountConfirm.tsx +9 -3
  170. package/src/login/pages/FrontchannelLogout.tsx +1 -1
  171. package/src/login/pages/Login.tsx +2 -2
  172. package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +3 -3
  173. package/src/login/pages/LoginResetPassword.tsx +2 -2
  174. package/src/login/pages/LoginUsername.tsx +2 -2
  175. package/src/login/pages/WebauthnAuthenticate.tsx +11 -8
  176. package/src/login/pages/WebauthnRegister.tsx +7 -7
  177. package/src/tools/AndByDiscriminatingKey.ts +12 -6
  178. package/src/tools/Array.prototype.every.ts +4 -1
  179. package/src/tools/LazyOrNot.ts +3 -1
  180. package/src/tools/clsx.ts +7 -1
  181. package/src/tools/deepAssign.ts +15 -8
  182. package/src/tools/deepClone.ts +3 -1
  183. package/src/tools/formatNumber.ts +4 -1
  184. package/src/tools/useConstCallback.ts +3 -1
  185. package/src/tools/useInsertLinkTags.ts +20 -7
  186. package/src/tools/useInsertScriptTags.ts +7 -2
  187. package/src/tools/useSetClassName.ts +4 -1
  188. package/src/vite-plugin/vite-plugin.ts +45 -21
  189. package/tools/Array.prototype.every.js +2 -1
  190. package/tools/Array.prototype.every.js.map +1 -1
  191. package/tools/clsx.js.map +1 -1
  192. package/tools/deepAssign.js +9 -7
  193. package/tools/deepAssign.js.map +1 -1
  194. package/tools/deepClone.js.map +1 -1
  195. package/tools/formatNumber.js.map +1 -1
  196. package/tools/useConstCallback.js.map +1 -1
  197. package/tools/useInsertLinkTags.js +5 -4
  198. package/tools/useInsertLinkTags.js.map +1 -1
  199. package/tools/useInsertScriptTags.js +5 -2
  200. package/tools/useInsertScriptTags.js.map +1 -1
  201. package/tools/useSetClassName.js.map +1 -1
  202. package/vite-plugin/index.js +975 -1651
  203. package/src/bin/shared/downloadAndUnzip.ts +0 -203
  204. package/src/bin/start-keycloak.ts +0 -309
  205. package/src/bin/tools/unzip.ts +0 -141
@@ -1,203 +0,0 @@
1
- import { createHash } from "crypto";
2
- import { mkdir, writeFile, unlink } from "fs/promises";
3
- import fetch from "make-fetch-happen";
4
- import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path";
5
- import { assert } from "tsafe/assert";
6
- import { transformCodebase } from "../tools/transformCodebase";
7
- import { unzip, zip } from "../tools/unzip";
8
- import { rm } from "../tools/fs.rm";
9
- import * as child_process from "child_process";
10
- import { existsAsync } from "../tools/fs.existsAsync";
11
- import type { BuildOptions } from "./buildOptions";
12
- import { getProxyFetchOptions } from "../tools/fetchProxyOptions";
13
-
14
- export type BuildOptionsLike = {
15
- cacheDirPath: string;
16
- npmWorkspaceRootDirPath: string;
17
- };
18
-
19
- assert<BuildOptions extends BuildOptionsLike ? true : false>();
20
-
21
- export async function downloadAndUnzip(params: {
22
- url: string;
23
- destDirPath: string;
24
- specificDirsToExtract?: string[];
25
- preCacheTransform?: {
26
- actionCacheId: string;
27
- action: (params: { destDirPath: string }) => Promise<void>;
28
- };
29
- buildOptions: BuildOptionsLike;
30
- }) {
31
- const { url, destDirPath, specificDirsToExtract, preCacheTransform, buildOptions } = params;
32
-
33
- const { extractDirPath, zipFilePath } = (() => {
34
- const zipFileBasenameWithoutExt = generateFileNameFromURL({
35
- url,
36
- "preCacheTransform":
37
- preCacheTransform === undefined
38
- ? undefined
39
- : {
40
- "actionCacheId": preCacheTransform.actionCacheId,
41
- "actionFootprint": preCacheTransform.action.toString()
42
- }
43
- });
44
-
45
- const zipFilePath = pathJoin(buildOptions.cacheDirPath, `${zipFileBasenameWithoutExt}.zip`);
46
- const extractDirPath = pathJoin(buildOptions.cacheDirPath, `tmp_unzip_${zipFileBasenameWithoutExt}`);
47
-
48
- return { zipFilePath, extractDirPath };
49
- })();
50
-
51
- download_zip_and_transform: {
52
- if (await existsAsync(zipFilePath)) {
53
- break download_zip_and_transform;
54
- }
55
-
56
- const { response, isFromRemoteCache } = await (async () => {
57
- const proxyFetchOptions = await getProxyFetchOptions({
58
- "npmWorkspaceRootDirPath": buildOptions.npmWorkspaceRootDirPath
59
- });
60
-
61
- const response = await fetch(
62
- `https://github.com/keycloakify/keycloakify/releases/download/v0.0.1/${pathBasename(zipFilePath)}`,
63
- proxyFetchOptions
64
- );
65
-
66
- if (response.status === 200) {
67
- return {
68
- response,
69
- "isFromRemoteCache": true
70
- };
71
- }
72
-
73
- return {
74
- "response": await fetch(url, proxyFetchOptions),
75
- "isFromRemoteCache": false
76
- };
77
- })();
78
-
79
- await mkdir(pathDirname(zipFilePath), { "recursive": true });
80
-
81
- /**
82
- * The correct way to fix this is to upgrade node-fetch beyond 3.2.5
83
- * (see https://github.com/node-fetch/node-fetch/issues/1295#issuecomment-1144061991.)
84
- * Unfortunately, octokit (a dependency of keycloakify) also uses node-fetch, and
85
- * does not support node-fetch 3.x. So we stick around with this band-aid until
86
- * octokit upgrades.
87
- */
88
- response.body?.setMaxListeners(Number.MAX_VALUE);
89
- assert(typeof response.body !== "undefined" && response.body != null);
90
-
91
- await writeFile(zipFilePath, response.body);
92
-
93
- if (isFromRemoteCache) {
94
- break download_zip_and_transform;
95
- }
96
-
97
- if (specificDirsToExtract === undefined && preCacheTransform === undefined) {
98
- break download_zip_and_transform;
99
- }
100
-
101
- await unzip(zipFilePath, extractDirPath, specificDirsToExtract);
102
-
103
- try {
104
- await preCacheTransform?.action({
105
- "destDirPath": extractDirPath
106
- });
107
- } catch (error) {
108
- await Promise.all([rm(extractDirPath, { "recursive": true }), unlink(zipFilePath)]);
109
-
110
- throw error;
111
- }
112
-
113
- await unlink(zipFilePath);
114
-
115
- await zip(extractDirPath, zipFilePath);
116
-
117
- await rm(extractDirPath, { "recursive": true });
118
-
119
- upload_to_remote_cache_if_admin: {
120
- const githubToken = process.env["KEYCLOAKIFY_ADMIN_GITHUB_PERSONAL_ACCESS_TOKEN"];
121
-
122
- if (!githubToken) {
123
- break upload_to_remote_cache_if_admin;
124
- }
125
-
126
- console.log("uploading to remote cache");
127
-
128
- try {
129
- child_process.execSync(`which putasset`);
130
- } catch {
131
- child_process.execSync(`npm install -g putasset`);
132
- }
133
-
134
- try {
135
- child_process.execFileSync("putasset", [
136
- "--owner",
137
- "keycloakify",
138
- "--repo",
139
- "keycloakify",
140
- "--tag",
141
- "v0.0.1",
142
- "--filename",
143
- zipFilePath,
144
- "--token",
145
- githubToken
146
- ]);
147
- } catch {
148
- console.log("upload failed, asset probably already exists in remote cache");
149
- }
150
- }
151
- }
152
-
153
- await unzip(zipFilePath, extractDirPath);
154
-
155
- transformCodebase({
156
- "srcDirPath": extractDirPath,
157
- "destDirPath": destDirPath
158
- });
159
-
160
- await rm(extractDirPath, { "recursive": true });
161
- }
162
-
163
- function generateFileNameFromURL(params: {
164
- url: string;
165
- preCacheTransform:
166
- | {
167
- actionCacheId: string;
168
- actionFootprint: string;
169
- }
170
- | undefined;
171
- }): string {
172
- const { preCacheTransform } = params;
173
-
174
- // Parse the URL
175
- const url = new URL(params.url);
176
-
177
- // Extract pathname and remove leading slashes
178
- let fileName = url.pathname.replace(/^\//, "").replace(/\//g, "_");
179
-
180
- // Optionally, add query parameters replacing special characters
181
- if (url.search) {
182
- fileName += url.search.replace(/[&=?]/g, "-");
183
- }
184
-
185
- // Replace any characters that are not valid in filenames
186
- fileName = fileName.replace(/[^a-zA-Z0-9-_]/g, "");
187
-
188
- // Trim or pad the fileName to a specific length
189
- fileName = fileName.substring(0, 50);
190
-
191
- add_pre_cache_transform: {
192
- if (preCacheTransform === undefined) {
193
- break add_pre_cache_transform;
194
- }
195
-
196
- // Sanitize actionCacheId the same way as other components
197
- const sanitizedActionCacheId = preCacheTransform.actionCacheId.replace(/[^a-zA-Z0-9-_]/g, "_");
198
-
199
- fileName += `_${sanitizedActionCacheId}_${createHash("sha256").update(preCacheTransform.actionFootprint).digest("hex").substring(0, 5)}`;
200
- }
201
-
202
- return fileName;
203
- }
@@ -1,309 +0,0 @@
1
- import { readBuildOptions } from "./shared/buildOptions";
2
- import type { CliCommandOptions as CliCommandOptions_common } from "./main";
3
- import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
4
- import { readMetaInfKeycloakThemes } from "./shared/metaInfKeycloakThemes";
5
- import { accountV1ThemeName, skipBuildJarsEnvName } from "./shared/constants";
6
- import { SemVer } from "./tools/SemVer";
7
- import type { KeycloakVersionRange } from "./shared/KeycloakVersionRange";
8
- import { getJarFileBasename } from "./shared/getJarFileBasename";
9
- import { assert, type Equals } from "tsafe/assert";
10
- import * as fs from "fs";
11
- import { join as pathJoin, relative as pathRelative, sep as pathSep, posix as pathPosix } from "path";
12
- import * as child_process from "child_process";
13
- import chalk from "chalk";
14
- import chokidar from "chokidar";
15
- import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
16
- import { getThemeSrcDirPath } from "./shared/getThemeSrcDirPath";
17
- import { Deferred } from "evt/tools/Deferred";
18
-
19
- export type CliCommandOptions = CliCommandOptions_common & {
20
- port: number;
21
- keycloakVersion: string | undefined;
22
- };
23
-
24
- export async function command(params: { cliCommandOptions: CliCommandOptions }) {
25
- exit_if_docker_not_installed: {
26
- let commandOutput: Buffer | undefined = undefined;
27
-
28
- try {
29
- commandOutput = child_process.execSync("docker --version", { "stdio": ["ignore", "pipe", "ignore"] });
30
- } catch {}
31
-
32
- if (commandOutput?.toString("utf8").includes("Docker")) {
33
- break exit_if_docker_not_installed;
34
- }
35
-
36
- console.log(
37
- [
38
- `${chalk.red("Docker required.")}`,
39
- `Install it with Docker Desktop: ${chalk.bold.underline("https://www.docker.com/products/docker-desktop/")}`,
40
- `(or any other way)`
41
- ].join(" ")
42
- );
43
-
44
- process.exit(1);
45
- }
46
-
47
- exit_if_docker_not_running: {
48
- let isDockerRunning: boolean;
49
-
50
- try {
51
- child_process.execSync("docker info", { "stdio": "ignore" });
52
- isDockerRunning = true;
53
- } catch {
54
- isDockerRunning = false;
55
- }
56
-
57
- if (isDockerRunning) {
58
- break exit_if_docker_not_running;
59
- }
60
-
61
- console.log([`${chalk.red("Docker daemon is not running.")}`, `Please start Docker Desktop and try again.`].join(" "));
62
-
63
- process.exit(1);
64
- }
65
-
66
- const { cliCommandOptions } = params;
67
-
68
- const buildOptions = readBuildOptions({ cliCommandOptions });
69
-
70
- exit_if_theme_not_built: {
71
- if (fs.existsSync(buildOptions.keycloakifyBuildDirPath)) {
72
- break exit_if_theme_not_built;
73
- }
74
-
75
- console.log(
76
- [`${chalk.red("The theme has not been built.")}`, `Please run ${chalk.bold("npx vite && npx keycloakify build")} first.`].join(" ")
77
- );
78
- process.exit(1);
79
- }
80
-
81
- const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
82
- "keycloakifyBuildDirPath": buildOptions.keycloakifyBuildDirPath
83
- });
84
-
85
- const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some(({ name }) => name === accountV1ThemeName);
86
-
87
- const { keycloakVersion, keycloakMajorNumber } = await (async function getKeycloakMajor(): Promise<{
88
- keycloakVersion: string;
89
- keycloakMajorNumber: number;
90
- }> {
91
- if (cliCommandOptions.keycloakVersion !== undefined) {
92
- return {
93
- "keycloakVersion": cliCommandOptions.keycloakVersion,
94
- "keycloakMajorNumber": SemVer.parse(cliCommandOptions.keycloakVersion).major
95
- };
96
- }
97
-
98
- console.log(chalk.cyan("On which version of Keycloak do you want to test your theme?"));
99
-
100
- const { keycloakVersion } = await promptKeycloakVersion({
101
- "startingFromMajor": 17,
102
- "cacheDirPath": buildOptions.cacheDirPath
103
- });
104
-
105
- console.log(`→ ${keycloakVersion}`);
106
-
107
- const keycloakMajorNumber = SemVer.parse(keycloakVersion).major;
108
-
109
- if (doesImplementAccountTheme && keycloakMajorNumber === 22) {
110
- console.log(
111
- [
112
- "Unfortunately, Keycloakify themes that implements an account theme do not work on Keycloak 22",
113
- "Please select any other Keycloak version"
114
- ].join(" ")
115
- );
116
- return getKeycloakMajor();
117
- }
118
-
119
- return { keycloakVersion, keycloakMajorNumber };
120
- })();
121
-
122
- const keycloakVersionRange: KeycloakVersionRange = (() => {
123
- if (doesImplementAccountTheme) {
124
- const keycloakVersionRange = (() => {
125
- if (keycloakMajorNumber <= 21) {
126
- return "21-and-below" as const;
127
- }
128
-
129
- assert(keycloakMajorNumber !== 22);
130
-
131
- if (keycloakMajorNumber === 23) {
132
- return "23" as const;
133
- }
134
-
135
- return "24-and-above" as const;
136
- })();
137
-
138
- assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithAccountTheme>>();
139
-
140
- return keycloakVersionRange;
141
- } else {
142
- const keycloakVersionRange = (() => {
143
- if (keycloakMajorNumber <= 21) {
144
- return "21-and-below" as const;
145
- }
146
-
147
- return "22-and-above" as const;
148
- })();
149
-
150
- assert<Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithoutAccountTheme>>();
151
-
152
- return keycloakVersionRange;
153
- }
154
- })();
155
-
156
- const { jarFileBasename } = getJarFileBasename({ keycloakVersionRange });
157
-
158
- console.log(`Using Keycloak ${chalk.bold(jarFileBasename)}`);
159
-
160
- const mountTargets = buildOptions.themeNames
161
- .map(themeName => {
162
- const themeEntry = metaInfKeycloakThemes.themes.find(({ name }) => name === themeName);
163
-
164
- assert(themeEntry !== undefined);
165
-
166
- return themeEntry.types
167
- .map(themeType => {
168
- const localPathDirname = pathJoin(
169
- buildOptions.keycloakifyBuildDirPath,
170
- "src",
171
- "main",
172
- "resources",
173
- "theme",
174
- themeName,
175
- themeType
176
- );
177
-
178
- return fs
179
- .readdirSync(localPathDirname)
180
- .filter(fileOrDirectoryBasename => !fileOrDirectoryBasename.endsWith(".properties"))
181
- .map(fileOrDirectoryBasename => ({
182
- "localPath": pathJoin(localPathDirname, fileOrDirectoryBasename),
183
- "containerPath": pathPosix.join("/", "opt", "keycloak", "themes", themeName, themeType, fileOrDirectoryBasename)
184
- }));
185
- })
186
- .flat();
187
- })
188
- .flat();
189
-
190
- const containerName = "keycloak-keycloakify";
191
-
192
- try {
193
- child_process.execSync(`docker rm --force ${containerName}`, { "stdio": "ignore" });
194
- } catch {}
195
-
196
- const spawnParams = [
197
- "docker",
198
- [
199
- "run",
200
- ...["-p", `${cliCommandOptions.port}:8080`],
201
- ...["--name", containerName],
202
- ...["-e", "KEYCLOAK_ADMIN=admin"],
203
- ...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
204
- ...["-v", `${pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)}:/opt/keycloak/providers/keycloak-theme.jar`],
205
- ...(keycloakMajorNumber <= 20 ? ["-e", "JAVA_OPTS=-Dkeycloak.profile=preview"] : []),
206
- ...mountTargets.map(({ localPath, containerPath }) => ["-v", `${localPath}:${containerPath}:rw`]).flat(),
207
- `quay.io/keycloak/keycloak:${keycloakVersion}`,
208
- "start-dev",
209
- ...(21 <= keycloakMajorNumber && keycloakMajorNumber < 24 ? ["--features=declarative-user-profile"] : [])
210
- ],
211
- {
212
- "cwd": buildOptions.keycloakifyBuildDirPath
213
- }
214
- ] as const;
215
-
216
- const child = child_process.spawn(...spawnParams);
217
-
218
- child.stdout.on("data", data => process.stdout.write(data));
219
-
220
- child.stderr.on("data", data => process.stderr.write(data));
221
-
222
- child.on("exit", process.exit);
223
-
224
- const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
225
-
226
- {
227
- const handler = async (data: Buffer) => {
228
- if (!data.toString("utf8").includes("Listening on: http://0.0.0.0:8080")) {
229
- return;
230
- }
231
-
232
- child.stdout.off("data", handler);
233
-
234
- await new Promise(resolve => setTimeout(resolve, 1_000));
235
-
236
- console.log(
237
- [
238
- "",
239
- `${chalk.green("Your theme is accessible at:")}`,
240
- `${chalk.green("➜")} ${chalk.cyan.bold("https://test.keycloakify.dev/")}`,
241
- "",
242
- `Keycloak Admin console: ${chalk.cyan.bold(`http://localhost:${cliCommandOptions.port}`)}`,
243
- `- user: ${chalk.cyan.bold("admin")}`,
244
- `- password: ${chalk.cyan.bold("admin")}`,
245
- "",
246
- `Watching for changes in ${chalk.bold(`.${pathSep}${pathRelative(process.cwd(), themeSrcDirPath)}`)} ...`
247
- ].join("\n")
248
- );
249
- };
250
-
251
- child.stdout.on("data", handler);
252
- }
253
-
254
- {
255
- const { waitForDebounce } = waitForDebounceFactory({ "delay": 400 });
256
-
257
- chokidar.watch(themeSrcDirPath, { "ignoreInitial": true }).on("all", async (...eventArgs) => {
258
- console.log({ eventArgs });
259
-
260
- await waitForDebounce();
261
-
262
- console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
263
-
264
- const dViteBuildDone = new Deferred<void>();
265
-
266
- {
267
- const child = child_process.spawn("npx", ["vite", "build"], {
268
- "cwd": buildOptions.reactAppRootDirPath,
269
- "env": process.env
270
- });
271
-
272
- child.stdout.on("data", data => process.stdout.write(data));
273
-
274
- child.stderr.on("data", data => process.stderr.write(data));
275
-
276
- child.on("exit", code => {
277
- if (code === 0) {
278
- dViteBuildDone.resolve();
279
- }
280
- });
281
- }
282
-
283
- await dViteBuildDone.pr;
284
-
285
- {
286
- const child = child_process.spawn("npx", ["keycloakify", "build"], {
287
- "cwd": buildOptions.reactAppRootDirPath,
288
- "env": {
289
- ...process.env,
290
- [skipBuildJarsEnvName]: "true"
291
- }
292
- });
293
-
294
- child.stdout.on("data", data => process.stdout.write(data));
295
-
296
- child.stderr.on("data", data => process.stderr.write(data));
297
-
298
- child.on("exit", code => {
299
- if (code !== 0) {
300
- console.log(chalk.yellow("Theme not updated, build failed"));
301
- return;
302
- }
303
-
304
- console.log(chalk.green("Rebuild done"));
305
- });
306
- }
307
- });
308
- }
309
- }
@@ -1,141 +0,0 @@
1
- import fsp from "node:fs/promises";
2
- import fs from "fs";
3
- import path from "node:path";
4
- import yauzl from "yauzl";
5
- import yazl from "yazl";
6
- import stream from "node:stream";
7
- import { promisify } from "node:util";
8
-
9
- const pipeline = promisify(stream.pipeline);
10
-
11
- async function pathExists(path: string) {
12
- try {
13
- await fsp.stat(path);
14
- return true;
15
- } catch (error) {
16
- if ((error as { code: string }).code === "ENOENT") {
17
- return false;
18
- }
19
- throw error;
20
- }
21
- }
22
-
23
- // Handlings of non posix path is not implemented correctly
24
- // it work by coincidence. Don't have the time to fix but it should be fixed.
25
- export async function unzip(file: string, targetFolder: string, specificDirsToExtract?: string[]) {
26
- specificDirsToExtract = specificDirsToExtract?.map(dirPath => {
27
- if (!dirPath.endsWith("/") || !dirPath.endsWith("\\")) {
28
- dirPath += "/";
29
- }
30
-
31
- return dirPath;
32
- });
33
-
34
- if (!targetFolder.endsWith("/") || !targetFolder.endsWith("\\")) {
35
- targetFolder += "/";
36
- }
37
- if (!fs.existsSync(targetFolder)) {
38
- fs.mkdirSync(targetFolder, { recursive: true });
39
- }
40
-
41
- return new Promise<void>((resolve, reject) => {
42
- yauzl.open(file, { lazyEntries: true }, async (err, zipfile) => {
43
- if (err) {
44
- reject(err);
45
- return;
46
- }
47
-
48
- zipfile.readEntry();
49
-
50
- zipfile.on("entry", async entry => {
51
- if (specificDirsToExtract !== undefined) {
52
- const dirPath = specificDirsToExtract.find(dirPath => entry.fileName.startsWith(dirPath));
53
-
54
- // Skip files outside of the unzipSubPath
55
- if (dirPath === undefined) {
56
- zipfile.readEntry();
57
- return;
58
- }
59
-
60
- // Remove the unzipSubPath from the file name
61
- entry.fileName = entry.fileName.substring(dirPath.length);
62
- }
63
-
64
- const target = path.join(targetFolder, entry.fileName);
65
-
66
- // Directory file names end with '/'.
67
- // Note that entries for directories themselves are optional.
68
- // An entry's fileName implicitly requires its parent directories to exist.
69
- if (/[\/\\]$/.test(target)) {
70
- await fsp.mkdir(target, { recursive: true });
71
-
72
- zipfile.readEntry();
73
- return;
74
- }
75
-
76
- // Skip existing files
77
- if (await pathExists(target)) {
78
- zipfile.readEntry();
79
- return;
80
- }
81
-
82
- zipfile.openReadStream(entry, async (err, readStream) => {
83
- if (err) {
84
- reject(err);
85
- return;
86
- }
87
-
88
- await fsp.mkdir(path.dirname(target), { "recursive": true });
89
-
90
- await pipeline(readStream, fs.createWriteStream(target));
91
-
92
- zipfile.readEntry();
93
- });
94
- });
95
-
96
- zipfile.once("end", function () {
97
- zipfile.close();
98
- resolve();
99
- });
100
- });
101
- });
102
- }
103
-
104
- // NOTE: This code was directly copied from ChatGPT and appears to function as expected.
105
- // However, confidence in its complete accuracy and robustness is limited.
106
- export async function zip(sourceFolder: string, targetZip: string) {
107
- return new Promise<void>(async (resolve, reject) => {
108
- const zipfile = new yazl.ZipFile();
109
- const files: string[] = [];
110
-
111
- // Recursive function to explore directories and their subdirectories
112
- async function exploreDir(dir: string) {
113
- const dirContent = await fsp.readdir(dir);
114
- for (const file of dirContent) {
115
- const filePath = path.join(dir, file);
116
- const stat = await fsp.stat(filePath);
117
- if (stat.isDirectory()) {
118
- await exploreDir(filePath);
119
- } else if (stat.isFile()) {
120
- files.push(filePath);
121
- }
122
- }
123
- }
124
-
125
- // Collecting all files to be zipped
126
- await exploreDir(sourceFolder);
127
-
128
- // Adding files to zip
129
- for (const file of files) {
130
- const relativePath = path.relative(sourceFolder, file);
131
- zipfile.addFile(file, relativePath);
132
- }
133
-
134
- zipfile.outputStream
135
- .pipe(fs.createWriteStream(targetZip))
136
- .on("close", () => resolve())
137
- .on("error", err => reject(err)); // Listen to error events
138
-
139
- zipfile.end();
140
- });
141
- }