keycloakify 11.6.0 → 11.6.2

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 (49) hide show
  1. package/bin/{805.index.js → 174.index.js} +111 -2
  2. package/bin/363.index.js +1528 -0
  3. package/bin/453.index.js +1 -1
  4. package/bin/{33.index.js → 568.index.js} +2 -113
  5. package/bin/735.index.js +53 -24
  6. package/bin/{653.index.js → 840.index.js} +387 -213
  7. package/bin/921.index.js +1 -1
  8. package/bin/930.index.js +165 -0
  9. package/bin/946.index.js +20 -0
  10. package/bin/initialize-admin-theme.d.ts +4 -0
  11. package/bin/main.js +72 -18
  12. package/bin/{eject-file.d.ts → own.d.ts} +2 -1
  13. package/bin/shared/addSyncExtensionsToPostinstallScript.d.ts +10 -0
  14. package/bin/shared/customHandler.d.ts +1 -1
  15. package/bin/shared/customHandler.js.map +1 -1
  16. package/bin/{postinstall/uiModuleMeta.d.ts → sync-extensions/extensionModuleMeta.d.ts} +5 -5
  17. package/bin/sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.d.ts +12 -0
  18. package/bin/sync-extensions/index.d.ts +1 -0
  19. package/bin/{postinstall/installUiModulesPeerDependencies.d.ts → sync-extensions/installExtensionModulesPeerDependencies.d.ts} +3 -3
  20. package/bin/{postinstall → sync-extensions}/managedGitignoreFile.d.ts +4 -4
  21. package/bin/tools/isKnownByGit.d.ts +3 -0
  22. package/bin/tools/npmInstall.d.ts +1 -1
  23. package/package.json +27 -22
  24. package/src/bin/eject-page.ts +1 -3
  25. package/src/bin/initialize-account-theme/initializeAccountTheme_singlePage.ts +3 -1
  26. package/src/bin/initialize-admin-theme.ts +146 -0
  27. package/src/bin/main.ts +76 -17
  28. package/src/bin/own.ts +209 -0
  29. package/src/bin/shared/addSyncExtensionsToPostinstallScript.ts +70 -0
  30. package/src/bin/shared/customHandler.ts +1 -0
  31. package/src/bin/{postinstall/uiModuleMeta.ts → sync-extensions/extensionModuleMeta.ts} +55 -43
  32. package/src/bin/{postinstall/getUiModuleFileSourceCodeReadyToBeCopied.ts → sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.ts} +32 -21
  33. package/src/bin/sync-extensions/index.ts +1 -0
  34. package/src/bin/{postinstall/installUiModulesPeerDependencies.ts → sync-extensions/installExtensionModulesPeerDependencies.ts} +16 -14
  35. package/src/bin/{postinstall → sync-extensions}/managedGitignoreFile.ts +18 -18
  36. package/src/bin/{postinstall/postinstall.ts → sync-extensions/sync-extension.ts} +14 -26
  37. package/src/bin/tools/isKnownByGit.ts +45 -0
  38. package/src/bin/tools/listInstalledModules.ts +2 -2
  39. package/src/bin/tools/npmInstall.ts +46 -9
  40. package/src/bin/tools/untrackFromGit.ts +19 -3
  41. package/bin/356.index.js +0 -755
  42. package/bin/854.index.js +0 -68
  43. package/bin/postinstall/getUiModuleFileSourceCodeReadyToBeCopied.d.ts +0 -12
  44. package/bin/postinstall/index.d.ts +0 -1
  45. package/bin/tools/isTrackedByGit.d.ts +0 -3
  46. package/src/bin/eject-file.ts +0 -68
  47. package/src/bin/postinstall/index.ts +0 -1
  48. package/src/bin/tools/isTrackedByGit.ts +0 -29
  49. /package/bin/{postinstall/postinstall.d.ts → sync-extensions/sync-extension.d.ts} +0 -0
