keycloakify 11.6.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keycloakify",
3
- "version": "11.6.0",
3
+ "version": "11.6.1",
4
4
  "description": "Framework to create custom Keycloak UIs",
5
5
  "repository": {
6
6
  "type": "git",
@@ -661,6 +661,7 @@
661
661
  "src/bin/initialize-account-theme/src/single-page/KcContext.ts",
662
662
  "src/bin/initialize-account-theme/src/single-page/KcPage.tsx",
663
663
  "src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts",
664
+ "src/bin/initialize-admin-theme.ts",
664
665
  "src/bin/initialize-email-theme.ts",
665
666
  "src/bin/keycloakify/buildJars/buildJar.ts",
666
667
  "src/bin/keycloakify/buildJars/buildJars.ts",
@@ -691,6 +692,7 @@
691
692
  "src/bin/postinstall/postinstall.ts",
692
693
  "src/bin/postinstall/uiModuleMeta.ts",
693
694
  "src/bin/shared/KeycloakVersionRange.ts",
695
+ "src/bin/shared/addPostinstallScriptIfNotPresent.ts",
694
696
  "src/bin/shared/buildContext.ts",
695
697
  "src/bin/shared/constants.ts",
696
698
  "src/bin/shared/customHandler.ts",
@@ -1042,6 +1044,7 @@
1042
1044
  "bin/initialize-account-theme/initializeAccountTheme_multiPage.d.ts",
1043
1045
  "bin/initialize-account-theme/initializeAccountTheme_singlePage.d.ts",
1044
1046
  "bin/initialize-account-theme/updateAccountThemeImplementationInConfig.d.ts",
1047
+ "bin/initialize-admin-theme.d.ts",
1045
1048
  "bin/initialize-email-theme.d.ts",
1046
1049
  "bin/keycloakify/buildJars/buildJar.d.ts",
1047
1050
  "bin/keycloakify/buildJars/buildJars.d.ts",
@@ -1070,6 +1073,7 @@
1070
1073
  "bin/postinstall/managedGitignoreFile.d.ts",
1071
1074
  "bin/postinstall/postinstall.d.ts",
1072
1075
  "bin/postinstall/uiModuleMeta.d.ts",
1076
+ "bin/shared/addPostinstallScriptIfNotPresent.d.ts",
1073
1077
  "bin/shared/buildContext.d.ts",
1074
1078
  "bin/shared/constants.d.ts",
1075
1079
  "bin/shared/customHandler_delegate.d.ts",
@@ -1134,16 +1138,18 @@
1134
1138
  "bin/update-kc-gen.d.ts",
1135
1139
  "bin/main.js",
1136
1140
  "bin/153.index.js",
1141
+ "bin/174.index.js",
1137
1142
  "bin/266.index.js",
1143
+ "bin/300.index.js",
1138
1144
  "bin/304.index.js",
1139
- "bin/33.index.js",
1140
- "bin/356.index.js",
1141
1145
  "bin/375.index.js",
1142
1146
  "bin/40.index.js",
1143
1147
  "bin/453.index.js",
1144
1148
  "bin/490.index.js",
1145
1149
  "bin/503.index.js",
1146
1150
  "bin/525.index.js",
1151
+ "bin/568.index.js",
1152
+ "bin/615.index.js",
1147
1153
  "bin/653.index.js",
1148
1154
  "bin/658.index.js",
1149
1155
  "bin/720.index.js",
@@ -1151,7 +1157,6 @@
1151
1157
  "bin/743.index.js",
1152
1158
  "bin/783.index.js",
1153
1159
  "bin/786.index.js",
1154
- "bin/805.index.js",
1155
1160
  "bin/854.index.js",
1156
1161
  "bin/877.index.js",
1157
1162
  "bin/880.index.js",
@@ -119,7 +119,9 @@ export async function initializeAccountTheme_singlePage(params: {
119
119
  JSON.stringify(parsedPackageJson, undefined, 4)
120
120
  );
121
121
 
122
- npmInstall({ packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath) });
122
+ await npmInstall({
123
+ packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath)
124
+ });
123
125
 
