keycloakify 11.8.45 → 11.8.47-rc.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 (98) hide show
  1. package/bin/254.index.js +283 -0
  2. package/bin/30.index.js +25 -0
  3. package/bin/{864.index.js → 309.index.js} +6944 -28439
  4. package/bin/311.index.js +198 -0
  5. package/bin/{313.index.js → 355.index.js} +101 -147
  6. package/bin/{84.index.js → 502.index.js} +147 -101
  7. package/bin/626.index.js +194 -0
  8. package/bin/{698.index.js → 656.index.js} +101 -147
  9. package/bin/675.index.js +177 -0
  10. package/bin/69.index.js +1 -1
  11. package/bin/762.index.js +1475 -0
  12. package/bin/780.index.js +4 -2
  13. package/bin/786.index.js +115 -0
  14. package/bin/895.index.js +21501 -0
  15. package/bin/{618.index.js → 932.index.js} +117 -163
  16. package/bin/949.index.js +1 -1
  17. package/bin/97.index.js +537 -4
  18. package/bin/init/index.d.ts +1 -0
  19. package/bin/init/init.d.ts +3 -0
  20. package/bin/init/setupEslint.d.ts +4 -0
  21. package/bin/init/setupVitePluginIfNeeded.d.ts +4 -0
  22. package/bin/initialize-login-theme.d.ts +4 -0
  23. package/bin/main.js +19 -30
  24. package/bin/shared/customHandler.d.ts +1 -1
  25. package/bin/shared/customHandler.js.map +1 -1
  26. package/package.json +82 -66
  27. package/src/bin/init/index.ts +1 -0
  28. package/src/bin/init/init.ts +354 -0
  29. package/src/bin/init/setupEslint.ts +80 -0
  30. package/src/bin/init/setupVitePluginIfNeeded.ts +143 -0
  31. package/src/bin/initialize-account-theme/initialize-account-theme.ts +4 -0
  32. package/src/bin/initialize-login-theme.ts +323 -0
  33. package/src/bin/main.ts +14 -0
  34. package/src/bin/shared/buildContext.ts +2 -37
  35. package/src/bin/shared/customHandler.ts +3 -1
  36. package/src/bin/sync-extensions/extensionModuleMeta.ts +89 -73
  37. package/src/bin/sync-extensions/managedGitignoreFiles.ts +32 -2
  38. package/vite-plugin/index.js +1 -24
  39. package/bin/433.index.js +0 -140
  40. /package/res/public/keycloakify-dev-resources/account/{account/css → css}/account.css +0 -0
  41. /package/res/public/keycloakify-dev-resources/account/{account/img → img}/icon-sidebar-active.png +0 -0
  42. /package/res/public/keycloakify-dev-resources/account/{account/img → img}/keycloak-logo.png +0 -0
  43. /package/res/public/keycloakify-dev-resources/account/{account/img → img}/logo.png +0 -0
  44. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/img/favicon.ico +0 -0
  45. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/css/patternfly-additions.min.css +0 -0
  46. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/css/patternfly.min.css +0 -0
  47. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2 +0 -0
  48. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2 +0 -0
  49. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2 +0 -0
  50. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2 +0 -0
  51. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf +0 -0
  52. /package/res/public/keycloakify-dev-resources/account/{account/resources-common → resources-common}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.woff +0 -0
  53. /package/res/public/keycloakify-dev-resources/login/{login/css → css}/login.css +0 -0
  54. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-error-arrow-down.png +0 -0
  55. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-error-sign.png +0 -0
  56. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-success-arrow-down.png +0 -0
  57. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-success-sign.png +0 -0
  58. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-warning-arrow-down.png +0 -0
  59. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/feedback-warning-sign.png +0 -0
  60. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/keycloak-bg.png +0 -0
  61. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/keycloak-logo-text.png +0 -0
  62. /package/res/public/keycloakify-dev-resources/login/{login/img → img}/keycloak-logo.png +0 -0
  63. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/authChecker.js +0 -0
  64. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/common.js +0 -0
  65. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/kcMultivalued.js +0 -0
  66. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/kcNumberFormat.js +0 -0
  67. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/kcNumberUnFormat.js +0 -0
  68. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/menu-button-links.js +0 -0
  69. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/passkeysConditionalAuth.js +0 -0
  70. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/passwordVisibility.js +0 -0
  71. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/userProfile.js +0 -0
  72. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/webauthnAuthenticate.js +0 -0
  73. /package/res/public/keycloakify-dev-resources/login/{login/js → js}/webauthnRegister.js +0 -0
  74. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/img/favicon.ico +0 -0
  75. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/lib/pficon/pficon.css +0 -0
  76. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/lib/pficon/pficon.woff2 +0 -0
  77. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/@patternfly/patternfly/assets/fonts/RedHatDisplay/RedHatDisplay-Bold.woff +0 -0
  78. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/@patternfly/patternfly/assets/fonts/RedHatDisplay/RedHatDisplay-Bold.woff2 +0 -0
  79. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-bold.woff +0 -0
  80. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-bold.woff2 +0 -0
  81. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/@patternfly/patternfly/patternfly.min.css +0 -0
  82. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/jquery/dist/jquery.min.js +0 -0
  83. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/css/patternfly-additions.min.css +0 -0
  84. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/css/patternfly.min.css +0 -0
  85. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.ttf +0 -0
  86. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff +0 -0
  87. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2 +0 -0
  88. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2 +0 -0
  89. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2 +0 -0
  90. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2 +0 -0
  91. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  92. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  93. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.woff2 +0 -0
  94. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf +0 -0
  95. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.woff +0 -0
  96. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2 +0 -0
  97. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/patternfly/dist/img/bg-login.jpg +0 -0
  98. /package/res/public/keycloakify-dev-resources/login/{login/resources-common → resources-common}/node_modules/rfc4648/lib/rfc4648.js +0 -0
