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
@@ -0,0 +1,467 @@
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 {
6
+ accountV1ThemeName,
7
+ skipBuildJarsEnvName,
8
+ containerName
9
+ } from "../shared/constants";
10
+ import { SemVer } from "../tools/SemVer";
11
+ import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange";
12
+ import { getJarFileBasename } from "../shared/getJarFileBasename";
13
+ import { assert, type Equals } from "tsafe/assert";
14
+ import * as fs from "fs";
15
+ import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
16
+ import * as child_process from "child_process";
17
+ import chalk from "chalk";
18
+ import chokidar from "chokidar";
19
+ import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
20
+ import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
21
+ import { Deferred } from "evt/tools/Deferred";
22
+ import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
23
+ import cliSelect from "cli-select";
24
+ import * as runExclusive from "run-exclusive";
25
+ import { extractArchive } from "../tools/extractArchive";
26
+
27
+ export type CliCommandOptions = CliCommandOptions_common & {
28
+ port: number;
29
+ keycloakVersion: string | undefined;
30
+ realmJsonFilePath: string | undefined;
31
+ };
32
+
33
+ export async function command(params: { cliCommandOptions: CliCommandOptions }) {
34
+ exit_if_docker_not_installed: {
35
+ let commandOutput: Buffer | undefined = undefined;
36
+
37
+ try {
38
+ commandOutput = child_process.execSync("docker --version", {
39
+ stdio: ["ignore", "pipe", "ignore"]
40
+ });
41
+ } catch {}
42
+
43
+ if (commandOutput?.toString("utf8").includes("Docker")) {
44
+ break exit_if_docker_not_installed;
45
+ }
46
+
47
+ console.log(
48
+ [
49
+ `${chalk.red("Docker required.")}`,
50
+ `Install it with Docker Desktop: ${chalk.bold.underline(
51
+ "https://www.docker.com/products/docker-desktop/"
52
+ )}`,
53
+ `(or any other way)`
54
+ ].join(" ")
55
+ );
56
+
57
+ process.exit(1);
58
+ }
59
+
60
+ exit_if_docker_not_running: {
61
+ let isDockerRunning: boolean;
62
+
63
+ try {
64
+ child_process.execSync("docker info", { stdio: "ignore" });
65
+ isDockerRunning = true;
66
+ } catch {
67
+ isDockerRunning = false;
68
+ }
69
+
70
+ if (isDockerRunning) {
71
+ break exit_if_docker_not_running;
72
+ }
73
+
74
+ console.log(
75
+ [
76
+ `${chalk.red("Docker daemon is not running.")}`,
77
+ `Please start Docker Desktop and try again.`
78
+ ].join(" ")
79
+ );
80
+
81
+ process.exit(1);
82
+ }
83
+
84
+ const { cliCommandOptions } = params;
85
+
86
+ const buildOptions = readBuildOptions({ cliCommandOptions });
87
+
88
+ exit_if_theme_not_built: {
89
+ if (fs.existsSync(buildOptions.keycloakifyBuildDirPath)) {
90
+ break exit_if_theme_not_built;
91
+ }
92
+
93
+ console.log(
94
+ [
95
+ `${chalk.red("The theme has not been built.")}`,
96
+ `Please run ${chalk.bold("npx vite && npx keycloakify build")} first.`
97
+ ].join(" ")
98
+ );
99
+ process.exit(1);
100
+ }
101
+
102
+ const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
103
+ keycloakifyBuildDirPath: buildOptions.keycloakifyBuildDirPath
104
+ });
105
+
106
+ const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some(
107
+ ({ name }) => name === accountV1ThemeName
108
+ );
109
+
110
+ const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } =
111
+ await (async function getKeycloakMajor(): Promise<{
112
+ keycloakVersion: string;
113
+ keycloakMajorNumber: number;
114
+ }> {
115
+ if (cliCommandOptions.keycloakVersion !== undefined) {
116
+ return {
117
+ keycloakVersion: cliCommandOptions.keycloakVersion,
118
+ keycloakMajorNumber: SemVer.parse(cliCommandOptions.keycloakVersion)
119
+ .major
120
+ };
121
+ }
122
+
123
+ console.log(
124
+ chalk.cyan("On which version of Keycloak do you want to test your theme?")
125
+ );
126
+
127
+ const { keycloakVersion } = await promptKeycloakVersion({
128
+ startingFromMajor: 17,
129
+ cacheDirPath: buildOptions.cacheDirPath
130
+ });
131
+
132
+ console.log(`→ ${keycloakVersion}`);
133
+
134
+ const keycloakMajorNumber = SemVer.parse(keycloakVersion).major;
135
+
136
+ if (doesImplementAccountTheme && keycloakMajorNumber === 22) {
137
+ console.log(
138
+ [
139
+ "Unfortunately, Keycloakify themes that implements an account theme do not work on Keycloak 22",
140
+ "Please select any other Keycloak version"
141
+ ].join(" ")
142
+ );
143
+ return getKeycloakMajor();
144
+ }
145
+
146
+ return { keycloakVersion, keycloakMajorNumber };
147
+ })();
148
+
149
+ const keycloakVersionRange: KeycloakVersionRange = (() => {
150
+ if (doesImplementAccountTheme) {
151
+ const keycloakVersionRange = (() => {
152
+ if (keycloakMajorVersionNumber <= 21) {
153
+ return "21-and-below" as const;
154
+ }
155
+
156
+ assert(keycloakMajorVersionNumber !== 22);
157
+
158
+ if (keycloakMajorVersionNumber === 23) {
159
+ return "23" as const;
160
+ }
161
+
162
+ return "24-and-above" as const;
163
+ })();
164
+
165
+ assert<
166
+ Equals<typeof keycloakVersionRange, KeycloakVersionRange.WithAccountTheme>
167
+ >();
168
+
169
+ return keycloakVersionRange;
170
+ } else {
171
+ const keycloakVersionRange = (() => {
172
+ if (keycloakMajorVersionNumber <= 21) {
173
+ return "21-and-below" as const;
174
+ }
175
+
176
+ return "22-and-above" as const;
177
+ })();
178
+
179
+ assert<
180
+ Equals<
181
+ typeof keycloakVersionRange,
182
+ KeycloakVersionRange.WithoutAccountTheme
183
+ >
184
+ >();
185
+
186
+ return keycloakVersionRange;
187
+ }
188
+ })();
189
+
190
+ const { jarFileBasename } = getJarFileBasename({ keycloakVersionRange });
191
+
192
+ console.log(`Using Keycloak ${chalk.bold(jarFileBasename)}`);
193
+
194
+ try {
195
+ child_process.execSync(`docker rm --force ${containerName}`, {
196
+ stdio: "ignore"
197
+ });
198
+ } catch {}
199
+
200
+ const realmJsonFilePath = await (async () => {
201
+ if (cliCommandOptions.realmJsonFilePath !== undefined) {
202
+ console.log(
203
+ chalk.green(
204
+ `Using realm json file: ${cliCommandOptions.realmJsonFilePath}`
205
+ )
206
+ );
207
+
208
+ return getAbsoluteAndInOsFormatPath({
209
+ pathIsh: cliCommandOptions.realmJsonFilePath,
210
+ cwd: process.cwd()
211
+ });
212
+ }
213
+
214
+ const dirPath = pathJoin(
215
+ getThisCodebaseRootDirPath(),
216
+ "src",
217
+ "bin",
218
+ "start-keycloak"
219
+ );
220
+
221
+ const filePath = pathJoin(
222
+ dirPath,
223
+ `myrealm-realm-${keycloakMajorVersionNumber}.json`
224
+ );
225
+
226
+ if (fs.existsSync(filePath)) {
227
+ return filePath;
228
+ }
229
+
230
+ console.log(
231
+ `${chalk.yellow(
232
+ `Keycloakify do not have a realm configuration for Keycloak ${keycloakMajorVersionNumber} yet.`
233
+ )}`
234
+ );
235
+
236
+ console.log(chalk.cyan("Select what configuration to use:"));
237
+
238
+ const { value } = await cliSelect<string>({
239
+ values: [
240
+ ...fs
241
+ .readdirSync(dirPath)
242
+ .filter(fileBasename => fileBasename.endsWith(".json")),
243
+ "none"
244
+ ]
245
+ }).catch(() => {
246
+ process.exit(-1);
247
+ });
248
+
249
+ if (value === "none") {
250
+ return undefined;
251
+ }
252
+
253
+ return pathJoin(dirPath, value);
254
+ })();
255
+
256
+ const jarFilePath = pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename);
257
+
258
+ let doLinkAccountV1Theme = false;
259
+
260
+ await extractArchive({
261
+ archiveFilePath: jarFilePath,
262
+ onArchiveFile: async ({ relativeFilePathInArchive, readFile, writeFile }) => {
263
+ for (const themeName of buildOptions.themeNames) {
264
+ if (
265
+ relativeFilePathInArchive ===
266
+ pathJoin("theme", themeName, "account", "theme.properties")
267
+ ) {
268
+ if (
269
+ (await readFile())
270
+ .toString("utf8")
271
+ .includes(`parent=${accountV1ThemeName}`)
272
+ ) {
273
+ doLinkAccountV1Theme = true;
274
+ }
275
+
276
+ await writeFile({
277
+ filePath: pathJoin(
278
+ buildOptions.keycloakifyBuildDirPath,
279
+ "src",
280
+ "main",
281
+ "resources",
282
+ "theme",
283
+ themeName,
284
+ "account",
285
+ "theme.properties"
286
+ )
287
+ });
288
+ }
289
+ }
290
+ }
291
+ });
292
+
293
+ const spawnArgs = [
294
+ "docker",
295
+ [
296
+ "run",
297
+ ...["-p", `${cliCommandOptions.port}:8080`],
298
+ ...["--name", containerName],
299
+ ...["-e", "KEYCLOAK_ADMIN=admin"],
300
+ ...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
301
+ ...(realmJsonFilePath === undefined
302
+ ? []
303
+ : [
304
+ "-v",
305
+ `${realmJsonFilePath}:/opt/keycloak/data/import/myrealm-realm.json`
306
+ ]),
307
+ ...["-v", `${jarFilePath}:/opt/keycloak/providers/keycloak-theme.jar`],
308
+ ...(keycloakMajorVersionNumber <= 20
309
+ ? ["-e", "JAVA_OPTS=-Dkeycloak.profile=preview"]
310
+ : []),
311
+ ...[
312
+ ...buildOptions.themeNames,
313
+ ...(doLinkAccountV1Theme ? [accountV1ThemeName] : [])
314
+ ]
315
+ .map(themeName => ({
316
+ localDirPath: pathJoin(
317
+ buildOptions.keycloakifyBuildDirPath,
318
+ "src",
319
+ "main",
320
+ "resources",
321
+ "theme",
322
+ themeName
323
+ ),
324
+ containerDirPath: `/opt/keycloak/themes/${themeName}`
325
+ }))
326
+ .map(({ localDirPath, containerDirPath }) => [
327
+ "-v",
328
+ `${localDirPath}:${containerDirPath}:rw`
329
+ ])
330
+ .flat(),
331
+ `quay.io/keycloak/keycloak:${keycloakVersion}`,
332
+ "start-dev",
333
+ ...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24
334
+ ? ["--features=declarative-user-profile"]
335
+ : []),
336
+ ...(realmJsonFilePath === undefined ? [] : ["--import-realm"])
337
+ ],
338
+ {
339
+ cwd: buildOptions.keycloakifyBuildDirPath
340
+ }
341
+ ] as const;
342
+
343
+ const child = child_process.spawn(...spawnArgs);
344
+
345
+ child.stdout.on("data", data => process.stdout.write(data));
346
+
347
+ child.stderr.on("data", data => process.stderr.write(data));
348
+
349
+ child.on("exit", process.exit);
350
+
351
+ const srcDirPath = pathJoin(buildOptions.reactAppRootDirPath, "src");
352
+
353
+ {
354
+ const handler = async (data: Buffer) => {
355
+ if (!data.toString("utf8").includes("Listening on: http://0.0.0.0:8080")) {
356
+ return;
357
+ }
358
+
359
+ child.stdout.off("data", handler);
360
+
361
+ await new Promise(resolve => setTimeout(resolve, 1_000));
362
+
363
+ console.log(
364
+ [
365
+ "",
366
+ `${chalk.green("Your theme is accessible at:")}`,
367
+ `${chalk.green("➜")} ${chalk.cyan.bold(
368
+ "https://my-theme.keycloakify.dev/"
369
+ )}`,
370
+ "",
371
+ "You can login with the following credentials:",
372
+ `- username: ${chalk.cyan.bold("testuser")}`,
373
+ `- password: ${chalk.cyan.bold("password123")}`,
374
+ "",
375
+ `Keycloak Admin console: ${chalk.cyan.bold(
376
+ `http://localhost:${cliCommandOptions.port}`
377
+ )}`,
378
+ `- user: ${chalk.cyan.bold("admin")}`,
379
+ `- password: ${chalk.cyan.bold("admin")}`,
380
+ "",
381
+ `Watching for changes in ${chalk.bold(
382
+ `.${pathSep}${pathRelative(process.cwd(), srcDirPath)}`
383
+ )}`
384
+ ].join("\n")
385
+ );
386
+ };
387
+
388
+ child.stdout.on("data", handler);
389
+ }
390
+
391
+ {
392
+ const runBuildKeycloakTheme = runExclusive.build(async () => {
393
+ console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
394
+
395
+ {
396
+ const dResult = new Deferred<{ isSuccess: boolean }>();
397
+
398
+ const child = child_process.spawn("npx", ["vite", "build"], {
399
+ cwd: buildOptions.reactAppRootDirPath
400
+ });
401
+
402
+ child.stdout.on("data", data => {
403
+ if (data.toString("utf8").includes("gzip:")) {
404
+ return;
405
+ }
406
+
407
+ process.stdout.write(data);
408
+ });
409
+
410
+ child.stderr.on("data", data => process.stderr.write(data));
411
+
412
+ child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
413
+
414
+ const { isSuccess } = await dResult.pr;
415
+
416
+ if (!isSuccess) {
417
+ return;
418
+ }
419
+ }
420
+
421
+ {
422
+ const dResult = new Deferred<{ isSuccess: boolean }>();
423
+
424
+ const child = child_process.spawn("npx", ["keycloakify", "build"], {
425
+ cwd: buildOptions.reactAppRootDirPath,
426
+ env: {
427
+ ...process.env,
428
+ [skipBuildJarsEnvName]: "true"
429
+ }
430
+ });
431
+
432
+ child.stdout.on("data", data => process.stdout.write(data));
433
+
434
+ child.stderr.on("data", data => process.stderr.write(data));
435
+
436
+ child.on("exit", code => {
437
+ if (code !== 0) {
438
+ console.log(chalk.yellow("Theme not updated, build failed"));
439
+ return;
440
+ }
441
+
442
+ console.log(chalk.green("Rebuild done"));
443
+ });
444
+
445
+ child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
446
+
447
+ const { isSuccess } = await dResult.pr;
448
+
449
+ if (!isSuccess) {
450
+ return;
451
+ }
452
+ }
453
+ });
454
+
455
+ const { waitForDebounce } = waitForDebounceFactory({ delay: 400 });
456
+
457
+ chokidar
458
+ .watch([srcDirPath, pathJoin(getThisCodebaseRootDirPath(), "src")], {
459
+ ignoreInitial: true
460
+ })
461
+ .on("all", async () => {
462
+ await waitForDebounce();
463
+
464
+ runBuildKeycloakTheme();
465
+ });
466
+ }
467
+ }
@@ -12,35 +12,39 @@ export namespace SemVer {
12
12
  export type BumpType = (typeof bumpTypes)[number];
13
13
 
14
14
  export function parse(versionStr: string): SemVer {
15
- const match = versionStr.match(/^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-rc.([0-9]+))?$/);
15
+ const match = versionStr.match(
16
+ /^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-rc.([0-9]+))?$/
17
+ );
16
18
 
17
19
  if (!match) {
18
20
  throw new Error(`${versionStr} is not a valid semantic version`);
19
21
  }
20
22
 
21
23
  const semVer: Omit<SemVer, "parsedFrom"> = {
22
- "major": parseInt(match[1]),
23
- "minor": parseInt(match[2]),
24
- "patch": (() => {
24
+ major: parseInt(match[1]),
25
+ minor: parseInt(match[2]),
26
+ patch: (() => {
25
27
  const str = match[3];
26
28
 
27
29
  return str === undefined ? 0 : parseInt(str);
28
30
  })(),
29
31
  ...(() => {
30
32
  const str = match[4];
31
- return str === undefined ? {} : { "rc": parseInt(str) };
33
+ return str === undefined ? {} : { rc: parseInt(str) };
32
34
  })()
33
35
  };
34
36
 
35
37
  const initialStr = stringify(semVer);
36
38
 
37
39
  Object.defineProperty(semVer, "parsedFrom", {
38
- "enumerable": true,
39
- "get": function () {
40
+ enumerable: true,
41
+ get: function () {
40
42
  const currentStr = stringify(this);
41
43
 
42
44
  if (currentStr !== initialStr) {
43
- throw new Error(`SemVer.parsedFrom can't be read anymore, the version have been modified from ${initialStr} to ${currentStr}`);
45
+ throw new Error(
46
+ `SemVer.parsedFrom can't be read anymore, the version have been modified from ${initialStr} to ${currentStr}`
47
+ );
44
48
  }
45
49
 
46
50
  return versionStr;
@@ -51,7 +55,9 @@ export namespace SemVer {
51
55
  }
52
56
 
53
57
  export function stringify(v: Omit<SemVer, "parsedFrom">): string {
54
- return `${v.major}.${v.minor}.${v.patch}${v.rc === undefined ? "" : `-rc.${v.rc}`}`;
58
+ return `${v.major}.${v.minor}.${v.patch}${
59
+ v.rc === undefined ? "" : `-rc.${v.rc}`
60
+ }`;
55
61
  }
56
62
 
57
63
  /**
@@ -80,12 +86,25 @@ export namespace SemVer {
80
86
  console.log(compare(parse("3.0.0-rc.3"), parse("4.0.0")) === -1 )
81
87
  */
82
88
 
83
- export function bumpType(params: { versionBehind: string | SemVer; versionAhead: string | SemVer }): BumpType | "no bump" {
84
- const versionAhead = typeof params.versionAhead === "string" ? parse(params.versionAhead) : params.versionAhead;
85
- const versionBehind = typeof params.versionBehind === "string" ? parse(params.versionBehind) : params.versionBehind;
89
+ export function bumpType(params: {
90
+ versionBehind: string | SemVer;
91
+ versionAhead: string | SemVer;
92
+ }): BumpType | "no bump" {
93
+ const versionAhead =
94
+ typeof params.versionAhead === "string"
95
+ ? parse(params.versionAhead)
96
+ : params.versionAhead;
97
+ const versionBehind =
98
+ typeof params.versionBehind === "string"
99
+ ? parse(params.versionBehind)
100
+ : params.versionBehind;
86
101
 
87
102
  if (compare(versionBehind, versionAhead) === 1) {
88
- throw new Error(`Version regression ${stringify(versionBehind)} -> ${stringify(versionAhead)}`);
103
+ throw new Error(
104
+ `Version regression ${stringify(versionBehind)} -> ${stringify(
105
+ versionAhead
106
+ )}`
107
+ );
89
108
  }
90
109
 
91
110
  for (const level of ["major", "minor", "patch", "rc"] as const) {
@@ -1,4 +1,8 @@
1
- export function replaceAll(string: string, searchValue: string | RegExp, replaceValue: string): string {
1
+ export function replaceAll(
2
+ string: string,
3
+ searchValue: string | RegExp,
4
+ replaceValue: string
5
+ ): string {
2
6
  if ((string as any).replaceAll !== undefined) {
3
7
  return (string as any).replaceAll(searchValue, replaceValue);
4
8
  }
@@ -24,7 +28,10 @@ export function replaceAll(string: string, searchValue: string | RegExp, replace
24
28
 
25
29
  // Convert searchValue to string if it's not a string or RegExp
26
30
  var searchString = String(searchValue);
27
- var regexFromString = new RegExp(searchString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
31
+ var regexFromString = new RegExp(
32
+ searchString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
33
+ "g"
34
+ );
28
35
 
29
36
  return string.replace(regexFromString, replaceValue);
30
37
  }
@@ -16,7 +16,10 @@ const crawlRec = (dirPath: string, filePaths: string[]) => {
16
16
  };
17
17
 
18
18
  /** List all files in a given directory return paths relative to the dir_path */
19
- export function crawl(params: { dirPath: string; returnedPathsType: "absolute" | "relative to dirPath" }): string[] {
19
+ export function crawl(params: {
20
+ dirPath: string;
21
+ returnedPathsType: "absolute" | "relative to dirPath";
22
+ }): string[] {
20
23
  const { dirPath, returnedPathsType } = params;
21
24
 
22
25
  const filePaths: string[] = [];