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({
|
||||
srcDirPath: join("dist", "ncc_out"),
|
||||
|
@ -76,3 +76,5 @@ export const CUSTOM_HANDLER_ENV_NAMES = {
|
||||
COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME",
|
||||
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
|
||||
} from "./customHandler";
|
||||
import * as child_process from "child_process";
|
||||
import { sep as pathSep } from "path";
|
||||
import { getNodeModulesBinDirPath } from "../tools/nodeModulesBinDirPath";
|
||||
import * as fs from "fs";
|
||||
|
||||
assert<Equals<ApiVersion, "v1">>();
|
||||
@ -19,32 +19,7 @@ export function maybeDelegateCommandToCustomHandler(params: {
|
||||
}): { hasBeenHandled: boolean } {
|
||||
const { commandName, buildContext } = params;
|
||||
|
||||
const nodeModulesBinDirPath = (() => {
|
||||
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);
|
||||
})();
|
||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
||||
|
||||
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
||||
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 { join as pathJoin } from "path";
|
||||
|
||||
let cache: string | undefined = undefined;
|
||||
|
||||
export function readThisNpmPackageVersion(): string {
|
||||
if (cache !== undefined) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
const version = JSON.parse(
|
||||
fs
|
||||
.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json"))
|
||||
@ -12,5 +18,7 @@ export function readThisNpmPackageVersion(): string {
|
||||
|
||||
assert(typeof version === "string");
|
||||
|
||||
cache = 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 { KC_GEN_FILE_PATH_RELATIVE_TO_THEME_SRC_DIR } from "./shared/constants";
|
||||
import * as fs from "fs/promises";
|
||||
import { join as pathJoin } from "path";
|
||||
import { existsAsync } from "./tools/fs.existsAsync";
|
||||
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
|
||||
import { runFormat } from "./tools/runFormat";
|
||||
import * as crypto from "crypto";
|
||||
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
|
||||
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
const { buildContext } = params;
|
||||
@ -18,13 +19,16 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
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 hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented;
|
||||
const hasAdminTheme = buildContext.implementedThemeTypes.admin.isImplemented;
|
||||
|
||||
const newContent = [
|
||||
let newContent = [
|
||||
``,
|
||||
`/* eslint-disable */`,
|
||||
``,
|
||||
@ -114,20 +118,25 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
return;
|
||||
}
|
||||
|
||||
await fs.writeFile(
|
||||
filePath,
|
||||
Buffer.from(
|
||||
[
|
||||
`// This file is auto-generated by the \`update-kc-gen\` command. Do not edit it manually.`,
|
||||
`// Hash: ${hash}`,
|
||||
``,
|
||||
newContent
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
newContent = [
|
||||
`// This file is auto-generated by the \`update-kc-gen\` command. Do not edit it manually.`,
|
||||
`// Hash: ${hash}`,
|
||||
``,
|
||||
newContent
|
||||
].join("\n");
|
||||
|
||||
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: {
|
||||
const legacyFilePath = filePath.replace(/tsx$/, "ts");
|
||||
|
Loading…
x
Reference in New Issue
Block a user