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 { assert, type Equals } from "tsafe/assert";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { z } from "zod";
|
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 * as fsPr from "fs/promises";
|
||||||
import type { BuildContext } from "../shared/buildContext";
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
import { is } from "tsafe/is";
|
import { is } from "tsafe/is";
|
||||||
@ -16,25 +16,18 @@ import {
|
|||||||
} from "./getSourceCodeToCopyInUserCodebase";
|
} from "./getSourceCodeToCopyInUserCodebase";
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
export type UiModulesMeta = {
|
export type UiModuleMeta = {
|
||||||
keycloakifyVersion: string;
|
|
||||||
prettierConfigHash: string | null;
|
|
||||||
entries: UiModulesMeta.Entry[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export namespace UiModulesMeta {
|
|
||||||
export type Entry = {
|
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
version: string;
|
version: string;
|
||||||
files: {
|
files: {
|
||||||
fileRelativePath: string;
|
fileRelativePath: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
peerDependencies: Record<string, string>;
|
||||||
}
|
};
|
||||||
|
|
||||||
const zUiModuleMetasEntry = (() => {
|
const zUiModuleMeta = (() => {
|
||||||
type ExpectedType = UiModulesMeta.Entry;
|
type ExpectedType = UiModuleMeta;
|
||||||
|
|
||||||
const zTargetType = z.object({
|
const zTargetType = z.object({
|
||||||
moduleName: z.string(),
|
moduleName: z.string(),
|
||||||
@ -44,7 +37,8 @@ const zUiModuleMetasEntry = (() => {
|
|||||||
fileRelativePath: z.string(),
|
fileRelativePath: z.string(),
|
||||||
hash: z.string()
|
hash: z.string()
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
|
peerDependencies: z.record(z.string())
|
||||||
});
|
});
|
||||||
|
|
||||||
type InferredType = z.infer<typeof zTargetType>;
|
type InferredType = z.infer<typeof zTargetType>;
|
||||||
@ -54,13 +48,21 @@ const zUiModuleMetasEntry = (() => {
|
|||||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const zUiModulesMeta = (() => {
|
type ParsedCacheFile = {
|
||||||
type ExpectedType = UiModulesMeta;
|
keycloakifyVersion: string;
|
||||||
|
prettierConfigHash: string | null;
|
||||||
|
pathSep: string;
|
||||||
|
uiModuleMetas: UiModuleMeta[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const zParsedCacheFile = (() => {
|
||||||
|
type ExpectedType = ParsedCacheFile;
|
||||||
|
|
||||||
const zTargetType = z.object({
|
const zTargetType = z.object({
|
||||||
keycloakifyVersion: z.string(),
|
keycloakifyVersion: z.string(),
|
||||||
prettierConfigHash: z.union([z.string(), z.null()]),
|
prettierConfigHash: z.union([z.string(), z.null()]),
|
||||||
entries: z.array(zUiModuleMetasEntry)
|
pathSep: z.string(),
|
||||||
|
uiModuleMetas: z.array(zUiModuleMeta)
|
||||||
});
|
});
|
||||||
|
|
||||||
type InferredType = z.infer<typeof zTargetType>;
|
type InferredType = z.infer<typeof zTargetType>;
|
||||||
@ -70,7 +72,7 @@ const zUiModulesMeta = (() => {
|
|||||||
return id<z.ZodType<ExpectedType>>(zTargetType);
|
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const RELATIVE_FILE_PATH = pathJoin("uiModulesMeta.json");
|
const CACHE_FILE_BASENAME = "uiModulesMeta.json";
|
||||||
|
|
||||||
export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebase & {
|
export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebase & {
|
||||||
cacheDirPath: string;
|
cacheDirPath: string;
|
||||||
@ -80,12 +82,12 @@ export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebas
|
|||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
export async function readOrCreateUiModulesMeta(params: {
|
export async function getUiModuleMetas(params: {
|
||||||
buildContext: BuildContextLike;
|
buildContext: BuildContextLike;
|
||||||
}): Promise<UiModulesMeta> {
|
}): Promise<UiModuleMeta[]> {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const filePath = pathJoin(buildContext.cacheDirPath, RELATIVE_FILE_PATH);
|
const cacheFilePath = pathJoin(buildContext.cacheDirPath, CACHE_FILE_BASENAME);
|
||||||
|
|
||||||
const keycloakifyVersion = readThisNpmPackageVersion();
|
const keycloakifyVersion = readThisNpmPackageVersion();
|
||||||
|
|
||||||
@ -106,76 +108,96 @@ export async function readOrCreateUiModulesMeta(params: {
|
|||||||
moduleName.includes("keycloakify") && moduleName.endsWith("-ui")
|
moduleName.includes("keycloakify") && moduleName.endsWith("-ui")
|
||||||
});
|
});
|
||||||
|
|
||||||
const upToDateEntries: UiModulesMeta.Entry[] = await (async () => {
|
const cacheContent = await (async () => {
|
||||||
const uiModulesMeta_cache: UiModulesMeta | undefined = await (async () => {
|
if (!(await existsAsync(cacheFilePath))) {
|
||||||
if (!(await existsAsync(filePath))) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentStr = (await fsPr.readFile(filePath)).toString("utf8");
|
return await fsPr.readFile(cacheFilePath);
|
||||||
|
|
||||||
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) {
|
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 [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiModulesMeta_cache.keycloakifyVersion !== keycloakifyVersion) {
|
if (parsedCacheFile.keycloakifyVersion !== keycloakifyVersion) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiModulesMeta_cache.prettierConfigHash !== prettierConfigHash) {
|
if (parsedCacheFile.prettierConfigHash !== prettierConfigHash) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const upToDateEntries = uiModulesMeta_cache.entries.filter(entry => {
|
if (parsedCacheFile.pathSep !== pathSep) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uiModuleMetas_cacheUpToDate = parsedCacheFile.uiModuleMetas.filter(
|
||||||
|
uiModuleMeta => {
|
||||||
const correspondingInstalledUiModule = installedUiModules.find(
|
const correspondingInstalledUiModule = installedUiModules.find(
|
||||||
installedUiModule => installedUiModule.moduleName === entry.moduleName
|
installedUiModule =>
|
||||||
|
installedUiModule.moduleName === uiModuleMeta.moduleName
|
||||||
);
|
);
|
||||||
|
|
||||||
if (correspondingInstalledUiModule === undefined) {
|
if (correspondingInstalledUiModule === undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return correspondingInstalledUiModule.version === entry.version;
|
return correspondingInstalledUiModule.version === uiModuleMeta.version;
|
||||||
});
|
}
|
||||||
|
|
||||||
return upToDateEntries;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const entries = await Promise.all(
|
|
||||||
installedUiModules.map(
|
|
||||||
async ({ moduleName, version, dirPath }): Promise<UiModulesMeta.Entry> => {
|
|
||||||
use_cache: {
|
|
||||||
const cachedEntry = upToDateEntries.find(
|
|
||||||
entry => entry.moduleName === moduleName
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cachedEntry === undefined) {
|
return uiModuleMetas_cacheUpToDate;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const uiModuleMetas = await Promise.all(
|
||||||
|
installedUiModules.map(
|
||||||
|
async ({
|
||||||
|
moduleName,
|
||||||
|
version,
|
||||||
|
peerDependencies,
|
||||||
|
dirPath
|
||||||
|
}): Promise<UiModuleMeta> => {
|
||||||
|
use_cache: {
|
||||||
|
const uiModuleMeta_cache = uiModuleMetas_cacheUpToDate.find(
|
||||||
|
uiModuleMeta => uiModuleMeta.moduleName === moduleName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (uiModuleMeta_cache === undefined) {
|
||||||
break use_cache;
|
break use_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedEntry;
|
return uiModuleMeta_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
const files: UiModulesMeta.Entry["files"] = [];
|
const files: UiModuleMeta["files"] = [];
|
||||||
|
|
||||||
{
|
{
|
||||||
const srcDirPath = pathJoin(dirPath, "src");
|
const srcDirPath = pathJoin(dirPath, "src");
|
||||||
@ -208,18 +230,45 @@ export async function readOrCreateUiModulesMeta(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return id<UiModulesMeta.Entry>({
|
return id<UiModuleMeta>({
|
||||||
files,
|
|
||||||
moduleName,
|
moduleName,
|
||||||
version
|
version,
|
||||||
|
files,
|
||||||
|
peerDependencies
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return id<UiModulesMeta>({
|
update_cache: {
|
||||||
|
const parsedCacheFile = id<ParsedCacheFile>({
|
||||||
keycloakifyVersion,
|
keycloakifyVersion,
|
||||||
prettierConfigHash,
|
prettierConfigHash,
|
||||||
entries
|
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;
|
packageJsonFilePath: string;
|
||||||
projectDirPath: string;
|
projectDirPath: string;
|
||||||
filter: (params: { moduleName: string }) => boolean;
|
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 { packageJsonFilePath, projectDirPath, filter } = params;
|
||||||
|
|
||||||
const parsedPackageJson = await readPackageJsonDependencies({
|
const parsedPackageJson = await readPackageJsonDependencies({
|
||||||
@ -24,6 +31,7 @@ export async function listInstalledModules(params: {
|
|||||||
.map(obj => Object.keys(obj))
|
.map(obj => Object.keys(obj))
|
||||||
.flat()
|
.flat()
|
||||||
.filter(moduleName => filter({ moduleName }));
|
.filter(moduleName => filter({ moduleName }));
|
||||||
|
|
||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
uiModuleNames.map(async moduleName => {
|
uiModuleNames.map(async moduleName => {
|
||||||
const dirPath = await getInstalledModuleDirPath({
|
const dirPath = await getInstalledModuleDirPath({
|
||||||
@ -32,11 +40,12 @@ export async function listInstalledModules(params: {
|
|||||||
projectDirPath
|
projectDirPath
|
||||||
});
|
});
|
||||||
|
|
||||||
const { version } = await readPackageJsonVersion({
|
const { version, peerDependencies } =
|
||||||
|
await readPackageJsonVersionAndPeerDependencies({
|
||||||
packageJsonFilePath: pathJoin(dirPath, "package.json")
|
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 };
|
return { readPackageJsonDependencies };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const { readPackageJsonVersion } = (() => {
|
const { readPackageJsonVersionAndPeerDependencies } = (() => {
|
||||||
type ParsedPackageJson = {
|
type ParsedPackageJson = {
|
||||||
version: string;
|
version: string;
|
||||||
|
peerDependencies?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const zParsedPackageJson = (() => {
|
const zParsedPackageJson = (() => {
|
||||||
type TargetType = ParsedPackageJson;
|
type TargetType = ParsedPackageJson;
|
||||||
|
|
||||||
const zTargetType = z.object({
|
const zTargetType = z.object({
|
||||||
version: z.string()
|
version: z.string(),
|
||||||
|
peerDependencies: z.record(z.string()).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
@ -96,7 +107,9 @@ const { readPackageJsonVersion } = (() => {
|
|||||||
return id<z.ZodType<TargetType>>(zTargetType);
|
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 { packageJsonFilePath } = params;
|
||||||
|
|
||||||
const parsedPackageJson = JSON.parse(
|
const parsedPackageJson = JSON.parse(
|
||||||
@ -107,8 +120,11 @@ const { readPackageJsonVersion } = (() => {
|
|||||||
|
|
||||||
assert(is<ParsedPackageJson>(parsedPackageJson));
|
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