keycloakify 7.16.0-rc.1 → 8.0.0-rc.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 (143) hide show
  1. package/README.md +3 -3
  2. package/account/Template.js +5 -3
  3. package/account/Template.js.map +1 -1
  4. package/account/kcContext/kcContextMocks.js +0 -4
  5. package/account/kcContext/kcContextMocks.js.map +1 -1
  6. package/bin/copy-keycloak-resources-to-public.js +3 -2
  7. package/bin/copy-keycloak-resources-to-public.js.map +1 -1
  8. package/bin/download-builtin-keycloak-theme.d.ts +1 -1
  9. package/bin/download-builtin-keycloak-theme.js +84 -11
  10. package/bin/download-builtin-keycloak-theme.js.map +1 -1
  11. package/bin/initialize-email-theme.js +6 -5
  12. package/bin/initialize-email-theme.js.map +1 -1
  13. package/bin/keycloakify/BuildOptions.d.ts +18 -36
  14. package/bin/keycloakify/BuildOptions.js +71 -145
  15. package/bin/keycloakify/BuildOptions.js.map +1 -1
  16. package/bin/keycloakify/generateFtl/generateFtl.d.ts +5 -25
  17. package/bin/keycloakify/generateFtl/generateFtl.js +7 -11
  18. package/bin/keycloakify/generateFtl/generateFtl.js.map +1 -1
  19. package/bin/keycloakify/{generateJavaStackFiles/generateJavaStackFiles.d.ts → generateJavaStackFiles.d.ts} +3 -4
  20. package/bin/keycloakify/generateJavaStackFiles.js +103 -0
  21. package/bin/keycloakify/generateJavaStackFiles.js.map +1 -0
  22. package/bin/keycloakify/generateStartKeycloakTestingContainer.js +1 -5
  23. package/bin/keycloakify/generateStartKeycloakTestingContainer.js.map +1 -1
  24. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.d.ts +5 -1
  25. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.js +24 -6
  26. package/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.js.map +1 -1
  27. package/bin/keycloakify/generateTheme/generateTheme.d.ts +8 -28
  28. package/bin/keycloakify/generateTheme/generateTheme.js +22 -33
  29. package/bin/keycloakify/generateTheme/generateTheme.js.map +1 -1
  30. package/bin/keycloakify/generateTheme/readFieldNameUsage.js +1 -2
  31. package/bin/keycloakify/generateTheme/readFieldNameUsage.js.map +1 -1
  32. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.d.ts +17 -0
  33. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.js +172 -0
  34. package/bin/keycloakify/generateTheme/readStaticResourcesUsage.js.map +1 -0
  35. package/bin/keycloakify/keycloakify.js +17 -17
  36. package/bin/keycloakify/keycloakify.js.map +1 -1
  37. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.d.ts +0 -11
  38. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.js +6 -27
  39. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.js.map +1 -1
  40. package/bin/keycloakify/replacers/replaceImportsInCssCode.js +1 -7
  41. package/bin/keycloakify/replacers/replaceImportsInCssCode.js.map +1 -1
  42. package/bin/keycloakify/replacers/replaceImportsInInlineCssCode.d.ts +3 -13
  43. package/bin/keycloakify/replacers/replaceImportsInInlineCssCode.js +5 -12
  44. package/bin/keycloakify/replacers/replaceImportsInInlineCssCode.js.map +1 -1
  45. package/bin/tools/downloadAndUnzip.d.ts +13 -2
  46. package/bin/tools/downloadAndUnzip.js +81 -15
  47. package/bin/tools/downloadAndUnzip.js.map +1 -1
  48. package/bin/tools/transformCodebase.d.ts +1 -0
  49. package/bin/tools/transformCodebase.js +6 -5
  50. package/bin/tools/transformCodebase.js.map +1 -1
  51. package/bin/tools/unzip.d.ts +2 -1
  52. package/bin/tools/unzip.js +129 -11
  53. package/bin/tools/unzip.js.map +1 -1
  54. package/lib/usePrepareTemplate.d.ts +0 -5
  55. package/lib/usePrepareTemplate.js +4 -10
  56. package/lib/usePrepareTemplate.js.map +1 -1
  57. package/login/Template.js +5 -6
  58. package/login/Template.js.map +1 -1
  59. package/login/kcContext/KcContext.d.ts +1 -1
  60. package/login/kcContext/kcContextMocks.js +1 -5
  61. package/login/kcContext/kcContextMocks.js.map +1 -1
  62. package/login/pages/Login.js +16 -20
  63. package/login/pages/Login.js.map +1 -1
  64. package/login/pages/LoginOtp.js +1 -2
  65. package/login/pages/LoginOtp.js.map +1 -1
  66. package/package.json +11 -77
  67. package/src/account/Template.tsx +5 -3
  68. package/src/account/kcContext/kcContextMocks.ts +0 -4
  69. package/src/bin/copy-keycloak-resources-to-public.ts +3 -2
  70. package/src/bin/download-builtin-keycloak-theme.ts +71 -14
  71. package/src/bin/initialize-email-theme.ts +6 -4
  72. package/src/bin/keycloakify/BuildOptions.ts +99 -192
  73. package/src/bin/keycloakify/generateFtl/generateFtl.ts +16 -45
  74. package/src/bin/keycloakify/generateJavaStackFiles.ts +84 -0
  75. package/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +1 -6
  76. package/src/bin/keycloakify/generateTheme/downloadKeycloakStaticResources.ts +30 -6
  77. package/src/bin/keycloakify/generateTheme/generateTheme.ts +34 -69
  78. package/src/bin/keycloakify/generateTheme/readFieldNameUsage.ts +1 -4
  79. package/src/bin/keycloakify/generateTheme/readStaticResourcesUsage.ts +83 -0
  80. package/src/bin/keycloakify/keycloakify.ts +2 -1
  81. package/src/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.ts +9 -51
  82. package/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts +1 -9
  83. package/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts +5 -29
  84. package/src/bin/tools/downloadAndUnzip.ts +99 -15
  85. package/src/bin/tools/transformCodebase.ts +7 -6
  86. package/src/bin/tools/unzip.ts +57 -8
  87. package/src/lib/usePrepareTemplate.ts +13 -24
  88. package/src/login/Template.tsx +5 -6
  89. package/src/login/kcContext/KcContext.ts +1 -1
  90. package/src/login/kcContext/kcContextMocks.ts +1 -5
  91. package/src/login/pages/Login.tsx +31 -34
  92. package/src/login/pages/LoginOtp.tsx +1 -2
  93. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountPages.java +0 -33
  94. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProvider.java +0 -76
  95. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProviderFactory.java +0 -25
  96. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountSpi.java +0 -50
  97. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProvider.java +0 -424
  98. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProviderFactory.java +0 -51
  99. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/Templates.java +0 -51
  100. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountBean.java +0 -91
  101. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountFederatedIdentityBean.java +0 -157
  102. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ApplicationsBean.java +0 -258
  103. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AuthorizationBean.java +0 -515
  104. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/FeaturesBean.java +0 -56
  105. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/LogBean.java +0 -95
  106. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/PasswordBean.java +0 -34
  107. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/RealmBean.java +0 -75
  108. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ReferrerBean.java +0 -38
  109. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/SessionsBean.java +0 -93
  110. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/TotpBean.java +0 -125
  111. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/UrlBean.java +0 -121
  112. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/AccountUrls.java +0 -115
  113. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormService.java +0 -1320
  114. package/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormServiceFactory.java +0 -64
  115. package/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.js +0 -298
  116. package/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.js.map +0 -1
  117. package/bin/keycloakify/generateJavaStackFiles/index.d.ts +0 -1
  118. package/bin/keycloakify/generateJavaStackFiles/index.js +0 -18
  119. package/bin/keycloakify/generateJavaStackFiles/index.js.map +0 -1
  120. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountPages.java +0 -33
  121. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProvider.java +0 -76
  122. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountProviderFactory.java +0 -25
  123. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/AccountSpi.java +0 -50
  124. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProvider.java +0 -424
  125. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/FreeMarkerAccountProviderFactory.java +0 -51
  126. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/Templates.java +0 -51
  127. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountBean.java +0 -91
  128. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AccountFederatedIdentityBean.java +0 -157
  129. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ApplicationsBean.java +0 -258
  130. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/AuthorizationBean.java +0 -515
  131. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/FeaturesBean.java +0 -56
  132. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/LogBean.java +0 -95
  133. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/PasswordBean.java +0 -34
  134. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/RealmBean.java +0 -75
  135. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/ReferrerBean.java +0 -38
  136. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/SessionsBean.java +0 -93
  137. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/TotpBean.java +0 -125
  138. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/forms/account/freemarker/model/UrlBean.java +0 -121
  139. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/AccountUrls.java +0 -115
  140. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormService.java +0 -1320
  141. package/src/bin/keycloakify/generateJavaStackFiles/account-v1-java/services/resources/account/AccountFormServiceFactory.java +0 -64
  142. package/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts +0 -249
  143. package/src/bin/keycloakify/generateJavaStackFiles/index.ts +0 -1
