keycloakify 9.4.0-rc.9 → 9.4.0

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 (103) hide show
  1. package/README.md +4 -0
  2. package/account/i18n/baseMessages/index.d.ts +358 -0
  3. package/account/i18n/baseMessages/index.js +2 -1
  4. package/account/i18n/baseMessages/index.js.map +1 -1
  5. package/account/pages/PageProps.d.ts +3 -2
  6. package/bin/constants.d.ts +1 -1
  7. package/bin/constants.js +1 -1
  8. package/bin/constants.js.map +1 -1
  9. package/bin/copy-keycloak-resources-to-public.d.ts +13 -1
  10. package/bin/copy-keycloak-resources-to-public.js +134 -68
  11. package/bin/copy-keycloak-resources-to-public.js.map +1 -1
  12. package/bin/download-builtin-keycloak-theme.d.ts +1 -0
  13. package/bin/download-builtin-keycloak-theme.js +2 -3
  14. package/bin/download-builtin-keycloak-theme.js.map +1 -1
  15. package/bin/{tools/downloadAndUnzip.d.ts → downloadAndUnzip.d.ts} +6 -6
  16. package/bin/downloadAndUnzip.js +255 -0
  17. package/bin/downloadAndUnzip.js.map +1 -0
  18. package/bin/eject-keycloak-page.js +6 -3
  19. package/bin/eject-keycloak-page.js.map +1 -1
  20. package/bin/keycloakify/buildOptions/buildOptions.d.ts +1 -0
  21. package/bin/keycloakify/buildOptions/buildOptions.js +54 -39
  22. package/bin/keycloakify/buildOptions/buildOptions.js.map +1 -1
  23. package/bin/keycloakify/buildOptions/getCacheDirPath.d.ts +5 -0
  24. package/bin/keycloakify/buildOptions/getCacheDirPath.js +22 -0
  25. package/bin/keycloakify/buildOptions/getCacheDirPath.js.map +1 -0
  26. package/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.d.ts +5 -0
  27. package/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.js +86 -0
  28. package/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.js.map +1 -0
  29. package/bin/keycloakify/buildOptions/getReactAppRootDirPath.d.ts +5 -0
  30. package/bin/keycloakify/buildOptions/getReactAppRootDirPath.js +26 -0
  31. package/bin/keycloakify/buildOptions/getReactAppRootDirPath.js.map +1 -0
  32. package/bin/keycloakify/buildOptions/resolvedViteConfig.d.ts +3 -4
  33. package/bin/keycloakify/buildOptions/resolvedViteConfig.js +4 -11
  34. package/bin/keycloakify/buildOptions/resolvedViteConfig.js.map +1 -1
  35. package/bin/keycloakify/generateTheme/bringInAccountV1.d.ts +1 -0
  36. package/bin/keycloakify/generateTheme/bringInAccountV1.js.map +1 -1
  37. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.d.ts +1 -0
  38. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.js.map +1 -1
  39. package/bin/keycloakify/generateTheme/generateTheme.d.ts +1 -0
  40. package/bin/keycloakify/generateTheme/generateTheme.js +7 -3
  41. package/bin/keycloakify/generateTheme/generateTheme.js.map +1 -1
  42. package/bin/keycloakify/keycloakify.js +7 -12
  43. package/bin/keycloakify/keycloakify.js.map +1 -1
  44. package/bin/tools/fetchProxyOptions.d.ts +5 -0
  45. package/bin/tools/fetchProxyOptions.js +172 -0
  46. package/bin/tools/fetchProxyOptions.js.map +1 -0
  47. package/bin/tools/fs.existsAsync.d.ts +1 -0
  48. package/bin/tools/fs.existsAsync.js +86 -0
  49. package/bin/tools/fs.existsAsync.js.map +1 -0
  50. package/bin/tools/getThisCodebaseRootDirPath.d.ts +1 -0
  51. package/bin/tools/{getProjectRoot.js → getThisCodebaseRootDirPath.js} +7 -7
  52. package/bin/tools/getThisCodebaseRootDirPath.js.map +1 -0
  53. package/bin/tools/grant-exec-perms.js +6 -4
  54. package/bin/tools/grant-exec-perms.js.map +1 -1
  55. package/bin/tools/readThisNpmProjectVersion.d.ts +1 -0
  56. package/bin/tools/readThisNpmProjectVersion.js +37 -0
  57. package/bin/tools/readThisNpmProjectVersion.js.map +1 -0
  58. package/lib/isStorybook.js +1 -2
  59. package/lib/isStorybook.js.map +1 -1
  60. package/login/i18n/baseMessages/index.d.ts +458 -0
  61. package/login/i18n/baseMessages/index.js +2 -1
  62. package/login/i18n/baseMessages/index.js.map +1 -1
  63. package/login/pages/PageProps.d.ts +3 -2
  64. package/package.json +41 -19
  65. package/src/account/i18n/baseMessages/index.ts +2 -1
  66. package/src/account/pages/PageProps.ts +3 -2
  67. package/src/bin/constants.ts +1 -1
  68. package/src/bin/copy-keycloak-resources-to-public.ts +75 -9
  69. package/src/bin/download-builtin-keycloak-theme.ts +3 -3
  70. package/src/bin/downloadAndUnzip.ts +203 -0
  71. package/src/bin/eject-keycloak-page.ts +6 -3
  72. package/src/bin/keycloakify/buildOptions/buildOptions.ts +28 -39
  73. package/src/bin/keycloakify/buildOptions/getCacheDirPath.ts +25 -0
  74. package/src/bin/keycloakify/buildOptions/getNpmWorkspaceRootDirPath.ts +49 -0
  75. package/src/bin/keycloakify/buildOptions/getReactAppRootDirPath.ts +23 -0
  76. package/src/bin/keycloakify/buildOptions/resolvedViteConfig.ts +7 -21
  77. package/src/bin/keycloakify/generateTheme/bringInAccountV1.ts +1 -0
  78. package/src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts +1 -0
  79. package/src/bin/keycloakify/generateTheme/generateTheme.ts +9 -2
  80. package/src/bin/keycloakify/keycloakify.ts +6 -14
  81. package/src/bin/tools/fetchProxyOptions.ts +73 -0
  82. package/src/bin/tools/fs.existsAsync.ts +11 -0
  83. package/src/bin/tools/{getProjectRoot.ts → getThisCodebaseRootDirPath.ts} +4 -4
  84. package/src/bin/tools/grant-exec-perms.ts +5 -3
  85. package/src/bin/tools/readThisNpmProjectVersion.ts +12 -0
  86. package/src/lib/isStorybook.ts +1 -3
  87. package/src/login/i18n/baseMessages/index.ts +2 -1
  88. package/src/login/pages/PageProps.ts +3 -2
  89. package/src/vite-plugin/vite-plugin.ts +48 -25
  90. package/vite-plugin/tsconfig.tsbuildinfo +1 -1
  91. package/vite-plugin/vite-plugin.d.ts +1 -2
  92. package/vite-plugin/vite-plugin.js +35 -20
  93. package/vite-plugin/vite-plugin.js.map +1 -1
  94. package/bin/keycloakify/buildOptions/getKeycloakifyBuildDirPath.d.ts +0 -7
  95. package/bin/keycloakify/buildOptions/getKeycloakifyBuildDirPath.js +0 -27
  96. package/bin/keycloakify/buildOptions/getKeycloakifyBuildDirPath.js.map +0 -1
  97. package/bin/tools/downloadAndUnzip.js +0 -445
  98. package/bin/tools/downloadAndUnzip.js.map +0 -1
  99. package/bin/tools/getProjectRoot.d.ts +0 -1
  100. package/bin/tools/getProjectRoot.js.map +0 -1
  101. package/src/bin/keycloakify/buildOptions/getKeycloakifyBuildDirPath.ts +0 -33
  102. package/src/bin/tools/downloadAndUnzip.ts +0 -301
  103. package/src/vite-plugin/config.json +0 -232
