keycloakify 11.5.4 → 11.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/153.index.js +81 -28
- package/bin/{805.index.js → 174.index.js} +111 -2
- package/bin/{356.index.js → 300.index.js} +67 -29
- package/bin/{33.index.js → 568.index.js} +2 -113
- package/bin/615.index.js +1009 -0
- package/bin/653.index.js +53 -135
- package/bin/735.index.js +53 -24
- package/bin/854.index.js +1 -1
- package/bin/{573.index.js → 880.index.js} +155 -9
- package/bin/921.index.js +1 -1
- package/bin/initialize-admin-theme.d.ts +4 -0
- package/bin/main.js +22 -9
- package/bin/shared/addPostinstallScriptIfNotPresent.d.ts +10 -0
- package/bin/shared/customHandler.d.ts +1 -1
- package/bin/shared/customHandler.js.map +1 -1
- package/bin/tools/createObjectThatThrowsIfAccessed.d.ts +21 -0
- package/bin/tools/npmInstall.d.ts +1 -1
- package/package.json +13 -5
- package/src/bin/initialize-account-theme/initializeAccountTheme_singlePage.ts +3 -1
- package/src/bin/initialize-admin-theme.ts +146 -0
- package/src/bin/keycloakify/generateResources/generateResources.ts +162 -6
- package/src/bin/main.ts +19 -4
- package/src/bin/postinstall/getUiModuleFileSourceCodeReadyToBeCopied.ts +63 -24
- package/src/bin/postinstall/installUiModulesPeerDependencies.ts +1 -1
- package/src/bin/postinstall/uiModuleMeta.ts +7 -1
- package/src/bin/shared/addPostinstallScriptIfNotPresent.ts +70 -0
- package/src/bin/shared/customHandler.ts +1 -0
- package/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-22.json +2201 -0
- package/src/bin/start-keycloak/start-keycloak.ts +131 -36
- package/src/bin/tools/createObjectThatThrowsIfAccessed.ts +90 -0
- package/src/bin/tools/npmInstall.ts +46 -9
@@ -40,6 +40,7 @@ import { escapeStringForPropertiesFile } from "../../tools/escapeStringForProper
|
|
40
40
|
import * as child_process from "child_process";
|
41
41
|
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
42
42
|
import propertiesParser from "properties-parser";
|
43
|
+
import { createObjectThatThrowsIfAccessed } from "../../tools/createObjectThatThrowsIfAccessed";
|
43
44
|
|
44
45
|
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
45
46
|
BuildContextLike_generateMessageProperties & {
|
@@ -256,15 +257,17 @@ export async function generateResources(params: {
|
|
256
257
|
writeMessagePropertiesFiles;
|
257
258
|
}
|
258
259
|
|
259
|
-
|
260
|
+
bring_in_account_spa_messages: {
|
260
261
|
if (!isSpa) {
|
261
|
-
break
|
262
|
+
break bring_in_account_spa_messages;
|
262
263
|
}
|
263
264
|
|
264
|
-
|
265
|
+
if (themeType !== "account") {
|
266
|
+
break bring_in_account_spa_messages;
|
267
|
+
}
|
265
268
|
|
266
269
|
const accountUiDirPath = child_process
|
267
|
-
.execSync(`npm list @keycloakify/keycloak
|
270
|
+
.execSync(`npm list @keycloakify/keycloak-account-ui --parseable`, {
|
268
271
|
cwd: pathDirname(buildContext.packageJsonFilePath)
|
269
272
|
})
|
270
273
|
.toString("utf8")
|
@@ -279,7 +282,7 @@ export async function generateResources(params: {
|
|
279
282
|
}
|
280
283
|
|
281
284
|
const messagesDirPath_dest = pathJoin(
|
282
|
-
getThemeTypeDirPath({ themeName, themeType }),
|
285
|
+
getThemeTypeDirPath({ themeName, themeType: "account" }),
|
283
286
|
"messages"
|
284
287
|
);
|
285
288
|
|
@@ -291,7 +294,7 @@ export async function generateResources(params: {
|
|
291
294
|
apply_theme_changes: {
|
292
295
|
const messagesDirPath_theme = pathJoin(
|
293
296
|
buildContext.themeSrcDirPath,
|
294
|
-
|
297
|
+
"account",
|
295
298
|
"messages"
|
296
299
|
);
|
297
300
|
|
@@ -339,6 +342,159 @@ export async function generateResources(params: {
|
|
339
342
|
);
|
340
343
|
}
|
341
344
|
|
345
|
+
bring_in_admin_messages: {
|
346
|
+
if (themeType !== "admin") {
|
347
|
+
break bring_in_admin_messages;
|
348
|
+
}
|
349
|
+
|
350
|
+
const messagesDirPath_theme = pathJoin(
|
351
|
+
buildContext.themeSrcDirPath,
|
352
|
+
"admin",
|
353
|
+
"i18n"
|
354
|
+
);
|
355
|
+
|
356
|
+
assert(
|
357
|
+
fs.existsSync(messagesDirPath_theme),
|
358
|
+
`${messagesDirPath_theme} is supposed to exist`
|
359
|
+
);
|
360
|
+
|
361
|
+
const propertiesByLang: Record<
|
362
|
+
string,
|
363
|
+
{
|
364
|
+
base: Buffer;
|
365
|
+
override: Buffer | undefined;
|
366
|
+
overrideByThemeName: Record<string, Buffer>;
|
367
|
+
}
|
368
|
+
> = {};
|
369
|
+
|
370
|
+
fs.readdirSync(messagesDirPath_theme).forEach(basename => {
|
371
|
+
type ParsedBasename = { lang: string } & (
|
372
|
+
| {
|
373
|
+
isOverride: false;
|
374
|
+
}
|
375
|
+
| {
|
376
|
+
isOverride: true;
|
377
|
+
themeName: string | undefined;
|
378
|
+
}
|
379
|
+
);
|
380
|
+
|
381
|
+
const parsedBasename = ((): ParsedBasename | undefined => {
|
382
|
+
const match = basename.match(/^messages_([^.]+)\.properties$/);
|
383
|
+
|
384
|
+
if (match === null) {
|
385
|
+
return undefined;
|
386
|
+
}
|
387
|
+
|
388
|
+
const discriminator = match[1];
|
389
|
+
|
390
|
+
const split = discriminator.split("_override");
|
391
|
+
|
392
|
+
if (split.length === 1) {
|
393
|
+
return {
|
394
|
+
lang: discriminator,
|
395
|
+
isOverride: false
|
396
|
+
};
|
397
|
+
}
|
398
|
+
|
399
|
+
assert(split.length === 2);
|
400
|
+
|
401
|
+
if (split[1] === "") {
|
402
|
+
return {
|
403
|
+
lang: split[0],
|
404
|
+
isOverride: true,
|
405
|
+
themeName: undefined
|
406
|
+
};
|
407
|
+
}
|
408
|
+
|
409
|
+
const match2 = split[1].match(/^_(.+)$/);
|
410
|
+
|
411
|
+
assert(match2 !== null);
|
412
|
+
|
413
|
+
return {
|
414
|
+
lang: split[0],
|
415
|
+
isOverride: true,
|
416
|
+
themeName: match2[1]
|
417
|
+
};
|
418
|
+
})();
|
419
|
+
|
420
|
+
if (parsedBasename === undefined) {
|
421
|
+
return;
|
422
|
+
}
|
423
|
+
|
424
|
+
propertiesByLang[parsedBasename.lang] ??= {
|
425
|
+
base: createObjectThatThrowsIfAccessed<Buffer>({
|
426
|
+
debugMessage: `No base ${parsedBasename.lang} translation for admin theme`
|
427
|
+
}),
|
428
|
+
override: undefined,
|
429
|
+
overrideByThemeName: {}
|
430
|
+
};
|
431
|
+
|
432
|
+
const buffer = fs.readFileSync(pathJoin(messagesDirPath_theme, basename));
|
433
|
+
|
434
|
+
if (parsedBasename.isOverride === false) {
|
435
|
+
propertiesByLang[parsedBasename.lang].base = buffer;
|
436
|
+
return;
|
437
|
+
}
|
438
|
+
|
439
|
+
if (parsedBasename.themeName === undefined) {
|
440
|
+
propertiesByLang[parsedBasename.lang].override = buffer;
|
441
|
+
return;
|
442
|
+
}
|
443
|
+
|
444
|
+
propertiesByLang[parsedBasename.lang].overrideByThemeName[
|
445
|
+
parsedBasename.themeName
|
446
|
+
] = buffer;
|
447
|
+
});
|
448
|
+
|
449
|
+
writeMessagePropertiesFilesByThemeType.admin = ({
|
450
|
+
messageDirPath,
|
451
|
+
themeName
|
452
|
+
}) => {
|
453
|
+
if (!fs.existsSync(messageDirPath)) {
|
454
|
+
fs.mkdirSync(messageDirPath, { recursive: true });
|
455
|
+
}
|
456
|
+
|
457
|
+
Object.entries(propertiesByLang).forEach(
|
458
|
+
([lang, { base, override, overrideByThemeName }]) => {
|
459
|
+
(languageTags ??= []).push(lang);
|
460
|
+
|
461
|
+
const messages = propertiesParser.parse(base.toString("utf8"));
|
462
|
+
|
463
|
+
if (override !== undefined) {
|
464
|
+
const overrideMessages = propertiesParser.parse(
|
465
|
+
override.toString("utf8")
|
466
|
+
);
|
467
|
+
|
468
|
+
Object.entries(overrideMessages).forEach(
|
469
|
+
([key, value]) => (messages[key] = value)
|
470
|
+
);
|
471
|
+
}
|
472
|
+
|
473
|
+
if (themeName in overrideByThemeName) {
|
474
|
+
const overrideMessages = propertiesParser.parse(
|
475
|
+
overrideByThemeName[themeName].toString("utf8")
|
476
|
+
);
|
477
|
+
|
478
|
+
Object.entries(overrideMessages).forEach(
|
479
|
+
([key, value]) => (messages[key] = value)
|
480
|
+
);
|
481
|
+
}
|
482
|
+
|
483
|
+
const editor = propertiesParser.createEditor();
|
484
|
+
|
485
|
+
Object.entries(messages).forEach(([key, value]) => {
|
486
|
+
editor.set(key, value);
|
487
|
+
});
|
488
|
+
|
489
|
+
fs.writeFileSync(
|
490
|
+
pathJoin(messageDirPath, `messages_${lang}.properties`),
|
491
|
+
Buffer.from(editor.toString(), "utf8")
|
492
|
+
);
|
493
|
+
}
|
494
|
+
);
|
495
|
+
};
|
496
|
+
}
|
497
|
+
|
342
498
|
keycloak_static_resources: {
|
343
499
|
if (isSpa) {
|
344
500
|
break keycloak_static_resources;
|
package/src/bin/main.ts
CHANGED
@@ -191,7 +191,7 @@ program
|
|
191
191
|
program
|
192
192
|
.command({
|
193
193
|
name: "initialize-account-theme",
|
194
|
-
description: "Initialize
|
194
|
+
description: "Initialize an Account Single-Page or Multi-Page custom Account UI."
|
195
195
|
})
|
196
196
|
.task({
|
197
197
|
skip,
|
@@ -202,6 +202,20 @@ program
|
|
202
202
|
}
|
203
203
|
});
|
204
204
|
|
205
|
+
program
|
206
|
+
.command({
|
207
|
+
name: "initialize-admin-theme",
|
208
|
+
description: "Initialize an Admin Console custom UI."
|
209
|
+
})
|
210
|
+
.task({
|
211
|
+
skip,
|
212
|
+
handler: async ({ projectDirPath }) => {
|
213
|
+
const { command } = await import("./initialize-admin-theme");
|
214
|
+
|
215
|
+
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
216
|
+
}
|
217
|
+
});
|
218
|
+
|
205
219
|
program
|
206
220
|
.command({
|
207
221
|
name: "copy-keycloak-resources-to-public",
|
@@ -259,11 +273,12 @@ program
|
|
259
273
|
.option({
|
260
274
|
key: "file",
|
261
275
|
name: (() => {
|
262
|
-
const
|
276
|
+
const long = "file";
|
277
|
+
const short = "f";
|
263
278
|
|
264
|
-
optionsKeys.push(
|
279
|
+
optionsKeys.push(long, short);
|
265
280
|
|
266
|
-
return
|
281
|
+
return { long, short };
|
267
282
|
})(),
|
268
283
|
description: [
|
269
284
|
"Relative path of the file relative to the directory of your keycloak theme source",
|
@@ -32,38 +32,20 @@ export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
|
32
32
|
await fsPr.readFile(pathJoin(uiModuleDirPath, KEYCLOAK_THEME, fileRelativePath))
|
33
33
|
).toString("utf8");
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
}
|
40
|
-
|
41
|
-
return [`/**`, ...lines.map(line => ` * ${line}`), ` */`].join("\n");
|
42
|
-
}
|
43
|
-
|
44
|
-
if (fileRelativePath.endsWith(".html")) {
|
45
|
-
return [`<!--`, ...lines.map(line => ` ${line}`), `-->`].join("\n");
|
46
|
-
}
|
47
|
-
|
48
|
-
return undefined;
|
49
|
-
};
|
50
|
-
|
51
|
-
const comment = toComment(
|
52
|
-
isForEjection
|
35
|
+
sourceCode = addCommentToSourceCode({
|
36
|
+
sourceCode,
|
37
|
+
fileRelativePath,
|
38
|
+
commentLines: isForEjection
|
53
39
|
? [`This file was ejected from ${uiModuleName} version ${uiModuleVersion}.`]
|
54
40
|
: [
|
55
41
|
`WARNING: Before modifying this file run the following command:`,
|
56
42
|
``,
|
57
|
-
`$ npx keycloakify eject-file --file ${fileRelativePath.split(pathSep).join("/")}`,
|
43
|
+
`$ npx keycloakify eject-file --file '${fileRelativePath.split(pathSep).join("/")}'`,
|
58
44
|
``,
|
59
45
|
`This file comes from ${uiModuleName} version ${uiModuleVersion}.`,
|
60
46
|
`This file has been copied over to your repo by your postinstall script: \`npx keycloakify postinstall\``
|
61
47
|
]
|
62
|
-
);
|
63
|
-
|
64
|
-
if (comment !== undefined) {
|
65
|
-
sourceCode = [comment, ``, sourceCode].join("\n");
|
66
|
-
}
|
48
|
+
});
|
67
49
|
|
68
50
|
const destFilePath = pathJoin(buildContext.themeSrcDirPath, fileRelativePath);
|
69
51
|
|
@@ -80,3 +62,60 @@ export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
|
80
62
|
|
81
63
|
return Buffer.from(sourceCode, "utf8");
|
82
64
|
}
|
65
|
+
|
66
|
+
function addCommentToSourceCode(params: {
|
67
|
+
sourceCode: string;
|
68
|
+
fileRelativePath: string;
|
69
|
+
commentLines: string[];
|
70
|
+
}): string {
|
71
|
+
const { sourceCode, fileRelativePath, commentLines } = params;
|
72
|
+
|
73
|
+
const toResult = (comment: string) => {
|
74
|
+
return [comment, ``, sourceCode].join("\n");
|
75
|
+
};
|
76
|
+
|
77
|
+
for (const ext of [".ts", ".tsx", ".css", ".less", ".sass", ".js", ".jsx"]) {
|
78
|
+
if (!fileRelativePath.endsWith(ext)) {
|
79
|
+
continue;
|
80
|
+
}
|
81
|
+
|
82
|
+
return toResult(
|
83
|
+
[`/**`, ...commentLines.map(line => ` * ${line}`), ` */`].join("\n")
|
84
|
+
);
|
85
|
+
}
|
86
|
+
|
87
|
+
if (fileRelativePath.endsWith(".properties")) {
|
88
|
+
return toResult(commentLines.map(line => `# ${line}`).join("\n"));
|
89
|
+
}
|
90
|
+
|
91
|
+
if (fileRelativePath.endsWith(".html") || fileRelativePath.endsWith(".svg")) {
|
92
|
+
const comment = [
|
93
|
+
`<!--`,
|
94
|
+
...commentLines.map(
|
95
|
+
line =>
|
96
|
+
` ${line.replace("--file", "-f").replace("Before modifying", "Before modifying or replacing")}`
|
97
|
+
),
|
98
|
+
`-->`
|
99
|
+
].join("\n");
|
100
|
+
|
101
|
+
if (fileRelativePath.endsWith(".html") && sourceCode.trim().startsWith("<!")) {
|
102
|
+
const [first, ...rest] = sourceCode.split(">");
|
103
|
+
|
104
|
+
const last = rest.join(">");
|
105
|
+
|
106
|
+
return [`${first}>`, comment, last].join("\n");
|
107
|
+
}
|
108
|
+
|
109
|
+
if (fileRelativePath.endsWith(".svg") && sourceCode.trim().startsWith("<?")) {
|
110
|
+
const [first, ...rest] = sourceCode.split("?>");
|
111
|
+
|
112
|
+
const last = rest.join("?>");
|
113
|
+
|
114
|
+
return [`${first}?>`, comment, last].join("\n");
|
115
|
+
}
|
116
|
+
|
117
|
+
return toResult(comment);
|
118
|
+
}
|
119
|
+
|
120
|
+
return sourceCode;
|
121
|
+
}
|
@@ -149,7 +149,7 @@ export async function installUiModulesPeerDependencies(params: {
|
|
149
149
|
|
150
150
|
await fsPr.writeFile(buildContext.packageJsonFilePath, packageJsonContentStr);
|
151
151
|
|
152
|
-
npmInstall({
|
152
|
+
await npmInstall({
|
153
153
|
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath)
|
154
154
|
});
|
155
155
|
|
@@ -16,6 +16,7 @@ import {
|
|
16
16
|
import * as crypto from "crypto";
|
17
17
|
import { KEYCLOAK_THEME } from "../shared/constants";
|
18
18
|
import { exclude } from "tsafe/exclude";
|
19
|
+
import { isAmong } from "tsafe/isAmong";
|
19
20
|
|
20
21
|
export type UiModuleMeta = {
|
21
22
|
moduleName: string;
|
@@ -264,7 +265,12 @@ export async function getUiModuleMetas(params: {
|
|
264
265
|
moduleName,
|
265
266
|
version,
|
266
267
|
files,
|
267
|
-
peerDependencies
|
268
|
+
peerDependencies: Object.fromEntries(
|
269
|
+
Object.entries(peerDependencies).filter(
|
270
|
+
([moduleName]) =>
|
271
|
+
!isAmong(["react", "@types/react"], moduleName)
|
272
|
+
)
|
273
|
+
)
|
268
274
|
});
|
269
275
|
}
|
270
276
|
)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import { dirname as pathDirname, relative as pathRelative, sep as pathSep } from "path";
|
2
|
+
import { assert } from "tsafe/assert";
|
3
|
+
import type { BuildContext } from "./buildContext";
|
4
|
+
|
5
|
+
export type BuildContextLike = {
|
6
|
+
projectDirPath: string;
|
7
|
+
packageJsonFilePath: string;
|
8
|
+
};
|
9
|
+
|
10
|
+
assert<BuildContext extends BuildContextLike ? true : false>();
|
11
|
+
|
12
|
+
export function addPostinstallScriptIfNotPresent(params: {
|
13
|
+
parsedPackageJson: { scripts?: Record<string, string | undefined> };
|
14
|
+
buildContext: BuildContextLike;
|
15
|
+
}) {
|
16
|
+
const { parsedPackageJson, buildContext } = params;
|
17
|
+
|
18
|
+
const cmd_base = "keycloakify postinstall";
|
19
|
+
|
20
|
+
const projectCliOptionValue = (() => {
|
21
|
+
const packageJsonDirPath = pathDirname(buildContext.packageJsonFilePath);
|
22
|
+
|
23
|
+
const relativePath = pathRelative(
|
24
|
+
packageJsonDirPath,
|
25
|
+
buildContext.projectDirPath
|
26
|
+
);
|
27
|
+
|
28
|
+
if (relativePath === "") {
|
29
|
+
return undefined;
|
30
|
+
}
|
31
|
+
|
32
|
+
return relativePath.split(pathSep).join("/");
|
33
|
+
})();
|
34
|
+
|
35
|
+
const generateCmd = (params: { cmd_preexisting: string | undefined }) => {
|
36
|
+
const { cmd_preexisting } = params;
|
37
|
+
|
38
|
+
let cmd = cmd_preexisting === undefined ? "" : `${cmd_preexisting} && `;
|
39
|
+
|
40
|
+
cmd += cmd_base;
|
41
|
+
|
42
|
+
if (projectCliOptionValue !== undefined) {
|
43
|
+
cmd += ` -p ${projectCliOptionValue}`;
|
44
|
+
}
|
45
|
+
|
46
|
+
return cmd;
|
47
|
+
};
|
48
|
+
|
49
|
+
{
|
50
|
+
const scripts = (parsedPackageJson.scripts ??= {});
|
51
|
+
|
52
|
+
for (const scriptName of ["postinstall", "prepare"]) {
|
53
|
+
const cmd_preexisting = scripts[scriptName];
|
54
|
+
|
55
|
+
if (cmd_preexisting === undefined) {
|
56
|
+
continue;
|
57
|
+
}
|
58
|
+
|
59
|
+
if (cmd_preexisting.includes(cmd_base)) {
|
60
|
+
scripts[scriptName] = generateCmd({ cmd_preexisting });
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
parsedPackageJson.scripts = {
|
67
|
+
postinstall: generateCmd({ cmd_preexisting: undefined }),
|
68
|
+
...parsedPackageJson.scripts
|
69
|
+
};
|
70
|
+
}
|