keycloakify 11.0.1 → 11.2.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.
- package/bin/31.index.js +28 -18
- package/bin/{499.index.js → 573.index.js} +119 -114
- package/bin/{246.index.js → 860.index.js} +491 -2
- package/bin/main.js +1 -1
- package/bin/shared/buildContext.js.map +1 -1
- package/package.json +15 -16
- package/src/bin/keycloakify/generateResources/generateResources.ts +477 -19
- package/src/bin/shared/buildContext.ts +35 -18
- package/vite-plugin/index.js +28 -18
- package/src/bin/keycloakify/generateResources/generateResourcesForMainTheme.ts +0 -373
- package/src/bin/keycloakify/generateResources/generateResourcesForThemeVariant.ts +0 -76
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "keycloakify",
|
3
|
-
"version": "11.0
|
3
|
+
"version": "11.2.0",
|
4
4
|
"description": "Framework to create custom Keycloak UIs",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -663,8 +663,6 @@
|
|
663
663
|
"src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl",
|
664
664
|
"src/bin/keycloakify/generateResources/generateMessageProperties.ts",
|
665
665
|
"src/bin/keycloakify/generateResources/generateResources.ts",
|
666
|
-
"src/bin/keycloakify/generateResources/generateResourcesForMainTheme.ts",
|
667
|
-
"src/bin/keycloakify/generateResources/generateResourcesForThemeVariant.ts",
|
668
666
|
"src/bin/keycloakify/generateResources/index.ts",
|
669
667
|
"src/bin/keycloakify/generateResources/readExtraPageNames.ts",
|
670
668
|
"src/bin/keycloakify/generateResources/readFieldNameUsage.ts",
|
@@ -981,20 +979,20 @@
|
|
981
979
|
"bin/main.js",
|
982
980
|
"bin/193.index.js",
|
983
981
|
"bin/20.index.js",
|
984
|
-
"bin/246.index.js",
|
985
982
|
"bin/266.index.js",
|
986
983
|
"bin/304.index.js",
|
987
984
|
"bin/31.index.js",
|
988
985
|
"bin/33.index.js",
|
989
986
|
"bin/36.index.js",
|
990
987
|
"bin/453.index.js",
|
991
|
-
"bin/499.index.js",
|
992
988
|
"bin/526.index.js",
|
989
|
+
"bin/573.index.js",
|
993
990
|
"bin/599.index.js",
|
994
991
|
"bin/720.index.js",
|
995
992
|
"bin/743.index.js",
|
996
993
|
"bin/780.index.js",
|
997
994
|
"bin/786.index.js",
|
995
|
+
"bin/860.index.js",
|
998
996
|
"bin/877.index.js",
|
999
997
|
"bin/903.index.js",
|
1000
998
|
"bin/932.index.js",
|
@@ -1040,6 +1038,7 @@
|
|
1040
1038
|
"@babel/core": "^7.24.5",
|
1041
1039
|
"@babel/generator": "^7.24.5",
|
1042
1040
|
"@babel/parser": "^7.24.5",
|
1041
|
+
"@babel/preset-env": "7.24.8",
|
1043
1042
|
"@babel/types": "^7.24.5",
|
1044
1043
|
"@emotion/react": "^11.11.4",
|
1045
1044
|
"@octokit/rest": "^20.1.1",
|
@@ -1047,20 +1046,27 @@
|
|
1047
1046
|
"@storybook/builder-webpack5": "^6.5.13",
|
1048
1047
|
"@storybook/manager-webpack5": "^6.5.13",
|
1049
1048
|
"@storybook/react": "^6.5.13",
|
1050
|
-
"eslint-plugin-storybook": "^0.6.7",
|
1051
1049
|
"@types/babel__generator": "^7.6.4",
|
1050
|
+
"@types/dompurify": "^2.0.0",
|
1052
1051
|
"@types/make-fetch-happen": "^10.0.1",
|
1053
1052
|
"@types/minimist": "^1.2.2",
|
1054
1053
|
"@types/node": "^18.15.3",
|
1054
|
+
"@types/properties-parser": "^0.3.3",
|
1055
1055
|
"@types/react": "^18.0.35",
|
1056
1056
|
"@types/react-dom": "^18.0.11",
|
1057
1057
|
"@types/yauzl": "^2.10.3",
|
1058
1058
|
"@vercel/ncc": "^0.38.1",
|
1059
|
+
"babel-loader": "9.1.3",
|
1059
1060
|
"chalk": "^4.1.2",
|
1060
1061
|
"cheerio": "1.0.0-rc.12",
|
1061
1062
|
"chokidar-cli": "^3.0.0",
|
1062
1063
|
"cli-select": "^1.1.2",
|
1064
|
+
"dompurify": "^3.1.6",
|
1065
|
+
"eslint-plugin-storybook": "^0.6.7",
|
1066
|
+
"evt": "^2.5.7",
|
1067
|
+
"html-entities": "^2.5.2",
|
1063
1068
|
"husky": "^4.3.8",
|
1069
|
+
"isomorphic-dompurify": "^2.15.0",
|
1064
1070
|
"lint-staged": "^11.0.0",
|
1065
1071
|
"magic-string": "^0.30.7",
|
1066
1072
|
"make-fetch-happen": "^11.0.3",
|
@@ -1075,20 +1081,13 @@
|
|
1075
1081
|
"termost": "^v0.12.1",
|
1076
1082
|
"tsc-alias": "^1.8.10",
|
1077
1083
|
"tss-react": "^4.9.10",
|
1084
|
+
"tsx": "^4.15.5",
|
1078
1085
|
"typescript": "^4.9.4",
|
1079
1086
|
"vite": "^5.2.11",
|
1080
1087
|
"vitest": "^1.6.0",
|
1081
|
-
"yauzl": "^2.10.0",
|
1082
|
-
"zod": "^3.17.10",
|
1083
|
-
"evt": "^2.5.7",
|
1084
|
-
"tsx": "^4.15.5",
|
1085
|
-
"html-entities": "^2.5.2",
|
1086
|
-
"isomorphic-dompurify": "^2.15.0",
|
1087
|
-
"dompurify": "^3.1.6",
|
1088
|
-
"@types/dompurify": "^2.0.0",
|
1089
1088
|
"webpack": "5.93.0",
|
1090
1089
|
"webpack-cli": "5.1.4",
|
1091
|
-
"
|
1092
|
-
"
|
1090
|
+
"yauzl": "^2.10.0",
|
1091
|
+
"zod": "^3.17.10"
|
1093
1092
|
}
|
1094
1093
|
}
|
@@ -1,16 +1,56 @@
|
|
1
1
|
import type { BuildContext } from "../../shared/buildContext";
|
2
|
-
import { assert } from "tsafe/assert";
|
3
|
-
import {
|
4
|
-
generateResourcesForMainTheme,
|
5
|
-
type BuildContextLike as BuildContextLike_generateResourcesForMainTheme
|
6
|
-
} from "./generateResourcesForMainTheme";
|
7
|
-
import { generateResourcesForThemeVariant } from "./generateResourcesForThemeVariant";
|
8
2
|
import fs from "fs";
|
9
3
|
import { rmSync } from "../../tools/fs.rmSync";
|
4
|
+
import { transformCodebase } from "../../tools/transformCodebase";
|
5
|
+
import {
|
6
|
+
join as pathJoin,
|
7
|
+
relative as pathRelative,
|
8
|
+
dirname as pathDirname,
|
9
|
+
extname as pathExtname,
|
10
|
+
sep as pathSep
|
11
|
+
} from "path";
|
12
|
+
import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode";
|
13
|
+
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
|
14
|
+
import {
|
15
|
+
generateFtlFilesCodeFactory,
|
16
|
+
type BuildContextLike as BuildContextLike_kcContextExclusionsFtlCode
|
17
|
+
} from "../generateFtl";
|
18
|
+
import {
|
19
|
+
type ThemeType,
|
20
|
+
LOGIN_THEME_PAGE_IDS,
|
21
|
+
ACCOUNT_THEME_PAGE_IDS,
|
22
|
+
WELL_KNOWN_DIRECTORY_BASE_NAME
|
23
|
+
} from "../../shared/constants";
|
24
|
+
import { assert, type Equals } from "tsafe/assert";
|
25
|
+
import { readFieldNameUsage } from "./readFieldNameUsage";
|
26
|
+
import { readExtraPagesNames } from "./readExtraPageNames";
|
27
|
+
import {
|
28
|
+
generateMessageProperties,
|
29
|
+
type BuildContextLike as BuildContextLike_generateMessageProperties
|
30
|
+
} from "./generateMessageProperties";
|
31
|
+
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
|
32
|
+
import {
|
33
|
+
writeMetaInfKeycloakThemes,
|
34
|
+
type MetaInfKeycloakTheme
|
35
|
+
} from "../../shared/metaInfKeycloakThemes";
|
36
|
+
import { objectEntries } from "tsafe/objectEntries";
|
37
|
+
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
|
38
|
+
import * as child_process from "child_process";
|
39
|
+
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
40
|
+
import propertiesParser from "properties-parser";
|
10
41
|
|
11
|
-
export type BuildContextLike =
|
12
|
-
|
13
|
-
|
42
|
+
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
43
|
+
BuildContextLike_generateMessageProperties & {
|
44
|
+
themeNames: string[];
|
45
|
+
extraThemeProperties: string[] | undefined;
|
46
|
+
projectDirPath: string;
|
47
|
+
projectBuildDirPath: string;
|
48
|
+
environmentVariables: { name: string; default: string }[];
|
49
|
+
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
50
|
+
themeSrcDirPath: string;
|
51
|
+
bundler: "vite" | "webpack";
|
52
|
+
packageJsonFilePath: string;
|
53
|
+
};
|
14
54
|
|
15
55
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
16
56
|
|
@@ -20,25 +60,443 @@ export async function generateResources(params: {
|
|
20
60
|
}): Promise<void> {
|
21
61
|
const { resourcesDirPath, buildContext } = params;
|
22
62
|
|
23
|
-
const [themeName
|
63
|
+
const [themeName] = buildContext.themeNames;
|
24
64
|
|
25
65
|
if (fs.existsSync(resourcesDirPath)) {
|
26
66
|
rmSync(resourcesDirPath, { recursive: true });
|
27
67
|
}
|
28
68
|
|
29
|
-
const
|
30
|
-
|
31
|
-
|
69
|
+
const getThemeTypeDirPath = (params: {
|
70
|
+
themeType: ThemeType | "email";
|
71
|
+
themeName: string;
|
72
|
+
}) => {
|
73
|
+
const { themeType, themeName } = params;
|
74
|
+
return pathJoin(resourcesDirPath, "theme", themeName, themeType);
|
75
|
+
};
|
76
|
+
|
77
|
+
const writeMessagePropertiesFilesByThemeType: Partial<
|
78
|
+
Record<ThemeType, (params: { messageDirPath: string; themeName: string }) => void>
|
79
|
+
> = {};
|
80
|
+
|
81
|
+
for (const themeType of ["login", "account"] as const) {
|
82
|
+
if (!buildContext.implementedThemeTypes[themeType].isImplemented) {
|
83
|
+
continue;
|
84
|
+
}
|
85
|
+
|
86
|
+
const isForAccountSpa =
|
87
|
+
themeType === "account" &&
|
88
|
+
(assert(buildContext.implementedThemeTypes.account.isImplemented),
|
89
|
+
buildContext.implementedThemeTypes.account.type === "Single-Page");
|
90
|
+
|
91
|
+
const themeTypeDirPath = getThemeTypeDirPath({ themeName, themeType });
|
92
|
+
|
93
|
+
apply_replacers_and_move_to_theme_resources: {
|
94
|
+
const destDirPath = pathJoin(
|
95
|
+
themeTypeDirPath,
|
96
|
+
"resources",
|
97
|
+
WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
|
98
|
+
);
|
99
|
+
|
100
|
+
// NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
|
101
|
+
rmSync(destDirPath, { recursive: true, force: true });
|
102
|
+
|
103
|
+
if (
|
104
|
+
themeType === "account" &&
|
105
|
+
buildContext.implementedThemeTypes.login.isImplemented
|
106
|
+
) {
|
107
|
+
// NOTE: We prevent doing it twice, it has been done for the login theme.
|
108
|
+
|
109
|
+
transformCodebase({
|
110
|
+
srcDirPath: pathJoin(
|
111
|
+
getThemeTypeDirPath({
|
112
|
+
themeName,
|
113
|
+
themeType: "login"
|
114
|
+
}),
|
115
|
+
"resources",
|
116
|
+
WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
|
117
|
+
),
|
118
|
+
destDirPath
|
119
|
+
});
|
120
|
+
|
121
|
+
break apply_replacers_and_move_to_theme_resources;
|
122
|
+
}
|
123
|
+
|
124
|
+
{
|
125
|
+
const dirPath = pathJoin(
|
126
|
+
buildContext.projectBuildDirPath,
|
127
|
+
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
|
128
|
+
);
|
129
|
+
|
130
|
+
if (fs.existsSync(dirPath)) {
|
131
|
+
assert(buildContext.bundler === "webpack");
|
132
|
+
|
133
|
+
throw new Error(
|
134
|
+
[
|
135
|
+
`Keycloakify build error: The ${WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES} directory shouldn't exist in your build directory.`,
|
136
|
+
`(${pathRelative(process.cwd(), dirPath)}).\n`,
|
137
|
+
`Theses assets are only required for local development with Storybook.",
|
138
|
+
"Please remove this directory as an additional step of your command.\n`,
|
139
|
+
`For example: \`"build": "... && rimraf ${pathRelative(buildContext.projectDirPath, dirPath)}"\``
|
140
|
+
].join(" ")
|
141
|
+
);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
transformCodebase({
|
146
|
+
srcDirPath: buildContext.projectBuildDirPath,
|
147
|
+
destDirPath,
|
148
|
+
transformSourceCode: ({ filePath, fileRelativePath, sourceCode }) => {
|
149
|
+
if (filePath.endsWith(".css")) {
|
150
|
+
const { fixedCssCode } = replaceImportsInCssCode({
|
151
|
+
cssCode: sourceCode.toString("utf8"),
|
152
|
+
cssFileRelativeDirPath: pathDirname(fileRelativePath),
|
153
|
+
buildContext
|
154
|
+
});
|
155
|
+
|
156
|
+
return {
|
157
|
+
modifiedSourceCode: Buffer.from(fixedCssCode, "utf8")
|
158
|
+
};
|
159
|
+
}
|
160
|
+
|
161
|
+
if (filePath.endsWith(".js")) {
|
162
|
+
const { fixedJsCode } = replaceImportsInJsCode({
|
163
|
+
jsCode: sourceCode.toString("utf8"),
|
164
|
+
buildContext
|
165
|
+
});
|
166
|
+
|
167
|
+
return {
|
168
|
+
modifiedSourceCode: Buffer.from(fixedJsCode, "utf8")
|
169
|
+
};
|
170
|
+
}
|
171
|
+
|
172
|
+
return { modifiedSourceCode: sourceCode };
|
173
|
+
}
|
174
|
+
});
|
175
|
+
}
|
176
|
+
|
177
|
+
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
32
178
|
themeName,
|
33
|
-
|
179
|
+
indexHtmlCode: fs
|
180
|
+
.readFileSync(pathJoin(buildContext.projectBuildDirPath, "index.html"))
|
181
|
+
.toString("utf8"),
|
182
|
+
buildContext,
|
183
|
+
keycloakifyVersion: readThisNpmPackageVersion(),
|
184
|
+
themeType,
|
185
|
+
fieldNames: readFieldNameUsage({
|
186
|
+
themeSrcDirPath: buildContext.themeSrcDirPath,
|
187
|
+
themeType
|
188
|
+
})
|
189
|
+
});
|
190
|
+
|
191
|
+
[
|
192
|
+
...(() => {
|
193
|
+
switch (themeType) {
|
194
|
+
case "login":
|
195
|
+
return LOGIN_THEME_PAGE_IDS;
|
196
|
+
case "account":
|
197
|
+
return isForAccountSpa ? ["index.ftl"] : ACCOUNT_THEME_PAGE_IDS;
|
198
|
+
}
|
199
|
+
})(),
|
200
|
+
...(isForAccountSpa
|
201
|
+
? []
|
202
|
+
: readExtraPagesNames({
|
203
|
+
themeType,
|
204
|
+
themeSrcDirPath: buildContext.themeSrcDirPath
|
205
|
+
}))
|
206
|
+
].forEach(pageId => {
|
207
|
+
const { ftlCode } = generateFtlFilesCode({ pageId });
|
208
|
+
|
209
|
+
fs.writeFileSync(
|
210
|
+
pathJoin(themeTypeDirPath, pageId),
|
211
|
+
Buffer.from(ftlCode, "utf8")
|
212
|
+
);
|
34
213
|
});
|
35
214
|
|
36
|
-
|
37
|
-
|
215
|
+
let languageTags: string[] | undefined = undefined;
|
216
|
+
|
217
|
+
i18n_messages_generation: {
|
218
|
+
if (isForAccountSpa) {
|
219
|
+
break i18n_messages_generation;
|
220
|
+
}
|
221
|
+
|
222
|
+
const wrap = generateMessageProperties({
|
223
|
+
buildContext,
|
224
|
+
themeType
|
225
|
+
});
|
226
|
+
|
227
|
+
languageTags = wrap.languageTags;
|
228
|
+
const { writeMessagePropertiesFiles } = wrap;
|
229
|
+
|
230
|
+
writeMessagePropertiesFilesByThemeType[themeType] =
|
231
|
+
writeMessagePropertiesFiles;
|
232
|
+
}
|
233
|
+
|
234
|
+
bring_in_account_v3_i18n_messages: {
|
235
|
+
if (!buildContext.implementedThemeTypes.account.isImplemented) {
|
236
|
+
break bring_in_account_v3_i18n_messages;
|
237
|
+
}
|
238
|
+
if (buildContext.implementedThemeTypes.account.type !== "Single-Page") {
|
239
|
+
break bring_in_account_v3_i18n_messages;
|
240
|
+
}
|
241
|
+
|
242
|
+
const accountUiDirPath = child_process
|
243
|
+
.execSync("npm list @keycloakify/keycloak-account-ui --parseable", {
|
244
|
+
cwd: pathDirname(buildContext.packageJsonFilePath)
|
245
|
+
})
|
246
|
+
.toString("utf8")
|
247
|
+
.trim();
|
248
|
+
|
249
|
+
const messageDirPath_defaults = pathJoin(accountUiDirPath, "messages");
|
250
|
+
|
251
|
+
if (!fs.existsSync(messageDirPath_defaults)) {
|
252
|
+
throw new Error(
|
253
|
+
`Please update @keycloakify/keycloak-account-ui to 25.0.4-rc.5 or later.`
|
254
|
+
);
|
255
|
+
}
|
256
|
+
|
257
|
+
const messagesDirPath_dest = pathJoin(
|
258
|
+
getThemeTypeDirPath({ themeName, themeType: "account" }),
|
259
|
+
"messages"
|
260
|
+
);
|
261
|
+
|
262
|
+
transformCodebase({
|
263
|
+
srcDirPath: messageDirPath_defaults,
|
264
|
+
destDirPath: messagesDirPath_dest
|
265
|
+
});
|
266
|
+
|
267
|
+
apply_theme_changes: {
|
268
|
+
const messagesDirPath_theme = pathJoin(
|
269
|
+
buildContext.themeSrcDirPath,
|
270
|
+
"account",
|
271
|
+
"messages"
|
272
|
+
);
|
273
|
+
|
274
|
+
if (!fs.existsSync(messagesDirPath_theme)) {
|
275
|
+
break apply_theme_changes;
|
276
|
+
}
|
277
|
+
|
278
|
+
fs.readdirSync(messagesDirPath_theme).forEach(basename => {
|
279
|
+
const filePath_src = pathJoin(messagesDirPath_theme, basename);
|
280
|
+
const filePath_dest = pathJoin(messagesDirPath_dest, basename);
|
281
|
+
|
282
|
+
if (!fs.existsSync(filePath_dest)) {
|
283
|
+
fs.cpSync(filePath_src, filePath_dest);
|
284
|
+
}
|
285
|
+
|
286
|
+
const messages_src = propertiesParser.parse(
|
287
|
+
fs.readFileSync(filePath_src).toString("utf8")
|
288
|
+
);
|
289
|
+
const messages_dest = propertiesParser.parse(
|
290
|
+
fs.readFileSync(filePath_dest).toString("utf8")
|
291
|
+
);
|
292
|
+
|
293
|
+
const messages = {
|
294
|
+
...messages_dest,
|
295
|
+
...messages_src
|
296
|
+
};
|
297
|
+
|
298
|
+
const editor = propertiesParser.createEditor();
|
299
|
+
|
300
|
+
Object.entries(messages).forEach(([key, value]) => {
|
301
|
+
editor.set(key, value);
|
302
|
+
});
|
303
|
+
|
304
|
+
fs.writeFileSync(
|
305
|
+
filePath_dest,
|
306
|
+
Buffer.from(editor.toString(), "utf8")
|
307
|
+
);
|
308
|
+
});
|
309
|
+
}
|
310
|
+
|
311
|
+
languageTags = fs
|
312
|
+
.readdirSync(messagesDirPath_dest)
|
313
|
+
.map(basename =>
|
314
|
+
basename.replace(/^messages_/, "").replace(/\.properties$/, "")
|
315
|
+
);
|
316
|
+
}
|
317
|
+
|
318
|
+
keycloak_static_resources: {
|
319
|
+
if (isForAccountSpa) {
|
320
|
+
break keycloak_static_resources;
|
321
|
+
}
|
322
|
+
|
323
|
+
transformCodebase({
|
324
|
+
srcDirPath: pathJoin(
|
325
|
+
getThisCodebaseRootDirPath(),
|
326
|
+
"res",
|
327
|
+
"public",
|
328
|
+
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES,
|
329
|
+
themeType
|
330
|
+
),
|
331
|
+
destDirPath: pathJoin(themeTypeDirPath, "resources")
|
332
|
+
});
|
333
|
+
}
|
334
|
+
|
335
|
+
fs.writeFileSync(
|
336
|
+
pathJoin(themeTypeDirPath, "theme.properties"),
|
337
|
+
Buffer.from(
|
338
|
+
[
|
339
|
+
`parent=${(() => {
|
340
|
+
switch (themeType) {
|
341
|
+
case "account":
|
342
|
+
return isForAccountSpa ? "base" : "account-v1";
|
343
|
+
case "login":
|
344
|
+
return "keycloak";
|
345
|
+
}
|
346
|
+
assert<Equals<typeof themeType, never>>(false);
|
347
|
+
})()}`,
|
348
|
+
...(isForAccountSpa ? ["deprecatedMode=false"] : []),
|
349
|
+
...(buildContext.extraThemeProperties ?? []),
|
350
|
+
...buildContext.environmentVariables.map(
|
351
|
+
({ name, default: defaultValue }) =>
|
352
|
+
`${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`
|
353
|
+
),
|
354
|
+
...(languageTags === undefined
|
355
|
+
? []
|
356
|
+
: [`locales=${languageTags.join(",")}`])
|
357
|
+
].join("\n\n"),
|
358
|
+
"utf8"
|
359
|
+
)
|
360
|
+
);
|
361
|
+
}
|
362
|
+
|
363
|
+
email: {
|
364
|
+
if (!buildContext.implementedThemeTypes.email.isImplemented) {
|
365
|
+
break email;
|
366
|
+
}
|
367
|
+
|
368
|
+
const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
|
369
|
+
|
370
|
+
transformCodebase({
|
371
|
+
srcDirPath: emailThemeSrcDirPath,
|
372
|
+
destDirPath: getThemeTypeDirPath({ themeName, themeType: "email" })
|
373
|
+
});
|
374
|
+
}
|
375
|
+
|
376
|
+
bring_in_account_v1: {
|
377
|
+
if (!buildContext.implementedThemeTypes.account.isImplemented) {
|
378
|
+
break bring_in_account_v1;
|
379
|
+
}
|
380
|
+
|
381
|
+
if (buildContext.implementedThemeTypes.account.type !== "Multi-Page") {
|
382
|
+
break bring_in_account_v1;
|
383
|
+
}
|
384
|
+
|
385
|
+
transformCodebase({
|
386
|
+
srcDirPath: pathJoin(getThisCodebaseRootDirPath(), "res", "account-v1"),
|
387
|
+
destDirPath: getThemeTypeDirPath({
|
388
|
+
themeName: "account-v1",
|
389
|
+
themeType: "account"
|
390
|
+
})
|
391
|
+
});
|
392
|
+
}
|
393
|
+
|
394
|
+
{
|
395
|
+
const metaInfKeycloakThemes: MetaInfKeycloakTheme = { themes: [] };
|
396
|
+
|
397
|
+
for (const themeName of buildContext.themeNames) {
|
398
|
+
metaInfKeycloakThemes.themes.push({
|
399
|
+
name: themeName,
|
400
|
+
types: objectEntries(buildContext.implementedThemeTypes)
|
401
|
+
.filter(([, { isImplemented }]) => isImplemented)
|
402
|
+
.map(([themeType]) => themeType)
|
403
|
+
});
|
404
|
+
}
|
405
|
+
|
406
|
+
if (buildContext.implementedThemeTypes.account.isImplemented) {
|
407
|
+
metaInfKeycloakThemes.themes.push({
|
408
|
+
name: "account-v1",
|
409
|
+
types: ["account"]
|
410
|
+
});
|
411
|
+
}
|
412
|
+
|
413
|
+
writeMetaInfKeycloakThemes({
|
38
414
|
resourcesDirPath,
|
39
|
-
|
40
|
-
|
41
|
-
|
415
|
+
getNewMetaInfKeycloakTheme: () => metaInfKeycloakThemes
|
416
|
+
});
|
417
|
+
}
|
418
|
+
|
419
|
+
for (const themeVariantName of buildContext.themeNames) {
|
420
|
+
if (themeVariantName === themeName) {
|
421
|
+
continue;
|
422
|
+
}
|
423
|
+
|
424
|
+
transformCodebase({
|
425
|
+
srcDirPath: pathJoin(resourcesDirPath, "theme", themeName),
|
426
|
+
destDirPath: pathJoin(resourcesDirPath, "theme", themeVariantName),
|
427
|
+
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
428
|
+
if (
|
429
|
+
pathExtname(fileRelativePath) === ".ftl" &&
|
430
|
+
fileRelativePath.split(pathSep).length === 2
|
431
|
+
) {
|
432
|
+
const modifiedSourceCode = Buffer.from(
|
433
|
+
Buffer.from(sourceCode)
|
434
|
+
.toString("utf-8")
|
435
|
+
.replace(
|
436
|
+
`"themeName": "${themeName}"`,
|
437
|
+
`"themeName": "${themeVariantName}"`
|
438
|
+
),
|
439
|
+
"utf8"
|
440
|
+
);
|
441
|
+
|
442
|
+
assert(Buffer.compare(modifiedSourceCode, sourceCode) !== 0);
|
443
|
+
|
444
|
+
return { modifiedSourceCode };
|
445
|
+
}
|
446
|
+
|
447
|
+
return { modifiedSourceCode: sourceCode };
|
448
|
+
}
|
42
449
|
});
|
43
450
|
}
|
451
|
+
|
452
|
+
for (const themeName of buildContext.themeNames) {
|
453
|
+
for (const [themeType, writeMessagePropertiesFiles] of objectEntries(
|
454
|
+
writeMessagePropertiesFilesByThemeType
|
455
|
+
)) {
|
456
|
+
// NOTE: This is just a quirk of the type system: We can't really differentiate in a record
|
457
|
+
// between the case where the key isn't present and the case where the value is `undefined`.
|
458
|
+
if (writeMessagePropertiesFiles === undefined) {
|
459
|
+
return;
|
460
|
+
}
|
461
|
+
writeMessagePropertiesFiles({
|
462
|
+
messageDirPath: pathJoin(
|
463
|
+
getThemeTypeDirPath({ themeName, themeType }),
|
464
|
+
"messages"
|
465
|
+
),
|
466
|
+
themeName
|
467
|
+
});
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
modify_email_theme_per_variant: {
|
472
|
+
if (!buildContext.implementedThemeTypes.email.isImplemented) {
|
473
|
+
break modify_email_theme_per_variant;
|
474
|
+
}
|
475
|
+
|
476
|
+
for (const themeName of buildContext.themeNames) {
|
477
|
+
const emailThemeDirPath = getThemeTypeDirPath({
|
478
|
+
themeName,
|
479
|
+
themeType: "email"
|
480
|
+
});
|
481
|
+
|
482
|
+
transformCodebase({
|
483
|
+
srcDirPath: emailThemeDirPath,
|
484
|
+
destDirPath: emailThemeDirPath,
|
485
|
+
transformSourceCode: ({ filePath, sourceCode }) => {
|
486
|
+
if (!filePath.endsWith(".ftl")) {
|
487
|
+
return { modifiedSourceCode: sourceCode };
|
488
|
+
}
|
489
|
+
|
490
|
+
return {
|
491
|
+
modifiedSourceCode: Buffer.from(
|
492
|
+
sourceCode
|
493
|
+
.toString("utf8")
|
494
|
+
.replace(/xKeycloakify\.themeName/g, `"${themeName}"`),
|
495
|
+
"utf8"
|
496
|
+
)
|
497
|
+
};
|
498
|
+
}
|
499
|
+
});
|
500
|
+
}
|
501
|
+
}
|
44
502
|
}
|
@@ -240,8 +240,7 @@ export function getBuildContext(params: {
|
|
240
240
|
|
241
241
|
if (
|
242
242
|
parsedPackageJson.dependencies?.keycloakify === undefined &&
|
243
|
-
parsedPackageJson.devDependencies?.keycloakify === undefined
|
244
|
-
parsedPackageJson.name !== "keycloakify" // NOTE: For local storybook build
|
243
|
+
parsedPackageJson.devDependencies?.keycloakify === undefined
|
245
244
|
) {
|
246
245
|
break success;
|
247
246
|
}
|
@@ -470,26 +469,44 @@ export function getBuildContext(params: {
|
|
470
469
|
}
|
471
470
|
|
472
471
|
const themeNames = ((): [string, ...string[]] => {
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
.
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
472
|
+
const themeNames = ((): [string, ...string[]] => {
|
473
|
+
if (buildOptions.themeName === undefined) {
|
474
|
+
return parsedPackageJson.name === undefined
|
475
|
+
? ["keycloakify"]
|
476
|
+
: [
|
477
|
+
parsedPackageJson.name
|
478
|
+
.replace(/^@(.*)/, "$1")
|
479
|
+
.split("/")
|
480
|
+
.join("-")
|
481
|
+
];
|
482
|
+
}
|
483
483
|
|
484
|
-
|
485
|
-
|
486
|
-
|
484
|
+
if (typeof buildOptions.themeName === "string") {
|
485
|
+
return [buildOptions.themeName];
|
486
|
+
}
|
487
|
+
|
488
|
+
const [mainThemeName, ...themeVariantNames] = buildOptions.themeName;
|
489
|
+
|
490
|
+
assert(mainThemeName !== undefined);
|
487
491
|
|
488
|
-
|
492
|
+
return [mainThemeName, ...themeVariantNames];
|
493
|
+
})();
|
489
494
|
|
490
|
-
|
495
|
+
for (const themeName of themeNames) {
|
496
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(themeName)) {
|
497
|
+
console.error(
|
498
|
+
chalk.red(
|
499
|
+
[
|
500
|
+
`Invalid theme name: ${themeName}`,
|
501
|
+
`Theme names should only contain letters, numbers, and "_" or "-"`
|
502
|
+
].join(" ")
|
503
|
+
)
|
504
|
+
);
|
505
|
+
process.exit(-1);
|
506
|
+
}
|
507
|
+
}
|
491
508
|
|
492
|
-
return
|
509
|
+
return themeNames;
|
493
510
|
})();
|
494
511
|
|
495
512
|
const projectBuildDirPath = (() => {
|