@@ -12,46 +12,20 @@ import { downloadKeycloakStaticResources } from "./downloadKeycloakStaticResourc
12
12
  import { readFieldNameUsage } from "./readFieldNameUsage";
13
13
  import { readExtraPagesNames } from "./readExtraPageNames";
14
14
  import { generateMessageProperties } from "./generateMessageProperties";
15
- import { accountV1Keycloak } from "../generateJavaStackFiles/generateJavaStackFiles";
16
-
17
- export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
18
-
19
- export namespace BuildOptionsLike {
20
- export type Common = {
21
- themeName: string;
22
- extraThemeProperties: string[] | undefined;
23
- isSilent: boolean;
24
- themeVersion: string;
25
- keycloakVersionDefaultAssets: string;
26
- };
27
-
28
- export type Standalone = Common & {
29
- isStandalone: true;
30
- urlPathname: string | undefined;
31
- };
32
-
33
- export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
34
-
35
- export namespace ExternalAssets {
36
- export type CommonExternalAssets = Common & {
37
- isStandalone: false;
38
- };
39
-
40
- export type SameDomain = CommonExternalAssets & {
41
- areAppAndKeycloakServerSharingSameDomain: true;
42
- };
43
-
44
- export type DifferentDomains = CommonExternalAssets & {
45
- areAppAndKeycloakServerSharingSameDomain: false;
46
- urlOrigin: string;
47
- urlPathname: string | undefined;
48
- };
49
- }
50
- }
15
+ import { readStaticResourcesUsage } from "./readStaticResourcesUsage";
16
+
17
+ export type BuildOptionsLike = {
18
+ themeName: string;
19
+ extraThemeProperties: string[] | undefined;
20
+ themeVersion: string;
21
+ keycloakVersionDefaultAssets: string;
22
+ urlPathname: string | undefined;
23
+ };
51
24
 