@@ -0,0 +1,323 @@
1
+ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
2
+ import { dirname as pathDirname, join as pathJoin } from "path";
3
+ import type { BuildContext } from "./shared/buildContext";
4
+ import * as fs from "fs/promises";
5
+ import { assert, is, type Equals } from "tsafe/assert";
6
+ import { id } from "tsafe/id";
7
+ import { addSyncExtensionsToPostinstallScript } from "./shared/addSyncExtensionsToPostinstallScript";
8
+ import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
9
+ import { npmInstall } from "./tools/npmInstall";
10
+ import * as child_process from "child_process";
11
+ import { z } from "zod";
12
+ import chalk from "chalk";
13
+ import cliSelect from "cli-select";
14
+ import { existsAsync } from "./tools/fs.existsAsync";
15
+
16
+ export async function command(params: { buildContext: BuildContext }) {
17
+ const { buildContext } = params;
18
+
19
+ const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
20
+ commandName: "initialize-login-theme",
21
+ buildContext
22
+ });
23
+
24
+ if (hasBeenHandled) {
25
+ return;
26
+ }
27
+
28
+ if (
29
+ buildContext.implementedThemeTypes.login.isImplemented ||
30
+ buildContext.implementedThemeTypes.login.isImplemented_native
31
+ ) {
32
+ console.warn(chalk.red("There is already a login theme in your project"));
33
+
34
+ process.exit(-1);
35
+ }
36
+
37
+ const parsedPackageJson = await (async () => {
38
+ type ParsedPackageJson = {
39
+ scripts?: Record<string, string | undefined>;
40
+ dependencies?: Record<string, string | undefined>;
41
+ devDependencies?: Record<string, string | undefined>;
42
+ };
43
+
44
+ const zParsedPackageJson = (() => {
45
+ type TargetType = ParsedPackageJson;
46
+
47
+ const zTargetType = z.object({
48
+ scripts: z.record(z.union([z.string(), z.undefined()])).optional(),
49
+ dependencies: z.record(z.union([z.string(), z.undefined()])).optional(),
50
+ devDependencies: z.record(z.union([z.string(), z.undefined()])).optional()
51
+ });
52
+
53
+ assert<Equals<z.infer<typeof zTargetType>, TargetType>>;
54
+
55
+ return id<z.ZodType<TargetType>>(zTargetType);
56
+ })();
57
+ const parsedPackageJson = JSON.parse(
58
+ (await fs.readFile(buildContext.packageJsonFilePath)).toString("utf8")
59
+ );
60
+
61
+ zParsedPackageJson.parse(parsedPackageJson);
62
+
63
+ assert(is<ParsedPackageJson>(parsedPackageJson));
64
+
65
+ return parsedPackageJson;
66
+ })();
67
+
68
+ addSyncExtensionsToPostinstallScript({
69
+ parsedPackageJson,
70
+ buildContext
71
+ });
72
+
73
+ const doInstallStories = await (async () => {
74
+ console.log(chalk.cyan(`\nDo you want to install the Stories?`));
75
+
76
+ const YES = "Yes (Recommended)";
77
+ const NO = "No";
78
+
79
+ const { value } = await cliSelect({
80
+ values: [YES, NO]
81
+ }).catch(() => {
82
+ process.exit(-1);
83
+ });
84
+
85
+ console.log(`${value}\n`);
86
+
87
+ return value === YES;
88
+ })();
89
+
90
+ install_storybook: {
91
+ if (!doInstallStories) {
92
+ break install_storybook;
93
+ }
94
+
95
+ if (buildContext.bundler !== "vite") {
96
+ break install_storybook;
97
+ }
98
+
99
+ if (
100
+ Object.keys({
101
+ ...parsedPackageJson.dependencies,
102
+ ...parsedPackageJson.devDependencies
103
+ }).includes("storybook")
104
+ ) {
105
+ break install_storybook;
106
+ }
107
+
108
+ (parsedPackageJson.scripts ??= {})["storybook"] = "storybook dev -p 6006";
109
+ parsedPackageJson.scripts["build-storybook"] = "storybook build";
110
+
111
+ (parsedPackageJson.devDependencies ??= {})["storybook"] = "^9.0.4";
112
+ parsedPackageJson.devDependencies["@storybook/react-vite"] = "^9.0.4";
113
+
114
+ const files: { relativeFilePath: string; fileContent: string }[] = [
115
+ {
116
+ relativeFilePath: "main.ts",
117
+ fileContent: [
118
+ `import type { StorybookConfig } from "@storybook/react-vite";`,
119
+ ``,
120
+ `const config: StorybookConfig = {`,
121
+ ` stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],`,
122
+ ` addons: [],`,
123
+ ` framework: {`,
124
+ ` name: "@storybook/react-vite",`,
125
+ ` options: {}`,
126
+ ` },`,
127
+ `};`,
128
+ `export default config;`,
129
+ ``
130
+ ].join("\n")
131
+ },
132
+ {
133
+ relativeFilePath: "preview.ts",
134
+ fileContent: storybookPreviewTsFileContent
135
+ }
136
+ ];
137
+
138
+ for (let { relativeFilePath, fileContent } of files) {
139
+ const filePath = pathJoin(
140
+ buildContext.projectDirPath,
141
+ ".storybook",
142
+ relativeFilePath
143
+ );
144
+
145
+ {
146
+ const dirPath = pathDirname(filePath);
147
+
148
+ if (!(await existsAsync(dirPath))) {
149
+ await fs.mkdir(dirPath, { recursive: true });
150
+ }
151
+ }
152
+
153
+ run_prettier: {
154
+ if (!(await getIsPrettierAvailable())) {
155
+ break run_prettier;
156
+ }
157
+
158
+ fileContent = await runPrettier({
159
+ filePath: filePath,
160
+ sourceCode: fileContent
161
+ });
162
+ }
163
+
164
+ await fs.writeFile(filePath, Buffer.from(fileContent, "utf8"));
165
+ }
166
+ }
167
+
168
+ {
169
+ const moduleName = "@keycloakify/login-ui";
170
+
171
+ const latestVersion = await getModuleLatestVersion({ moduleName });
172
+
173
+ (parsedPackageJson.dependencies ??= {})[moduleName] = `~${latestVersion}`;
174
+
175
+ if (parsedPackageJson.devDependencies !== undefined) {
176
+ delete parsedPackageJson.devDependencies[moduleName];
177
+ }
178
+ }
179
+
180
+ install_stories: {
181
+ if (!doInstallStories) {
182
+ break install_stories;
183
+ }
184
+
185
+ const moduleName = "@keycloakify/login-ui-storybook";
186
+
187
+ const latestVersion = await getModuleLatestVersion({ moduleName });
188
+
189
+ (parsedPackageJson.devDependencies ??= {})[moduleName] = `~${latestVersion}`;
190
+
191
+ delete parsedPackageJson.dependencies[moduleName];
192
+ }
193
+
194
+ {
195
+ let sourceCode = JSON.stringify(parsedPackageJson, null, 2);
196
+
197
+ if (await getIsPrettierAvailable()) {
198
+ sourceCode = await runPrettier({
199
+ sourceCode,
200
+ filePath: buildContext.packageJsonFilePath
201
+ });
202
+ }
203
+
204
+ await fs.writeFile(
205
+ buildContext.packageJsonFilePath,
206
+ Buffer.from(sourceCode, "utf8")
207
+ );
208
+ }
209
+
210
+ await npmInstall({
211
+ packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath)
212
+ });
213
+ }
214
+
215
+ async function getModuleLatestVersion(params: { moduleName: string }) {
216
+ const { moduleName } = params;
217
+
218
+ const versions = ((): string[] => {
219
+ const cmdOutput = child_process
220
+ .execSync(`npm show ${moduleName} versions --json`)
221
+ .toString("utf8")
222
+ .trim();
223
+
224
+ const versions = JSON.parse(cmdOutput) as string | string[];
225
+
226
+ // NOTE: Bug in some older npm versions
227
+ if (typeof versions === "string") {
228
+ return [versions];
229
+ }
230
+
231
+ return versions;
232
+ })();
233
+
234
+ const version = versions.reverse().filter(version => !version.includes("-"))[0];
235
+
236
+ assert(version !== undefined);
237
+
238
+ return version;
239
+ }
240
+
241
+ const storybookPreviewTsFileContent = [
242
+ `import type { Preview } from "@storybook/react-vite";`,
243
+ ``,
244
+ `const preview: Preview = {`,
245
+ ` parameters: {`,
246
+ ` controls: {`,
247
+ ` matchers: {`,
248
+ ` color: /(background|color)$/i,`,
249
+ ` date: /Date$/i`,
250
+ ` }`,
251
+ ` },`,
252
+ ` options: {`,
253
+ ` storySort: (a, b)=> {`,
254
+ ``,
255
+ ` const orderedPagesPrefix = [`,
256
+ ` "Introduction",`,
257
+ ` "login/login.ftl",`,
258
+ ` "login/register.ftl",`,
259
+ ` "login/terms.ftl",`,
260
+ ` "login/error.ftl",`,
261
+ ` "login/code.ftl",`,
262
+ ` "login/delete-account-confirm.ftl",`,
263
+ ` "login/delete-credential.ftl",`,
264
+ ` "login/frontchannel-logout.ftl",`,
265
+ ` "login/idp-review-user-profile.ftl",`,
266
+ ` "login/info.ftl",`,
267
+ ` "login/login-config-totp.ftl",`,
268
+ ` "login/login-idp-link-confirm.ftl",`,
269
+ ` "login/login-idp-link-email.ftl",`,
270
+ ` "login/login-oauth-grant.ftl",`,
271
+ ` "login/login-otp.ftl",`,
272
+ ` "login/login-page-expired.ftl",`,
273
+ ` "login/login-password.ftl",`,
274
+ ` "login/login-reset-otp.ftl",`,
275
+ ` "login/login-reset-password.ftl",`,
276
+ ` "login/login-update-password.ftl",`,
277
+ ` "login/login-update-profile.ftl",`,
278
+ ` "login/login-username.ftl",`,
279
+ ` "login/login-verify-email.ftl",`,
280
+ ` "login/login-x509-info.ftl",`,
281
+ ` "login/logout-confirm.ftl",`,
282
+ ` "login/saml-post-form.ftl",`,
283
+ ` "login/select-authenticator.ftl",`,
284
+ ` "login/update-email.ftl",`,
285
+ ` "login/webauthn-authenticate.ftl",`,
286
+ ` "login/webauthn-error.ftl",`,
287
+ ` "login/webauthn-register.ftl",`,
288
+ ` "login/login-oauth2-device-verify-user-code.ftl",`,
289
+ ` "login/login-recovery-authn-code-config.ftl",`,
290
+ ` "login/login-recovery-authn-code-input.ftl",`,
291
+ ` "account/account.ftl",`,
292
+ ` "account/password.ftl",`,
293
+ ` "account/federatedIdentity.ftl",`,
294
+ ` "account/log.ftl",`,
295
+ ` "account/sessions.ftl",`,
296
+ ` "account/totp.ftl"`,
297
+ ` ];`,
298
+ ``,
299
+ ` function getHardCodedWeight(title) {`,
300
+ ` for (let i = 0; i < orderedPagesPrefix.length; i++) {`,
301
+ ` if (`,
302
+ ` title`,
303
+ ` .toLowerCase()`,
304
+ ` .startsWith(orderedPagesPrefix[i].toLowerCase())`,
305
+ ` ) {`,
306
+ ` return orderedPagesPrefix.length - i;`,
307
+ ` }`,
308
+ ` }`,
309
+ ``,
310
+ ` return 0;`,
311
+ ` }`,
312
+ ``,
313
+ ` return getHardCodedWeight(b.title) - getHardCodedWeight(a.title);`,
314
+ ``,
315
+ ` }`,
316
+ ``,
317
+ ` }`,
318
+ ` }`,
319
+ `};`,
320
+ ``,
321
+ `export default preview;`,
322
+ ``
323
+ ].join("\n");
package/src/bin/main.ts CHANGED
@@ -146,6 +146,20 @@ program
146
146
  }
