checkpoint
This commit is contained in:
parent
db37320280
commit
af7a45d125
@ -40,7 +40,9 @@ import { vendorFrontendDependencies } from "./vendorFrontendDependencies";
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
run(`npx ncc build ${join("dist", "bin", "main.js")} -o ${join("dist", "ncc_out")}`);
|
run(
|
||||||
|
`npx ncc build ${join("dist", "bin", "main.js")} --external prettier -o ${join("dist", "ncc_out")}`
|
||||||
|
);
|
||||||
|
|
||||||
transformCodebase({
|
transformCodebase({
|
||||||
srcDirPath: join("dist", "ncc_out"),
|
srcDirPath: join("dist", "ncc_out"),
|
||||||
|
@ -76,3 +76,5 @@ export const CUSTOM_HANDLER_ENV_NAMES = {
|
|||||||
COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME",
|
COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME",
|
||||||
BUILD_CONTEXT: "KEYCLOAKIFY_BUILD_CONTEXT"
|
BUILD_CONTEXT: "KEYCLOAKIFY_BUILD_CONTEXT"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const KC_GEN_FILE_PATH_RELATIVE_TO_THEME_SRC_DIR = "kc-gen.tsx";
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
ApiVersion
|
ApiVersion
|
||||||
} from "./customHandler";
|
} from "./customHandler";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import { sep as pathSep } from "path";
|
import { getNodeModulesBinDirPath } from "../tools/nodeModulesBinDirPath";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
assert<Equals<ApiVersion, "v1">>();
|
assert<Equals<ApiVersion, "v1">>();
|
||||||
@ -19,32 +19,7 @@ export function maybeDelegateCommandToCustomHandler(params: {
|
|||||||
}): { hasBeenHandled: boolean } {
|
}): { hasBeenHandled: boolean } {
|
||||||
const { commandName, buildContext } = params;
|
const { commandName, buildContext } = params;
|
||||||
|
|
||||||
const nodeModulesBinDirPath = (() => {
|
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
||||||
const binPath = process.argv[1];
|
|
||||||
|
|
||||||
const segments: string[] = [".bin"];
|
|
||||||
|
|
||||||
let foundNodeModules = false;
|
|
||||||
|
|
||||||
for (const segment of binPath.split(pathSep).reverse()) {
|
|
||||||
skip_segment: {
|
|
||||||
if (foundNodeModules) {
|
|
||||||
break skip_segment;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segment === "node_modules") {
|
|
||||||
foundNodeModules = true;
|
|
||||||
break skip_segment;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
segments.unshift(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
return segments.join(pathSep);
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
||||||
return { hasBeenHandled: false };
|
return { hasBeenHandled: false };
|
||||||
|
62
src/bin/sync-ui-modules/getSourceCodeToCopyInUserCodebase.ts
Normal file
62
src/bin/sync-ui-modules/getSourceCodeToCopyInUserCodebase.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { getIsPrettierAvailable, runPrettier } from "../tools/runPrettier";
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
import { join as pathJoin, sep as pathSep } from "path";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
|
|
||||||
|
export type BuildContextLike = {
|
||||||
|
themeSrcDirPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
|
export async function getSourceCodeToCopyInUserCodebase(params: {
|
||||||
|
buildContext: BuildContextLike;
|
||||||
|
relativeFromDirPath: string;
|
||||||
|
fileRelativePath: string;
|
||||||
|
commentData: {
|
||||||
|
isForEjection: boolean;
|
||||||
|
uiModuleName: string;
|
||||||
|
uiModuleVersion: string;
|
||||||
|
};
|
||||||
|
}): Promise<string> {
|
||||||
|
const { buildContext, relativeFromDirPath, fileRelativePath, commentData } = params;
|
||||||
|
|
||||||
|
let sourceCode = (
|
||||||
|
await fsPr.readFile(pathJoin(relativeFromDirPath, fileRelativePath))
|
||||||
|
).toString("utf8");
|
||||||
|
|
||||||
|
const comment = (() => {
|
||||||
|
if (commentData.isForEjection) {
|
||||||
|
return [
|
||||||
|
`/*`,
|
||||||
|
` This file was ejected from ${commentData.uiModuleName} version ${commentData.uiModuleVersion}.`,
|
||||||
|
`*/`
|
||||||
|
].join("\n");
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
`/*`,
|
||||||
|
` WARNING: Before modifying this file run the following command:`,
|
||||||
|
` \`npx keycloakify eject-file ${fileRelativePath.split(pathSep).join("/")}\``,
|
||||||
|
` `,
|
||||||
|
` This file comes from ${commentData.uiModuleName} version ${commentData.uiModuleVersion}.`,
|
||||||
|
`*/`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
sourceCode = [comment, ``, sourceCode].join("\n");
|
||||||
|
|
||||||
|
format: {
|
||||||
|
if (!(await getIsPrettierAvailable())) {
|
||||||
|
break format;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCode = await runPrettier({
|
||||||
|
filePath: pathJoin(buildContext.themeSrcDirPath, fileRelativePath),
|
||||||
|
sourceCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceCode;
|
||||||
|
}
|
1
src/bin/sync-ui-modules/index.ts
Normal file
1
src/bin/sync-ui-modules/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./sync-ui-modules";
|
3
src/bin/sync-ui-modules/listOfEjectedFiles.ts
Normal file
3
src/bin/sync-ui-modules/listOfEjectedFiles.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export async function getListOfEjectedFiles(params: {}): Promise<string[]> {}
|
||||||
|
|
||||||
|
export async function writeListOfEjectedFiles(params: {}) {}
|
13
src/bin/sync-ui-modules/sync-ui-modules.ts
Normal file
13
src/bin/sync-ui-modules/sync-ui-modules.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { is } from "tsafe/is";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import { existsAsync } from "./tools/fs.existsAsync";
|
||||||
|
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
|
||||||
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
|
const { buildContext } = params;
|
||||||
|
}
|
225
src/bin/sync-ui-modules/uiModulesMeta.ts
Normal file
225
src/bin/sync-ui-modules/uiModulesMeta.ts
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
import type { BuildContext } from "../shared/buildContext";
|
||||||
|
import { is } from "tsafe/is";
|
||||||
|
import { existsAsync } from "../tools/fs.existsAsync";
|
||||||
|
import { listInstalledModules } from "../tools/listInstalledModules";
|
||||||
|
import { crawlAsync } from "../tools/crawlAsync";
|
||||||
|
import { getIsPrettierAvailable, getPrettierAndConfig } from "../tools/runPrettier";
|
||||||
|
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||||
|
import {
|
||||||
|
getSourceCodeToCopyInUserCodebase,
|
||||||
|
type BuildContextLike as BuildContextLike_getSourceCodeToCopyInUserCodebase
|
||||||
|
} from "./getSourceCodeToCopyInUserCodebase";
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
|
export type UiModulesMeta = {
|
||||||
|
keycloakifyVersion: string;
|
||||||
|
prettierConfigHash: string | null;
|
||||||
|
entries: UiModulesMeta.Entry[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export namespace UiModulesMeta {
|
||||||
|
export type Entry = {
|
||||||
|
moduleName: string;
|
||||||
|
version: string;
|
||||||
|
files: {
|
||||||
|
fileRelativePath: string;
|
||||||
|
hash: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const zUiModuleMetasEntry = (() => {
|
||||||
|
type ExpectedType = UiModulesMeta.Entry;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
moduleName: z.string(),
|
||||||
|
version: z.string(),
|
||||||
|
files: z.array(
|
||||||
|
z.object({
|
||||||
|
fileRelativePath: z.string(),
|
||||||
|
hash: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
type InferredType = z.infer<typeof zTargetType>;
|
||||||
|
|
||||||
|
assert<Equals<InferredType, ExpectedType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const zUiModulesMeta = (() => {
|
||||||
|
type ExpectedType = UiModulesMeta;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
keycloakifyVersion: z.string(),
|
||||||
|
prettierConfigHash: z.union([z.string(), z.null()]),
|
||||||
|
entries: z.array(zUiModuleMetasEntry)
|
||||||
|
});
|
||||||
|
|
||||||
|
type InferredType = z.infer<typeof zTargetType>;
|
||||||
|
|
||||||
|
assert<Equals<InferredType, ExpectedType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<ExpectedType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const RELATIVE_FILE_PATH = pathJoin("uiModulesMeta.json");
|
||||||
|
|
||||||
|
export type BuildContextLike = BuildContextLike_getSourceCodeToCopyInUserCodebase & {
|
||||||
|
cacheDirPath: string;
|
||||||
|
packageJsonFilePath: string;
|
||||||
|
projectDirPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
|
export async function readOrCreateUiModulesMeta(params: {
|
||||||
|
buildContext: BuildContextLike;
|
||||||
|
}): Promise<UiModulesMeta> {
|
||||||
|
const { buildContext } = params;
|
||||||
|
|
||||||
|
const filePath = pathJoin(buildContext.cacheDirPath, RELATIVE_FILE_PATH);
|
||||||
|
|
||||||
|
const keycloakifyVersion = readThisNpmPackageVersion();
|
||||||
|
|
||||||
|
const prettierConfigHash = await (async () => {
|
||||||
|
if (!(await getIsPrettierAvailable())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { config } = await getPrettierAndConfig();
|
||||||
|
|
||||||
|
return crypto.createHash("sha256").update(JSON.stringify(config)).digest("hex");
|
||||||
|
})();
|
||||||
|
|
||||||
|
const installedUiModules = await listInstalledModules({
|
||||||
|
packageJsonFilePath: buildContext.packageJsonFilePath,
|
||||||
|
projectDirPath: buildContext.packageJsonFilePath,
|
||||||
|
filter: ({ moduleName }) =>
|
||||||
|
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 [];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
})();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
break use_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files: UiModulesMeta.Entry["files"] = [];
|
||||||
|
|
||||||
|
{
|
||||||
|
const srcDirPath = pathJoin(dirPath, "src");
|
||||||
|
|
||||||
|
await crawlAsync({
|
||||||
|
dirPath: srcDirPath,
|
||||||
|
returnedPathsType: "relative to dirPath",
|
||||||
|
onFileFound: async fileRelativePath => {
|
||||||
|
const sourceCode = await getSourceCodeToCopyInUserCodebase({
|
||||||
|
buildContext,
|
||||||
|
relativeFromDirPath: srcDirPath,
|
||||||
|
fileRelativePath,
|
||||||
|
commentData: {
|
||||||
|
isForEjection: false,
|
||||||
|
uiModuleName: moduleName,
|
||||||
|
uiModuleVersion: version
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const hash = crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(sourceCode)
|
||||||
|
.digest("hex");
|
||||||
|
|
||||||
|
files.push({
|
||||||
|
fileRelativePath,
|
||||||
|
hash
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return id<UiModulesMeta.Entry>({
|
||||||
|
files,
|
||||||
|
moduleName,
|
||||||
|
version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return id<UiModulesMeta>({
|
||||||
|
keycloakifyVersion,
|
||||||
|
prettierConfigHash,
|
||||||
|
entries
|
||||||
|
});
|
||||||
|
}
|
51
src/bin/tools/crawlAsync.ts
Normal file
51
src/bin/tools/crawlAsync.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
import { join as pathJoin, relative as pathRelative } from "path";
|
||||||
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
|
||||||
|
/** List all files in a given directory return paths relative to the dir_path */
|
||||||
|
export async function crawlAsync(params: {
|
||||||
|
dirPath: string;
|
||||||
|
returnedPathsType: "absolute" | "relative to dirPath";
|
||||||
|
onFileFound: (filePath: string) => void;
|
||||||
|
}) {
|
||||||
|
const { dirPath, returnedPathsType, onFileFound } = params;
|
||||||
|
|
||||||
|
await crawlAsyncRec({
|
||||||
|
dirPath,
|
||||||
|
onFileFound: ({ filePath }) => {
|
||||||
|
switch (returnedPathsType) {
|
||||||
|
case "absolute":
|
||||||
|
onFileFound(filePath);
|
||||||
|
return;
|
||||||
|
case "relative to dirPath":
|
||||||
|
onFileFound(pathRelative(dirPath, filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert<Equals<typeof returnedPathsType, never>>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function crawlAsyncRec(params: {
|
||||||
|
dirPath: string;
|
||||||
|
onFileFound: (params: { filePath: string }) => void;
|
||||||
|
}) {
|
||||||
|
const { dirPath, onFileFound } = params;
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
(await fsPr.readdir(dirPath)).map(async basename => {
|
||||||
|
const fileOrDirPath = pathJoin(dirPath, basename);
|
||||||
|
|
||||||
|
const isDirectory = await fsPr
|
||||||
|
.lstat(fileOrDirPath)
|
||||||
|
.then(stat => stat.isDirectory());
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
await crawlAsyncRec({ dirPath: fileOrDirPath, onFileFound });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileFound({ filePath: fileOrDirPath });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
53
src/bin/tools/getInstalledModuleDirPath.ts
Normal file
53
src/bin/tools/getInstalledModuleDirPath.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { dirname as pathDirname, join as pathJoin } from "path";
|
||||||
|
import { existsAsync } from "./fs.existsAsync";
|
||||||
|
import * as child_process from "child_process";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
|
||||||
|
export async function getInstalledModuleDirPath(params: {
|
||||||
|
moduleName: string;
|
||||||
|
packageJsonFilePath: string;
|
||||||
|
projectDirPath: string;
|
||||||
|
}) {
|
||||||
|
const { moduleName, packageJsonFilePath, projectDirPath } = params;
|
||||||
|
|
||||||
|
const packageJsonDirPath = pathDirname(packageJsonFilePath);
|
||||||
|
|
||||||
|
common_case: {
|
||||||
|
const dirPath = pathJoin(
|
||||||
|
...[packageJsonDirPath, "node_modules", ...moduleName.split("/")]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!(await existsAsync(dirPath))) {
|
||||||
|
break common_case;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_modules_at_root_case: {
|
||||||
|
if (projectDirPath === packageJsonDirPath) {
|
||||||
|
break node_modules_at_root_case;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirPath = pathJoin(
|
||||||
|
...[projectDirPath, "node_modules", ...moduleName.split("/")]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!(await existsAsync(dirPath))) {
|
||||||
|
break node_modules_at_root_case;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirPath = child_process
|
||||||
|
.execSync(`npm list ${moduleName}`, {
|
||||||
|
cwd: packageJsonDirPath
|
||||||
|
})
|
||||||
|
.toString("utf8")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
assert(dirPath !== "");
|
||||||
|
|
||||||
|
return dirPath;
|
||||||
|
}
|
114
src/bin/tools/listInstalledModules.ts
Normal file
114
src/bin/tools/listInstalledModules.ts
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
import { is } from "tsafe/is";
|
||||||
|
import { getInstalledModuleDirPath } from "../tools/getInstalledModuleDirPath";
|
||||||
|
|
||||||
|
export async function listInstalledModules(params: {
|
||||||
|
packageJsonFilePath: string;
|
||||||
|
projectDirPath: string;
|
||||||
|
filter: (params: { moduleName: string }) => boolean;
|
||||||
|
}): Promise<{ moduleName: string; version: string; dirPath: string }[]> {
|
||||||
|
const { packageJsonFilePath, projectDirPath, filter } = params;
|
||||||
|
|
||||||
|
const parsedPackageJson = await readPackageJsonDependencies({
|
||||||
|
packageJsonFilePath
|
||||||
|
});
|
||||||
|
|
||||||
|
const uiModuleNames = (
|
||||||
|
[parsedPackageJson.dependencies, parsedPackageJson.devDependencies] as const
|
||||||
|
)
|
||||||
|
.filter(obj => obj !== undefined)
|
||||||
|
.map(obj => Object.keys(obj))
|
||||||
|
.flat()
|
||||||
|
.filter(moduleName => filter({ moduleName }));
|
||||||
|
const result = await Promise.all(
|
||||||
|
uiModuleNames.map(async moduleName => {
|
||||||
|
const dirPath = await getInstalledModuleDirPath({
|
||||||
|
moduleName,
|
||||||
|
packageJsonFilePath,
|
||||||
|
projectDirPath
|
||||||
|
});
|
||||||
|
|
||||||
|
const { version } = await readPackageJsonVersion({
|
||||||
|
packageJsonFilePath: pathJoin(dirPath, "package.json")
|
||||||
|
});
|
||||||
|
|
||||||
|
return { moduleName, version, dirPath } as const;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { readPackageJsonDependencies } = (() => {
|
||||||
|
type ParsedPackageJson = {
|
||||||
|
dependencies?: Record<string, string>;
|
||||||
|
devDependencies?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const zParsedPackageJson = (() => {
|
||||||
|
type TargetType = ParsedPackageJson;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
dependencies: z.record(z.string()).optional(),
|
||||||
|
devDependencies: z.record(z.string()).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function readPackageJsonDependencies(params: { packageJsonFilePath: string }) {
|
||||||
|
const { packageJsonFilePath } = params;
|
||||||
|
|
||||||
|
const parsedPackageJson = JSON.parse(
|
||||||
|
(await fsPr.readFile(packageJsonFilePath)).toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
|
zParsedPackageJson.parse(parsedPackageJson);
|
||||||
|
|
||||||
|
assert(is<ParsedPackageJson>(parsedPackageJson));
|
||||||
|
|
||||||
|
return parsedPackageJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { readPackageJsonDependencies };
|
||||||
|
})();
|
||||||
|
|
||||||
|
const { readPackageJsonVersion } = (() => {
|
||||||
|
type ParsedPackageJson = {
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const zParsedPackageJson = (() => {
|
||||||
|
type TargetType = ParsedPackageJson;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
version: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function readPackageJsonVersion(params: { packageJsonFilePath: string }) {
|
||||||
|
const { packageJsonFilePath } = params;
|
||||||
|
|
||||||
|
const parsedPackageJson = JSON.parse(
|
||||||
|
(await fsPr.readFile(packageJsonFilePath)).toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
|
zParsedPackageJson.parse(parsedPackageJson);
|
||||||
|
|
||||||
|
assert(is<ParsedPackageJson>(parsedPackageJson));
|
||||||
|
|
||||||
|
return parsedPackageJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { readPackageJsonVersion };
|
||||||
|
})();
|
38
src/bin/tools/nodeModulesBinDirPath.ts
Normal file
38
src/bin/tools/nodeModulesBinDirPath.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { sep as pathSep } from "path";
|
||||||
|
|
||||||
|
let cache: string | undefined = undefined;
|
||||||
|
|
||||||
|
export function getNodeModulesBinDirPath() {
|
||||||
|
if (cache !== undefined) {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const binPath = process.argv[1];
|
||||||
|
|
||||||
|
const segments: string[] = [".bin"];
|
||||||
|
|
||||||
|
let foundNodeModules = false;
|
||||||
|
|
||||||
|
for (const segment of binPath.split(pathSep).reverse()) {
|
||||||
|
skip_segment: {
|
||||||
|
if (foundNodeModules) {
|
||||||
|
break skip_segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment === "node_modules") {
|
||||||
|
foundNodeModules = true;
|
||||||
|
break skip_segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.unshift(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeModulesBinDirPath = segments.join(pathSep);
|
||||||
|
|
||||||
|
cache = nodeModulesBinDirPath;
|
||||||
|
|
||||||
|
return nodeModulesBinDirPath;
|
||||||
|
}
|
@ -3,7 +3,13 @@ import { assert } from "tsafe/assert";
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
|
|
||||||
|
let cache: string | undefined = undefined;
|
||||||
|
|
||||||
export function readThisNpmPackageVersion(): string {
|
export function readThisNpmPackageVersion(): string {
|
||||||
|
if (cache !== undefined) {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
const version = JSON.parse(
|
const version = JSON.parse(
|
||||||
fs
|
fs
|
||||||
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json"))
|
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json"))
|
||||||
@ -12,5 +18,7 @@ export function readThisNpmPackageVersion(): string {
|
|||||||
|
|
||||||
assert(typeof version === "string");
|
assert(typeof version === "string");
|
||||||
|
|
||||||
|
cache = version;
|
||||||
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { dirname as pathDirname } from "path";
|
|
||||||
import { assert, Equals } from "tsafe/assert";
|
|
||||||
import chalk from "chalk";
|
|
||||||
import { id } from "tsafe/id";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { is } from "tsafe/is";
|
|
||||||
import * as child_process from "child_process";
|
|
||||||
|
|
||||||
export function runFormat(params: { packageJsonFilePath: string }) {
|
|
||||||
const { packageJsonFilePath } = params;
|
|
||||||
|
|
||||||
const parsedPackageJson = (() => {
|
|
||||||
type ParsedPackageJson = {
|
|
||||||
scripts?: Record<string, string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const zParsedPackageJson = (() => {
|
|
||||||
type TargetType = ParsedPackageJson;
|
|
||||||
|
|
||||||
const zTargetType = z.object({
|
|
||||||
scripts: z.record(z.string()).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
|
||||||
|
|
||||||
return id<z.ZodType<TargetType>>(zTargetType);
|
|
||||||
})();
|
|
||||||
|
|
||||||
const parsedPackageJson = JSON.parse(
|
|
||||||
fs.readFileSync(packageJsonFilePath).toString("utf8")
|
|
||||||
);
|
|
||||||
|
|
||||||
zParsedPackageJson.parse(parsedPackageJson);
|
|
||||||
|
|
||||||
assert(is<ParsedPackageJson>(parsedPackageJson));
|
|
||||||
|
|
||||||
return parsedPackageJson;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const { scripts } = parsedPackageJson;
|
|
||||||
|
|
||||||
if (scripts === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const scriptName of ["format", "lint"]) {
|
|
||||||
if (!(scriptName in scripts)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = `npm run ${scriptName}`;
|
|
||||||
|
|
||||||
console.log(chalk.grey(`$ ${command}`));
|
|
||||||
|
|
||||||
try {
|
|
||||||
child_process.execSync(`npm run ${scriptName}`, {
|
|
||||||
stdio: "inherit",
|
|
||||||
cwd: pathDirname(packageJsonFilePath)
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
`\`${command}\` failed, it does not matter, please format your code manually, continuing...`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
77
src/bin/tools/runPrettier.ts
Normal file
77
src/bin/tools/runPrettier.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { getNodeModulesBinDirPath } from "./nodeModulesBinDirPath";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
getIsPrettierAvailable.cache = id<boolean | undefined>(undefined);
|
||||||
|
|
||||||
|
export async function getIsPrettierAvailable(): Promise<boolean> {
|
||||||
|
if (getIsPrettierAvailable.cache !== undefined) {
|
||||||
|
return getIsPrettierAvailable.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
||||||
|
|
||||||
|
const prettierBinPath = pathJoin(nodeModulesBinDirPath, "prettier");
|
||||||
|
|
||||||
|
const stats = await fsPr.stat(prettierBinPath).catch(() => undefined);
|
||||||
|
|
||||||
|
const isPrettierAvailable = stats?.isFile() ?? false;
|
||||||
|
|
||||||
|
getIsPrettierAvailable.cache = isPrettierAvailable;
|
||||||
|
|
||||||
|
return isPrettierAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrettierAndConfig = {
|
||||||
|
prettier: typeof import("prettier");
|
||||||
|
config: import("prettier").Options | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
getPrettierAndConfig.cache = id<PrettierAndConfig | undefined>(undefined);
|
||||||
|
|
||||||
|
export async function getPrettierAndConfig(): Promise<PrettierAndConfig> {
|
||||||
|
assert(getIsPrettierAvailable());
|
||||||
|
|
||||||
|
if (getPrettierAndConfig.cache !== undefined) {
|
||||||
|
return getPrettierAndConfig.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prettier = await import("prettier");
|
||||||
|
|
||||||
|
const prettierAndConfig: PrettierAndConfig = {
|
||||||
|
prettier,
|
||||||
|
config: await prettier.resolveConfig(pathJoin(getNodeModulesBinDirPath(), ".."))
|
||||||
|
};
|
||||||
|
|
||||||
|
getPrettierAndConfig.cache = prettierAndConfig;
|
||||||
|
|
||||||
|
return prettierAndConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runPrettier(params: {
|
||||||
|
sourceCode: string;
|
||||||
|
filePath: string;
|
||||||
|
}): Promise<string> {
|
||||||
|
const { sourceCode, filePath } = params;
|
||||||
|
|
||||||
|
let formattedSourceCode: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { prettier, config } = await getPrettierAndConfig();
|
||||||
|
|
||||||
|
formattedSourceCode = await prettier.format(sourceCode, { ...config, filePath });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
`You probably need to upgrade the version of prettier in your project`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedSourceCode;
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
import type { BuildContext } from "./shared/buildContext";
|
import type { BuildContext } from "./shared/buildContext";
|
||||||
|
import { KC_GEN_FILE_PATH_RELATIVE_TO_THEME_SRC_DIR } from "./shared/constants";
|
||||||
import * as fs from "fs/promises";
|
import * as fs from "fs/promises";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { existsAsync } from "./tools/fs.existsAsync";
|
import { existsAsync } from "./tools/fs.existsAsync";
|
||||||
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||||
import { runFormat } from "./tools/runFormat";
|
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
|
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
|
||||||
|
|
||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
@ -18,13 +19,16 @@ export async function command(params: { buildContext: BuildContext }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`);
|
const filePath = pathJoin(
|
||||||
|
buildContext.themeSrcDirPath,
|
||||||
|
KC_GEN_FILE_PATH_RELATIVE_TO_THEME_SRC_DIR
|
||||||
|
);
|
||||||
|
|
||||||
const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented;
|
const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented;
|
||||||
const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented;
|
const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented;
|
||||||
const hasAdminTheme = buildContext.implementedThemeTypes.admin.isImplemented;
|
const hasAdminTheme = buildContext.implementedThemeTypes.admin.isImplemented;
|
||||||
|
|
||||||
const newContent = [
|
let newContent = [
|
||||||
``,
|
``,
|
||||||
`/* eslint-disable */`,
|
`/* eslint-disable */`,
|
||||||
``,
|
``,
|
||||||
@ -114,20 +118,25 @@ export async function command(params: { buildContext: BuildContext }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(
|
newContent = [
|
||||||
filePath,
|
`// This file is auto-generated by the \`update-kc-gen\` command. Do not edit it manually.`,
|
||||||
Buffer.from(
|
`// Hash: ${hash}`,
|
||||||
[
|
``,
|
||||||
`// This file is auto-generated by the \`update-kc-gen\` command. Do not edit it manually.`,
|
newContent
|
||||||
`// Hash: ${hash}`,
|
].join("\n");
|
||||||
``,
|
|
||||||
newContent
|
|
||||||
].join("\n"),
|
|
||||||
"utf8"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
runFormat({ packageJsonFilePath: buildContext.packageJsonFilePath });
|
format: {
|
||||||
|
if (!(await getIsPrettierAvailable())) {
|
||||||
|
break format;
|
||||||
|
}
|
||||||
|
|
||||||
|
newContent = await runPrettier({
|
||||||
|
filePath,
|
||||||
|
sourceCode: newContent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, Buffer.from(newContent, "utf8"));
|
||||||
|
|
||||||
delete_legacy_file: {
|
delete_legacy_file: {
|
||||||
const legacyFilePath = filePath.replace(/tsx$/, "ts");
|
const legacyFilePath = filePath.replace(/tsx$/, "ts");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user