52
25
  assert<BuildOptions extends BuildOptionsLike ? true : false>();
53
26
 
54
27
  export async function generateTheme(params: {
28
+ projectDirPath: string;
55
29
  reactAppBuildDirPath: string;
56
30
  keycloakThemeBuildingDirPath: string;
57
31
  themeSrcDirPath: string;
@@ -59,7 +33,15 @@ export async function generateTheme(params: {
59
33
  buildOptions: BuildOptionsLike;
60
34
  keycloakifyVersion: string;
61
35
  }): Promise<void> {
62
- const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, themeSrcDirPath, keycloakifySrcDirPath, buildOptions, keycloakifyVersion } = params;
36
+ const {
37
+ projectDirPath,
38
+ reactAppBuildDirPath,
39
+ keycloakThemeBuildingDirPath,
40
+ themeSrcDirPath,
41
+ keycloakifySrcDirPath,
42
+ buildOptions,
43
+ keycloakifyVersion
44
+ } = params;
63
45
 
64
46
  const getThemeDirPath = (themeType: ThemeType | "email") =>
65
47
  pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
@@ -78,17 +60,16 @@ export async function generateTheme(params: {
78
60
  copy_app_resources_to_theme_path: {
79
61
  const isFirstPass = themeType.indexOf(themeType) === 0;
80
62
 
81
- if (!isFirstPass && !buildOptions.isStandalone) {
63
+ if (!isFirstPass) {
82
64
  break copy_app_resources_to_theme_path;
83
65
  }
84
66
 
85
67
  transformCodebase({
86
- "destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
68
+ "destDirPath": pathJoin(themeDirPath, "resources", "build"),
87
69
  "srcDirPath": reactAppBuildDirPath,
88
70
  "transformSourceCode": ({ filePath, sourceCode }) => {
89
71
  //NOTE: Prevent cycles, excludes the folder we generated for debug in public/
90
72
  if (
91
- buildOptions.isStandalone &&
92
73
  isInside({
93
74
  "dirPath": pathJoin(reactAppBuildDirPath, basenameOfKeycloakDirInPublicDir),
94
75
  filePath
@@ -98,10 +79,6 @@ export async function generateTheme(params: {
98
79
  }
99
80
 
100
81
  if (/\.css?$/i.test(filePath)) {
101
- if (!buildOptions.isStandalone) {
102
- return undefined;
103
- }
104
-
105
82
  const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
106
83
  "cssCode": sourceCode.toString("utf8")
107
84
  });
@@ -121,19 +98,14 @@ export async function generateTheme(params: {
121
98
  }
122
99
 
123
100
  if (/\.js?$/i.test(filePath)) {
124
- if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
125
- return undefined;
126
- }
127
-
128
101
  const { fixedJsCode } = replaceImportsFromStaticInJsCode({
129
- "jsCode": sourceCode.toString("utf8"),
130
- buildOptions
102
+ "jsCode": sourceCode.toString("utf8")
131
103
  });
132
104
 
133
105
  return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
134
106
  }
135
107
 
136
- return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
108
+ return { "modifiedSourceCode": sourceCode };
137
109
  }
138
110
  });
139
111
  }
@@ -198,10 +170,11 @@ export async function generateTheme(params: {
198
170
  }
199
171
 
200
172
  await downloadKeycloakStaticResources({
201
- "isSilent": buildOptions.isSilent,
173
+ projectDirPath,
202
174
  "keycloakVersion": buildOptions.keycloakVersionDefaultAssets,
203
175
  "themeDirPath": keycloakDirInPublicDir,
204
- themeType
176
+ themeType,
177
+ "usedResources": undefined
205
178
  });
206
179
 
207
180
  if (themeType !== themeTypes[0]) {
@@ -223,28 +196,20 @@ export async function generateTheme(params: {
223
196
  }
224
197
 
225
198
  await downloadKeycloakStaticResources({
226
- "isSilent": buildOptions.isSilent,
199
+ projectDirPath,
227
200
  "keycloakVersion": buildOptions.keycloakVersionDefaultAssets,
228
201
  themeDirPath,
229
- themeType
202
+ themeType,
203
+ "usedResources": readStaticResourcesUsage({
204
+ keycloakifySrcDirPath,
205
+ themeSrcDirPath,
206
+ themeType
207
+ })
230
208
  });
231
209
 
232
210
  fs.writeFileSync(
233
211
  pathJoin(themeDirPath, "theme.properties"),
234
- Buffer.from(
235
- [
236
- `parent=${(() => {
237
- switch (themeType) {
238
- case "login":
239
- return "keycloak";
240
- case "account":
241
- return accountV1Keycloak;
242
- }
243
- })()}`,
244
- ...(buildOptions.extraThemeProperties ?? [])
245
- ].join("\n\n"),
246
- "utf8"
247
- )
212
+ Buffer.from([`parent=keycloak`, ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
248
213
  );
249
214
  }
250
215
 
@@ -3,7 +3,6 @@ import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
3
3
  import { join as pathJoin } from "path";
4
4
  import * as fs from "fs";
5
5
  import type { ThemeType } from "../generateFtl";
6
- import { exclude } from "tsafe/exclude";
7
6
 
8
7
  /** Assumes the theme type exists */
9
8
  export function readFieldNameUsage(params: { keycloakifySrcDirPath: string; themeSrcDirPath: string; themeType: ThemeType }): string[] {
@@ -11,9 +10,7 @@ export function readFieldNameUsage(params: { keycloakifySrcDirPath: string; them
11
10
 
12
11
  const fieldNames: string[] = [];
13
12
 
14
- for (const srcDirPath of ([pathJoin(keycloakifySrcDirPath, themeType), pathJoin(themeSrcDirPath, themeType)] as const).filter(
15
- exclude(undefined)
16
- )) {
13
+ for (const srcDirPath of [pathJoin(keycloakifySrcDirPath, themeType), pathJoin(themeSrcDirPath, themeType)]) {
17
14
  const filePaths = crawl({ "dirPath": srcDirPath, "returnedPathsType": "absolute" }).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
18
15
 
19
16
  for (const filePath of filePaths) {
@@ -0,0 +1,83 @@
1
+ import { crawl } from "../../tools/crawl";
2
+ import { join as pathJoin } from "path";
3
+ import * as fs from "fs";
4
+ import type { ThemeType } from "../generateFtl";
5
+
6
+ /** Assumes the theme type exists */
7
+ export function readStaticResourcesUsage(params: { keycloakifySrcDirPath: string; themeSrcDirPath: string; themeType: ThemeType }): {
8
+ resourcesCommonFilePaths: string[];
9
+ resourcesFilePaths: string[];
10
+ } {
11
+ const { keycloakifySrcDirPath, themeSrcDirPath, themeType } = params;
12
+
13
+ const resourcesCommonFilePaths = new Set<string>();
14
+ const resourcesFilePaths = new Set<string>();
15
+
16
+ for (const srcDirPath of [pathJoin(keycloakifySrcDirPath, themeType), pathJoin(themeSrcDirPath, themeType)]) {
17
+ const filePaths = crawl({ "dirPath": srcDirPath, "returnedPathsType": "absolute" }).filter(filePath => /\.(ts|tsx|js|jsx)$/.test(filePath));
18
+
19
+ for (const filePath of filePaths) {
20
+ const rawSourceFile = fs.readFileSync(filePath).toString("utf8");
21
+
22
+ if (!rawSourceFile.includes("resourcesCommonPath") && !rawSourceFile.includes("resourcesPath")) {
23
+ continue;
24
+ }
25
+
26
+ const wrap = readPaths({ rawSourceFile });
27
+
28
+ wrap.resourcesCommonFilePaths.forEach(filePath => resourcesCommonFilePaths.add(filePath));
29
+ wrap.resourcesFilePaths.forEach(filePath => resourcesFilePaths.add(filePath));
30
+ }
31
+ }
32
+
33
+ return {
34
+ "resourcesCommonFilePaths": Array.from(resourcesCommonFilePaths),
35
+ "resourcesFilePaths": Array.from(resourcesFilePaths)
36
+ };
37
+ }
38
+
39
+ /** Exported for testing purpose */
40
+ export function readPaths(params: { rawSourceFile: string }): {
41
+ resourcesCommonFilePaths: string[];
42
+ resourcesFilePaths: string[];
43
+ } {
44
+ const { rawSourceFile } = params;
45
+
46
+ const resourcesCommonFilePaths = new Set<string>();
47
+ const resourcesFilePaths = new Set<string>();
48
+
49
+ for (const isCommon of [true, false]) {
50
+ const set = isCommon ? resourcesCommonFilePaths : resourcesFilePaths;
51
+
52
+ {
53
+ const regexp = new RegExp(`resources${isCommon ? "Common" : ""}Path\\s*}([^\`]+)\``, "g");
54
+
55
+ const matches = [...rawSourceFile.matchAll(regexp)];
56
+
57
+ for (const match of matches) {
58
+ const filePath = match[1];
59
+
60
+ set.add(filePath);
61
+ }
62
+ }
63
+
64
+ {
65
+ const regexp = new RegExp(`resources${isCommon ? "Common" : ""}Path\\s*[+,]\\s*["']([^"'\`]+)["'\`]`, "g");
66
+
67
+ const matches = [...rawSourceFile.matchAll(regexp)];
68
+
69
+ for (const match of matches) {
70
+ const filePath = match[1];
71
+
72
+ set.add(filePath);
73
+ }
74
+ }
75
+ }
76
+
77
+ const removePrefixSlash = (filePath: string) => (filePath.startsWith("/") ? filePath.slice(1) : filePath);
78
+
79
+ return {
80
+ "resourcesCommonFilePaths": Array.from(resourcesCommonFilePaths).map(removePrefixSlash),
81
+ "resourcesFilePaths": Array.from(resourcesFilePaths).map(removePrefixSlash)
82
+ };
83
+ }
@@ -30,6 +30,7 @@ export async function main() {
30
30
 
31
31
  for (const themeName of [buildOptions.themeName, ...buildOptions.extraThemeNames]) {
32
32
  await generateTheme({
33
+ projectDirPath,
33
34
  "keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
34
35
  themeSrcDirPath,
35
36
  "keycloakifySrcDirPath": pathJoin(keycloakifyDirPath, "src"),
@@ -48,7 +49,7 @@ export async function main() {
48
49
  });
49
50
  }
50
51
 
51
- const { jarFilePath } = await generateJavaStackFiles({
52
+ const { jarFilePath } = generateJavaStackFiles({
52
53
  "keycloakThemeBuildingDirPath": buildOptions.keycloakifyBuildDirPath,
53
54
  "implementedThemeTypes": (() => {
54
55
  const implementedThemeTypes = {
@@ -1,31 +1,6 @@
1
1
  import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
2
- import type { BuildOptions } from "../BuildOptions";
3
- import { assert } from "tsafe/assert";
4
- import { is } from "tsafe/is";
5
- import { Reflect } from "tsafe/Reflect";
6
2
 
7
- export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
8
-
9
- export namespace BuildOptionsLike {
10
- export type Standalone = {
11
- isStandalone: true;
12
- };
13
-
14
- export type ExternalAssets = {
15
- isStandalone: false;
16
- urlOrigin: string;
17
- };
18
- }
19
-
20
- {
21
- const buildOptions = Reflect<BuildOptions>();
22
-
23
- assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
24
-
25
- assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
26
- }
27
-
28
- export function replaceImportsFromStaticInJsCode(params: { jsCode: string; buildOptions: BuildOptionsLike }): { fixedJsCode: string } {
3
+ export function replaceImportsFromStaticInJsCode(params: { jsCode: string }): { fixedJsCode: string } {
29
4
  /*
30
5
  NOTE:
31
6
 
@@ -38,7 +13,7 @@ export function replaceImportsFromStaticInJsCode(params: { jsCode: string; build
38
13
  will always run in keycloak context.
39
14
  */
40
15
 
41
- const { jsCode, buildOptions } = params;
16
+ const { jsCode } = params;
42
17
 
43
18
  const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
44
19
  new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=function\\(([a-zA-Z]+)\\){return"static\\/${language}\\/"`, "g"),
@@ -46,40 +21,23 @@ export function replaceImportsFromStaticInJsCode(params: { jsCode: string; build
46
21
  ${n}[(function(){
47
22
  var pd= Object.getOwnPropertyDescriptor(${n}, "p");
48
23
  if( pd === undefined || pd.configurable ){
49
- ${
50
- buildOptions.isStandalone
51
- ? `
52
- Object.defineProperty(${n}, "p", {
53
- get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
54
- set: function (){}
55
- });
56
- `
57
- : `
58
- var p= "";
59
24
  Object.defineProperty(${n}, "p", {
60
- get: function() { return "${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : p; },
61
- set: function (value){ p = value;}
25
+ get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
26
+ set: function (){}
62
27
  });
63
- `
64
- }
65
28
  }
66
29
  return "${u}";
67
- })()] = function(${e}) { return "${buildOptions.isStandalone ? "/build/" : ""}static/${language}/"`
30
+ })()] = function(${e}) { return "${true ? "/build/" : ""}static/${language}/"`
68
31
  ];
69
32
 
70
33
  const fixedJsCode = jsCode
71
34
  .replace(...getReplaceArgs("js"))
72
35
  .replace(...getReplaceArgs("css"))
73
- .replace(/([a-zA-Z]+\.[a-zA-Z]+)\+"static\//g, (...[, group]) =>
74
- buildOptions.isStandalone
75
- ? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
76
- : `("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group}) + "static/`
77
- )
36
+ .replace(/[a-zA-Z]+\.[a-zA-Z]+\+"static\//g, `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`)
78
37
  //TODO: Write a test case for this
79
- .replace(/".chunk.css",([a-zA-Z])+=([a-zA-Z]+\.[a-zA-Z]+)\+([a-zA-Z]+),/, (...[, group1, group2, group3]) =>
80
- buildOptions.isStandalone
81
- ? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
82
- : `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group2}) + ${group3},`
38
+ .replace(
39
+ /".chunk.css",([a-zA-Z])+=[a-zA-Z]+\.[a-zA-Z]+\+([a-zA-Z]+),/,
40
+ (...[, group1, group2]) => `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group2},`
83
41
  );
84
42
 
85
43
  return { fixedJsCode };
@@ -1,20 +1,12 @@
1
1
  import * as crypto from "crypto";
2
2
  import type { BuildOptions } from "../BuildOptions";
3
3
  import { assert } from "tsafe/assert";
4
- import { is } from "tsafe/is";
5
- import { Reflect } from "tsafe/Reflect";
6
4
 
7
5
  export type BuildOptionsLike = {
8
6
  urlPathname: string | undefined;
9
7
  };
10
8
 
11
- {
12
- const buildOptions = Reflect<BuildOptions>();
13
-
14
- assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
15
-
16
- assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
17
- }
9
+ assert<BuildOptions extends BuildOptionsLike ? true : false>();
18
10
 
19
11
  export function replaceImportsInCssCode(params: { cssCode: string }): {
20
12
  fixedCssCode: string;
@@ -1,32 +1,11 @@
1
1
  import type { BuildOptions } from "../BuildOptions";
2
2
  import { assert } from "tsafe/assert";
3
- import { is } from "tsafe/is";
4
- import { Reflect } from "tsafe/Reflect";
5
3
 
6
- export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
4
+ export type BuildOptionsLike = {
5
+ urlPathname: string | undefined;
6
+ };
7
7
 
8
- export namespace BuildOptionsLike {
9
- export type Common = {
10
- urlPathname: string | undefined;
11
- };
12
-
13
- export type Standalone = Common & {
14
- isStandalone: true;
15
- };
16
-
17
- export type ExternalAssets = Common & {
18
- isStandalone: false;
19
- urlOrigin: string;
20
- };
21
- }
22
-
23
- {
24
- const buildOptions = Reflect<BuildOptions>();
25
-
26
- assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
27
-
28
- assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
29
- }
8
+ assert<BuildOptions extends BuildOptionsLike ? true : false>();
30
9
 
31
10
  export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOptions: BuildOptionsLike }): {
32
11
  fixedCssCode: string;
@@ -37,10 +16,7 @@ export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOp
37
16
  buildOptions.urlPathname === undefined
38
17
  ? /url\(["']?\/([^/][^)"']+)["']?\)/g
39
18
  : new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
40
- (...[, group]) =>
41
- `url(${
42
- buildOptions.isStandalone ? "${url.resourcesPath}/build/" + group : buildOptions.urlOrigin + (buildOptions.urlPathname ?? "/") + group
43
- })`
19
+ (...[, group]) => `url(\${url.resourcesPath}/build/${group})`
44
20
  );
45
21
 
46
22
  return { fixedCssCode };
@@ -1,18 +1,55 @@
1
1
  import { exec as execCallback } from "child_process";
2
2
  import { createHash } from "crypto";
3
- import { mkdir, readFile, stat, writeFile } from "fs/promises";
3
+ import { mkdir, readFile, stat, writeFile, unlink, rm } from "fs/promises";
4
4
  import fetch, { type FetchOptions } from "make-fetch-happen";
5
5
  import { dirname as pathDirname, join as pathJoin, resolve as pathResolve, sep as pathSep } from "path";
6
6
  import { assert } from "tsafe/assert";
7
7
  import { promisify } from "util";
8
- import { getProjectRoot } from "./getProjectRoot";
9
8
  import { transformCodebase } from "./transformCodebase";
10
- import { unzip } from "./unzip";
9
+ import { unzip, zip } from "./unzip";
11
10
 
12
11
  const exec = promisify(execCallback);
13
12
 
14
- function hash(s: string) {
15
- return createHash("sha256").update(s).digest("hex");
13
+ function generateFileNameFromURL(params: {
14
+ url: string;
15
+ preCacheTransform:
16
+ | {
17
+ actionCacheId: string;
18
+ actionFootprint: string;
19
+ }
20
+ | undefined;
21
+ }): string {
22
+ const { preCacheTransform } = params;
23
+
24
+ // Parse the URL
25
+ const url = new URL(params.url);
26
+
27
+ // Extract pathname and remove leading slashes
28
+ let fileName = url.pathname.replace(/^\//, "").replace(/\//g, "_");
29
+
30
+ // Optionally, add query parameters replacing special characters
31
+ if (url.search) {
32
+ fileName += url.search.replace(/[&=?]/g, "-");
33
+ }
34
+
35
+ // Replace any characters that are not valid in filenames
36
+ fileName = fileName.replace(/[^a-zA-Z0-9-_]/g, "");
37
+
38
+ // Trim or pad the fileName to a specific length
39
+ fileName = fileName.substring(0, 50);
40
+
41
+ add_pre_cache_transform: {
42
+ if (preCacheTransform === undefined) {
43
+ break add_pre_cache_transform;
44
+ }
45
+
46
+ // Sanitize actionCacheId the same way as other components
47
+ const sanitizedActionCacheId = preCacheTransform.actionCacheId.replace(/[^a-zA-Z0-9-_]/g, "_");
48
+
49
+ fileName += `_${sanitizedActionCacheId}_${createHash("sha256").update(preCacheTransform.actionFootprint).digest("hex").substring(0, 5)}`;
50
+ }
51
+
52
+ return fileName;
16
53
  }
17
54
 
18
55
  async function exists(path: string) {
@@ -57,8 +94,6 @@ function readNpmConfig(): Promise<string> {
57
94
  try {
58
95
  stdout = await exec("npm config get", { "encoding": "utf8", cwd }).then(({ stdout }) => stdout);
59
96
  } catch (error) {
60
- console.log(String(error), error);
61
-
62
97
  if (String(error).includes("ENOWORKSPACES")) {
63
98
  assert(cwd !== pathSep);
64
99
 
@@ -115,14 +150,43 @@ async function getFetchOptions(): Promise<Pick<FetchOptions, "proxy" | "noProxy"
115
150
  return { proxy, noProxy, strictSSL, cert, ca: ca.length === 0 ? undefined : ca };
116
151
  }
117
152
 
118
- export async function downloadAndUnzip(params: { url: string; destDirPath: string; pathOfDirToExtractInArchive?: string }) {
119
- const { url, destDirPath, pathOfDirToExtractInArchive } = params;
153
+ export async function downloadAndUnzip(
154
+ params: {
155
+ url: string;
156
+ destDirPath: string;
157
+ specificDirsToExtract?: string[];
158
+ preCacheTransform?: {
159
+ actionCacheId: string;
160
+ action: (params: { destDirPath: string }) => Promise<void>;
161
+ };
162
+ } & (
163
+ | {
164
+ doUseCache: true;
165
+ projectDirPath: string;
166
+ }
167
+ | {
168
+ doUseCache: false;
169
+ }
170
+ )
171
+ ) {
172
+ const { url, destDirPath, specificDirsToExtract, preCacheTransform, ...rest } = params;
173
+
174
+ const zipFileBasename = generateFileNameFromURL({
175
+ url,
176
+ "preCacheTransform":
177
+ preCacheTransform === undefined
178
+ ? undefined
179
+ : {
180
+ "actionCacheId": preCacheTransform.actionCacheId,
181
+ "actionFootprint": preCacheTransform.action.toString()
182
+ }
183
+ });
120
184
 
121
- const downloadHash = hash(JSON.stringify({ url })).substring(0, 15);
122
- const projectRoot = getProjectRoot();
123
- const cacheRoot = process.env.XDG_CACHE_HOME ?? pathJoin(projectRoot, "node_modules", ".cache");
124
- const zipFilePath = pathJoin(cacheRoot, "keycloakify", "zip", `_${downloadHash}.zip`);
125
- const extractDirPath = pathJoin(cacheRoot, "keycloakify", "unzip", `_${downloadHash}`);
185
+ const cacheRoot = !rest.doUseCache
186
+ ? `tmp_${Math.random().toString().slice(2, 12)}`
187
+ : pathJoin(process.env.XDG_CACHE_HOME ?? pathJoin(rest.projectDirPath, "node_modules", ".cache"), "keycloakify");
188
+ const zipFilePath = pathJoin(cacheRoot, `${zipFileBasename}.zip`);
189
+ const extractDirPath = pathJoin(cacheRoot, `tmp_unzip_${zipFileBasename}`);
126
190
 
127
191
  if (!(await exists(zipFilePath))) {
128
192
  const opts = await getFetchOptions();
@@ -138,12 +202,32 @@ export async function downloadAndUnzip(params: { url: string; destDirPath: strin
138
202
  response.body?.setMaxListeners(Number.MAX_VALUE);
139
203
  assert(typeof response.body !== "undefined" && response.body != null);
140
204
  await writeFile(zipFilePath, response.body);
205
+
206
+ if (specificDirsToExtract !== undefined || preCacheTransform !== undefined) {
207
+ await unzip(zipFilePath, extractDirPath, specificDirsToExtract);
208
+
209
+ await preCacheTransform?.action({
210
+ "destDirPath": extractDirPath
211
+ });
212
+
213
+ await unlink(zipFilePath);
214
+
215
+ await zip(extractDirPath, zipFilePath);
216
+
217
+ await rm(extractDirPath, { "recursive": true });
218
+ }
141
219
  }
142
220
 
143
- await unzip(zipFilePath, extractDirPath, pathOfDirToExtractInArchive);
221
+ await unzip(zipFilePath, extractDirPath);
144
222
 
145
223
  transformCodebase({
146
224
  "srcDirPath": extractDirPath,
147
225
  "destDirPath": destDirPath
148
226
  });
227
+
228
+ if (!rest.doUseCache) {
229
+ await rm(cacheRoot, { "recursive": true });
230
+ } else {
231
+ await rm(extractDirPath, { "recursive": true });
232
+ }
149
233
  }
@@ -3,7 +3,7 @@ import * as path from "path";
3
3
  import { crawl } from "./crawl";
4
4
  import { id } from "tsafe/id";
5
5
 
6
- type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string }) =>
6
+ type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string; fileRelativePath: string }) =>
7
7
  | {
8
8
  modifiedSourceCode: Buffer;
9
9
  newFileName?: string;
@@ -20,26 +20,27 @@ export function transformCodebase(params: { srcDirPath: string; destDirPath: str
20
20
  }))
21
21
  } = params;
22
22
 
23
- for (const file_relative_path of crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })) {
24
- const filePath = path.join(srcDirPath, file_relative_path);
23
+ for (const fileRelativePath of crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })) {
24
+ const filePath = path.join(srcDirPath, fileRelativePath);
25
25
 
26
26
  const transformSourceCodeResult = transformSourceCode({
27
27
  "sourceCode": fs.readFileSync(filePath),
28
- filePath
28
+ filePath,
29
+ fileRelativePath
29
30
  });
30
31
 
31
32
  if (transformSourceCodeResult === undefined) {
32
33
  continue;
33
34
  }
34
35
 
35
- fs.mkdirSync(path.dirname(path.join(destDirPath, file_relative_path)), {
36
+ fs.mkdirSync(path.dirname(path.join(destDirPath, fileRelativePath)), {
36
37
  "recursive": true
37
38
  });
38
39
 
39
40
  const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
40
41
 
41
42
  fs.writeFileSync(
42
- path.join(path.dirname(path.join(destDirPath, file_relative_path)), newFileName ?? path.basename(file_relative_path)),
43
+ path.join(path.dirname(path.join(destDirPath, fileRelativePath)), newFileName ?? path.basename(fileRelativePath)),
43
44
  modifiedSourceCode
44
45
  );
45
46
  }