checkpoint
This commit is contained in:
parent
d7455fd100
commit
93fcf96cde
@ -1,3 +0,0 @@
|
||||
export async function getListOfEjectedFiles(params: {}): Promise<string[]> {}
|
||||
|
||||
export async function writeListOfEjectedFiles(params: {}) {}
|
84
src/bin/sync-ui-modules/managedGitignoreFile.ts
Normal file
84
src/bin/sync-ui-modules/managedGitignoreFile.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import * as fsPr from "fs/promises";
|
||||
import { join as pathJoin, sep as pathSep, dirname as pathDirname } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../shared/buildContext";
|
||||
import type { UiModuleMeta } from "./uiModuleMeta";
|
||||
import { existsAsync } from "../tools/fs.existsAsync";
|
||||
|
||||
export type BuildContextLike = {
|
||||
themeSrcDirPath: string;
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
const DELIMITER_START = `# === Ejected files start ===`;
|
||||
const DELIMITER_END = `# === Ejected files end =====`;
|
||||
|
||||
export async function writeManagedGitignoreFile(params: {
|
||||
buildContext: BuildContextLike;
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
ejectedFilesRelativePaths: string[];
|
||||
}): Promise<void> {
|
||||
const { buildContext, uiModuleMetas, ejectedFilesRelativePaths } = params;
|
||||
|
||||
if (uiModuleMetas.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = pathJoin(buildContext.themeSrcDirPath, ".gitignore");
|
||||
|
||||
const content_new = Buffer.from(
|
||||
[
|
||||
`# This file is managed by Keycloakify, do not edit it manually.`,
|
||||
``,
|
||||
DELIMITER_START,
|
||||
...ejectedFilesRelativePaths.map(fileRelativePath =>
|
||||
fileRelativePath.split(pathSep).join("/")
|
||||
),
|
||||
DELIMITER_END,
|
||||
``,
|
||||
...uiModuleMetas
|
||||
.map(uiModuleMeta => [
|
||||
`# === ${uiModuleMeta.moduleName} v${uiModuleMeta.version} ===`,
|
||||
...uiModuleMeta.files
|
||||
.map(({ fileRelativePath }) => fileRelativePath)
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
!ejectedFilesRelativePaths.includes(fileRelativePath)
|
||||
)
|
||||
.map(
|
||||
fileRelativePath =>
|
||||
`/${fileRelativePath.split(pathSep).join("/").replace(/^\.\//, "")}`
|
||||
),
|
||||
|
||||
``
|
||||
])
|
||||
.flat()
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
const content_current = await (async () => {
|
||||
if (!(await existsAsync(filePath))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await fsPr.readFile(filePath);
|
||||
})();
|
||||
|
||||
if (content_current !== undefined && content_current.equals(content_new)) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_dir: {
|
||||
const dirPath = pathDirname(filePath);
|
||||
|
||||
if (await existsAsync(dirPath)) {
|
||||
break create_dir;
|
||||
}
|
||||
|
||||
await fsPr.mkdir(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
await fsPr.writeFile(filePath, content_new);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import { id } from "tsafe/id";
|
||||
import { z } from "zod";
|
||||
import { join as pathJoin } from "path";
|
||||
import { join as pathJoin, sep as pathSep, dirname as pathDirname } from "path";
|
||||
import * as fsPr from "fs/promises";
|
||||
import type { BuildContext } from "../shared/buildContext";
|
||||
import { is } from "tsafe/is";
|
||||
@ -16,25 +16,18 @@ import {
|
||||
} from "./getSourceCodeToCopyInUserCodebase";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export type UiModulesMeta = {
|
||||
keycloakifyVersion: string;
|
||||
prettierConfigHash: string | null;
|
||||
entries: UiModulesMeta.Entry[];
|
||||
export type UiModuleMeta = {
|
||||
moduleName: string;
|
||||
version: string;
|
||||
files: {
|
||||
fileRelativePath: string;
|
||||
hash: string;
|
||||
}[];
|
||||
peerDependencies: Record<string, string>;
|
||||
};
|
||||
|
||||
export namespace UiModulesMeta {
|
||||
export type Entry = {
|
||||
moduleName: string;
|
||||
version: string;
|
||||
files: {
|
||||
fileRelativePath: string;
|
||||
hash: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
const zUiModuleMetasEntry = (() => {
|
||||
type ExpectedType = UiModulesMeta.Entry;
|
||||
const zUiModuleMeta = (() => {
|
||||
type ExpectedType = UiModuleMeta;
|
||||
|
||||
const zTargetType = z.object({
|
||||
moduleName: z.string(),
|
||||
@ -44,7 +37,8 @@ const zUiModuleMetasEntry = (() => {
|
||||
fileRelativePath: z.string(),
|
||||
hash: z.string()
|
||||
})
|
||||
)
|
||||
),
|
||||
peerDependencies: z.record(z.string())
|
||||
});
|
||||
|
||||
type InferredType = z.infer<typeof zTargetType>;
|
||||
@ -54,13 +48,21 @@ const zUiModuleMetasEntry = (() => {
|
||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||
})();
|
||||
|
||||
const zUiModulesMeta = (() => {
|
||||
type ExpectedType = UiModulesMeta;
|
||||
type ParsedCacheFile = {
|
||||
keycloakifyVersion: string;
|
||||
prettierConfigHash: string | null;
|
||||
pathSep: string;
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
};
|
||||
|
||||
const zParsedCacheFile = (() => {
|
||||
type ExpectedType = ParsedCacheFile;
|
||||
|
||||
const zTargetType = z.object({
|
||||
keycloakifyVersion: z.string(),
|
||||
prettierConfigHash: z.union([z.string(), z.null()]),
|
||||
entries: z.array(zUiModuleMetasEntry)
|
||||
pathSep: z.string(),
|
||||
uiModuleMetas: z.array(zUiModuleMeta)
|
||||
});
|
||||
|
||||
type InferredType = z.infer<typeof zTargetType>;
|
||||
@ -70,7 +72,7 @@ const zUiModulesMeta = (() => {
|
||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||
})();
|
||||
|
||||
const RELATIVE_FILE_PATH = pathJoin("uiModulesMeta.json");
|
||||
const CACHE_FILE_BASENAME = "uiModulesMeta.json";
|
||||
|
||||
export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebase & {
|
||||
cacheDirPath: string;
|
||||
@ -80,12 +82,12 @@ export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebas
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export async function readOrCreateUiModulesMeta(params: {
|
||||
export async function getUiModuleMetas(params: {
|
||||
buildContext: BuildContextLike;
|
||||
}): Promise<UiModulesMeta> {
|
||||
}): Promise<UiModuleMeta[]> {
|
||||
const { buildContext } = params;
|
||||
|
||||
const filePath = pathJoin(buildContext.cacheDirPath, RELATIVE_FILE_PATH);
|
||||
const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_BASENAME);
|
||||
|
||||
const keycloakifyVersion = readThisNpmPackageVersion();
|
||||
|
||||
@ -106,76 +108,96 @@ export async function readOrCreateUiModulesMeta(params: {
|
||||
moduleName.includes("keycloakify") && moduleName.endsWith("-ui")
|
||||
});
|
||||
|
||||
const upToDateEntries: UiModulesMeta.Entry[] = await (async () => {
|
||||
const uiModulesMeta_cache: UiModulesMeta | undefined = await (async () => {
|
||||
if (!(await existsAsync(filePath))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const contentStr = (await fsPr.readFile(filePath)).toString("utf8");
|
||||
|
||||
let uiModuleMeta: unknown;
|
||||
|
||||
try {
|
||||
uiModuleMeta = JSON.parse(contentStr);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
zUiModulesMeta.parse(uiModuleMeta);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
assert(is<UiModulesMeta>(uiModuleMeta));
|
||||
|
||||
return uiModuleMeta;
|
||||
})();
|
||||
|
||||
if (uiModulesMeta_cache === undefined) {
|
||||
return [];
|
||||
const cacheContent = await (async () => {
|
||||
if (!(await existsAsync(cacheFilePath))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (uiModulesMeta_cache.keycloakifyVersion !== keycloakifyVersion) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (uiModulesMeta_cache.prettierConfigHash !== prettierConfigHash) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const upToDateEntries = uiModulesMeta_cache.entries.filter(entry => {
|
||||
const correspondingInstalledUiModule = installedUiModules.find(
|
||||
installedUiModule => installedUiModule.moduleName === entry.moduleName
|
||||
);
|
||||
|
||||
if (correspondingInstalledUiModule === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return correspondingInstalledUiModule.version === entry.version;
|
||||
});
|
||||
|
||||
return upToDateEntries;
|
||||
return await fsPr.readFile(cacheFilePath);
|
||||
})();
|
||||
|
||||
const entries = await Promise.all(
|
||||
const uiModuleMetas_cacheUpToDate: UiModuleMeta[] = await (async () => {
|
||||
const parsedCacheFile: ParsedCacheFile | undefined = await (async () => {
|
||||
if (cacheContent === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cacheContentStr = cacheContent.toString("utf8");
|
||||
|
||||
let parsedCacheFile: unknown;
|
||||
|
||||
try {
|
||||
parsedCacheFile = JSON.parse(cacheContentStr);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
zParsedCacheFile.parse(parsedCacheFile);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
assert(is<ParsedCacheFile>(parsedCacheFile));
|
||||
|
||||
return parsedCacheFile;
|
||||
})();
|
||||
|
||||
if (parsedCacheFile === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (parsedCacheFile.keycloakifyVersion !== keycloakifyVersion) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (parsedCacheFile.prettierConfigHash !== prettierConfigHash) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (parsedCacheFile.pathSep !== pathSep) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uiModuleMetas_cacheUpToDate = parsedCacheFile.uiModuleMetas.filter(
|
||||
uiModuleMeta => {
|
||||
const correspondingInstalledUiModule = installedUiModules.find(
|
||||
installedUiModule =>
|
||||
installedUiModule.moduleName === uiModuleMeta.moduleName
|
||||
);
|
||||
|
||||
if (correspondingInstalledUiModule === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return correspondingInstalledUiModule.version === uiModuleMeta.version;
|
||||
}
|
||||
);
|
||||
|
||||
return uiModuleMetas_cacheUpToDate;
|
||||
})();
|
||||
|
||||
const uiModuleMetas = await Promise.all(
|
||||
installedUiModules.map(
|
||||
async ({ moduleName, version, dirPath }): Promise<UiModulesMeta.Entry> => {
|
||||
async ({
|
||||
moduleName,
|
||||
version,
|
||||
peerDependencies,
|
||||
dirPath
|
||||
}): Promise<UiModuleMeta> => {
|
||||
use_cache: {
|
||||
const cachedEntry = upToDateEntries.find(
|
||||
entry => entry.moduleName === moduleName
|
||||
const uiModuleMeta_cache = uiModuleMetas_cacheUpToDate.find(
|
||||
uiModuleMeta => uiModuleMeta.moduleName === moduleName
|
||||
);
|
||||
|
||||
if (cachedEntry === undefined) {
|
||||
if (uiModuleMeta_cache === undefined) {
|
||||
break use_cache;
|
||||
}
|
||||
|
||||
return cachedEntry;
|
||||
return uiModuleMeta_cache;
|
||||
}
|
||||
|
||||
const files: UiModulesMeta.Entry["files"] = [];
|
||||
const files: UiModuleMeta["files"] = [];
|
||||
|
||||
{
|
||||
const srcDirPath = pathJoin(dirPath, "src");
|
||||
@ -208,18 +230,45 @@ export async function readOrCreateUiModulesMeta(params: {
|
||||
});
|
||||
}
|
||||
|
||||
return id<UiModulesMeta.Entry>({
|
||||
files,
|
||||
return id<UiModuleMeta>({
|
||||
moduleName,
|
||||
version
|
||||
version,
|
||||
files,
|
||||
peerDependencies
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return id<UiModulesMeta>({
|
||||
keycloakifyVersion,
|
||||
prettierConfigHash,
|
||||
entries
|
||||
});
|
||||
update_cache: {
|
||||
const parsedCacheFile = id<ParsedCacheFile>({
|
||||
keycloakifyVersion,
|
||||
prettierConfigHash,
|
||||
pathSep,
|
||||
uiModuleMetas
|
||||
});
|
||||
|
||||
const cacheContent_new = Buffer.from(
|
||||
JSON.stringify(parsedCacheFile, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
if (cacheContent !== undefined && cacheContent_new.equals(cacheContent)) {
|
||||
break update_cache;
|
||||
}
|
||||
|
||||
create_dir: {
|
||||
const dirPath = pathDirname(cacheFilePath);
|
||||
|
||||
if (await existsAsync(dirPath)) {
|
||||
break create_dir;
|
||||
}
|
||||
|
||||
await fsPr.mkdir(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
await fsPr.writeFile(cacheFilePath, cacheContent_new);
|
||||
}
|
||||
|
||||
return uiModuleMetas;
|
||||
}
|
@ -10,7 +10,14 @@ export async function listInstalledModules(params: {
|
||||
packageJsonFilePath: string;
|
||||
projectDirPath: string;
|
||||
filter: (params: { moduleName: string }) => boolean;
|
||||
}): Promise<{ moduleName: string; version: string; dirPath: string }[]> {
|
||||
}): Promise<
|
||||
{
|
||||
moduleName: string;
|
||||
version: string;
|
||||
dirPath: string;
|
||||
peerDependencies: Record<string, string>;
|
||||
}[]
|
||||
> {
|
||||
const { packageJsonFilePath, projectDirPath, filter } = params;
|
||||
|
||||
const parsedPackageJson = await readPackageJsonDependencies({
|
||||
@ -24,6 +31,7 @@ export async function listInstalledModules(params: {
|
||||
.map(obj => Object.keys(obj))
|
||||
.flat()
|
||||
.filter(moduleName => filter({ moduleName }));
|
||||
|
||||
const result = await Promise.all(
|
||||
uiModuleNames.map(async moduleName => {
|
||||
const dirPath = await getInstalledModuleDirPath({
|
||||
@ -32,11 +40,12 @@ export async function listInstalledModules(params: {
|
||||
projectDirPath
|
||||
});
|
||||
|
||||
const { version } = await readPackageJsonVersion({
|
||||
packageJsonFilePath: pathJoin(dirPath, "package.json")
|
||||
});
|
||||
const { version, peerDependencies } =
|
||||
await readPackageJsonVersionAndPeerDependencies({
|
||||
packageJsonFilePath: pathJoin(dirPath, "package.json")
|
||||
});
|
||||
|
||||
return { moduleName, version, dirPath } as const;
|
||||
return { moduleName, version, peerDependencies, dirPath } as const;
|
||||
})
|
||||
);
|
||||
|
||||
@ -79,16 +88,18 @@ const { readPackageJsonDependencies } = (() => {
|
||||
return { readPackageJsonDependencies };
|
||||
})();
|
||||
|
||||
const { readPackageJsonVersion } = (() => {
|
||||
const { readPackageJsonVersionAndPeerDependencies } = (() => {
|
||||
type ParsedPackageJson = {
|
||||
version: string;
|
||||
peerDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
const zParsedPackageJson = (() => {
|
||||
type TargetType = ParsedPackageJson;
|
||||
|
||||
const zTargetType = z.object({
|
||||
version: z.string()
|
||||
version: z.string(),
|
||||
peerDependencies: z.record(z.string()).optional()
|
||||
});
|
||||
|
||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||
@ -96,7 +107,9 @@ const { readPackageJsonVersion } = (() => {
|
||||
return id<z.ZodType<TargetType>>(zTargetType);
|
||||
})();
|
||||
|
||||
async function readPackageJsonVersion(params: { packageJsonFilePath: string }) {
|
||||
async function readPackageJsonVersionAndPeerDependencies(params: {
|
||||
packageJsonFilePath: string;
|
||||
}): Promise<{ version: string; peerDependencies: Record<string, string> }> {
|
||||
const { packageJsonFilePath } = params;
|
||||
|
||||
const parsedPackageJson = JSON.parse(
|
||||
@ -107,8 +120,11 @@ const { readPackageJsonVersion } = (() => {
|
||||
|
||||
assert(is<ParsedPackageJson>(parsedPackageJson));
|
||||
|
||||
return parsedPackageJson;
|
||||
return {
|
||||
version: parsedPackageJson.version,
|
||||
peerDependencies: parsedPackageJson.peerDependencies ?? {}
|
||||
};
|
||||
}
|
||||
|
||||
return { readPackageJsonVersion };
|
||||
return { readPackageJsonVersionAndPeerDependencies };
|
||||
})();
|
||||
|
Loading…
x
Reference in New Issue
Block a user