147
147
  });
148
148
 
149
+ program
150
+ .command({
151
+ name: "init",
152
+ description: "(BETA) Initialize a new theme type (login/account/admin/email)"
153
+ })
154
+ .task({
155
+ skip,
156
+ handler: async ({ projectDirPath }) => {
157
+ const { command } = await import("./init");
158
+
159
+ await command({ projectDirPath: projectDirPath ?? process.cwd() });
160
+ }
161
+ });
162
+
149
163
  program
150
164
  .command({
151
165
  name: "eject-page",
@@ -2,7 +2,6 @@ import { parse as urlParse } from "url";
2
2
  import {
3
3
  join as pathJoin,
4
4
  sep as pathSep,
5
- relative as pathRelative,
6
5
  resolve as pathResolve,
7
6
  dirname as pathDirname
8
7
  } from "path";
@@ -13,8 +12,7 @@ import { assert, type Equals, is } from "tsafe/assert";
13
12
  import * as child_process from "child_process";
14
13
  import {
15
14
  VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES,
16
- BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME,
17
- THEME_TYPES
15
+ BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME
18
16
  } from "./constants";
19
17
  import type { KeycloakVersionRange } from "./KeycloakVersionRange";
20
18
  import { exclude } from "tsafe";
@@ -167,40 +165,7 @@ export function getBuildContext(params: {
167
165
  return { themeSrcDirPath };
168
166
  }
169
167
 
170
- {
171
- const basenames = fs.readdirSync(srcDirPath);
172
-
173
- for (const basename of basenames) {
174
- const path = pathJoin(srcDirPath, basename);
175
-
176
- if (!fs.statSync(path).isFile()) {
177
- continue;
178
- }
179
-
180
- if (fs.readFileSync(path).toString("utf8").includes("./kc.gen")) {
181
- return { themeSrcDirPath: srcDirPath };
182
- }
183
- }
184
- }
185
-
186
- for (const themeType of [...THEME_TYPES, "email"]) {
187
- if (!fs.existsSync(pathJoin(srcDirPath, themeType))) {
188
- continue;
189
- }
190
- return { themeSrcDirPath: srcDirPath };
191
- }
192
-
193
- console.log(
194
- chalk.red(
195
- [
196
- `Can't locate your Keycloak theme source directory in .${pathSep}${pathRelative(process.cwd(), srcDirPath)}`,
197
- `Make sure to either use the Keycloakify CLI in the root of your Keycloakify project or use the --project CLI option`,
198
- `If you are collocating your Keycloak theme with your app you must have a directory named '${KEYCLOAK_THEME}' or '${KEYCLOAK_THEME.replace(/-/g, "_")}' in your 'src' directory`
199
- ].join("\n")
200
- )
201
- );
202
-
203
- process.exit(1);
168
+ return { themeSrcDirPath: srcDirPath };
204
169
  })();
205
170
 
206
171
  const { resolvedViteConfig } = (() => {
@@ -10,11 +10,13 @@ export type CommandName =
10
10
  | "update-kc-gen"
11
11
  | "eject-page"
12
12
  | "add-story"
13
+ | "initialize-login-theme"
13
14
  | "initialize-account-theme"
14
15
  | "initialize-admin-theme"
15
16
  | "initialize-admin-theme"
16
17
  | "initialize-email-theme"
17
- | "copy-keycloak-resources-to-public";
18
+ | "copy-keycloak-resources-to-public"
19
+ | "init";
18
20
 
19
21
  export type ApiVersion = "v1";
20
22
 
@@ -206,92 +206,108 @@ export async function getExtensionModuleMetas(params: {
206
206
  })();
207
207
 
208
208
  const extensionModuleMetas = await Promise.all(
209
- installedExtensionModules.map(
210
- async ({
211
- moduleName,
212
- version,
213
- peerDependencies,
214
- dirPath
215
- }): Promise<ExtensionModuleMeta> => {
216
- use_cache: {
217
- const extensionModuleMeta_cache =
218
- extensionModuleMetas_cacheUpToDate.find(
219
- extensionModuleMeta =>
220
- extensionModuleMeta.moduleName === moduleName
221
- );
222
-
223
- if (extensionModuleMeta_cache === undefined) {
224
- break use_cache;
225
- }
226
-
227
- return extensionModuleMeta_cache;
228
- }
209
+ [...installedExtensionModules]
210
+ .sort((a, b) => a.moduleName.localeCompare(b.moduleName))
211
+ .map(
212
+ async ({
213
+ moduleName,
214
+ version,
215
+ peerDependencies,
216
+ dirPath
217
+ }): Promise<ExtensionModuleMeta> => {
218
+ use_cache: {
219
+ const extensionModuleMeta_cache =
220
+ extensionModuleMetas_cacheUpToDate.find(
221
+ extensionModuleMeta =>
222
+ extensionModuleMeta.moduleName === moduleName
223
+ );
224
+
225
+ if (extensionModuleMeta_cache === undefined) {
226
+ break use_cache;
227
+ }
229
228
 
230
- const files: ExtensionModuleMeta["files"] = [];
229
+ return extensionModuleMeta_cache;
230
+ }
231
231
 
232
- await crawlAsync({
233
- dirPath: pathJoin(dirPath, KEYCLOAK_THEME),
234
- returnedPathsType: "relative to dirPath",
235
- onFileFound: async fileRelativePath_fromReservedDir => {
236
- const isPublic = fileRelativePath_fromReservedDir.startsWith(
237
- `public${pathSep}`
238
- );
232
+ const files: ExtensionModuleMeta["files"] = [];
233
+
234
+ await crawlAsync({
235
+ dirPath: pathJoin(dirPath, KEYCLOAK_THEME),
236
+ returnedPathsType: "relative to dirPath",
237
+ onFileFound: async fileRelativePath_fromReservedDir => {
238
+ const isPublic = fileRelativePath_fromReservedDir.startsWith(
239
+ `public${pathSep}`
240
+ );
241
+
242
+ const fileRelativePath = isPublic
243
+ ? pathRelative("public", fileRelativePath_fromReservedDir)
244
+ : fileRelativePath_fromReservedDir;
245
+
246
+ const sourceCode =
247
+ await getExtensionModuleFileSourceCodeReadyToBeCopied({
248
+ buildContext,
249
+ isPublic,
250
+ fileRelativePath,
251
+ isOwnershipAction: false,
252
+ extensionModuleDirPath: dirPath,
253
+ extensionModuleName: moduleName,
254
+ extensionModuleVersion: version
255
+ });
256
+
257
+ const hash = computeHash(sourceCode);
258
+
259
+ const copyableFilePath = pathJoin(
260
+ pathDirname(cacheFilePath),
261
+ KEYCLOAK_THEME,
262
+ fileRelativePath_fromReservedDir
263
+ );
264
+
265
+ {
266
+ const dirPath = pathDirname(copyableFilePath);
267
+
268
+ if (!(await existsAsync(dirPath))) {
269
+ await fsPr.mkdir(dirPath, { recursive: true });
270
+ }
271
+ }
239
272
 
240
- const fileRelativePath = isPublic
241
- ? pathRelative("public", fileRelativePath_fromReservedDir)
242
- : fileRelativePath_fromReservedDir;
273
+ fsPr.writeFile(copyableFilePath, sourceCode);
243
274
 
244
- const sourceCode =
245
- await getExtensionModuleFileSourceCodeReadyToBeCopied({
246
- buildContext,
275
+ files.push({
247
276
  isPublic,
248
277
  fileRelativePath,
249
- isOwnershipAction: false,
250
- extensionModuleDirPath: dirPath,
251
- extensionModuleName: moduleName,
252
- extensionModuleVersion: version
278
+ hash,
279
+ copyableFilePath
253
280
  });
254
-
255
- const hash = computeHash(sourceCode);
256
-
257
- const copyableFilePath = pathJoin(
258
- pathDirname(cacheFilePath),
259
- KEYCLOAK_THEME,
260
- fileRelativePath_fromReservedDir
261
- );
262
-
263
- {
264
- const dirPath = pathDirname(copyableFilePath);
265
-
266
- if (!(await existsAsync(dirPath))) {
267
- await fsPr.mkdir(dirPath, { recursive: true });
268
- }
269
281
  }
282
+ });
270
283
 
271
- fsPr.writeFile(copyableFilePath, sourceCode);
284
+ {
285
+ const getId = (file: {
286
+ isPublic: boolean;
287
+ fileRelativePath: string;
288
+ }) =>
289
+ `${file.isPublic ? "public" : "src"} - ${file.fileRelativePath}`;
272
290
 
273
- files.push({
274
- isPublic,
275
- fileRelativePath,
276
- hash,
277
- copyableFilePath
278
- });
291
+ files.sort((a, b) => getId(a).localeCompare(getId(b)));
279
292
  }
280
- });
281
293
 
282
- return id<ExtensionModuleMeta>({
283
- moduleName,
284
- version,
285
- files,
286
- peerDependencies: Object.fromEntries(
287
- Object.entries(peerDependencies).filter(
288
- ([moduleName]) =>
289
- !isAmong(["react", "@types/react"], moduleName)
294
+ return id<ExtensionModuleMeta>({
295
+ moduleName,
296
+ version,
297
+ files,
298
+ peerDependencies: Object.fromEntries(
299
+ Object.entries(peerDependencies)
300
+ .filter(
301
+ ([moduleName]) =>
302
+ !isAmong(["react", "@types/react"], moduleName)
303
+ )
304
+ .sort(([moduleName_a], [moduleName_b]) =>
305
+ moduleName_a.localeCompare(moduleName_b)
306
+ )
290
307
  )
291
- )
292
- });
293
- }
294
- )
308
+ });
309
+ }
310
+ )
295
311
  );
296
312
 
297
313
  update_cache: {
@@ -73,10 +73,16 @@ export async function writeManagedGitignoreFiles(params: {
73
73
  ...ownedFilesRelativePaths_ctx
74
74
  .map(({ fileRelativePath }) => fileRelativePath)
75
75
  .map(fileRelativePath => fileRelativePath.split(pathSep).join("/"))
76
+ .sort(posixPathCompareFn)
76
77
  .map(line => `# ${line}`),
77
78
  DELIMITER_END,
78
79
  ``,
79
- ...extensionModuleMetas_ctx
80
+ ...[...extensionModuleMetas_ctx]
81
+ .sort((a, b) => {
82
+ const n = a.moduleName.length - b.moduleName.length;
83
+
84
+ return n !== 0 ? n : a.moduleName.localeCompare(b.moduleName);
85
+ })
80
86
  .map(extensionModuleMeta => [
81
87
  `# === ${extensionModuleMeta.moduleName} v${extensionModuleMeta.version} ===`,
82
88
  ...extensionModuleMeta.files
@@ -90,7 +96,8 @@ export async function writeManagedGitignoreFiles(params: {
90
96
  .map(
91
97
  fileRelativePath =>
92
98
  `/${fileRelativePath.split(pathSep).join("/").replace(/^\.\//, "")}`
93
- ),
99
+ )
100
+ .sort(posixPathCompareFn),
94
101
 
95
102
  ``
96
103
  ])
@@ -187,3 +194,26 @@ export async function readManagedGitignoresFile(params: {
187
194
 
188
195
  return { ownedFilesRelativePaths };
189
196
  }
197
+
198
+ function posixPathCompareFn(a: string, b: string) {
199
+ const aParts = a.split("/");
200
+ const bParts = b.split("/");
201
+
202
+ const diff = aParts.length - bParts.length;
203
+
204
+ if (diff !== 0) {
205
+ return diff;
206
+ }
207
+
208
+ const len = Math.min(aParts.length, bParts.length);
209
+
210
+ for (let i = 0; i < len; i++) {
211
+ const cmp = aParts[i].localeCompare(bParts[i], undefined, {
212
+ numeric: true,
213
+ sensitivity: "base"
214
+ });
215
+ if (cmp !== 0) return cmp;
216
+ }
217
+
218
+ return 0;
219
+ }