keycloakify 11.6.1 → 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 (41) hide show
  1. package/bin/363.index.js +1528 -0
  2. package/bin/453.index.js +1 -1
  3. package/bin/735.index.js +1 -1
  4. package/bin/{615.index.js → 840.index.js} +9 -9
  5. package/bin/930.index.js +165 -0
  6. package/bin/946.index.js +20 -0
  7. package/bin/main.js +58 -16
  8. package/bin/{eject-file.d.ts → own.d.ts} +2 -1
  9. package/bin/shared/{addPostinstallScriptIfNotPresent.d.ts → addSyncExtensionsToPostinstallScript.d.ts} +1 -1
  10. package/bin/{postinstall/uiModuleMeta.d.ts → sync-extensions/extensionModuleMeta.d.ts} +5 -5
  11. package/bin/sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.d.ts +12 -0
  12. package/bin/sync-extensions/index.d.ts +1 -0
  13. package/bin/{postinstall/installUiModulesPeerDependencies.d.ts → sync-extensions/installExtensionModulesPeerDependencies.d.ts} +3 -3
  14. package/bin/{postinstall → sync-extensions}/managedGitignoreFile.d.ts +4 -4
  15. package/bin/tools/isKnownByGit.d.ts +3 -0
  16. package/package.json +23 -23
  17. package/src/bin/eject-page.ts +1 -3
  18. package/src/bin/initialize-admin-theme.ts +2 -2
  19. package/src/bin/main.ts +61 -16
  20. package/src/bin/own.ts +209 -0
  21. package/src/bin/shared/{addPostinstallScriptIfNotPresent.ts → addSyncExtensionsToPostinstallScript.ts} +2 -2
  22. package/src/bin/{postinstall/uiModuleMeta.ts → sync-extensions/extensionModuleMeta.ts} +48 -42
  23. package/src/bin/{postinstall/getUiModuleFileSourceCodeReadyToBeCopied.ts → sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied.ts} +32 -21
  24. package/src/bin/sync-extensions/index.ts +1 -0
  25. package/src/bin/{postinstall/installUiModulesPeerDependencies.ts → sync-extensions/installExtensionModulesPeerDependencies.ts} +15 -13
  26. package/src/bin/{postinstall → sync-extensions}/managedGitignoreFile.ts +18 -18
  27. package/src/bin/{postinstall/postinstall.ts → sync-extensions/sync-extension.ts} +14 -26
  28. package/src/bin/tools/isKnownByGit.ts +45 -0
  29. package/src/bin/tools/listInstalledModules.ts +2 -2
  30. package/src/bin/tools/npmInstall.ts +1 -1
  31. package/src/bin/tools/untrackFromGit.ts +19 -3
  32. package/bin/300.index.js +0 -770
  33. package/bin/653.index.js +0 -753
  34. package/bin/854.index.js +0 -68
  35. package/bin/postinstall/getUiModuleFileSourceCodeReadyToBeCopied.d.ts +0 -12
  36. package/bin/postinstall/index.d.ts +0 -1
  37. package/bin/tools/isTrackedByGit.d.ts +0 -3
  38. package/src/bin/eject-file.ts +0 -68
  39. package/src/bin/postinstall/index.ts +0 -1
  40. package/src/bin/tools/isTrackedByGit.ts +0 -29
  41. /package/bin/{postinstall/postinstall.d.ts → sync-extensions/sync-extension.d.ts} +0 -0
package/src/bin/main.ts CHANGED
@@ -248,13 +248,30 @@ program
248
248
 
249
249
  program
250
250
  .command({
251
- name: "postinstall",
252
- 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")
253
270
  })