124
126
  copyBoilerplate({
125
127
  accountThemeType: "Single-Page",
@@ -0,0 +1,146 @@
1
+ import { dirname as pathDirname, join as pathJoin, relative as pathRelative } from "path";
2
+ import type { BuildContext } from "./shared/buildContext";
3
+ import * as fs from "fs";
4
+ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
5
+ import { assert, is, type Equals } from "tsafe/assert";
6
+ import { id } from "tsafe/id";
7
+ import { addPostinstallScriptIfNotPresent } from "./shared/addPostinstallScriptIfNotPresent";
8
+ import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
9
+ import { npmInstall } from "./tools/npmInstall";
10
+ import * as child_process from "child_process";
11
+ import { z } from "zod";
12
+ import chalk from "chalk";
13
+
14
+ export async function command(params: { buildContext: BuildContext }) {
15
+ const { buildContext } = params;
16
+
17
+ const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
18
+ commandName: "initialize-admin-theme",
19
+ buildContext
20
+ });
21
+
22
+ if (hasBeenHandled) {
23
+ return;
24
+ }
25
+
26
+ {
27
+ const adminThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "admin");
28
+
29
+ if (
30
+ fs.existsSync(adminThemeSrcDirPath) &&
31
+ fs.readdirSync(adminThemeSrcDirPath).length > 0
32
+ ) {
33
+ console.warn(
34
+ chalk.red(
35
+ `There is already a ${pathRelative(
36
+ process.cwd(),
37
+ adminThemeSrcDirPath
38
+ )} directory in your project. Aborting.`
39
+ )
40
+ );
41
+
42
+ process.exit(-1);
43
+ }
44
+ }
45
+
46
+ const parsedPackageJson = (() => {
47
+ type ParsedPackageJson = {
48
+ scripts?: Record<string, string | undefined>;
49
+ dependencies?: Record<string, string | undefined>;
50
+ devDependencies?: Record<string, string | undefined>;
51
+ };
52
+
53
+ const zParsedPackageJson = (() => {
54
+ type TargetType = ParsedPackageJson;
55
+
56
+ const zTargetType = z.object({
57
+ scripts: z.record(z.union([z.string(), z.undefined()])).optional(),
58
+ dependencies: z.record(z.union([z.string(), z.undefined()])).optional(),
59
+ devDependencies: z.record(z.union([z.string(), z.undefined()])).optional()
60
+ });
61
+
62
+ assert<Equals<z.infer<typeof zTargetType>, TargetType>>;
63
+
64
+ return id<z.ZodType<TargetType>>(zTargetType);
65
+ })();
66
+ const parsedPackageJson = JSON.parse(
67
+ fs.readFileSync(buildContext.packageJsonFilePath).toString("utf8")
68
+ );
69
+
70
+ zParsedPackageJson.parse(parsedPackageJson);
71
+
72
+ assert(is<ParsedPackageJson>(parsedPackageJson));
73
+
74
+ return parsedPackageJson;
75
+ })();
76
+
77
+ addPostinstallScriptIfNotPresent({
78
+ parsedPackageJson,
79
+ buildContext
80
+ });
81
+
82
+ const uiSharedMajor = (() => {
83
+ const dependencies = {
84
+ ...parsedPackageJson.devDependencies,
85
+ ...parsedPackageJson.dependencies
86
+ };
87
+
88
+ const version = dependencies["@keycloakify/keycloak-ui-shared"];
89
+
90
+ if (version === undefined) {
91
+ return undefined;
92
+ }
93
+
94
+ const match = version.match(/^[^~]?(\d+)\./);
95
+
96
+ if (match === null) {
97
+ return undefined;
98
+ }
99
+
100
+ return match[1];
101
+ })();
102
+
103
+ const moduleName = "@keycloakify/keycloak-admin-ui";
104
+
105
+ const version = (
106
+ JSON.parse(
107
+ child_process
108
+ .execSync(`npm show ${moduleName} versions --json`)
109
+ .toString("utf8")
110
+ .trim()
111
+ ) as string[]
112
+ )
113
+ .reverse()
114
+ .filter(version => !version.includes("-"))
115
+ .find(version =>
116
+ uiSharedMajor === undefined ? true : version.startsWith(`${uiSharedMajor}.`)
117
+ );
118
+
119
+ assert(version !== undefined);
120
+
121
+ (parsedPackageJson.dependencies ??= {})[moduleName] = `~${version}`;
122
+
123
+ if (parsedPackageJson.devDependencies !== undefined) {
124
+ delete parsedPackageJson.devDependencies[moduleName];
125
+ }
126
+
127
+ {
128
+ let sourceCode = JSON.stringify(parsedPackageJson, undefined, 2);
129
+
130
+ if (await getIsPrettierAvailable()) {
131
+ sourceCode = await runPrettier({
132
+ sourceCode,
133
+ filePath: buildContext.packageJsonFilePath
134
+ });
135
+ }
136
+
137
+ fs.writeFileSync(
138
+ buildContext.packageJsonFilePath,
139
+ Buffer.from(sourceCode, "utf8")
140
+ );
141
+ }
142
+
143
+ await npmInstall({
144
+ packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath)
145
+ });
146
+ }
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 the account theme."
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",
@@ -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
+ }
@@ -12,6 +12,7 @@ export type CommandName =
12
12
  | "add-story"
