keycloakify 11.1.0 → 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.
@@ -1,426 +0,0 @@
1
- import { transformCodebase } from "../../tools/transformCodebase";
2
- import * as fs from "fs";
3
- import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
4
- import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode";
5
- import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
6
- import {
7
- generateFtlFilesCodeFactory,
8
- type BuildContextLike as BuildContextLike_kcContextExclusionsFtlCode
9
- } from "../generateFtl";
10
- import {
11
- type ThemeType,
12
- LOGIN_THEME_PAGE_IDS,
13
- ACCOUNT_THEME_PAGE_IDS,
14
- WELL_KNOWN_DIRECTORY_BASE_NAME
15
- } from "../../shared/constants";
16
- import type { BuildContext } from "../../shared/buildContext";
17
- import { assert, type Equals } from "tsafe/assert";
18
- import { readFieldNameUsage } from "./readFieldNameUsage";
19
- import { readExtraPagesNames } from "./readExtraPageNames";
20
- import {
21
- generateMessageProperties,
22
- type BuildContextLike as BuildContextLike_generateMessageProperties
23
- } from "./generateMessageProperties";
24
- import { rmSync } from "../../tools/fs.rmSync";
25
- import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
26
- import {
27
- writeMetaInfKeycloakThemes,
28
- type MetaInfKeycloakTheme
29
- } from "../../shared/metaInfKeycloakThemes";
30
- import { objectEntries } from "tsafe/objectEntries";
31
- import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
32
- import * as child_process from "child_process";
33
- import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
34
- import propertiesParser from "properties-parser";
35
-
36
- export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
37
- BuildContextLike_generateMessageProperties & {
38
- extraThemeProperties: string[] | undefined;
39
- projectDirPath: string;
40
- projectBuildDirPath: string;
41
- environmentVariables: { name: string; default: string }[];
42
- implementedThemeTypes: BuildContext["implementedThemeTypes"];
43
- themeSrcDirPath: string;
44
- bundler: "vite" | "webpack";
45
- packageJsonFilePath: string;
46
- };
47
-
48
- assert<BuildContext extends BuildContextLike ? true : false>();
49
-
50
- export async function generateResourcesForMainTheme(params: {
51
- buildContext: BuildContextLike;
52
- themeName: string;
53
- resourcesDirPath: string;
54
- }): Promise<{
55
- writeMessagePropertiesFilesForThemeVariant: (params: {
56
- getMessageDirPath: (params: { themeType: ThemeType }) => string;
57
- themeName: string;
58
- }) => void;
59
- }> {
60
- const { themeName, resourcesDirPath, buildContext } = params;
61
-
62
- const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
63
- const { themeType } = params;
64
- return pathJoin(resourcesDirPath, "theme", themeName, themeType);
65
- };
66
-
67
- const writeMessagePropertiesFilesByThemeType: Partial<
68
- Record<ThemeType, (params: { messageDirPath: string; themeName: string }) => void>
69
- > = {};
70
-
71
- for (const themeType of ["login", "account"] as const) {
72
- if (!buildContext.implementedThemeTypes[themeType].isImplemented) {
73
- continue;
74
- }
75
-
76
- const isForAccountSpa =
77
- themeType === "account" &&
78
- (assert(buildContext.implementedThemeTypes.account.isImplemented),
79
- buildContext.implementedThemeTypes.account.type === "Single-Page");
80
-
81
- const themeTypeDirPath = getThemeTypeDirPath({ themeType });
82
-
83
- apply_replacers_and_move_to_theme_resources: {
84
- const destDirPath = pathJoin(
85
- themeTypeDirPath,
86
- "resources",
87
- WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
88
- );
89
-
90
- // NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
91
- rmSync(destDirPath, { recursive: true, force: true });
92
-
93
- if (
94
- themeType === "account" &&
95
- buildContext.implementedThemeTypes.login.isImplemented
96
- ) {
97
- // NOTE: We prevent doing it twice, it has been done for the login theme.
98
-
99
- transformCodebase({
100
- srcDirPath: pathJoin(
101
- getThemeTypeDirPath({
102
- themeType: "login"
103
- }),
104
- "resources",
105
- WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
106
- ),
107
- destDirPath
108
- });
109
-
110
- break apply_replacers_and_move_to_theme_resources;
111
- }
112
-
113
- {
114
- const dirPath = pathJoin(
115
- buildContext.projectBuildDirPath,
116
- WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
117
- );
118
-
119
- if (fs.existsSync(dirPath)) {
120
- assert(buildContext.bundler === "webpack");
121
-
122
- throw new Error(
123
- [
124
- `Keycloakify build error: The ${WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES} directory shouldn't exist in your build directory.`,
125
- `(${pathRelative(process.cwd(), dirPath)}).\n`,
126
- `Theses assets are only required for local development with Storybook.",
127
- "Please remove this directory as an additional step of your command.\n`,
128
- `For example: \`"build": "... && rimraf ${pathRelative(buildContext.projectDirPath, dirPath)}"\``
129
- ].join(" ")
130
- );
131
- }
132
- }
133
-
134
- transformCodebase({
135
- srcDirPath: buildContext.projectBuildDirPath,
136
- destDirPath,
137
- transformSourceCode: ({ filePath, fileRelativePath, sourceCode }) => {
138
- if (filePath.endsWith(".css")) {
139
- const { fixedCssCode } = replaceImportsInCssCode({
140
- cssCode: sourceCode.toString("utf8"),
141
- cssFileRelativeDirPath: pathDirname(fileRelativePath),
142
- buildContext
143
- });
144
-
145
- return {
146
- modifiedSourceCode: Buffer.from(fixedCssCode, "utf8")
147
- };
148
- }
149
-
150
- if (filePath.endsWith(".js")) {
151
- const { fixedJsCode } = replaceImportsInJsCode({
152
- jsCode: sourceCode.toString("utf8"),
153
- buildContext
154
- });
155
-
156
- return {
157
- modifiedSourceCode: Buffer.from(fixedJsCode, "utf8")
158
- };
159
- }
160
-
161
- return { modifiedSourceCode: sourceCode };
162
- }
163
- });
164
- }
165
-
166
- const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
167
- themeName,
168
- indexHtmlCode: fs
169
- .readFileSync(pathJoin(buildContext.projectBuildDirPath, "index.html"))
170
- .toString("utf8"),
171
- buildContext,
172
- keycloakifyVersion: readThisNpmPackageVersion(),
173
- themeType,
174
- fieldNames: readFieldNameUsage({
175
- themeSrcDirPath: buildContext.themeSrcDirPath,
176
- themeType
177
- })
178
- });
179
-
180
- [
181
- ...(() => {
182
- switch (themeType) {
183
- case "login":
184
- return LOGIN_THEME_PAGE_IDS;
185
- case "account":
186
- return isForAccountSpa ? ["index.ftl"] : ACCOUNT_THEME_PAGE_IDS;
187
- }
188
- })(),
189
- ...(isForAccountSpa
190
- ? []
191
- : readExtraPagesNames({
192
- themeType,
193
- themeSrcDirPath: buildContext.themeSrcDirPath
194
- }))
195
- ].forEach(pageId => {
196
- const { ftlCode } = generateFtlFilesCode({ pageId });
197
-
198
- fs.writeFileSync(
199
- pathJoin(themeTypeDirPath, pageId),
200
- Buffer.from(ftlCode, "utf8")
201
- );
202
- });
203
-
204
- let languageTags: string[] | undefined = undefined;
205
-
206
- i18n_messages_generation: {
207
- if (isForAccountSpa) {
208
- break i18n_messages_generation;
209
- }
210
-
211
- const wrap = generateMessageProperties({
212
- buildContext,
213
- themeType
214
- });
215
-
216
- languageTags = wrap.languageTags;
217
- const { writeMessagePropertiesFiles } = wrap;
218
-
219
- writeMessagePropertiesFilesByThemeType[themeType] =
220
- writeMessagePropertiesFiles;
221
-
222
- writeMessagePropertiesFiles({
223
- messageDirPath: pathJoin(themeTypeDirPath, "messages"),
224
- themeName
225
- });
226
- }
227
-
228
- bring_in_account_v3_i18n_messages: {
229
- if (!buildContext.implementedThemeTypes.account.isImplemented) {
230
- break bring_in_account_v3_i18n_messages;
231
- }
232
- if (buildContext.implementedThemeTypes.account.type !== "Single-Page") {
233
- break bring_in_account_v3_i18n_messages;
234
- }
235
-
236
- const accountUiDirPath = child_process
237
- .execSync("npm list @keycloakify/keycloak-account-ui --parseable", {
238
- cwd: pathDirname(buildContext.packageJsonFilePath)
239
- })
240
- .toString("utf8")
241
- .trim();
242
-
243
- const messageDirPath_defaults = pathJoin(accountUiDirPath, "messages");
244
-
245
- if (!fs.existsSync(messageDirPath_defaults)) {
246
- throw new Error(
247
- `Please update @keycloakify/keycloak-account-ui to 25.0.4-rc.5 or later.`
248
- );
249
- }
250
-
251
- const messagesDirPath_dest = pathJoin(
252
- getThemeTypeDirPath({ themeType: "account" }),
253
- "messages"
254
- );
255
-
256
- transformCodebase({
257
- srcDirPath: messageDirPath_defaults,
258
- destDirPath: messagesDirPath_dest
259
- });
260
-
261
- apply_theme_changes: {
262
- const messagesDirPath_theme = pathJoin(
263
- buildContext.themeSrcDirPath,
264
- "account",
265
- "messages"
266
- );
267
-
268
- if (!fs.existsSync(messagesDirPath_theme)) {
269
- break apply_theme_changes;
270
- }
271
-
272
- fs.readdirSync(messagesDirPath_theme).forEach(basename => {
273
- const filePath_src = pathJoin(messagesDirPath_theme, basename);
274
- const filePath_dest = pathJoin(messagesDirPath_dest, basename);
275
-
276
- if (!fs.existsSync(filePath_dest)) {
277
- fs.cpSync(filePath_src, filePath_dest);
278
- }
279
-
280
- const messages_src = propertiesParser.parse(
281
- fs.readFileSync(filePath_src).toString("utf8")
282
- );
283
- const messages_dest = propertiesParser.parse(
284
- fs.readFileSync(filePath_dest).toString("utf8")
285
- );
286
-
287
- const messages = {
288
- ...messages_dest,
289
- ...messages_src
290
- };
291
-
292
- const editor = propertiesParser.createEditor();
293
-
294
- Object.entries(messages).forEach(([key, value]) => {
295
- editor.set(key, value);
296
- });
297
-
298
- fs.writeFileSync(
299
- filePath_dest,
300
- Buffer.from(editor.toString(), "utf8")
301
- );
302
- });
303
- }
304
-
305
- languageTags = fs
306
- .readdirSync(messagesDirPath_dest)
307
- .map(basename =>
308
- basename.replace(/^messages_/, "").replace(/\.properties$/, "")
309
- );
310
- }
311
-
312
- keycloak_static_resources: {
313
- if (isForAccountSpa) {
314
- break keycloak_static_resources;
315
- }
316
-
317
- transformCodebase({
318
- srcDirPath: pathJoin(
319
- getThisCodebaseRootDirPath(),
320
- "res",
321
- "public",
322
- WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES,
323
- themeType
324
- ),
325
- destDirPath: pathJoin(themeTypeDirPath, "resources")
326
- });
327
- }
328
-
329
- fs.writeFileSync(
330
- pathJoin(themeTypeDirPath, "theme.properties"),
331
- Buffer.from(
332
- [
333
- `parent=${(() => {
334
- switch (themeType) {
335
- case "account":
336
- return isForAccountSpa ? "base" : "account-v1";
337
- case "login":
338
- return "keycloak";
339
- }
340
- assert<Equals<typeof themeType, never>>(false);
341
- })()}`,
342
- ...(isForAccountSpa ? ["deprecatedMode=false"] : []),
343
- ...(buildContext.extraThemeProperties ?? []),
344
- ...buildContext.environmentVariables.map(
345
- ({ name, default: defaultValue }) =>
346
- `${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`
347
- ),
348
- ...(languageTags === undefined
349
- ? []
350
- : [`locales=${languageTags.join(",")}`])
351
- ].join("\n\n"),
352
- "utf8"
353
- )
354
- );
355
- }
356
-
357
- email: {
358
- if (!buildContext.implementedThemeTypes.email.isImplemented) {
359
- break email;
360
- }
361
-
362
- const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
363
-
364
- transformCodebase({
365
- srcDirPath: emailThemeSrcDirPath,
366
- destDirPath: getThemeTypeDirPath({ themeType: "email" })
367
- });
368
- }
369
-
370
- bring_in_account_v1: {
371
- if (!buildContext.implementedThemeTypes.account.isImplemented) {
372
- break bring_in_account_v1;
373
- }
374
-
375
- if (buildContext.implementedThemeTypes.account.type !== "Multi-Page") {
376
- break bring_in_account_v1;
377
- }
378
-
379
- transformCodebase({
380
- srcDirPath: pathJoin(getThisCodebaseRootDirPath(), "res", "account-v1"),
381
- destDirPath: pathJoin(resourcesDirPath, "theme", "account-v1", "account")
382
- });
383
- }
384
-
385
- {
386
- const metaInfKeycloakThemes: MetaInfKeycloakTheme = { themes: [] };
387
-
388
- metaInfKeycloakThemes.themes.push({
389
- name: themeName,
390
- types: objectEntries(buildContext.implementedThemeTypes)
391
- .filter(([, { isImplemented }]) => isImplemented)
392
- .map(([themeType]) => themeType)
393
- });
394
-
395
- if (buildContext.implementedThemeTypes.account.isImplemented) {
396
- metaInfKeycloakThemes.themes.push({
397
- name: "account-v1",
398
- types: ["account"]
399
- });
400
- }
401
-
402
- writeMetaInfKeycloakThemes({
403
- resourcesDirPath,
404
- getNewMetaInfKeycloakTheme: () => metaInfKeycloakThemes
405
- });
406
- }
407
-
408
- return {
409
- writeMessagePropertiesFilesForThemeVariant: ({
410
- getMessageDirPath,
411
- themeName
412
- }) => {
413
- objectEntries(writeMessagePropertiesFilesByThemeType).forEach(
414
- ([themeType, writeMessagePropertiesFiles]) => {
415
- if (writeMessagePropertiesFiles === undefined) {
416
- return;
417
- }
418
- writeMessagePropertiesFiles({
419
- messageDirPath: getMessageDirPath({ themeType }),
420
- themeName
421
- });
422
- }
423
- );
424
- }
425
- };
426
- }
@@ -1,76 +0,0 @@
1
- import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
2
- import { transformCodebase } from "../../tools/transformCodebase";
3
- import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
4
- import { assert } from "tsafe/assert";
5
- import type { ThemeType } from "../../shared/constants";
6
-
7
- export function generateResourcesForThemeVariant(params: {
8
- resourcesDirPath: string;
9
- themeName: string;
10
- themeVariantName: string;
11
- writeMessagePropertiesFiles: (params: {
12
- getMessageDirPath: (params: { themeType: ThemeType }) => string;
13
- themeName: string;
14
- }) => void;
15
- }) {
16
- const { resourcesDirPath, themeName, themeVariantName, writeMessagePropertiesFiles } =
17
- params;
18
-
19
- const mainThemeDirPath = pathJoin(resourcesDirPath, "theme", themeName);
20
- const themeVariantDirPath = pathJoin(mainThemeDirPath, "..", themeVariantName);
21
-
22
- transformCodebase({
23
- srcDirPath: mainThemeDirPath,
24
- destDirPath: themeVariantDirPath,
25
- transformSourceCode: ({ fileRelativePath, sourceCode }) => {
26
- if (
27
- pathExtname(fileRelativePath) === ".ftl" &&
28
- fileRelativePath.split(pathSep).length === 2
29
- ) {
30
- const modifiedSourceCode = Buffer.from(
31
- Buffer.from(sourceCode)
32
- .toString("utf-8")
33
- .replace(
34
- `"themeName": "${themeName}"`,
35
- `"themeName": "${themeVariantName}"`
36
- ),
37
- "utf8"
38
- );
39
-
40
- assert(Buffer.compare(modifiedSourceCode, sourceCode) !== 0);
41
-
42
- return { modifiedSourceCode };
43
- }
44
-
45
- return { modifiedSourceCode: sourceCode };
46
- }
47
- });
48
-
49
- writeMetaInfKeycloakThemes({
50
- resourcesDirPath,
51
- getNewMetaInfKeycloakTheme: ({ metaInfKeycloakTheme }) => {
52
- assert(metaInfKeycloakTheme !== undefined);
53
-
54
- const newMetaInfKeycloakTheme = metaInfKeycloakTheme;
55
-
56
- newMetaInfKeycloakTheme.themes.push({
57
- name: themeVariantName,
58
- types: (() => {
59
- const theme = newMetaInfKeycloakTheme.themes.find(
60
- ({ name }) => name === themeName
61
- );
62
- assert(theme !== undefined);
63
- return theme.types;
64
- })()
65
- });
66
-
67
- return newMetaInfKeycloakTheme;
68
- }
69
- });
70
-
71
- writeMessagePropertiesFiles({
72
- getMessageDirPath: ({ themeType }) =>
73
- pathJoin(themeVariantDirPath, themeType, "messages"),
74
- themeName: themeVariantName
75
- });
76
- }