@@ -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 { addSyncExtensionsToPostinstallScript } from "./shared/addSyncExtensionsToPostinstallScript";
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
+ addSyncExtensionsToPostinstallScript({
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",
@@ -234,13 +248,30 @@ program
234
248
 
235
249
  program
236
250
  .command({
237
- name: "postinstall",
238
- description: "Initialize all the Keycloakify UI modules installed in the project."
251
+ name: "sync-extensions",
252
+ description: [
253
+ "Synchronizes all installed Keycloakify extension modules with your project.",
254
+ "",
255
+ "Example of extension modules: '@keycloakify/keycloak-account-ui', '@keycloakify/keycloak-admin-ui', '@keycloakify/keycloak-ui-shared'",
256
+ "",
257
+ "This command ensures that:",
258
+ "- All required files from installed extensions are copied into your project.",
259
+ "- The copied files are correctly ignored by Git to help you distinguish between your custom source files",
260
+ " and those provided by the extensions.",
261
+ "- Peer dependencies declared by the extensions are automatically added to your package.json.",
262
+ "",
263
+ "You can safely run this command multiple times. It will only update the files and dependencies if needed,",
264
+ "ensuring your project stays in sync with the installed extensions.",
265
+ "",
266
+ "Typical usage:",
267
+ "- Should be run as a postinstall script of your project.",
268
+ ""
269
+ ].join("\n")
239
270
  })
240
271
  .task({
241
272
  skip,
242
273
  handler: async ({ projectDirPath }) => {
243
- const { command } = await import("./postinstall");
274
+ const { command } = await import("./sync-extensions");
244
275
 
245
276
  await command({ buildContext: getBuildContext({ projectDirPath }) });
246
277
  }
@@ -248,37 +279,65 @@ program
248
279
 
249
280
  program
250
281
  .command<{
251
- file: string;
282
+ path: string;
283
+ revert: boolean;
252
284
  }>({
253
- name: "eject-file",
285
+ name: "own",
254
286
  description: [
255
- "WARNING: Not usable yet, will be used for future features",
256
- "Take ownership over a given file"
257
- ].join(" ")
287
+ "Manages ownership of auto-generated files provided by Keycloakify extensions.",
288
+ "",
289
+ "This command allows you to take ownership of a specific file or directory generated",
290
+ "by an extension. Once owned, you can freely modify and version-control the file.",
291
+ "",
292
+ "You can also use the --revert flag to relinquish ownership and restore the file",
293
+ "or directory to its original auto-generated state.",
294
+ "",
295
+ "For convenience, the exact command to take ownership of any file is included as a comment",
296
+ "in the header of each extension-generated file.",
297
+ "",
298
+ "Examples:",
299
+ "$ npx keycloakify own --path admin/KcPage.tsx"
300
+ ].join("\n")
258
301
  })
259
302
  .option({
260
- key: "file",
303
+ key: "path",
261
304
  name: (() => {
262
- const long = "file";
263
- const short = "f";
305
+ const long = "path";
306
+ const short = "p";
264
307
 
265
308
  optionsKeys.push(long, short);
266
309
 
267
310
  return { long, short };
268
311
  })(),
269
312
  description: [
270
- "Relative path of the file relative to the directory of your keycloak theme source",
271
- "Example `--file src/login/page/Login.tsx`"
313
+ "Specifies the relative path of the file or directory to take ownership of.",
314
+ "This path should be relative to your theme directory.",
315
+ "Example: `--path 'admin/KcPage.tsx'`"
272
316
  ].join(" ")
273
317
  })
318
+ .option({
319
+ key: "revert",
320
+ name: (() => {
321
+ const name = "revert";
322
+
323
+ optionsKeys.push(name);
324
+
325
+ return name;
326
+ })(),
327
+ description: [
328
+ "Restores a file or directory to its original auto-generated state,",
329
+ "removing your ownership claim and reverting any modifications."
330
+ ].join(" "),
331
+ defaultValue: false
332
+ })
274
333
  .task({
275
334
  skip,
276
- handler: async ({ projectDirPath, file }) => {
277
- const { command } = await import("./eject-file");
335
+ handler: async ({ projectDirPath, path, revert }) => {
336
+ const { command } = await import("./own");
278
337
 
279
338
  await command({
280
339
  buildContext: getBuildContext({ projectDirPath }),
281
- cliCommandOptions: { file }
340
+ cliCommandOptions: { path, isRevert: revert }
282
341
  });
283
342
  }
284
343
  });
package/src/bin/own.ts ADDED
@@ -0,0 +1,209 @@
1
+ import type { BuildContext } from "./shared/buildContext";
2
+ import { getExtensionModuleFileSourceCodeReadyToBeCopied } from "./sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied";
3
+ import type { ExtensionModuleMeta } from "./sync-extensions/extensionModuleMeta";
4
+ import { command as command_syncExtensions } from "./sync-extensions/sync-extension";
5
+ import {
6
+ readManagedGitignoreFile,
7
+ writeManagedGitignoreFile
8
+ } from "./sync-extensions/managedGitignoreFile";
9
+ import { getExtensionModuleMetas } from "./sync-extensions/extensionModuleMeta";
10
+ import { getAbsoluteAndInOsFormatPath } from "./tools/getAbsoluteAndInOsFormatPath";
11
+ import { relative as pathRelative, dirname as pathDirname, join as pathJoin } from "path";
12
+ import { getInstalledModuleDirPath } from "./tools/getInstalledModuleDirPath";
13
+ import * as fsPr from "fs/promises";
14
+ import { isInside } from "./tools/isInside";
15
+ import chalk from "chalk";
16
+
17
+ export async function command(params: {
18
+ buildContext: BuildContext;
19
+ cliCommandOptions: {
20
+ path: string;
21
+ isRevert: boolean;
22
+ };
23
+ }) {
24
+ const { buildContext, cliCommandOptions } = params;
25
+
26
+ const extensionModuleMetas = await getExtensionModuleMetas({ buildContext });
27
+
28
+ const { targetFileRelativePathsByExtensionModuleMeta } = await (async () => {
29
+ const fileOrDirectoryRelativePath = pathRelative(
30
+ buildContext.themeSrcDirPath,
31
+ getAbsoluteAndInOsFormatPath({
32
+ cwd: buildContext.themeSrcDirPath,
33
+ pathIsh: cliCommandOptions.path
34
+ })
35
+ );
36
+
37
+ const arr = extensionModuleMetas
38
+ .map(extensionModuleMeta => ({
39
+ extensionModuleMeta,
40
+ fileRelativePaths: extensionModuleMeta.files
41
+ .map(({ fileRelativePath }) => fileRelativePath)
42
+ .filter(
43
+ fileRelativePath =>
44
+ fileRelativePath === fileOrDirectoryRelativePath ||
45
+ isInside({
46
+ dirPath: fileOrDirectoryRelativePath,
47
+ filePath: fileRelativePath
48
+ })
49
+ )
50
+ }))
51
+ .filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
52
+
53
+ const targetFileRelativePathsByExtensionModuleMeta = new Map<
54
+ ExtensionModuleMeta,
55
+ string[]
56
+ >();
57
+
58
+ for (const { extensionModuleMeta, fileRelativePaths } of arr) {
59
+ targetFileRelativePathsByExtensionModuleMeta.set(
60
+ extensionModuleMeta,
61
+ fileRelativePaths
62
+ );
63
+ }
64
+
65
+ return { targetFileRelativePathsByExtensionModuleMeta };
66
+ })();
67
+
68
+ if (targetFileRelativePathsByExtensionModuleMeta.size === 0) {
69
+ console.log(
70
+ chalk.yellow(
71
+ "There is no Keycloakify extension modules files matching the provided path."
72
+ )
73
+ );
74
+ process.exit(1);
75
+ }
76
+
77
+ const { ownedFilesRelativePaths: ownedFilesRelativePaths_current } =
78
+ await readManagedGitignoreFile({
79
+ buildContext
80
+ });
81
+
82
+ await (cliCommandOptions.isRevert ? command_revert : command_own)({
83
+ extensionModuleMetas,
84
+ targetFileRelativePathsByExtensionModuleMeta,
85
+ ownedFilesRelativePaths_current,
86
+ buildContext
87
+ });
88
+ }
89
+
90
+ type Params_subcommands = {
91
+ extensionModuleMetas: ExtensionModuleMeta[];
92
+ targetFileRelativePathsByExtensionModuleMeta: Map<ExtensionModuleMeta, string[]>;
93
+ ownedFilesRelativePaths_current: string[];
94
+ buildContext: BuildContext;
95
+ };
96
+
97
+ async function command_own(params: Params_subcommands) {
98
+ const {
99
+ extensionModuleMetas,
100
+ targetFileRelativePathsByExtensionModuleMeta,
101
+ ownedFilesRelativePaths_current,
102
+ buildContext
103
+ } = params;
104
+
105
+ await writeManagedGitignoreFile({
106
+ buildContext,
107
+ extensionModuleMetas,
108
+ ownedFilesRelativePaths: [
109
+ ...ownedFilesRelativePaths_current,
110
+ ...Array.from(targetFileRelativePathsByExtensionModuleMeta.values())
111
+ .flat()
112
+ .filter(
113
+ fileRelativePath =>
114
+ !ownedFilesRelativePaths_current.includes(fileRelativePath)
115
+ )
116
+ ]
117
+ });
118
+
119
+ const writeActions: (() => Promise<void>)[] = [];
120
+
121
+ for (const [
122
+ extensionModuleMeta,
123
+ fileRelativePaths
124
+ ] of targetFileRelativePathsByExtensionModuleMeta.entries()) {
125
+ const extensionModuleDirPath = await getInstalledModuleDirPath({
126
+ moduleName: extensionModuleMeta.moduleName,
127
+ packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath),
128
+ projectDirPath: buildContext.projectDirPath
129
+ });
130
+
131
+ for (const fileRelativePath of fileRelativePaths) {
132
+ if (ownedFilesRelativePaths_current.includes(fileRelativePath)) {
133
+ console.log(
134
+ chalk.grey(`You already have ownership over '${fileRelativePath}'.`)
135
+ );
136
+ continue;
137
+ }
138
+
139
+ writeActions.push(async () => {
140
+ const sourceCode = await getExtensionModuleFileSourceCodeReadyToBeCopied({
141
+ buildContext,
142
+ fileRelativePath,
143
+ isOwnershipAction: true,
144
+ extensionModuleName: extensionModuleMeta.moduleName,
145
+ extensionModuleDirPath,
146
+ extensionModuleVersion: extensionModuleMeta.version
147
+ });
148
+
149
+ await fsPr.writeFile(
150
+ pathJoin(buildContext.themeSrcDirPath, fileRelativePath),
151
+ sourceCode
152
+ );
153
+
154
+ console.log(chalk.green(`Ownership over '${fileRelativePath}' claimed.`));
155
+ });
156
+ }
157
+ }
158
+
159
+ if (writeActions.length === 0) {
160
+ console.log(chalk.yellow("No new file claimed."));
161
+ return;
162
+ }
163
+
164
+ await Promise.all(writeActions.map(action => action()));
165
+ }
166
+
167
+ async function command_revert(params: Params_subcommands) {
168
+ const {
169
+ extensionModuleMetas,
170
+ targetFileRelativePathsByExtensionModuleMeta,
171
+ ownedFilesRelativePaths_current,
172
+ buildContext
173
+ } = params;
174
+
175
+ const ownedFilesRelativePaths_toRemove = Array.from(
176
+ targetFileRelativePathsByExtensionModuleMeta.values()
177
+ )
178
+ .flat()
179
+ .filter(fileRelativePath => {
180
+ if (!ownedFilesRelativePaths_current.includes(fileRelativePath)) {
181
+ console.log(
182
+ chalk.grey(`Ownership over '${fileRelativePath}' wasn't claimed.`)
183
+ );
184
+ return false;
185
+ }
186
+
187
+ console.log(
188
+ chalk.green(`Ownership over '${fileRelativePath}' relinquished.`)
189
+ );
190
+
191
+ return true;
192
+ });
193
+
194
+ if (ownedFilesRelativePaths_toRemove.length === 0) {
195
+ console.log(chalk.yellow("No file relinquished."));
196
+ return;
197
+ }
198
+
199
+ await writeManagedGitignoreFile({
200
+ buildContext,
201
+ extensionModuleMetas,
202
+ ownedFilesRelativePaths: ownedFilesRelativePaths_current.filter(
203
+ fileRelativePath =>
204
+ !ownedFilesRelativePaths_toRemove.includes(fileRelativePath)
205
+ )
206
+ });
207
+
208
+ await command_syncExtensions({ buildContext });
209
+ }
@@ -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 addSyncExtensionsToPostinstallScript(params: {
13
+ parsedPackageJson: { scripts?: Record<string, string | undefined> };
14
+ buildContext: BuildContextLike;
15
+ }) {
16
+ const { parsedPackageJson, buildContext } = params;
17
+
18
+ const cmd_base = "keycloakify sync-extensions";
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