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.
- package/bin/{805.index.js → 174.index.js} +111 -2
- package/bin/363.index.js +1528 -0
- package/bin/453.index.js +1 -1
- package/bin/{33.index.js → 568.index.js} +2 -113
- package/bin/735.index.js +53 -24
- package/bin/{653.index.js → 840.index.js} +387 -213
- package/bin/921.index.js +1 -1
- package/bin/930.index.js +165 -0
- package/bin/946.index.js +20 -0
- package/bin/initialize-admin-theme.d.ts +4 -0
- package/bin/main.js +72 -18
- package/bin/{eject-file.d.ts → own.d.ts} +2 -1
- package/bin/shared/addSyncExtensionsToPostinstallScript.d.ts +10 -0
- package/bin/shared/customHandler.d.ts +1 -1
- package/bin/shared/customHandler.js.map +1 -1
- package/bin/{postinstall/uiModuleMeta.d.ts → sync-extensions/extensionModuleMeta.d.ts} +5 -5
- package/bin/sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.d.ts +12 -0
- package/bin/sync-extensions/index.d.ts +1 -0
- package/bin/{postinstall/installUiModulesPeerDependencies.d.ts → sync-extensions/installExtensionModulesPeerDependencies.d.ts} +3 -3
- package/bin/{postinstall → sync-extensions}/managedGitignoreFile.d.ts +4 -4
- package/bin/tools/isKnownByGit.d.ts +3 -0
- package/bin/tools/npmInstall.d.ts +1 -1
- package/package.json +27 -22
- package/src/bin/eject-page.ts +1 -3
- package/src/bin/initialize-account-theme/initializeAccountTheme_singlePage.ts +3 -1
- package/src/bin/initialize-admin-theme.ts +146 -0
- package/src/bin/main.ts +76 -17
- package/src/bin/own.ts +209 -0
- package/src/bin/shared/addSyncExtensionsToPostinstallScript.ts +70 -0
- package/src/bin/shared/customHandler.ts +1 -0
- package/src/bin/{postinstall/uiModuleMeta.ts → sync-extensions/extensionModuleMeta.ts} +55 -43
- package/src/bin/{postinstall/getUiModuleFileSourceCodeReadyToBeCopied.ts → sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.ts} +32 -21
- package/src/bin/sync-extensions/index.ts +1 -0
- package/src/bin/{postinstall/installUiModulesPeerDependencies.ts → sync-extensions/installExtensionModulesPeerDependencies.ts} +16 -14
- package/src/bin/{postinstall → sync-extensions}/managedGitignoreFile.ts +18 -18
- package/src/bin/{postinstall/postinstall.ts → sync-extensions/sync-extension.ts} +14 -26
- package/src/bin/tools/isKnownByGit.ts +45 -0
- package/src/bin/tools/listInstalledModules.ts +2 -2
- package/src/bin/tools/npmInstall.ts +46 -9
- package/src/bin/tools/untrackFromGit.ts +19 -3
- package/bin/356.index.js +0 -755
- package/bin/854.index.js +0 -68
- package/bin/postinstall/getUiModuleFileSourceCodeReadyToBeCopied.d.ts +0 -12
- package/bin/postinstall/index.d.ts +0 -1
- package/bin/tools/isTrackedByGit.d.ts +0 -3
- package/src/bin/eject-file.ts +0 -68
- package/src/bin/postinstall/index.ts +0 -1
- package/src/bin/tools/isTrackedByGit.ts +0 -29
- /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({
|
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
|
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: "
|
238
|
-
description:
|
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("./
|
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
|
-
|
282
|
+
path: string;
|
283
|
+
revert: boolean;
|
252
284
|
}>({
|
253
|
-
name: "
|
285
|
+
name: "own",
|
254
286
|
description: [
|
255
|
-
"
|
256
|
-
"
|
257
|
-
|
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: "
|
303
|
+
key: "path",
|
261
304
|
name: (() => {
|
262
|
-
const long = "
|
263
|
-
const short = "
|
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
|
-
"
|
271
|
-
"
|
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,
|
277
|
-
const { command } = await import("./
|
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: {
|
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
|
+
}
|