@@ -1,16 +1,17 @@
1
1
  //This code was automatically generated by running scripts/generate-i18n-messages.ts
2
2
  //PLEASE DO NOT EDIT MANUALLY
3
+ import * as en from "./en";
3
4
 
4
5
  export async function getMessages(currentLanguageTag: string) {
5
6
  const { default: messages } = await (() => {
6
7
  switch (currentLanguageTag) {
8
+ case "en": return en;
7
9
  case "ar": return import("./ar");
8
10
  case "ca": return import("./ca");
9
11
  case "cs": return import("./cs");
10
12
  case "da": return import("./da");
11
13
  case "de": return import("./de");
12
14
  case "el": return import("./el");
13
- case "en": return import("./en");
14
15
  case "es": return import("./es");
15
16
  case "fa": return import("./fa");
16
17
  case "fi": return import("./fi");
@@ -1,10 +1,11 @@
1
1
  import type { I18n } from "keycloakify/account/i18n";
2
2
  import type { TemplateProps, ClassKey } from "keycloakify/account/TemplateProps";
3
3
  import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
4
+ import type { KcContext } from "keycloakify/account/kcContext";
4
5
 
5
- export type PageProps<KcContext, I18nExtended extends I18n> = {
6
+ export type PageProps<NarowedKcContext = KcContext, I18nExtended extends I18n = I18n> = {
6
7
  Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
7
- kcContext: KcContext;
8
+ kcContext: NarowedKcContext;
8
9
  i18n: I18nExtended;
9
10
  doUseDefaultCss: boolean;
10
11
  classes?: Partial<Record<ClassKey, string>>;
@@ -2,7 +2,7 @@ export const nameOfTheGlobal = "kcContext";
2
2
  export const keycloak_resources = "keycloak-resources";
3
3
  export const resources_common = "resources-common";
4
4
  export const lastKeycloakVersionWithAccountV1 = "21.1.2";
5
- export const resolvedViteConfigJsonBasename = ".keycloakifyViteConfig.json";
5
+ export const resolvedViteConfigJsonBasename = "vite.json";
6
6
  export const basenameOfTheKeycloakifyResourcesDir = "build";
7
7
 
8
8
  export const themeTypes = ["login", "account"] as const;
@@ -1,17 +1,44 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { downloadKeycloakStaticResources } from "./keycloakify/generateTheme/downloadKeycloakStaticResources";
3
+ import { downloadKeycloakStaticResources, type BuildOptionsLike } from "./keycloakify/generateTheme/downloadKeycloakStaticResources";
4
4
  import { join as pathJoin, relative as pathRelative } from "path";
5
5
  import { readBuildOptions } from "./keycloakify/buildOptions";
6
6
  import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from "./constants";
7
+ import { readThisNpmProjectVersion } from "./tools/readThisNpmProjectVersion";
8
+ import { assert, type Equals } from "tsafe/assert";
7
9
  import * as fs from "fs";
10
+ import { rmSync } from "./tools/fs.rmSync";
8
11
 
9
- (async () => {
10
- const buildOptions = readBuildOptions({
11
- "processArgv": process.argv.slice(2)
12
+ export async function copyKeycloakResourcesToPublic(params: { processArgv: string[] }) {
13
+ const { processArgv } = params;
14
+
15
+ const buildOptions = readBuildOptions({ processArgv });
16
+
17
+ const destDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
18
+
19
+ const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
20
+
21
+ const { keycloakifyBuildinfoRaw } = generateKeycloakifyBuildinfoRaw({
22
+ destDirPath,
23
+ "keycloakifyVersion": readThisNpmProjectVersion(),
24
+ buildOptions
12
25
  });
13
26
 
14
- const reservedDirPath = pathJoin(buildOptions.publicDirPath, keycloak_resources);
27
+ skip_if_already_done: {
28
+ if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
29
+ break skip_if_already_done;
30
+ }
31
+
32
+ const keycloakifyBuildinfoRaw_previousRun = fs.readFileSync(keycloakifyBuildinfoFilePath).toString("utf8");
33
+
34
+ if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
35
+ break skip_if_already_done;
36
+ }
37
+
38
+ return;
39
+ }
40
+
41
+ rmSync(destDirPath, { "force": true, "recursive": true });
15
42
 
16
43
  for (const themeType of themeTypes) {
17
44
  await downloadKeycloakStaticResources({
@@ -24,13 +51,13 @@ import * as fs from "fs";
24
51
  }
25
52
  })(),
26
53
  themeType,
27
- "themeDirPath": reservedDirPath,
54
+ "themeDirPath": destDirPath,
28
55
  buildOptions
29
56
  });
30
57
  }
31
58
 
32
59
  fs.writeFileSync(
33
- pathJoin(reservedDirPath, "README.txt"),
60
+ pathJoin(destDirPath, "README.txt"),
34
61
  Buffer.from(
35
62
  // prettier-ignore
36
63
  [
@@ -42,5 +69,44 @@ import * as fs from "fs";
42
69
 
43
70
  fs.writeFileSync(pathJoin(buildOptions.publicDirPath, keycloak_resources, ".gitignore"), Buffer.from("*", "utf8"));
44
71
 
45
- console.log(`${pathRelative(buildOptions.reactAppRootDirPath, reservedDirPath)} directory created.`);
46
- })();
72
+ fs.writeFileSync(keycloakifyBuildinfoFilePath, Buffer.from(keycloakifyBuildinfoRaw, "utf8"));
73
+ }
74
+
75
+ export function generateKeycloakifyBuildinfoRaw(params: {
76
+ destDirPath: string;
77
+ keycloakifyVersion: string;
78
+ buildOptions: BuildOptionsLike & {
79
+ loginThemeResourcesFromKeycloakVersion: string;
80
+ };
81
+ }) {
82
+ const { destDirPath, keycloakifyVersion, buildOptions } = params;
83
+
84
+ const { cacheDirPath, npmWorkspaceRootDirPath, loginThemeResourcesFromKeycloakVersion, ...rest } = buildOptions;
85
+
86
+ assert<Equals<typeof rest, {}>>(true);
87
+
88
+ const keycloakifyBuildinfoRaw = JSON.stringify(
89
+ {
90
+ keycloakifyVersion,
91
+ "buildOptions": {
92
+ loginThemeResourcesFromKeycloakVersion,
93
+ "cacheDirPath": pathRelative(destDirPath, cacheDirPath),
94
+ "npmWorkspaceRootDirPath": pathRelative(destDirPath, npmWorkspaceRootDirPath)
95
+ }
96
+ },
97
+ null,
98
+ 2
99
+ );
100
+
101
+ return { keycloakifyBuildinfoRaw };
102
+ }
103
+
104
+ async function main() {
105
+ await copyKeycloakResourcesToPublic({
106
+ "processArgv": process.argv.slice(2)
107
+ });
108
+ }
109
+
110
+ if (require.main === module) {
111
+ main();
112
+ }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { join as pathJoin } from "path";
3
- import { downloadAndUnzip } from "./tools/downloadAndUnzip";
3
+ import { downloadAndUnzip } from "./downloadAndUnzip";
4
4
  import { promptKeycloakVersion } from "./promptKeycloakVersion";
5
5
  import { getLogger } from "./tools/logger";
6
6
  import { readBuildOptions, type BuildOptions } from "./keycloakify/buildOptions";
@@ -13,6 +13,7 @@ import { transformCodebase } from "./tools/transformCodebase";
13
13
 
14
14
  export type BuildOptionsLike = {
15
15
  cacheDirPath: string;
16
+ npmWorkspaceRootDirPath: string;
16
17
  };
17
18
 
18
19
  assert<BuildOptions extends BuildOptionsLike ? true : false>();
@@ -21,11 +22,10 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
21
22
  const { keycloakVersion, destDirPath, buildOptions } = params;
22
23
 
23
24
  await downloadAndUnzip({
24
- "doUseCache": true,
25
- "cacheDirPath": buildOptions.cacheDirPath,
26
25
  destDirPath,
27
26
  "url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
28
27
  "specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
28
+ buildOptions,
29
29
  "preCacheTransform": {
30
30
  "actionCacheId": "npm install and build",
31
31
  "action": async ({ destDirPath }) => {
@@ -0,0 +1,203 @@
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 "./keycloakify/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_remot_cache_if_admin: {
120
+ const githubToken = process.env["KEYCLOAKIFY_ADMIN_GITHUB_PERSONAL_ACCESS_TOKEN"];
121
+
122
+ if (githubToken === undefined) {
123
+ break upload_to_remot_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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { getProjectRoot } from "./tools/getProjectRoot";
3
+ import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
4
4
  import cliSelect from "cli-select";
5
5
  import { loginThemePageIds, accountThemePageIds, type LoginThemePageId, type AccountThemePageId } from "./keycloakify/generateFtl";
6
6
  import { capitalize } from "tsafe/capitalize";
@@ -11,11 +11,14 @@ import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
11
11
  import { assert, Equals } from "tsafe/assert";
12
12
  import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
13
13
  import { themeTypes, type ThemeType } from "./constants";
14
+ import { getReactAppRootDirPath } from "./keycloakify/buildOptions/getReactAppRootDirPath";
14
15
 
15
16
  (async () => {
16
17
  console.log("Select a theme type");
17
18
 
18
- const reactAppRootDirPath = process.cwd();
19
+ const { reactAppRootDirPath } = getReactAppRootDirPath({
20
+ "processArgv": process.argv.slice(2)
21
+ });
19
22
 
20
23
  const { value: themeType } = await cliSelect<ThemeType>({
21
24
  "values": [...themeTypes]
@@ -55,7 +58,7 @@ import { themeTypes, type ThemeType } from "./constants";
55
58
  process.exit(-1);
56
59
  }
57
60
 
58
- await writeFile(targetFilePath, await readFile(pathJoin(getProjectRoot(), "src", themeType, "pages", pageBasename)));
61
+ await writeFile(targetFilePath, await readFile(pathJoin(getThisCodebaseRootDirPath(), "src", themeType, "pages", pageBasename)));
59
62
 
60
63
  console.log(`${pathRelative(process.cwd(), targetFilePath)} created`);
61
64
  })();
@@ -4,7 +4,10 @@ import { join as pathJoin } from "path";
4
4
  import parseArgv from "minimist";
5
5
  import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
6
6
  import { readResolvedViteConfig } from "./resolvedViteConfig";
7
- import { getKeycloakifyBuildDirPath } from "./getKeycloakifyBuildDirPath";
7
+ import * as fs from "fs";
8
+ import { getCacheDirPath } from "./getCacheDirPath";
9
+ import { getReactAppRootDirPath } from "./getReactAppRootDirPath";
10
+ import { getNpmWorkspaceRootDirPath } from "./getNpmWorkspaceRootDirPath";
8
11
 
9
12
  /** Consolidated build option gathered form CLI arguments and config in package.json */
10
13
  export type BuildOptions = {
@@ -28,34 +31,24 @@ export type BuildOptions = {
28
31
  urlPathname: string | undefined;
29
32
  assetsDirPath: string;
30
33
  doBuildRetrocompatAccountTheme: boolean;
34
+ npmWorkspaceRootDirPath: string;
31
35
  };
32
36
 
33
37
  export function readBuildOptions(params: { processArgv: string[] }): BuildOptions {
34
38
  const { processArgv } = params;
35
39
 
36
- const argv = parseArgv(processArgv);
40
+ const { reactAppRootDirPath } = getReactAppRootDirPath({ processArgv });
37
41
 
38
- const reactAppRootDirPath = (() => {
39
- const arg = argv["project"] ?? argv["p"];
42
+ const { cacheDirPath } = getCacheDirPath({ reactAppRootDirPath });
40
43
 
41
- if (typeof arg !== "string") {
42
- return process.cwd();
43
- }
44
+ const { resolvedViteConfig } = readResolvedViteConfig({ cacheDirPath });
44
45
 
45
- return getAbsoluteAndInOsFormatPath({
46
- "pathIsh": arg,
47
- "cwd": process.cwd()
48
- });
49
- })();
46
+ if (resolvedViteConfig === undefined && fs.existsSync(pathJoin(reactAppRootDirPath, "vite.config.ts"))) {
47
+ throw new Error("Keycloakify's Vite plugin output not found");
48
+ }
50
49
 
51
50
  const parsedPackageJson = readParsedPackageJson({ reactAppRootDirPath });
52
51
 
53
- const { resolvedViteConfig } =
54
- readResolvedViteConfig({
55
- "parsedPackageJson_keycloakify_keycloakifyBuildDirPath": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath,
56
- reactAppRootDirPath
57
- }) ?? {};
58
-
59
52
  const themeNames = (() => {
60
53
  if (parsedPackageJson.keycloakify?.themeName === undefined) {
61
54
  return [
@@ -73,12 +66,6 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
73
66
  return parsedPackageJson.keycloakify.themeName;
74
67
  })();
75
68
 
76
- const { keycloakifyBuildDirPath } = getKeycloakifyBuildDirPath({
77
- "parsedPackageJson_keycloakify_keycloakifyBuildDirPath": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath,
78
- reactAppRootDirPath,
79
- "bundler": resolvedViteConfig !== undefined ? "vite" : "webpack"
80
- });
81
-
82
69
  const reactAppBuildDirPath = (() => {
83
70
  webpack: {
84
71
  if (resolvedViteConfig !== undefined) {
@@ -98,6 +85,10 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
98
85
  return pathJoin(reactAppRootDirPath, resolvedViteConfig.buildDir);
99
86
  })();
100
87
 
88
+ const argv = parseArgv(processArgv);
89
+
90
+ const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({ reactAppRootDirPath });
91
+
101
92
  return {
102
93
  "bundler": resolvedViteConfig !== undefined ? "vite" : "webpack",
103
94
  "isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false,
@@ -124,7 +115,16 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
124
115
  "loginThemeResourcesFromKeycloakVersion": parsedPackageJson.keycloakify?.loginThemeResourcesFromKeycloakVersion ?? "11.0.3",
125
116
  reactAppRootDirPath,
126
117
  reactAppBuildDirPath,
127
- keycloakifyBuildDirPath,
118
+ "keycloakifyBuildDirPath": (() => {
119
+ if (parsedPackageJson.keycloakify?.keycloakifyBuildDirPath !== undefined) {
120
+ return getAbsoluteAndInOsFormatPath({
121
+ "pathIsh": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath,
122
+ "cwd": reactAppRootDirPath
123
+ });
124
+ }
125
+
126
+ return resolvedViteConfig?.buildDir === undefined ? "build_keycloak" : `${resolvedViteConfig.buildDir}_keycloak`;
127
+ })(),
128
128
  "publicDirPath": (() => {
129
129
  webpack: {
130
130
  if (resolvedViteConfig !== undefined) {
@@ -143,19 +143,7 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
143
143
 
144
144
  return pathJoin(reactAppRootDirPath, resolvedViteConfig.publicDir);
145
145
  })(),
146
- "cacheDirPath": pathJoin(
147
- (() => {
148
- if (process.env.XDG_CACHE_HOME !== undefined) {
149
- return getAbsoluteAndInOsFormatPath({
150
- "pathIsh": process.env.XDG_CACHE_HOME,
151
- "cwd": reactAppRootDirPath
152
- });
153
- }
154
-
155
- return pathJoin(reactAppRootDirPath, "node_modules", ".cache");
156
- })(),
157
- "keycloakify"
158
- ),
146
+ cacheDirPath,
159
147
  "urlPathname": (() => {
160
148
  webpack: {
161
149
  if (resolvedViteConfig !== undefined) {
@@ -191,6 +179,7 @@ export function readBuildOptions(params: { processArgv: string[] }): BuildOption
191
179
 
192
180
  return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir);
193
181
  })(),
194
- "doBuildRetrocompatAccountTheme": parsedPackageJson.keycloakify?.doBuildRetrocompatAccountTheme ?? true
182
+ "doBuildRetrocompatAccountTheme": parsedPackageJson.keycloakify?.doBuildRetrocompatAccountTheme ?? true,
183
+ npmWorkspaceRootDirPath
195
184
  };
196
185
  }
@@ -0,0 +1,25 @@
1
+ import { join as pathJoin } from "path";
2
+ import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
3
+ import { getNpmWorkspaceRootDirPath } from "./getNpmWorkspaceRootDirPath";
4
+
5
+ export function getCacheDirPath(params: { reactAppRootDirPath: string }) {
6
+ const { reactAppRootDirPath } = params;
7
+
8
+ const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({ reactAppRootDirPath });
9
+
10
+ const cacheDirPath = pathJoin(
11
+ (() => {
12
+ if (process.env.XDG_CACHE_HOME !== undefined) {
13
+ return getAbsoluteAndInOsFormatPath({
14
+ "pathIsh": process.env.XDG_CACHE_HOME,
15
+ "cwd": reactAppRootDirPath
16
+ });
17
+ }
18
+
19
+ return pathJoin(npmWorkspaceRootDirPath, "node_modules", ".cache");
20
+ })(),
21
+ "keycloakify"
22
+ );
23
+
24
+ return { cacheDirPath };
25
+ }
@@ -0,0 +1,49 @@
1
+ import * as child_process from "child_process";
2
+ import { join as pathJoin, resolve as pathResolve, sep as pathSep } from "path";
3
+ import { assert } from "tsafe/assert";
4
+
5
+ let cache:
6
+ | {
7
+ reactAppRootDirPath: string;
8
+ npmWorkspaceRootDirPath: string;
9
+ }
10
+ | undefined = undefined;
11
+
12
+ export function getNpmWorkspaceRootDirPath(params: { reactAppRootDirPath: string }) {
13
+ const { reactAppRootDirPath } = params;
14
+
15
+ use_cache: {
16
+ if (cache === undefined || cache.reactAppRootDirPath !== reactAppRootDirPath) {
17
+ break use_cache;
18
+ }
19
+
20
+ const { npmWorkspaceRootDirPath } = cache;
21
+
22
+ return { npmWorkspaceRootDirPath };
23
+ }
24
+
25
+ const npmWorkspaceRootDirPath = (function callee(depth: number): string {
26
+ const cwd = pathResolve(pathJoin(...[reactAppRootDirPath, ...Array(depth).fill("..")]));
27
+
28
+ try {
29
+ child_process.execSync("npm config get", { cwd: cwd });
30
+ } catch (error) {
31
+ if (String(error).includes("ENOWORKSPACES")) {
32
+ assert(cwd !== pathSep, "NPM workspace not found");
33
+
34
+ return callee(depth + 1);
35
+ }
36
+
37
+ throw error;
38
+ }
39
+
40
+ return cwd;
41
+ })(0);
42
+
43
+ cache = {
44
+ reactAppRootDirPath,
45
+ npmWorkspaceRootDirPath
46
+ };
47
+
48
+ return { npmWorkspaceRootDirPath };
49
+ }
@@ -0,0 +1,23 @@
1
+ import parseArgv from "minimist";
2
+ import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
3
+
4
+ export function getReactAppRootDirPath(params: { processArgv: string[] }) {
5
+ const { processArgv } = params;
6
+
7
+ const argv = parseArgv(processArgv);
8
+
9
+ const reactAppRootDirPath = (() => {
10
+ const arg = argv["project"] ?? argv["p"];
11
+
12
+ if (typeof arg !== "string") {
13
+ return process.cwd();
14
+ }
15
+
16
+ return getAbsoluteAndInOsFormatPath({
17
+ "pathIsh": arg,
18
+ "cwd": process.cwd()
19
+ });
20
+ })();
21
+
22
+ return { reactAppRootDirPath };
23
+ }