13
13
  | "initialize-account-theme"
14
14
  | "initialize-admin-theme"
15
+ | "initialize-admin-theme"
15
16
  | "initialize-email-theme"
16
17
  | "copy-keycloak-resources-to-public";
17
18
 
@@ -9,8 +9,9 @@ import { objectKeys } from "tsafe/objectKeys";
9
9
  import { getAbsoluteAndInOsFormatPath } from "./getAbsoluteAndInOsFormatPath";
10
10
  import { exclude } from "tsafe/exclude";
11
11
  import { rmSync } from "./fs.rmSync";
12
+ import { Deferred } from "evt/tools/Deferred";
12
13
 
13
- export function npmInstall(params: { packageJsonDirPath: string }) {
14
+ export async function npmInstall(params: { packageJsonDirPath: string }) {
14
15
  const { packageJsonDirPath } = params;
15
16
 
16
17
  const packageManagerBinName = (() => {
@@ -68,7 +69,7 @@ export function npmInstall(params: { packageJsonDirPath: string }) {
68
69
 
69
70
  console.log(chalk.green("Installing in a way that won't break the links..."));
70
71
 
71
- installWithoutBreakingLinks({
72
+ await installWithoutBreakingLinks({
72
73
  packageJsonDirPath,
73
74
  garronejLinkInfos
74
75
  });
@@ -77,9 +78,9 @@ export function npmInstall(params: { packageJsonDirPath: string }) {
77
78
  }
78
79
 
79
80
  try {
80
- child_process.execSync(`${packageManagerBinName} install`, {
81
- cwd: packageJsonDirPath,
82
- stdio: "inherit"
81
+ await runPackageManagerInstall({
82
+ packageManagerBinName,
83
+ cwd: packageJsonDirPath
83
84
  });
84
85
  } catch {
85
86
  console.log(
@@ -90,6 +91,42 @@ export function npmInstall(params: { packageJsonDirPath: string }) {
90
91
  }
91
92
  }
92
93
 
94
+ async function runPackageManagerInstall(params: {
95
+ packageManagerBinName: string;
96
+ cwd: string;
97
+ }) {
98
+ const { packageManagerBinName, cwd } = params;
99
+
100
+ const dCompleted = new Deferred<void>();
101
+
102
+ const child = child_process.spawn(packageManagerBinName, ["install"], {
103
+ cwd,
104
+ env: process.env,
105
+ shell: true
106
+ });
107
+
108
+ child.stdout.on("data", data => process.stdout.write(data));
109
+
110
+ child.stderr.on("data", data => {
111
+ if (data.toString("utf8").includes("has unmet peer dependency")) {
112
+ return;
113
+ }
114
+
115
+ process.stderr.write(data);
116
+ });
117
+
118
+ child.on("exit", code => {
119
+ if (code !== 0) {
120
+ dCompleted.reject(new Error(`Failed with code ${code}`));
121
+ return;
122
+ }
123
+
124
+ dCompleted.resolve();
125
+ });
126
+
127
+ await dCompleted.pr;
128
+ }
129
+
93
130
  function getGarronejLinkInfos(params: {
94
131
  packageJsonDirPath: string;
95
132
  }): { linkedModuleNames: string[]; yarnHomeDirPath: string } | undefined {
@@ -180,7 +217,7 @@ function getGarronejLinkInfos(params: {
180
217
  return { linkedModuleNames, yarnHomeDirPath };
181
218
  }
182
219
 
183
- function installWithoutBreakingLinks(params: {
220
+ async function installWithoutBreakingLinks(params: {
184
221
  packageJsonDirPath: string;
185
222
  garronejLinkInfos: Exclude<ReturnType<typeof getGarronejLinkInfos>, undefined>;
186
223
  }) {
@@ -261,9 +298,9 @@ function installWithoutBreakingLinks(params: {
261
298
  pathJoin(tmpProjectDirPath, YARN_LOCK)
262
299
  );
263
300
 
264
- child_process.execSync(`yarn install`, {
265
- cwd: tmpProjectDirPath,
266
- stdio: "inherit"
301
+ await runPackageManagerInstall({
302
+ packageManagerBinName: "yarn",
303
+ cwd: tmpProjectDirPath
267
304
  });
268
305
 
269
306
  // NOTE: Moving the modules from the tmp project to the actual project