254
271
  .task({
255
272
  skip,
256
273
  handler: async ({ projectDirPath }) => {
257
- const { command } = await import("./postinstall");
274
+ const { command } = await import("./sync-extensions");
258
275
 
259
276
  await command({ buildContext: getBuildContext({ projectDirPath }) });
260
277
  }
@@ -262,37 +279,65 @@ program
262
279
 
263
280
  program
264
281
  .command<{
265
- file: string;
282
+ path: string;
283
+ revert: boolean;
266
284
  }>({
267
- name: "eject-file",
285
+ name: "own",
268
286
  description: [
269
- "WARNING: Not usable yet, will be used for future features",
270
- "Take ownership over a given file"
271
- ].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")
272
301
  })
273
302
  .option({
274
- key: "file",
303
+ key: "path",
275
304
  name: (() => {
276
- const long = "file";
277
- const short = "f";
305
+ const long = "path";
306
+ const short = "p";
278
307
 
279
308
  optionsKeys.push(long, short);
280
309
 
281
310
  return { long, short };
282
311
  })(),
283
312
  description: [
284
- "Relative path of the file relative to the directory of your keycloak theme source",
285
- "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'`"
286
316
  ].join(" ")
287
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
+ })
288
333
  .task({
289
334
  skip,
290
- handler: async ({ projectDirPath, file }) => {
291
- const { command } = await import("./eject-file");
335
+ handler: async ({ projectDirPath, path, revert }) => {
336
+ const { command } = await import("./own");
292
337
 
293
338
  await command({
294
339
  buildContext: getBuildContext({ projectDirPath }),
295
- cliCommandOptions: { file }
340
+ cliCommandOptions: { path, isRevert: revert }
296
341
  });
297
342
  }
298
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
+ }
@@ -9,13 +9,13 @@ export type BuildContextLike = {
9
9
 
10
10
  assert<BuildContext extends BuildContextLike ? true : false>();
11
11
 
12
- export function addPostinstallScriptIfNotPresent(params: {
12
+ export function addSyncExtensionsToPostinstallScript(params: {
13
13
  parsedPackageJson: { scripts?: Record<string, string | undefined> };
14
14
  buildContext: BuildContextLike;
15
15
  }) {
16
16
  const { parsedPackageJson, buildContext } = params;
17
17
 
18
- const cmd_base = "keycloakify postinstall";
18
+ const cmd_base = "keycloakify sync-extensions";
19
19
 
20
20
  const projectCliOptionValue = (() => {
21
21
  const packageJsonDirPath = pathDirname(buildContext.packageJsonFilePath);
@@ -10,15 +10,15 @@ import { crawlAsync } from "../tools/crawlAsync";
10
10
  import { getIsPrettierAvailable, getPrettier } from "../tools/runPrettier";
11
11
  import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
12
12
  import {
13
- getUiModuleFileSourceCodeReadyToBeCopied,
14
- type BuildContextLike as BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied
15
- } from "./getUiModuleFileSourceCodeReadyToBeCopied";
13
+ getExtensionModuleFileSourceCodeReadyToBeCopied,
14
+ type BuildContextLike as BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied
15
+ } from "./getExtensionModuleFileSourceCodeReadyToBeCopied";
16
16
  import * as crypto from "crypto";
17
17
  import { KEYCLOAK_THEME } from "../shared/constants";
18
18
  import { exclude } from "tsafe/exclude";
19
19
  import { isAmong } from "tsafe/isAmong";
20
20
 
21
- export type UiModuleMeta = {
21
+ export type ExtensionModuleMeta = {
22
22
  moduleName: string;
23
23
  version: string;
24
24
  files: {
@@ -29,8 +29,8 @@ export type UiModuleMeta = {
29
29
  peerDependencies: Record<string, string>;
30
30
  };
31
31
 
32
- const zUiModuleMeta = (() => {
33
- type ExpectedType = UiModuleMeta;
32
+ const zExtensionModuleMeta = (() => {
33
+ type ExpectedType = ExtensionModuleMeta;
34
34
 
35
35
  const zTargetType = z.object({
36
36
  moduleName: z.string(),
@@ -56,7 +56,7 @@ type ParsedCacheFile = {
56
56
  keycloakifyVersion: string;
57
57
  prettierConfigHash: string | null;
58
58
  thisFilePath: string;
59
- uiModuleMetas: UiModuleMeta[];
59
+ extensionModuleMetas: ExtensionModuleMeta[];
60
60
  };
61
61
 
62
62
  const zParsedCacheFile = (() => {
@@ -66,7 +66,7 @@ const zParsedCacheFile = (() => {
66
66
  keycloakifyVersion: z.string(),
67
67
  prettierConfigHash: z.union([z.string(), z.null()]),
68
68
  thisFilePath: z.string(),
69
- uiModuleMetas: z.array(zUiModuleMeta)
69
+ extensionModuleMetas: z.array(zExtensionModuleMeta)
70
70
  });
71
71
 
72
72
  type InferredType = z.infer<typeof zTargetType>;
@@ -76,10 +76,10 @@ const zParsedCacheFile = (() => {
76
76
  return id<z.ZodType<ExpectedType>>(zTargetType);
77
77
  })();
78
78
 
79
- const CACHE_FILE_RELATIVE_PATH = pathJoin("ui-modules", "cache.json");
79
+ const CACHE_FILE_RELATIVE_PATH = pathJoin("extension-modules", "cache.json");
80
80
 
81
81
  export type BuildContextLike =
82
- BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied & {
82
+ BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied & {
83
83
  cacheDirPath: string;
84
84
  packageJsonFilePath: string;
85
85
  projectDirPath: string;
@@ -87,9 +87,9 @@ export type BuildContextLike =
87
87
 
88
88
  assert<BuildContext extends BuildContextLike ? true : false>();
89
89
 
90
- export async function getUiModuleMetas(params: {
90
+ export async function getExtensionModuleMetas(params: {
91
91
  buildContext: BuildContextLike;
92
- }): Promise<UiModuleMeta[]> {
92
+ }): Promise<ExtensionModuleMeta[]> {
93
93
  const { buildContext } = params;
94
94
 
95
95
  const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_RELATIVE_PATH);
@@ -106,7 +106,7 @@ export async function getUiModuleMetas(params: {
106
106
  return configHash;
107
107
  })();
108
108
 
109
- const installedUiModules = await (async () => {
109
+ const installedExtensionModules = await (async () => {
110
110
  const installedModulesWithKeycloakifyInTheName = await listInstalledModules({
111
111
  packageJsonFilePath: buildContext.packageJsonFilePath,
112
112
  projectDirPath: buildContext.packageJsonFilePath,
@@ -134,7 +134,7 @@ export async function getUiModuleMetas(params: {
134
134
  return await fsPr.readFile(cacheFilePath);
135
135
  })();
136
136
 
137
- const uiModuleMetas_cacheUpToDate: UiModuleMeta[] = await (async () => {
137
+ const extensionModuleMetas_cacheUpToDate: ExtensionModuleMeta[] = await (async () => {
138
138
  const parsedCacheFile: ParsedCacheFile | undefined = await (async () => {
139
139
  if (cacheContent === undefined) {
140
140
  return undefined;
@@ -177,45 +177,51 @@ export async function getUiModuleMetas(params: {
177
177
  return [];
178
178
  }
179
179
 
180
- const uiModuleMetas_cacheUpToDate = parsedCacheFile.uiModuleMetas.filter(
181
- uiModuleMeta => {
182
- const correspondingInstalledUiModule = installedUiModules.find(
183
- installedUiModule =>
184
- installedUiModule.moduleName === uiModuleMeta.moduleName
185
- );
180
+ const extensionModuleMetas_cacheUpToDate =
181
+ parsedCacheFile.extensionModuleMetas.filter(extensionModuleMeta => {
182
+ const correspondingInstalledExtensionModule =
183
+ installedExtensionModules.find(
184
+ installedExtensionModule =>
185
+ installedExtensionModule.moduleName ===
186
+ extensionModuleMeta.moduleName
187
+ );
186
188
 
187
- if (correspondingInstalledUiModule === undefined) {
189
+ if (correspondingInstalledExtensionModule === undefined) {
188
190
  return false;
189
191
  }
190
192
 
191
- return correspondingInstalledUiModule.version === uiModuleMeta.version;
192
- }
193
- );
193
+ return (
194
+ correspondingInstalledExtensionModule.version ===
195
+ extensionModuleMeta.version
196
+ );
197
+ });
194
198
 
195
- return uiModuleMetas_cacheUpToDate;
199
+ return extensionModuleMetas_cacheUpToDate;
196
200
  })();
197
201
 
198
- const uiModuleMetas = await Promise.all(
199
- installedUiModules.map(
202
+ const extensionModuleMetas = await Promise.all(
203
+ installedExtensionModules.map(
200
204
  async ({
201
205
  moduleName,
202
206
  version,
203
207
  peerDependencies,
204
208
  dirPath
205
- }): Promise<UiModuleMeta> => {
209
+ }): Promise<ExtensionModuleMeta> => {
206
210
  use_cache: {
207
- const uiModuleMeta_cache = uiModuleMetas_cacheUpToDate.find(
208
- uiModuleMeta => uiModuleMeta.moduleName === moduleName
209
- );
211
+ const extensionModuleMeta_cache =
212
+ extensionModuleMetas_cacheUpToDate.find(
213
+ extensionModuleMeta =>
214
+ extensionModuleMeta.moduleName === moduleName
215
+ );
210
216
 
211
- if (uiModuleMeta_cache === undefined) {
217
+ if (extensionModuleMeta_cache === undefined) {
212
218
  break use_cache;
213
219
  }
214
220
 
215
- return uiModuleMeta_cache;
221
+ return extensionModuleMeta_cache;
216
222
  }
217
223
 
218
- const files: UiModuleMeta["files"] = [];
224
+ const files: ExtensionModuleMeta["files"] = [];
219
225
 
220
226
  {
221
227
  const srcDirPath = pathJoin(dirPath, KEYCLOAK_THEME);
@@ -225,13 +231,13 @@ export async function getUiModuleMetas(params: {
225
231
  returnedPathsType: "relative to dirPath",
226
232
  onFileFound: async fileRelativePath => {
227
233
  const sourceCode =
228
- await getUiModuleFileSourceCodeReadyToBeCopied({
234
+ await getExtensionModuleFileSourceCodeReadyToBeCopied({
229
235
  buildContext,
230
236
  fileRelativePath,
231
- isForEjection: false,
232
- uiModuleDirPath: dirPath,
233
- uiModuleName: moduleName,
234
- uiModuleVersion: version
237
+ isOwnershipAction: false,
238
+ extensionModuleDirPath: dirPath,
239
+ extensionModuleName: moduleName,
240
+ extensionModuleVersion: version
235
241
  });
236
242
 
237
243
  const hash = computeHash(sourceCode);
@@ -261,7 +267,7 @@ export async function getUiModuleMetas(params: {
261
267
  });
262
268
  }
263
269
 
264
- return id<UiModuleMeta>({
270
+ return id<ExtensionModuleMeta>({
265
271
  moduleName,
266
272
  version,
267
273
  files,
@@ -281,7 +287,7 @@ export async function getUiModuleMetas(params: {
281
287
  keycloakifyVersion,
282
288
  prettierConfigHash,
283
289
  thisFilePath: cacheFilePath,
284
- uiModuleMetas
290
+ extensionModuleMetas
285
291
  });
286
292
 
287
293
  const cacheContent_new = Buffer.from(
@@ -306,7 +312,7 @@ export async function getUiModuleMetas(params: {
306
312
  await fsPr.writeFile(cacheFilePath, cacheContent_new);
307
313
  }
308
314
 
309
- return uiModuleMetas;
315
+ return extensionModuleMetas;
310
316
  }
311
317
 
312
318
  export function computeHash(data: Buffer) {
@@ -11,40 +11,51 @@ export type BuildContextLike = {
11
11
 
12
12
  assert<BuildContext extends BuildContextLike ? true : false>();
13
13
 
14
- export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
14
+ export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
15
15
  buildContext: BuildContextLike;
16
16
  fileRelativePath: string;
17
- isForEjection: boolean;
18
- uiModuleDirPath: string;
19
- uiModuleName: string;
20
- uiModuleVersion: string;
17
+ isOwnershipAction: boolean;
18
+ extensionModuleDirPath: string;
19
+ extensionModuleName: string;
20
+ extensionModuleVersion: string;
21
21
  }): Promise<Buffer> {
22
22
  const {
23
23
  buildContext,
24
- uiModuleDirPath,
24
+ extensionModuleDirPath,
25
25
  fileRelativePath,
26
- isForEjection,
27
- uiModuleName,
28
- uiModuleVersion
26
+ isOwnershipAction,
27
+ extensionModuleName,
28
+ extensionModuleVersion
29
29
  } = params;
30
30
 
31
31
  let sourceCode = (
32
- await fsPr.readFile(pathJoin(uiModuleDirPath, KEYCLOAK_THEME, fileRelativePath))
32
+ await fsPr.readFile(
33
+ pathJoin(extensionModuleDirPath, KEYCLOAK_THEME, fileRelativePath)
34
+ )
33
35
  ).toString("utf8");
34
36
 
35
37
  sourceCode = addCommentToSourceCode({
36
38
  sourceCode,
37
39
  fileRelativePath,
38
- commentLines: isForEjection
39
- ? [`This file was ejected from ${uiModuleName} version ${uiModuleVersion}.`]
40
- : [
41
- `WARNING: Before modifying this file run the following command:`,
42
- ``,
43
- `$ npx keycloakify eject-file --file '${fileRelativePath.split(pathSep).join("/")}'`,
44
- ``,
45
- `This file comes from ${uiModuleName} version ${uiModuleVersion}.`,
46
- `This file has been copied over to your repo by your postinstall script: \`npx keycloakify postinstall\``
47
- ]
40
+ commentLines: (() => {
41
+ const path = fileRelativePath.split(pathSep).join("/");
42
+
43
+ return isOwnershipAction
44
+ ? [
45
+ `This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
46
+ `To relinquish ownership and restore this file to its original content, run the following command:`,
47
+ ``,
48
+ `$ npx keycloakify own --path '${path}' --revert`
49
+ ]
50
+ : [
51
+ `WARNING: Before modifying this file, run the following command:`,
52
+ ``,
53
+ `$ npx keycloakify own --path '${path}'`,
54
+ ``,
55
+ `This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
56
+ `It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`
57
+ ];
58
+ })()
48
59
  });
49
60
 
50
61
  const destFilePath = pathJoin(buildContext.themeSrcDirPath, fileRelativePath);
@@ -93,7 +104,7 @@ function addCommentToSourceCode(params: {
93
104
  `<!--`,
94
105
  ...commentLines.map(
95
106
  line =>
96
- ` ${line.replace("--file", "-f").replace("Before modifying", "Before modifying or replacing")}`
107
+ ` ${line.replace("--path", "-p").replace("Before modifying", "Before modifying or replacing")}`
97
108
  ),
98
109
  `-->`
99
110
  ].join("\n");
@@ -0,0 +1 @@
1
+ export * from "./sync-extension";