Better naming convention 'uiModules' -> 'extensionModules'
This commit is contained in:
parent
d2da43c617
commit
c1dc899bc1
@ -4,7 +4,7 @@ import * as fs from "fs";
|
||||
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||
import { assert, is, type Equals } from "tsafe/assert";
|
||||
import { id } from "tsafe/id";
|
||||
import { addPostinstallScriptIfNotPresent } from "./shared/addPostinstallScriptIfNotPresent";
|
||||
import { addSyncExtensionsToPostinstallScript } from "./shared/addSyncExtensionsToPostinstallScript";
|
||||
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
|
||||
import { npmInstall } from "./tools/npmInstall";
|
||||
import * as child_process from "child_process";
|
||||
@ -74,7 +74,7 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
return parsedPackageJson;
|
||||
})();
|
||||
|
||||
addPostinstallScriptIfNotPresent({
|
||||
addSyncExtensionsToPostinstallScript({
|
||||
parsedPackageJson,
|
||||
buildContext
|
||||
});
|
||||
|
@ -248,13 +248,30 @@ program
|
||||
|
||||
program
|
||||
.command({
|
||||
name: "postinstall",
|
||||
description: "Initialize all the Keycloakify UI modules installed in the project."
|
||||
name: "sync-extensions",
|
||||
description: [
|
||||
"Synchronizes all installed Keycloakify extension modules with your project.",
|
||||
"",
|
||||
"Example of extension modules: '@keycloakify/keycloak-account-ui', '@keycloakify/keycloak-admin-ui', '@keycloakify/keycloak-ui-shared'",
|
||||
"",
|
||||
"This command ensures that:",
|
||||
"- All required files from installed extensions are copied into your project.",
|
||||
"- The copied files are correctly ignored by Git to help you distinguish between your custom source files",
|
||||
" and those provided by the extensions.",
|
||||
"- Peer dependencies declared by the extensions are automatically added to your package.json.",
|
||||
"",
|
||||
"You can safely run this command multiple times. It will only update the files and dependencies if needed,",
|
||||
"ensuring your project stays in sync with the installed extensions.",
|
||||
"",
|
||||
"Typical usage:",
|
||||
"- Should be run as a postinstall script of your project.",
|
||||
""
|
||||
].join("\n")
|
||||
})
|
||||
.task({
|
||||
skip,
|
||||
handler: async ({ projectDirPath }) => {
|
||||
const { command } = await import("./postinstall");
|
||||
const { command } = await import("./sync-extensions");
|
||||
|
||||
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||
}
|
||||
@ -267,9 +284,20 @@ program
|
||||
}>({
|
||||
name: "own",
|
||||
description: [
|
||||
"WARNING: Not usable yet, will be used for future features",
|
||||
"Take ownership over a given file"
|
||||
].join(" ")
|
||||
"Manages ownership of auto-generated files provided by Keycloakify extensions.",
|
||||
"",
|
||||
"This command allows you to take ownership of a specific file or directory generated",
|
||||
"by an extension. Once owned, you can freely modify and version-control the file.",
|
||||
"",
|
||||
"You can also use the --revert flag to relinquish ownership and restore the file",
|
||||
"or directory to its original auto-generated state.",
|
||||
"",
|
||||
"For convenience, the exact command to take ownership of any file is included as a comment",
|
||||
"in the header of each extension-generated file.",
|
||||
"",
|
||||
"Examples:",
|
||||
"$ npx keycloakify own --path admin/KcPage.tsx"
|
||||
].join("\n")
|
||||
})
|
||||
.option({
|
||||
key: "path",
|
||||
@ -282,9 +310,9 @@ program
|
||||
return { long, short };
|
||||
})(),
|
||||
description: [
|
||||
"Relative path of the file or the directory that you want to take ownership over.",
|
||||
"The path is relative to your theme directory.",
|
||||
"Example `--path admin/page/Login.tsx`"
|
||||
"Specifies the relative path of the file or directory to take ownership of.",
|
||||
"This path should be relative to your theme directory.",
|
||||
"Example: `--path 'admin/KcPage.tsx'`"
|
||||
].join(" ")
|
||||
})
|
||||
.option({
|
||||
@ -296,7 +324,10 @@ program
|
||||
|
||||
return name;
|
||||
})(),
|
||||
description: "Revert ownership claim over a given file or directory.",
|
||||
description: [
|
||||
"Restores a file or directory to its original auto-generated state,",
|
||||
"removing your ownership claim and reverting any modifications."
|
||||
].join(" "),
|
||||
defaultValue: false
|
||||
})
|
||||
.task({
|
||||
|
@ -1,18 +1,18 @@
|
||||
import type { BuildContext } from "./shared/buildContext";
|
||||
import { getUiModuleFileSourceCodeReadyToBeCopied } from "./postinstall/getUiModuleFileSourceCodeReadyToBeCopied";
|
||||
import { getAbsoluteAndInOsFormatPath } from "./tools/getAbsoluteAndInOsFormatPath";
|
||||
import { relative as pathRelative, dirname as pathDirname, join as pathJoin } from "path";
|
||||
import { getUiModuleMetas } from "./postinstall/uiModuleMeta";
|
||||
import { getInstalledModuleDirPath } from "./tools/getInstalledModuleDirPath";
|
||||
import * as fsPr from "fs/promises";
|
||||
import { getExtensionModuleFileSourceCodeReadyToBeCopied } from "./sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied";
|
||||
import type { ExtensionModuleMeta } from "./sync-extensions/extensionModuleMeta";
|
||||
import { command as command_syncExtensions } from "./sync-extensions/sync-extension";
|
||||
import {
|
||||
readManagedGitignoreFile,
|
||||
writeManagedGitignoreFile
|
||||
} from "./postinstall/managedGitignoreFile";
|
||||
} from "./sync-extensions/managedGitignoreFile";
|
||||
import { getExtensionModuleMetas } from "./sync-extensions/extensionModuleMeta";
|
||||
import { getAbsoluteAndInOsFormatPath } from "./tools/getAbsoluteAndInOsFormatPath";
|
||||
import { relative as pathRelative, dirname as pathDirname, join as pathJoin } from "path";
|
||||
import { getInstalledModuleDirPath } from "./tools/getInstalledModuleDirPath";
|
||||
import * as fsPr from "fs/promises";
|
||||
import { isInside } from "./tools/isInside";
|
||||
import chalk from "chalk";
|
||||
import type { UiModuleMeta } from "./postinstall/uiModuleMeta";
|
||||
import { command as command_postinstall } from "./postinstall";
|
||||
|
||||
export async function command(params: {
|
||||
buildContext: BuildContext;
|
||||
@ -23,9 +23,9 @@ export async function command(params: {
|
||||
}) {
|
||||
const { buildContext, cliCommandOptions } = params;
|
||||
|
||||
const uiModuleMetas = await getUiModuleMetas({ buildContext });
|
||||
const extensionModuleMetas = await getExtensionModuleMetas({ buildContext });
|
||||
|
||||
const { targetFileRelativePathsByUiModuleMeta } = await (async () => {
|
||||
const { targetFileRelativePathsByExtensionModuleMeta } = await (async () => {
|
||||
const fileOrDirectoryRelativePath = pathRelative(
|
||||
buildContext.themeSrcDirPath,
|
||||
getAbsoluteAndInOsFormatPath({
|
||||
@ -34,10 +34,10 @@ export async function command(params: {
|
||||
})
|
||||
);
|
||||
|
||||
const arr = uiModuleMetas
|
||||
.map(uiModuleMeta => ({
|
||||
uiModuleMeta,
|
||||
fileRelativePaths: uiModuleMeta.files
|
||||
const arr = extensionModuleMetas
|
||||
.map(extensionModuleMeta => ({
|
||||
extensionModuleMeta,
|
||||
fileRelativePaths: extensionModuleMeta.files
|
||||
.map(({ fileRelativePath }) => fileRelativePath)
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
@ -50,18 +50,26 @@ export async function command(params: {
|
||||
}))
|
||||
.filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
|
||||
|
||||
const targetFileRelativePathsByUiModuleMeta = new Map<UiModuleMeta, string[]>();
|
||||
const targetFileRelativePathsByExtensionModuleMeta = new Map<
|
||||
ExtensionModuleMeta,
|
||||
string[]
|
||||
>();
|
||||
|
||||
for (const { uiModuleMeta, fileRelativePaths } of arr) {
|
||||
targetFileRelativePathsByUiModuleMeta.set(uiModuleMeta, fileRelativePaths);
|
||||
for (const { extensionModuleMeta, fileRelativePaths } of arr) {
|
||||
targetFileRelativePathsByExtensionModuleMeta.set(
|
||||
extensionModuleMeta,
|
||||
fileRelativePaths
|
||||
);
|
||||
}
|
||||
|
||||
return { targetFileRelativePathsByUiModuleMeta };
|
||||
return { targetFileRelativePathsByExtensionModuleMeta };
|
||||
})();
|
||||
|
||||
if (targetFileRelativePathsByUiModuleMeta.size === 0) {
|
||||
if (targetFileRelativePathsByExtensionModuleMeta.size === 0) {
|
||||
console.log(
|
||||
chalk.yellow("There is no UI module files matching the provided path.")
|
||||
chalk.yellow(
|
||||
"There is no Keycloakify extension modules files matching the provided path."
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@ -72,34 +80,34 @@ export async function command(params: {
|
||||
});
|
||||
|
||||
await (cliCommandOptions.isRevert ? command_revert : command_own)({
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
extensionModuleMetas,
|
||||
targetFileRelativePathsByExtensionModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
});
|
||||
}
|
||||
|
||||
type Params_subcommands = {
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
targetFileRelativePathsByUiModuleMeta: Map<UiModuleMeta, string[]>;
|
||||
extensionModuleMetas: ExtensionModuleMeta[];
|
||||
targetFileRelativePathsByExtensionModuleMeta: Map<ExtensionModuleMeta, string[]>;
|
||||
ownedFilesRelativePaths_current: string[];
|
||||
buildContext: BuildContext;
|
||||
};
|
||||
|
||||
async function command_own(params: Params_subcommands) {
|
||||
const {
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
extensionModuleMetas,
|
||||
targetFileRelativePathsByExtensionModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
} = params;
|
||||
|
||||
await writeManagedGitignoreFile({
|
||||
buildContext,
|
||||
uiModuleMetas,
|
||||
extensionModuleMetas,
|
||||
ownedFilesRelativePaths: [
|
||||
...ownedFilesRelativePaths_current,
|
||||
...Array.from(targetFileRelativePathsByUiModuleMeta.values())
|
||||
...Array.from(targetFileRelativePathsByExtensionModuleMeta.values())
|
||||
.flat()
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
@ -111,11 +119,11 @@ async function command_own(params: Params_subcommands) {
|
||||
const writeActions: (() => Promise<void>)[] = [];
|
||||
|
||||
for (const [
|
||||
uiModuleMeta,
|
||||
extensionModuleMeta,
|
||||
fileRelativePaths
|
||||
] of targetFileRelativePathsByUiModuleMeta.entries()) {
|
||||
const uiModuleDirPath = await getInstalledModuleDirPath({
|
||||
moduleName: uiModuleMeta.moduleName,
|
||||
] of targetFileRelativePathsByExtensionModuleMeta.entries()) {
|
||||
const extensionModuleDirPath = await getInstalledModuleDirPath({
|
||||
moduleName: extensionModuleMeta.moduleName,
|
||||
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath),
|
||||
projectDirPath: buildContext.projectDirPath
|
||||
});
|
||||
@ -129,13 +137,13 @@ async function command_own(params: Params_subcommands) {
|
||||
}
|
||||
|
||||
writeActions.push(async () => {
|
||||
const sourceCode = await getUiModuleFileSourceCodeReadyToBeCopied({
|
||||
const sourceCode = await getExtensionModuleFileSourceCodeReadyToBeCopied({
|
||||
buildContext,
|
||||
fileRelativePath,
|
||||
isOwnershipAction: true,
|
||||
uiModuleName: uiModuleMeta.moduleName,
|
||||
uiModuleDirPath,
|
||||
uiModuleVersion: uiModuleMeta.version
|
||||
extensionModuleName: extensionModuleMeta.moduleName,
|
||||
extensionModuleDirPath,
|
||||
extensionModuleVersion: extensionModuleMeta.version
|
||||
});
|
||||
|
||||
await fsPr.writeFile(
|
||||
@ -158,14 +166,14 @@ async function command_own(params: Params_subcommands) {
|
||||
|
||||
async function command_revert(params: Params_subcommands) {
|
||||
const {
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
extensionModuleMetas,
|
||||
targetFileRelativePathsByExtensionModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
} = params;
|
||||
|
||||
const ownedFilesRelativePaths_toRemove = Array.from(
|
||||
targetFileRelativePathsByUiModuleMeta.values()
|
||||
targetFileRelativePathsByExtensionModuleMeta.values()
|
||||
)
|
||||
.flat()
|
||||
.filter(fileRelativePath => {
|
||||
@ -190,12 +198,12 @@ async function command_revert(params: Params_subcommands) {
|
||||
|
||||
await writeManagedGitignoreFile({
|
||||
buildContext,
|
||||
uiModuleMetas,
|
||||
extensionModuleMetas,
|
||||
ownedFilesRelativePaths: ownedFilesRelativePaths_current.filter(
|
||||
fileRelativePath =>
|
||||
!ownedFilesRelativePaths_toRemove.includes(fileRelativePath)
|
||||
)
|
||||
});
|
||||
|
||||
await command_postinstall({ buildContext });
|
||||
await command_syncExtensions({ buildContext });
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
export * from "./postinstall";
|
@ -9,13 +9,13 @@ export type BuildContextLike = {
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export function addPostinstallScriptIfNotPresent(params: {
|
||||
export function addSyncExtensionsToPostinstallScript(params: {
|
||||
parsedPackageJson: { scripts?: Record<string, string | undefined> };
|
||||
buildContext: BuildContextLike;
|
||||
}) {
|
||||
const { parsedPackageJson, buildContext } = params;
|
||||
|
||||
const cmd_base = "keycloakify postinstall";
|
||||
const cmd_base = "keycloakify sync-extensions";
|
||||
|
||||
const projectCliOptionValue = (() => {
|
||||
const packageJsonDirPath = pathDirname(buildContext.packageJsonFilePath);
|
@ -10,15 +10,15 @@ import { crawlAsync } from "../tools/crawlAsync";
|
||||
import { getIsPrettierAvailable, getPrettier } from "../tools/runPrettier";
|
||||
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||
import {
|
||||
getUiModuleFileSourceCodeReadyToBeCopied,
|
||||
type BuildContextLike as BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied
|
||||
} from "./getUiModuleFileSourceCodeReadyToBeCopied";
|
||||
getExtensionModuleFileSourceCodeReadyToBeCopied,
|
||||
type BuildContextLike as BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied
|
||||
} from "./getExtensionModuleFileSourceCodeReadyToBeCopied";
|
||||
import * as crypto from "crypto";
|
||||
import { KEYCLOAK_THEME } from "../shared/constants";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
import { isAmong } from "tsafe/isAmong";
|
||||
|
||||
export type UiModuleMeta = {
|
||||
export type ExtensionModuleMeta = {
|
||||
moduleName: string;
|
||||
version: string;
|
||||
files: {
|
||||
@ -29,8 +29,8 @@ export type UiModuleMeta = {
|
||||
peerDependencies: Record<string, string>;
|
||||
};
|
||||
|
||||
const zUiModuleMeta = (() => {
|
||||
type ExpectedType = UiModuleMeta;
|
||||
const zExtensionModuleMeta = (() => {
|
||||
type ExpectedType = ExtensionModuleMeta;
|
||||
|
||||
const zTargetType = z.object({
|
||||
moduleName: z.string(),
|
||||
@ -56,7 +56,7 @@ type ParsedCacheFile = {
|
||||
keycloakifyVersion: string;
|
||||
prettierConfigHash: string | null;
|
||||
thisFilePath: string;
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
extensionModuleMetas: ExtensionModuleMeta[];
|
||||
};
|
||||
|
||||
const zParsedCacheFile = (() => {
|
||||
@ -66,7 +66,7 @@ const zParsedCacheFile = (() => {
|
||||
keycloakifyVersion: z.string(),
|
||||
prettierConfigHash: z.union([z.string(), z.null()]),
|
||||
thisFilePath: z.string(),
|
||||
uiModuleMetas: z.array(zUiModuleMeta)
|
||||
extensionModuleMetas: z.array(zExtensionModuleMeta)
|
||||
});
|
||||
|
||||
type InferredType = z.infer<typeof zTargetType>;
|
||||
@ -76,10 +76,10 @@ const zParsedCacheFile = (() => {
|
||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||
})();
|
||||
|
||||
const CACHE_FILE_RELATIVE_PATH = pathJoin("ui-modules", "cache.json");
|
||||
const CACHE_FILE_RELATIVE_PATH = pathJoin("extension-modules", "cache.json");
|
||||
|
||||
export type BuildContextLike =
|
||||
BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied & {
|
||||
BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied & {
|
||||
cacheDirPath: string;
|
||||
packageJsonFilePath: string;
|
||||
projectDirPath: string;
|
||||
@ -87,9 +87,9 @@ export type BuildContextLike =
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export async function getUiModuleMetas(params: {
|
||||
export async function getExtensionModuleMetas(params: {
|
||||
buildContext: BuildContextLike;
|
||||
}): Promise<UiModuleMeta[]> {
|
||||
}): Promise<ExtensionModuleMeta[]> {
|
||||
const { buildContext } = params;
|
||||
|
||||
const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_RELATIVE_PATH);
|
||||
@ -106,7 +106,7 @@ export async function getUiModuleMetas(params: {
|
||||
return configHash;
|
||||
})();
|
||||
|
||||
const installedUiModules = await (async () => {
|
||||
const installedExtensionModules = await (async () => {
|
||||
const installedModulesWithKeycloakifyInTheName = await listInstalledModules({
|
||||
packageJsonFilePath: buildContext.packageJsonFilePath,
|
||||
projectDirPath: buildContext.packageJsonFilePath,
|
||||
@ -134,7 +134,7 @@ export async function getUiModuleMetas(params: {
|
||||
return await fsPr.readFile(cacheFilePath);
|
||||
})();
|
||||
|
||||
const uiModuleMetas_cacheUpToDate: UiModuleMeta[] = await (async () => {
|
||||
const extensionModuleMetas_cacheUpToDate: ExtensionModuleMeta[] = await (async () => {
|
||||
const parsedCacheFile: ParsedCacheFile | undefined = await (async () => {
|
||||
if (cacheContent === undefined) {
|
||||
return undefined;
|
||||
@ -177,45 +177,51 @@ export async function getUiModuleMetas(params: {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uiModuleMetas_cacheUpToDate = parsedCacheFile.uiModuleMetas.filter(
|
||||
uiModuleMeta => {
|
||||
const correspondingInstalledUiModule = installedUiModules.find(
|
||||
installedUiModule =>
|
||||
installedUiModule.moduleName === uiModuleMeta.moduleName
|
||||
);
|
||||
const extensionModuleMetas_cacheUpToDate =
|
||||
parsedCacheFile.extensionModuleMetas.filter(extensionModuleMeta => {
|
||||
const correspondingInstalledExtensionModule =
|
||||
installedExtensionModules.find(
|
||||
installedExtensionModule =>
|
||||
installedExtensionModule.moduleName ===
|
||||
extensionModuleMeta.moduleName
|
||||
);
|
||||
|
||||
if (correspondingInstalledUiModule === undefined) {
|
||||
if (correspondingInstalledExtensionModule === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return correspondingInstalledUiModule.version === uiModuleMeta.version;
|
||||
}
|
||||
);
|
||||
return (
|
||||
correspondingInstalledExtensionModule.version ===
|
||||
extensionModuleMeta.version
|
||||
);
|
||||
});
|
||||
|
||||
return uiModuleMetas_cacheUpToDate;
|
||||
return extensionModuleMetas_cacheUpToDate;
|
||||
})();
|
||||
|
||||
const uiModuleMetas = await Promise.all(
|
||||
installedUiModules.map(
|
||||
const extensionModuleMetas = await Promise.all(
|
||||
installedExtensionModules.map(
|
||||
async ({
|
||||
moduleName,
|
||||
version,
|
||||
peerDependencies,
|
||||
dirPath
|
||||
}): Promise<UiModuleMeta> => {
|
||||
}): Promise<ExtensionModuleMeta> => {
|
||||
use_cache: {
|
||||
const uiModuleMeta_cache = uiModuleMetas_cacheUpToDate.find(
|
||||
uiModuleMeta => uiModuleMeta.moduleName === moduleName
|
||||
);
|
||||
const extensionModuleMeta_cache =
|
||||
extensionModuleMetas_cacheUpToDate.find(
|
||||
extensionModuleMeta =>
|
||||
extensionModuleMeta.moduleName === moduleName
|
||||
);
|
||||
|
||||
if (uiModuleMeta_cache === undefined) {
|
||||
if (extensionModuleMeta_cache === undefined) {
|
||||
break use_cache;
|
||||
}
|
||||
|
||||
return uiModuleMeta_cache;
|
||||
return extensionModuleMeta_cache;
|
||||
}
|
||||
|
||||
const files: UiModuleMeta["files"] = [];
|
||||
const files: ExtensionModuleMeta["files"] = [];
|
||||
|
||||
{
|
||||
const srcDirPath = pathJoin(dirPath, KEYCLOAK_THEME);
|
||||
@ -225,13 +231,13 @@ export async function getUiModuleMetas(params: {
|
||||
returnedPathsType: "relative to dirPath",
|
||||
onFileFound: async fileRelativePath => {
|
||||
const sourceCode =
|
||||
await getUiModuleFileSourceCodeReadyToBeCopied({
|
||||
await getExtensionModuleFileSourceCodeReadyToBeCopied({
|
||||
buildContext,
|
||||
fileRelativePath,
|
||||
isOwnershipAction: false,
|
||||
uiModuleDirPath: dirPath,
|
||||
uiModuleName: moduleName,
|
||||
uiModuleVersion: version
|
||||
extensionModuleDirPath: dirPath,
|
||||
extensionModuleName: moduleName,
|
||||
extensionModuleVersion: version
|
||||
});
|
||||
|
||||
const hash = computeHash(sourceCode);
|
||||
@ -261,7 +267,7 @@ export async function getUiModuleMetas(params: {
|
||||
});
|
||||
}
|
||||
|
||||
return id<UiModuleMeta>({
|
||||
return id<ExtensionModuleMeta>({
|
||||
moduleName,
|
||||
version,
|
||||
files,
|
||||
@ -281,7 +287,7 @@ export async function getUiModuleMetas(params: {
|
||||
keycloakifyVersion,
|
||||
prettierConfigHash,
|
||||
thisFilePath: cacheFilePath,
|
||||
uiModuleMetas
|
||||
extensionModuleMetas
|
||||
});
|
||||
|
||||
const cacheContent_new = Buffer.from(
|
||||
@ -306,7 +312,7 @@ export async function getUiModuleMetas(params: {
|
||||
await fsPr.writeFile(cacheFilePath, cacheContent_new);
|
||||
}
|
||||
|
||||
return uiModuleMetas;
|
||||
return extensionModuleMetas;
|
||||
}
|
||||
|
||||
export function computeHash(data: Buffer) {
|
@ -11,25 +11,27 @@ export type BuildContextLike = {
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
||||
export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
|
||||
buildContext: BuildContextLike;
|
||||
fileRelativePath: string;
|
||||
isOwnershipAction: boolean;
|
||||
uiModuleDirPath: string;
|
||||
uiModuleName: string;
|
||||
uiModuleVersion: string;
|
||||
extensionModuleDirPath: string;
|
||||
extensionModuleName: string;
|
||||
extensionModuleVersion: string;
|
||||
}): Promise<Buffer> {
|
||||
const {
|
||||
buildContext,
|
||||
uiModuleDirPath,
|
||||
extensionModuleDirPath,
|
||||
fileRelativePath,
|
||||
isOwnershipAction,
|
||||
uiModuleName,
|
||||
uiModuleVersion
|
||||
extensionModuleName,
|
||||
extensionModuleVersion
|
||||
} = params;
|
||||
|
||||
let sourceCode = (
|
||||
await fsPr.readFile(pathJoin(uiModuleDirPath, KEYCLOAK_THEME, fileRelativePath))
|
||||
await fsPr.readFile(
|
||||
pathJoin(extensionModuleDirPath, KEYCLOAK_THEME, fileRelativePath)
|
||||
)
|
||||
).toString("utf8");
|
||||
|
||||
sourceCode = addCommentToSourceCode({
|
||||
@ -40,18 +42,18 @@ export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
||||
|
||||
return isOwnershipAction
|
||||
? [
|
||||
`This file has been claimed for ownership from ${uiModuleName} version ${uiModuleVersion}.`,
|
||||
`This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
|
||||
`To relinquish ownership and restore this file to its original content, run the following command:`,
|
||||
``,
|
||||
`$ npx keycloakify own --revert --path '${path}'`
|
||||
`$ npx keycloakify own --path '${path}' --revert`
|
||||
]
|
||||
: [
|
||||
`WARNING: Before modifying this file, run the following command:`,
|
||||
``,
|
||||
`$ npx keycloakify own --path '${path}'`,
|
||||
``,
|
||||
`This file is provided by ${uiModuleName} version ${uiModuleVersion}.`,
|
||||
`It was copied into your repository by the postinstall script: \`keycloakify postinstall\`.`
|
||||
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
|
||||
`It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`
|
||||
];
|
||||
})()
|
||||
});
|
1
src/bin/sync-extensions/index.ts
Normal file
1
src/bin/sync-extensions/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./sync-extension";
|
@ -1,6 +1,6 @@
|
||||
import { assert, type Equals, is } from "tsafe/assert";
|
||||
import type { BuildContext } from "../shared/buildContext";
|
||||
import type { UiModuleMeta } from "./uiModuleMeta";
|
||||
import type { ExtensionModuleMeta } from "./extensionModuleMeta";
|
||||
import { z } from "zod";
|
||||
import { id } from "tsafe/id";
|
||||
import * as fsPr from "fs/promises";
|
||||
@ -16,29 +16,29 @@ export type BuildContextLike = {
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export type UiModuleMetaLike = {
|
||||
export type ExtensionModuleMetaLike = {
|
||||
moduleName: string;
|
||||
peerDependencies: Record<string, string>;
|
||||
};
|
||||
|
||||
assert<UiModuleMeta extends UiModuleMetaLike ? true : false>();
|
||||
assert<ExtensionModuleMeta extends ExtensionModuleMetaLike ? true : false>();
|
||||
|
||||
export async function installUiModulesPeerDependencies(params: {
|
||||
export async function installExtensionModulesPeerDependencies(params: {
|
||||
buildContext: BuildContextLike;
|
||||
uiModuleMetas: UiModuleMetaLike[];
|
||||
extensionModuleMetas: ExtensionModuleMetaLike[];
|
||||
}): Promise<void | never> {
|
||||
const { buildContext, uiModuleMetas } = params;
|
||||
const { buildContext, extensionModuleMetas } = params;
|
||||
|
||||
const { uiModulesPerDependencies } = (() => {
|
||||
const uiModulesPerDependencies: Record<string, string> = {};
|
||||
const { extensionModulesPerDependencies } = (() => {
|
||||
const extensionModulesPerDependencies: Record<string, string> = {};
|
||||
|
||||
for (const { peerDependencies } of uiModuleMetas) {
|
||||
for (const { peerDependencies } of extensionModuleMetas) {
|
||||
for (const [peerDependencyName, versionRange_candidate] of Object.entries(
|
||||
peerDependencies
|
||||
)) {
|
||||
const versionRange = (() => {
|
||||
const versionRange_current =
|
||||
uiModulesPerDependencies[peerDependencyName];
|
||||
extensionModulesPerDependencies[peerDependencyName];
|
||||
|
||||
if (versionRange_current === undefined) {
|
||||
return versionRange_candidate;
|
||||
@ -76,11 +76,11 @@ export async function installUiModulesPeerDependencies(params: {
|
||||
return versionRange;
|
||||
})();
|
||||
|
||||
uiModulesPerDependencies[peerDependencyName] = versionRange;
|
||||
extensionModulesPerDependencies[peerDependencyName] = versionRange;
|
||||
}
|
||||
}
|
||||
|
||||
return { uiModulesPerDependencies };
|
||||
return { extensionModulesPerDependencies };
|
||||
})();
|
||||
|
||||
const parsedPackageJson = await (async () => {
|
||||
@ -117,7 +117,9 @@ export async function installUiModulesPeerDependencies(params: {
|
||||
|
||||
const parsedPackageJson_before = JSON.parse(JSON.stringify(parsedPackageJson));
|
||||
|
||||
for (const [moduleName, versionRange] of Object.entries(uiModulesPerDependencies)) {
|
||||
for (const [moduleName, versionRange] of Object.entries(
|
||||
extensionModulesPerDependencies
|
||||
)) {
|
||||
if (moduleName.startsWith("@types/")) {
|
||||
(parsedPackageJson.devDependencies ??= {})[moduleName] = versionRange;
|
||||
continue;
|
@ -7,7 +7,7 @@ import {
|
||||
} from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../shared/buildContext";
|
||||
import type { UiModuleMeta } from "./uiModuleMeta";
|
||||
import type { ExtensionModuleMeta } from "./extensionModuleMeta";
|
||||
import { existsAsync } from "../tools/fs.existsAsync";
|
||||
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
||||
|
||||
@ -22,12 +22,12 @@ const DELIMITER_END = `# === Owned files end =====`;
|
||||
|
||||
export async function writeManagedGitignoreFile(params: {
|
||||
buildContext: BuildContextLike;
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
extensionModuleMetas: ExtensionModuleMeta[];
|
||||
ownedFilesRelativePaths: string[];
|
||||
}): Promise<void> {
|
||||
const { buildContext, uiModuleMetas, ownedFilesRelativePaths } = params;
|
||||
const { buildContext, extensionModuleMetas, ownedFilesRelativePaths } = params;
|
||||
|
||||
if (uiModuleMetas.length === 0) {
|
||||
if (extensionModuleMetas.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -43,10 +43,10 @@ export async function writeManagedGitignoreFile(params: {
|
||||
.map(line => `# ${line}`),
|
||||
DELIMITER_END,
|
||||
``,
|
||||
...uiModuleMetas
|
||||
.map(uiModuleMeta => [
|
||||
`# === ${uiModuleMeta.moduleName} v${uiModuleMeta.version} ===`,
|
||||
...uiModuleMeta.files
|
||||
...extensionModuleMetas
|
||||
.map(extensionModuleMeta => [
|
||||
`# === ${extensionModuleMeta.moduleName} v${extensionModuleMeta.version} ===`,
|
||||
...extensionModuleMeta.files
|
||||
.map(({ fileRelativePath }) => fileRelativePath)
|
||||
.filter(
|
||||
fileRelativePath =>
|
@ -1,6 +1,6 @@
|
||||
import type { BuildContext } from "../shared/buildContext";
|
||||
import { getUiModuleMetas, computeHash } from "./uiModuleMeta";
|
||||
import { installUiModulesPeerDependencies } from "./installUiModulesPeerDependencies";
|
||||
import { getExtensionModuleMetas, computeHash } from "./extensionModuleMeta";
|
||||
import { installExtensionModulesPeerDependencies } from "./installExtensionModulesPeerDependencies";
|
||||
import {
|
||||
readManagedGitignoreFile,
|
||||
writeManagedGitignoreFile
|
||||
@ -15,11 +15,11 @@ import { untrackFromGit } from "../tools/untrackFromGit";
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const uiModuleMetas = await getUiModuleMetas({ buildContext });
|
||||
const extensionModuleMetas = await getExtensionModuleMetas({ buildContext });
|
||||
|
||||
await installUiModulesPeerDependencies({
|
||||
await installExtensionModulesPeerDependencies({
|
||||
buildContext,
|
||||
uiModuleMetas
|
||||
extensionModuleMetas
|
||||
});
|
||||
|
||||
const { ownedFilesRelativePaths } = await readManagedGitignoreFile({
|
||||
@ -29,14 +29,14 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
await writeManagedGitignoreFile({
|
||||
buildContext,
|
||||
ownedFilesRelativePaths,
|
||||
uiModuleMetas
|
||||
extensionModuleMetas
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
uiModuleMetas
|
||||
.map(uiModuleMeta =>
|
||||
extensionModuleMetas
|
||||
.map(extensionModuleMeta =>
|
||||
Promise.all(
|
||||
uiModuleMeta.files.map(
|
||||
extensionModuleMeta.files.map(
|
||||
async ({ fileRelativePath, copyableFilePath, hash }) => {
|
||||
if (ownedFilesRelativePaths.includes(fileRelativePath)) {
|
||||
return;
|
@ -24,7 +24,7 @@ export async function listInstalledModules(params: {
|
||||
packageJsonFilePath
|
||||
});
|
||||
|
||||
const uiModuleNames = (
|
||||
const extensionModuleNames = (
|
||||
[parsedPackageJson.dependencies, parsedPackageJson.devDependencies] as const
|
||||
)
|
||||
.filter(exclude(undefined))
|
||||
@ -33,7 +33,7 @@ export async function listInstalledModules(params: {
|
||||
.filter(moduleName => filter({ moduleName }));
|
||||
|
||||
const result = await Promise.all(
|
||||
uiModuleNames.map(async moduleName => {
|
||||
extensionModuleNames.map(async moduleName => {
|
||||
const dirPath = await getInstalledModuleDirPath({
|
||||
moduleName,
|
||||
packageJsonDirPath: pathDirname(packageJsonFilePath),
|
||||
|
Loading…
x
Reference in New Issue
Block a user