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 { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||||
import { assert, is, type Equals } from "tsafe/assert";
|
import { assert, is, type Equals } from "tsafe/assert";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { addPostinstallScriptIfNotPresent } from "./shared/addPostinstallScriptIfNotPresent";
|
import { addSyncExtensionsToPostinstallScript } from "./shared/addSyncExtensionsToPostinstallScript";
|
||||||
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
|
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
|
||||||
import { npmInstall } from "./tools/npmInstall";
|
import { npmInstall } from "./tools/npmInstall";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
@ -74,7 +74,7 @@ export async function command(params: { buildContext: BuildContext }) {
|
|||||||
return parsedPackageJson;
|
return parsedPackageJson;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
addPostinstallScriptIfNotPresent({
|
addSyncExtensionsToPostinstallScript({
|
||||||
parsedPackageJson,
|
parsedPackageJson,
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -248,13 +248,30 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command({
|
.command({
|
||||||
name: "postinstall",
|
name: "sync-extensions",
|
||||||
description: "Initialize all the Keycloakify UI modules installed in the project."
|
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({
|
.task({
|
||||||
skip,
|
skip,
|
||||||
handler: async ({ projectDirPath }) => {
|
handler: async ({ projectDirPath }) => {
|
||||||
const { command } = await import("./postinstall");
|
const { command } = await import("./sync-extensions");
|
||||||
|
|
||||||
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
await command({ buildContext: getBuildContext({ projectDirPath }) });
|
||||||
}
|
}
|
||||||
@ -267,9 +284,20 @@ program
|
|||||||
}>({
|
}>({
|
||||||
name: "own",
|
name: "own",
|
||||||
description: [
|
description: [
|
||||||
"WARNING: Not usable yet, will be used for future features",
|
"Manages ownership of auto-generated files provided by Keycloakify extensions.",
|
||||||
"Take ownership over a given file"
|
"",
|
||||||
].join(" ")
|
"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({
|
.option({
|
||||||
key: "path",
|
key: "path",
|
||||||
@ -282,9 +310,9 @@ program
|
|||||||
return { long, short };
|
return { long, short };
|
||||||
})(),
|
})(),
|
||||||
description: [
|
description: [
|
||||||
"Relative path of the file or the directory that you want to take ownership over.",
|
"Specifies the relative path of the file or directory to take ownership of.",
|
||||||
"The path is relative to your theme directory.",
|
"This path should be relative to your theme directory.",
|
||||||
"Example `--path admin/page/Login.tsx`"
|
"Example: `--path 'admin/KcPage.tsx'`"
|
||||||
].join(" ")
|
].join(" ")
|
||||||
})
|
})
|
||||||
.option({
|
.option({
|
||||||
@ -296,7 +324,10 @@ program
|
|||||||
|
|
||||||
return name;
|
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
|
defaultValue: false
|
||||||
})
|
})
|
||||||
.task({
|
.task({
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import type { BuildContext } from "./shared/buildContext";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
import { getUiModuleFileSourceCodeReadyToBeCopied } from "./postinstall/getUiModuleFileSourceCodeReadyToBeCopied";
|
import { getExtensionModuleFileSourceCodeReadyToBeCopied } from "./sync-extensions/getExtensionModuleFileSourceCodeReadyToBeCopied";
|
||||||
import { getAbsoluteAndInOsFormatPath } from "./tools/getAbsoluteAndInOsFormatPath";
|
import type { ExtensionModuleMeta } from "./sync-extensions/extensionModuleMeta";
|
||||||
import { relative as pathRelative, dirname as pathDirname, join as pathJoin } from "path";
|
import { command as command_syncExtensions } from "./sync-extensions/sync-extension";
|
||||||
import { getUiModuleMetas } from "./postinstall/uiModuleMeta";
|
|
||||||
import { getInstalledModuleDirPath } from "./tools/getInstalledModuleDirPath";
|
|
||||||
import * as fsPr from "fs/promises";
|
|
||||||
import {
|
import {
|
||||||
readManagedGitignoreFile,
|
readManagedGitignoreFile,
|
||||||
writeManagedGitignoreFile
|
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 { isInside } from "./tools/isInside";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { UiModuleMeta } from "./postinstall/uiModuleMeta";
|
|
||||||
import { command as command_postinstall } from "./postinstall";
|
|
||||||
|
|
||||||
export async function command(params: {
|
export async function command(params: {
|
||||||
buildContext: BuildContext;
|
buildContext: BuildContext;
|
||||||
@ -23,9 +23,9 @@ export async function command(params: {
|
|||||||
}) {
|
}) {
|
||||||
const { buildContext, cliCommandOptions } = 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(
|
const fileOrDirectoryRelativePath = pathRelative(
|
||||||
buildContext.themeSrcDirPath,
|
buildContext.themeSrcDirPath,
|
||||||
getAbsoluteAndInOsFormatPath({
|
getAbsoluteAndInOsFormatPath({
|
||||||
@ -34,10 +34,10 @@ export async function command(params: {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const arr = uiModuleMetas
|
const arr = extensionModuleMetas
|
||||||
.map(uiModuleMeta => ({
|
.map(extensionModuleMeta => ({
|
||||||
uiModuleMeta,
|
extensionModuleMeta,
|
||||||
fileRelativePaths: uiModuleMeta.files
|
fileRelativePaths: extensionModuleMeta.files
|
||||||
.map(({ fileRelativePath }) => fileRelativePath)
|
.map(({ fileRelativePath }) => fileRelativePath)
|
||||||
.filter(
|
.filter(
|
||||||
fileRelativePath =>
|
fileRelativePath =>
|
||||||
@ -50,18 +50,26 @@ export async function command(params: {
|
|||||||
}))
|
}))
|
||||||
.filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
|
.filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
|
||||||
|
|
||||||
const targetFileRelativePathsByUiModuleMeta = new Map<UiModuleMeta, string[]>();
|
const targetFileRelativePathsByExtensionModuleMeta = new Map<
|
||||||
|
ExtensionModuleMeta,
|
||||||
|
string[]
|
||||||
|
>();
|
||||||
|
|
||||||
for (const { uiModuleMeta, fileRelativePaths } of arr) {
|
for (const { extensionModuleMeta, fileRelativePaths } of arr) {
|
||||||
targetFileRelativePathsByUiModuleMeta.set(uiModuleMeta, fileRelativePaths);
|
targetFileRelativePathsByExtensionModuleMeta.set(
|
||||||
|
extensionModuleMeta,
|
||||||
|
fileRelativePaths
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { targetFileRelativePathsByUiModuleMeta };
|
return { targetFileRelativePathsByExtensionModuleMeta };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (targetFileRelativePathsByUiModuleMeta.size === 0) {
|
if (targetFileRelativePathsByExtensionModuleMeta.size === 0) {
|
||||||
console.log(
|
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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -72,34 +80,34 @@ export async function command(params: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await (cliCommandOptions.isRevert ? command_revert : command_own)({
|
await (cliCommandOptions.isRevert ? command_revert : command_own)({
|
||||||
uiModuleMetas,
|
extensionModuleMetas,
|
||||||
targetFileRelativePathsByUiModuleMeta,
|
targetFileRelativePathsByExtensionModuleMeta,
|
||||||
ownedFilesRelativePaths_current,
|
ownedFilesRelativePaths_current,
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type Params_subcommands = {
|
type Params_subcommands = {
|
||||||
uiModuleMetas: UiModuleMeta[];
|
extensionModuleMetas: ExtensionModuleMeta[];
|
||||||
targetFileRelativePathsByUiModuleMeta: Map<UiModuleMeta, string[]>;
|
targetFileRelativePathsByExtensionModuleMeta: Map<ExtensionModuleMeta, string[]>;
|
||||||
ownedFilesRelativePaths_current: string[];
|
ownedFilesRelativePaths_current: string[];
|
||||||
buildContext: BuildContext;
|
buildContext: BuildContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function command_own(params: Params_subcommands) {
|
async function command_own(params: Params_subcommands) {
|
||||||
const {
|
const {
|
||||||
uiModuleMetas,
|
extensionModuleMetas,
|
||||||
targetFileRelativePathsByUiModuleMeta,
|
targetFileRelativePathsByExtensionModuleMeta,
|
||||||
ownedFilesRelativePaths_current,
|
ownedFilesRelativePaths_current,
|
||||||
buildContext
|
buildContext
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
await writeManagedGitignoreFile({
|
await writeManagedGitignoreFile({
|
||||||
buildContext,
|
buildContext,
|
||||||
uiModuleMetas,
|
extensionModuleMetas,
|
||||||
ownedFilesRelativePaths: [
|
ownedFilesRelativePaths: [
|
||||||
...ownedFilesRelativePaths_current,
|
...ownedFilesRelativePaths_current,
|
||||||
...Array.from(targetFileRelativePathsByUiModuleMeta.values())
|
...Array.from(targetFileRelativePathsByExtensionModuleMeta.values())
|
||||||
.flat()
|
.flat()
|
||||||
.filter(
|
.filter(
|
||||||
fileRelativePath =>
|
fileRelativePath =>
|
||||||
@ -111,11 +119,11 @@ async function command_own(params: Params_subcommands) {
|
|||||||
const writeActions: (() => Promise<void>)[] = [];
|
const writeActions: (() => Promise<void>)[] = [];
|
||||||
|
|
||||||
for (const [
|
for (const [
|
||||||
uiModuleMeta,
|
extensionModuleMeta,
|
||||||
fileRelativePaths
|
fileRelativePaths
|
||||||
] of targetFileRelativePathsByUiModuleMeta.entries()) {
|
] of targetFileRelativePathsByExtensionModuleMeta.entries()) {
|
||||||
const uiModuleDirPath = await getInstalledModuleDirPath({
|
const extensionModuleDirPath = await getInstalledModuleDirPath({
|
||||||
moduleName: uiModuleMeta.moduleName,
|
moduleName: extensionModuleMeta.moduleName,
|
||||||
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath),
|
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath),
|
||||||
projectDirPath: buildContext.projectDirPath
|
projectDirPath: buildContext.projectDirPath
|
||||||
});
|
});
|
||||||
@ -129,13 +137,13 @@ async function command_own(params: Params_subcommands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeActions.push(async () => {
|
writeActions.push(async () => {
|
||||||
const sourceCode = await getUiModuleFileSourceCodeReadyToBeCopied({
|
const sourceCode = await getExtensionModuleFileSourceCodeReadyToBeCopied({
|
||||||
buildContext,
|
buildContext,
|
||||||
fileRelativePath,
|
fileRelativePath,
|
||||||
isOwnershipAction: true,
|
isOwnershipAction: true,
|
||||||
uiModuleName: uiModuleMeta.moduleName,
|
extensionModuleName: extensionModuleMeta.moduleName,
|
||||||
uiModuleDirPath,
|
extensionModuleDirPath,
|
||||||
uiModuleVersion: uiModuleMeta.version
|
extensionModuleVersion: extensionModuleMeta.version
|
||||||
});
|
});
|
||||||
|
|
||||||
await fsPr.writeFile(
|
await fsPr.writeFile(
|
||||||
@ -158,14 +166,14 @@ async function command_own(params: Params_subcommands) {
|
|||||||
|
|
||||||
async function command_revert(params: Params_subcommands) {
|
async function command_revert(params: Params_subcommands) {
|
||||||
const {
|
const {
|
||||||
uiModuleMetas,
|
extensionModuleMetas,
|
||||||
targetFileRelativePathsByUiModuleMeta,
|
targetFileRelativePathsByExtensionModuleMeta,
|
||||||
ownedFilesRelativePaths_current,
|
ownedFilesRelativePaths_current,
|
||||||
buildContext
|
buildContext
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const ownedFilesRelativePaths_toRemove = Array.from(
|
const ownedFilesRelativePaths_toRemove = Array.from(
|
||||||
targetFileRelativePathsByUiModuleMeta.values()
|
targetFileRelativePathsByExtensionModuleMeta.values()
|
||||||
)
|
)
|
||||||
.flat()
|
.flat()
|
||||||
.filter(fileRelativePath => {
|
.filter(fileRelativePath => {
|
||||||
@ -190,12 +198,12 @@ async function command_revert(params: Params_subcommands) {
|
|||||||
|
|
||||||
await writeManagedGitignoreFile({
|
await writeManagedGitignoreFile({
|
||||||
buildContext,
|
buildContext,
|
||||||
uiModuleMetas,
|
extensionModuleMetas,
|
||||||
ownedFilesRelativePaths: ownedFilesRelativePaths_current.filter(
|
ownedFilesRelativePaths: ownedFilesRelativePaths_current.filter(
|
||||||
fileRelativePath =>
|
fileRelativePath =>
|
||||||
!ownedFilesRelativePaths_toRemove.includes(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>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export function addPostinstallScriptIfNotPresent(params: {
|
export function addSyncExtensionsToPostinstallScript(params: {
|
||||||
parsedPackageJson: { scripts?: Record<string, string | undefined> };
|
parsedPackageJson: { scripts?: Record<string, string | undefined> };
|
||||||
buildContext: BuildContextLike;
|
buildContext: BuildContextLike;
|
||||||
}) {
|
}) {
|
||||||
const { parsedPackageJson, buildContext } = params;
|
const { parsedPackageJson, buildContext } = params;
|
||||||
|
|
||||||
const cmd_base = "keycloakify postinstall";
|
const cmd_base = "keycloakify sync-extensions";
|
||||||
|
|
||||||
const projectCliOptionValue = (() => {
|
const projectCliOptionValue = (() => {
|
||||||
const packageJsonDirPath = pathDirname(buildContext.packageJsonFilePath);
|
const packageJsonDirPath = pathDirname(buildContext.packageJsonFilePath);
|
@ -10,15 +10,15 @@ import { crawlAsync } from "../tools/crawlAsync";
|
|||||||
import { getIsPrettierAvailable, getPrettier } from "../tools/runPrettier";
|
import { getIsPrettierAvailable, getPrettier } from "../tools/runPrettier";
|
||||||
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||||
import {
|
import {
|
||||||
getUiModuleFileSourceCodeReadyToBeCopied,
|
getExtensionModuleFileSourceCodeReadyToBeCopied,
|
||||||
type BuildContextLike as BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied
|
type BuildContextLike as BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied
|
||||||
} from "./getUiModuleFileSourceCodeReadyToBeCopied";
|
} from "./getExtensionModuleFileSourceCodeReadyToBeCopied";
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import { KEYCLOAK_THEME } from "../shared/constants";
|
import { KEYCLOAK_THEME } from "../shared/constants";
|
||||||
import { exclude } from "tsafe/exclude";
|
import { exclude } from "tsafe/exclude";
|
||||||
import { isAmong } from "tsafe/isAmong";
|
import { isAmong } from "tsafe/isAmong";
|
||||||
|
|
||||||
export type UiModuleMeta = {
|
export type ExtensionModuleMeta = {
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
version: string;
|
version: string;
|
||||||
files: {
|
files: {
|
||||||
@ -29,8 +29,8 @@ export type UiModuleMeta = {
|
|||||||
peerDependencies: Record<string, string>;
|
peerDependencies: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const zUiModuleMeta = (() => {
|
const zExtensionModuleMeta = (() => {
|
||||||
type ExpectedType = UiModuleMeta;
|
type ExpectedType = ExtensionModuleMeta;
|
||||||
|
|
||||||
const zTargetType = z.object({
|
const zTargetType = z.object({
|
||||||
moduleName: z.string(),
|
moduleName: z.string(),
|
||||||
@ -56,7 +56,7 @@ type ParsedCacheFile = {
|
|||||||
keycloakifyVersion: string;
|
keycloakifyVersion: string;
|
||||||
prettierConfigHash: string | null;
|
prettierConfigHash: string | null;
|
||||||
thisFilePath: string;
|
thisFilePath: string;
|
||||||
uiModuleMetas: UiModuleMeta[];
|
extensionModuleMetas: ExtensionModuleMeta[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const zParsedCacheFile = (() => {
|
const zParsedCacheFile = (() => {
|
||||||
@ -66,7 +66,7 @@ const zParsedCacheFile = (() => {
|
|||||||
keycloakifyVersion: z.string(),
|
keycloakifyVersion: z.string(),
|
||||||
prettierConfigHash: z.union([z.string(), z.null()]),
|
prettierConfigHash: z.union([z.string(), z.null()]),
|
||||||
thisFilePath: z.string(),
|
thisFilePath: z.string(),
|
||||||
uiModuleMetas: z.array(zUiModuleMeta)
|
extensionModuleMetas: z.array(zExtensionModuleMeta)
|
||||||
});
|
});
|
||||||
|
|
||||||
type InferredType = z.infer<typeof zTargetType>;
|
type InferredType = z.infer<typeof zTargetType>;
|
||||||
@ -76,10 +76,10 @@ const zParsedCacheFile = (() => {
|
|||||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
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 =
|
export type BuildContextLike =
|
||||||
BuildContextLike_getUiModuleFileSourceCodeReadyToBeCopied & {
|
BuildContextLike_getExtensionModuleFileSourceCodeReadyToBeCopied & {
|
||||||
cacheDirPath: string;
|
cacheDirPath: string;
|
||||||
packageJsonFilePath: string;
|
packageJsonFilePath: string;
|
||||||
projectDirPath: string;
|
projectDirPath: string;
|
||||||
@ -87,9 +87,9 @@ export type BuildContextLike =
|
|||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export async function getUiModuleMetas(params: {
|
export async function getExtensionModuleMetas(params: {
|
||||||
buildContext: BuildContextLike;
|
buildContext: BuildContextLike;
|
||||||
}): Promise<UiModuleMeta[]> {
|
}): Promise<ExtensionModuleMeta[]> {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_RELATIVE_PATH);
|
const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_RELATIVE_PATH);
|
||||||
@ -106,7 +106,7 @@ export async function getUiModuleMetas(params: {
|
|||||||
return configHash;
|
return configHash;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const installedUiModules = await (async () => {
|
const installedExtensionModules = await (async () => {
|
||||||
const installedModulesWithKeycloakifyInTheName = await listInstalledModules({
|
const installedModulesWithKeycloakifyInTheName = await listInstalledModules({
|
||||||
packageJsonFilePath: buildContext.packageJsonFilePath,
|
packageJsonFilePath: buildContext.packageJsonFilePath,
|
||||||
projectDirPath: buildContext.packageJsonFilePath,
|
projectDirPath: buildContext.packageJsonFilePath,
|
||||||
@ -134,7 +134,7 @@ export async function getUiModuleMetas(params: {
|
|||||||
return await fsPr.readFile(cacheFilePath);
|
return await fsPr.readFile(cacheFilePath);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const uiModuleMetas_cacheUpToDate: UiModuleMeta[] = await (async () => {
|
const extensionModuleMetas_cacheUpToDate: ExtensionModuleMeta[] = await (async () => {
|
||||||
const parsedCacheFile: ParsedCacheFile | undefined = await (async () => {
|
const parsedCacheFile: ParsedCacheFile | undefined = await (async () => {
|
||||||
if (cacheContent === undefined) {
|
if (cacheContent === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -177,45 +177,51 @@ export async function getUiModuleMetas(params: {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const uiModuleMetas_cacheUpToDate = parsedCacheFile.uiModuleMetas.filter(
|
const extensionModuleMetas_cacheUpToDate =
|
||||||
uiModuleMeta => {
|
parsedCacheFile.extensionModuleMetas.filter(extensionModuleMeta => {
|
||||||
const correspondingInstalledUiModule = installedUiModules.find(
|
const correspondingInstalledExtensionModule =
|
||||||
installedUiModule =>
|
installedExtensionModules.find(
|
||||||
installedUiModule.moduleName === uiModuleMeta.moduleName
|
installedExtensionModule =>
|
||||||
);
|
installedExtensionModule.moduleName ===
|
||||||
|
extensionModuleMeta.moduleName
|
||||||
|
);
|
||||||
|
|
||||||
if (correspondingInstalledUiModule === undefined) {
|
if (correspondingInstalledExtensionModule === undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return correspondingInstalledUiModule.version === uiModuleMeta.version;
|
return (
|
||||||
}
|
correspondingInstalledExtensionModule.version ===
|
||||||
);
|
extensionModuleMeta.version
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return uiModuleMetas_cacheUpToDate;
|
return extensionModuleMetas_cacheUpToDate;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const uiModuleMetas = await Promise.all(
|
const extensionModuleMetas = await Promise.all(
|
||||||
installedUiModules.map(
|
installedExtensionModules.map(
|
||||||
async ({
|
async ({
|
||||||
moduleName,
|
moduleName,
|
||||||
version,
|
version,
|
||||||
peerDependencies,
|
peerDependencies,
|
||||||
dirPath
|
dirPath
|
||||||
}): Promise<UiModuleMeta> => {
|
}): Promise<ExtensionModuleMeta> => {
|
||||||
use_cache: {
|
use_cache: {
|
||||||
const uiModuleMeta_cache = uiModuleMetas_cacheUpToDate.find(
|
const extensionModuleMeta_cache =
|
||||||
uiModuleMeta => uiModuleMeta.moduleName === moduleName
|
extensionModuleMetas_cacheUpToDate.find(
|
||||||
);
|
extensionModuleMeta =>
|
||||||
|
extensionModuleMeta.moduleName === moduleName
|
||||||
|
);
|
||||||
|
|
||||||
if (uiModuleMeta_cache === undefined) {
|
if (extensionModuleMeta_cache === undefined) {
|
||||||
break use_cache;
|
break use_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
return uiModuleMeta_cache;
|
return extensionModuleMeta_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
const files: UiModuleMeta["files"] = [];
|
const files: ExtensionModuleMeta["files"] = [];
|
||||||
|
|
||||||
{
|
{
|
||||||
const srcDirPath = pathJoin(dirPath, KEYCLOAK_THEME);
|
const srcDirPath = pathJoin(dirPath, KEYCLOAK_THEME);
|
||||||
@ -225,13 +231,13 @@ export async function getUiModuleMetas(params: {
|
|||||||
returnedPathsType: "relative to dirPath",
|
returnedPathsType: "relative to dirPath",
|
||||||
onFileFound: async fileRelativePath => {
|
onFileFound: async fileRelativePath => {
|
||||||
const sourceCode =
|
const sourceCode =
|
||||||
await getUiModuleFileSourceCodeReadyToBeCopied({
|
await getExtensionModuleFileSourceCodeReadyToBeCopied({
|
||||||
buildContext,
|
buildContext,
|
||||||
fileRelativePath,
|
fileRelativePath,
|
||||||
isOwnershipAction: false,
|
isOwnershipAction: false,
|
||||||
uiModuleDirPath: dirPath,
|
extensionModuleDirPath: dirPath,
|
||||||
uiModuleName: moduleName,
|
extensionModuleName: moduleName,
|
||||||
uiModuleVersion: version
|
extensionModuleVersion: version
|
||||||
});
|
});
|
||||||
|
|
||||||
const hash = computeHash(sourceCode);
|
const hash = computeHash(sourceCode);
|
||||||
@ -261,7 +267,7 @@ export async function getUiModuleMetas(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return id<UiModuleMeta>({
|
return id<ExtensionModuleMeta>({
|
||||||
moduleName,
|
moduleName,
|
||||||
version,
|
version,
|
||||||
files,
|
files,
|
||||||
@ -281,7 +287,7 @@ export async function getUiModuleMetas(params: {
|
|||||||
keycloakifyVersion,
|
keycloakifyVersion,
|
||||||
prettierConfigHash,
|
prettierConfigHash,
|
||||||
thisFilePath: cacheFilePath,
|
thisFilePath: cacheFilePath,
|
||||||
uiModuleMetas
|
extensionModuleMetas
|
||||||
});
|
});
|
||||||
|
|
||||||
const cacheContent_new = Buffer.from(
|
const cacheContent_new = Buffer.from(
|
||||||
@ -306,7 +312,7 @@ export async function getUiModuleMetas(params: {
|
|||||||
await fsPr.writeFile(cacheFilePath, cacheContent_new);
|
await fsPr.writeFile(cacheFilePath, cacheContent_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uiModuleMetas;
|
return extensionModuleMetas;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function computeHash(data: Buffer) {
|
export function computeHash(data: Buffer) {
|
@ -11,25 +11,27 @@ export type BuildContextLike = {
|
|||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
|
||||||
buildContext: BuildContextLike;
|
buildContext: BuildContextLike;
|
||||||
fileRelativePath: string;
|
fileRelativePath: string;
|
||||||
isOwnershipAction: boolean;
|
isOwnershipAction: boolean;
|
||||||
uiModuleDirPath: string;
|
extensionModuleDirPath: string;
|
||||||
uiModuleName: string;
|
extensionModuleName: string;
|
||||||
uiModuleVersion: string;
|
extensionModuleVersion: string;
|
||||||
}): Promise<Buffer> {
|
}): Promise<Buffer> {
|
||||||
const {
|
const {
|
||||||
buildContext,
|
buildContext,
|
||||||
uiModuleDirPath,
|
extensionModuleDirPath,
|
||||||
fileRelativePath,
|
fileRelativePath,
|
||||||
isOwnershipAction,
|
isOwnershipAction,
|
||||||
uiModuleName,
|
extensionModuleName,
|
||||||
uiModuleVersion
|
extensionModuleVersion
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let sourceCode = (
|
let sourceCode = (
|
||||||
await fsPr.readFile(pathJoin(uiModuleDirPath, KEYCLOAK_THEME, fileRelativePath))
|
await fsPr.readFile(
|
||||||
|
pathJoin(extensionModuleDirPath, KEYCLOAK_THEME, fileRelativePath)
|
||||||
|
)
|
||||||
).toString("utf8");
|
).toString("utf8");
|
||||||
|
|
||||||
sourceCode = addCommentToSourceCode({
|
sourceCode = addCommentToSourceCode({
|
||||||
@ -40,18 +42,18 @@ export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
|||||||
|
|
||||||
return isOwnershipAction
|
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:`,
|
`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:`,
|
`WARNING: Before modifying this file, run the following command:`,
|
||||||
``,
|
``,
|
||||||
`$ npx keycloakify own --path '${path}'`,
|
`$ npx keycloakify own --path '${path}'`,
|
||||||
``,
|
``,
|
||||||
`This file is provided by ${uiModuleName} version ${uiModuleVersion}.`,
|
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
|
||||||
`It was copied into your repository by the postinstall script: \`keycloakify postinstall\`.`
|
`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 { assert, type Equals, is } from "tsafe/assert";
|
||||||
import type { BuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import type { UiModuleMeta } from "./uiModuleMeta";
|
import type { ExtensionModuleMeta } from "./extensionModuleMeta";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import * as fsPr from "fs/promises";
|
import * as fsPr from "fs/promises";
|
||||||
@ -16,29 +16,29 @@ export type BuildContextLike = {
|
|||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export type UiModuleMetaLike = {
|
export type ExtensionModuleMetaLike = {
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
peerDependencies: Record<string, 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;
|
buildContext: BuildContextLike;
|
||||||
uiModuleMetas: UiModuleMetaLike[];
|
extensionModuleMetas: ExtensionModuleMetaLike[];
|
||||||
}): Promise<void | never> {
|
}): Promise<void | never> {
|
||||||
const { buildContext, uiModuleMetas } = params;
|
const { buildContext, extensionModuleMetas } = params;
|
||||||
|
|
||||||
const { uiModulesPerDependencies } = (() => {
|
const { extensionModulesPerDependencies } = (() => {
|
||||||
const uiModulesPerDependencies: Record<string, string> = {};
|
const extensionModulesPerDependencies: Record<string, string> = {};
|
||||||
|
|
||||||
for (const { peerDependencies } of uiModuleMetas) {
|
for (const { peerDependencies } of extensionModuleMetas) {
|
||||||
for (const [peerDependencyName, versionRange_candidate] of Object.entries(
|
for (const [peerDependencyName, versionRange_candidate] of Object.entries(
|
||||||
peerDependencies
|
peerDependencies
|
||||||
)) {
|
)) {
|
||||||
const versionRange = (() => {
|
const versionRange = (() => {
|
||||||
const versionRange_current =
|
const versionRange_current =
|
||||||
uiModulesPerDependencies[peerDependencyName];
|
extensionModulesPerDependencies[peerDependencyName];
|
||||||
|
|
||||||
if (versionRange_current === undefined) {
|
if (versionRange_current === undefined) {
|
||||||
return versionRange_candidate;
|
return versionRange_candidate;
|
||||||
@ -76,11 +76,11 @@ export async function installUiModulesPeerDependencies(params: {
|
|||||||
return versionRange;
|
return versionRange;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
uiModulesPerDependencies[peerDependencyName] = versionRange;
|
extensionModulesPerDependencies[peerDependencyName] = versionRange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { uiModulesPerDependencies };
|
return { extensionModulesPerDependencies };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const parsedPackageJson = await (async () => {
|
const parsedPackageJson = await (async () => {
|
||||||
@ -117,7 +117,9 @@ export async function installUiModulesPeerDependencies(params: {
|
|||||||
|
|
||||||
const parsedPackageJson_before = JSON.parse(JSON.stringify(parsedPackageJson));
|
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/")) {
|
if (moduleName.startsWith("@types/")) {
|
||||||
(parsedPackageJson.devDependencies ??= {})[moduleName] = versionRange;
|
(parsedPackageJson.devDependencies ??= {})[moduleName] = versionRange;
|
||||||
continue;
|
continue;
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from "path";
|
} from "path";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import type { UiModuleMeta } from "./uiModuleMeta";
|
import type { ExtensionModuleMeta } from "./extensionModuleMeta";
|
||||||
import { existsAsync } from "../tools/fs.existsAsync";
|
import { existsAsync } from "../tools/fs.existsAsync";
|
||||||
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ const DELIMITER_END = `# === Owned files end =====`;
|
|||||||
|
|
||||||
export async function writeManagedGitignoreFile(params: {
|
export async function writeManagedGitignoreFile(params: {
|
||||||
buildContext: BuildContextLike;
|
buildContext: BuildContextLike;
|
||||||
uiModuleMetas: UiModuleMeta[];
|
extensionModuleMetas: ExtensionModuleMeta[];
|
||||||
ownedFilesRelativePaths: string[];
|
ownedFilesRelativePaths: string[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { buildContext, uiModuleMetas, ownedFilesRelativePaths } = params;
|
const { buildContext, extensionModuleMetas, ownedFilesRelativePaths } = params;
|
||||||
|
|
||||||
if (uiModuleMetas.length === 0) {
|
if (extensionModuleMetas.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,10 +43,10 @@ export async function writeManagedGitignoreFile(params: {
|
|||||||
.map(line => `# ${line}`),
|
.map(line => `# ${line}`),
|
||||||
DELIMITER_END,
|
DELIMITER_END,
|
||||||
``,
|
``,
|
||||||
...uiModuleMetas
|
...extensionModuleMetas
|
||||||
.map(uiModuleMeta => [
|
.map(extensionModuleMeta => [
|
||||||
`# === ${uiModuleMeta.moduleName} v${uiModuleMeta.version} ===`,
|
`# === ${extensionModuleMeta.moduleName} v${extensionModuleMeta.version} ===`,
|
||||||
...uiModuleMeta.files
|
...extensionModuleMeta.files
|
||||||
.map(({ fileRelativePath }) => fileRelativePath)
|
.map(({ fileRelativePath }) => fileRelativePath)
|
||||||
.filter(
|
.filter(
|
||||||
fileRelativePath =>
|
fileRelativePath =>
|
@ -1,6 +1,6 @@
|
|||||||
import type { BuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import { getUiModuleMetas, computeHash } from "./uiModuleMeta";
|
import { getExtensionModuleMetas, computeHash } from "./extensionModuleMeta";
|
||||||
import { installUiModulesPeerDependencies } from "./installUiModulesPeerDependencies";
|
import { installExtensionModulesPeerDependencies } from "./installExtensionModulesPeerDependencies";
|
||||||
import {
|
import {
|
||||||
readManagedGitignoreFile,
|
readManagedGitignoreFile,
|
||||||
writeManagedGitignoreFile
|
writeManagedGitignoreFile
|
||||||
@ -15,11 +15,11 @@ import { untrackFromGit } from "../tools/untrackFromGit";
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const uiModuleMetas = await getUiModuleMetas({ buildContext });
|
const extensionModuleMetas = await getExtensionModuleMetas({ buildContext });
|
||||||
|
|
||||||
await installUiModulesPeerDependencies({
|
await installExtensionModulesPeerDependencies({
|
||||||
buildContext,
|
buildContext,
|
||||||
uiModuleMetas
|
extensionModuleMetas
|
||||||
});
|
});
|
||||||
|
|
||||||
const { ownedFilesRelativePaths } = await readManagedGitignoreFile({
|
const { ownedFilesRelativePaths } = await readManagedGitignoreFile({
|
||||||
@ -29,14 +29,14 @@ export async function command(params: { buildContext: BuildContext }) {
|
|||||||
await writeManagedGitignoreFile({
|
await writeManagedGitignoreFile({
|
||||||
buildContext,
|
buildContext,
|
||||||
ownedFilesRelativePaths,
|
ownedFilesRelativePaths,
|
||||||
uiModuleMetas
|
extensionModuleMetas
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
uiModuleMetas
|
extensionModuleMetas
|
||||||
.map(uiModuleMeta =>
|
.map(extensionModuleMeta =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
uiModuleMeta.files.map(
|
extensionModuleMeta.files.map(
|
||||||
async ({ fileRelativePath, copyableFilePath, hash }) => {
|
async ({ fileRelativePath, copyableFilePath, hash }) => {
|
||||||
if (ownedFilesRelativePaths.includes(fileRelativePath)) {
|
if (ownedFilesRelativePaths.includes(fileRelativePath)) {
|
||||||
return;
|
return;
|
@ -24,7 +24,7 @@ export async function listInstalledModules(params: {
|
|||||||
packageJsonFilePath
|
packageJsonFilePath
|
||||||
});
|
});
|
||||||
|
|
||||||
const uiModuleNames = (
|
const extensionModuleNames = (
|
||||||
[parsedPackageJson.dependencies, parsedPackageJson.devDependencies] as const
|
[parsedPackageJson.dependencies, parsedPackageJson.devDependencies] as const
|
||||||
)
|
)
|
||||||
.filter(exclude(undefined))
|
.filter(exclude(undefined))
|
||||||
@ -33,7 +33,7 @@ export async function listInstalledModules(params: {
|
|||||||
.filter(moduleName => filter({ moduleName }));
|
.filter(moduleName => filter({ moduleName }));
|
||||||
|
|
||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
uiModuleNames.map(async moduleName => {
|
extensionModuleNames.map(async moduleName => {
|
||||||
const dirPath = await getInstalledModuleDirPath({
|
const dirPath = await getInstalledModuleDirPath({
|
||||||
moduleName,
|
moduleName,
|
||||||
packageJsonDirPath: pathDirname(packageJsonFilePath),
|
packageJsonDirPath: pathDirname(packageJsonFilePath),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user