193 lines
5.0 KiB
TypeScript
193 lines
5.0 KiB
TypeScript
import * as plugins from "./plugins.js";
|
|
import { rename, writeFile } from "fs/promises";
|
|
|
|
export const CLI_NAMESPACE = "@git.zone/cli";
|
|
|
|
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
};
|
|
|
|
export const getSmartconfigPath = (cwd: string = process.cwd()): string => {
|
|
return plugins.path.join(cwd, ".smartconfig.json");
|
|
};
|
|
|
|
export const readSmartconfigFile = async (
|
|
cwd: string = process.cwd(),
|
|
): Promise<Record<string, any>> => {
|
|
const smartconfigPath = getSmartconfigPath(cwd);
|
|
if (!(await plugins.smartfs.file(smartconfigPath).exists())) {
|
|
return {};
|
|
}
|
|
|
|
const content = (await plugins.smartfs
|
|
.file(smartconfigPath)
|
|
.encoding("utf8")
|
|
.read()) as string;
|
|
if (content.trim() === "") {
|
|
return {};
|
|
}
|
|
return JSON.parse(content);
|
|
};
|
|
|
|
export const writeSmartconfigFile = async (
|
|
data: Record<string, any>,
|
|
cwd: string = process.cwd(),
|
|
): Promise<void> => {
|
|
const smartconfigPath = getSmartconfigPath(cwd);
|
|
const tempPath = `${smartconfigPath}.tmp-${Date.now()}`;
|
|
const content = JSON.stringify(data, null, 2);
|
|
await writeFile(tempPath, content, "utf8");
|
|
await rename(tempPath, smartconfigPath);
|
|
};
|
|
|
|
export const normalizeCliConfigPath = (configPath: string): string => {
|
|
const trimmedPath = configPath.trim();
|
|
if (!trimmedPath || trimmedPath === CLI_NAMESPACE) {
|
|
return "";
|
|
}
|
|
|
|
if (trimmedPath.startsWith(`${CLI_NAMESPACE}.`)) {
|
|
return trimmedPath.slice(`${CLI_NAMESPACE}.`.length);
|
|
}
|
|
|
|
return trimmedPath;
|
|
};
|
|
|
|
export const getCliConfigPathSegments = (configPath: string): string[] => {
|
|
const normalizedPath = normalizeCliConfigPath(configPath);
|
|
if (!normalizedPath) {
|
|
return [];
|
|
}
|
|
|
|
return normalizedPath
|
|
.split(".")
|
|
.map((segment) => segment.trim())
|
|
.filter(Boolean);
|
|
};
|
|
|
|
export const getCliNamespaceConfig = (
|
|
smartconfigData: Record<string, any>,
|
|
): Record<string, any> => {
|
|
const cliConfig = smartconfigData[CLI_NAMESPACE];
|
|
if (isPlainObject(cliConfig)) {
|
|
return cliConfig;
|
|
}
|
|
return {};
|
|
};
|
|
|
|
export const getCliConfigValueFromData = (
|
|
smartconfigData: Record<string, any>,
|
|
configPath: string,
|
|
): any => {
|
|
const segments = getCliConfigPathSegments(configPath);
|
|
let currentValue: any = getCliNamespaceConfig(smartconfigData);
|
|
|
|
for (const segment of segments) {
|
|
if (!isPlainObject(currentValue) && !Array.isArray(currentValue)) {
|
|
return undefined;
|
|
}
|
|
currentValue = (currentValue as any)?.[segment];
|
|
}
|
|
|
|
return currentValue;
|
|
};
|
|
|
|
export const getCliConfigValue = async <T>(
|
|
configPath: string,
|
|
defaultValue: T,
|
|
cwd: string = process.cwd(),
|
|
): Promise<T> => {
|
|
const smartconfigData = await readSmartconfigFile(cwd);
|
|
const configValue = getCliConfigValueFromData(smartconfigData, configPath);
|
|
|
|
if (configValue === undefined) {
|
|
return defaultValue;
|
|
}
|
|
|
|
if (isPlainObject(defaultValue) && isPlainObject(configValue)) {
|
|
return {
|
|
...defaultValue,
|
|
...configValue,
|
|
} as T;
|
|
}
|
|
|
|
return configValue as T;
|
|
};
|
|
|
|
export const setCliConfigValueInData = (
|
|
smartconfigData: Record<string, any>,
|
|
configPath: string,
|
|
value: any,
|
|
): Record<string, any> => {
|
|
const segments = getCliConfigPathSegments(configPath);
|
|
|
|
if (!isPlainObject(smartconfigData[CLI_NAMESPACE])) {
|
|
smartconfigData[CLI_NAMESPACE] = {};
|
|
}
|
|
|
|
if (segments.length === 0) {
|
|
smartconfigData[CLI_NAMESPACE] = value;
|
|
return smartconfigData;
|
|
}
|
|
|
|
let currentValue = smartconfigData[CLI_NAMESPACE];
|
|
for (const segment of segments.slice(0, -1)) {
|
|
if (!isPlainObject(currentValue[segment])) {
|
|
currentValue[segment] = {};
|
|
}
|
|
currentValue = currentValue[segment];
|
|
}
|
|
|
|
currentValue[segments[segments.length - 1]] = value;
|
|
return smartconfigData;
|
|
};
|
|
|
|
export const unsetCliConfigValueInData = (
|
|
smartconfigData: Record<string, any>,
|
|
configPath: string,
|
|
): boolean => {
|
|
const segments = getCliConfigPathSegments(configPath);
|
|
if (segments.length === 0) {
|
|
if (smartconfigData[CLI_NAMESPACE] !== undefined) {
|
|
delete smartconfigData[CLI_NAMESPACE];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const parentSegments = segments.slice(0, -1);
|
|
let currentValue: any = getCliNamespaceConfig(smartconfigData);
|
|
const objectPath: Array<Record<string, any>> = [currentValue];
|
|
|
|
for (const segment of parentSegments) {
|
|
if (!isPlainObject(currentValue[segment])) {
|
|
return false;
|
|
}
|
|
currentValue = currentValue[segment];
|
|
objectPath.push(currentValue);
|
|
}
|
|
|
|
const lastSegment = segments[segments.length - 1];
|
|
if (!(lastSegment in currentValue)) {
|
|
return false;
|
|
}
|
|
|
|
delete currentValue[lastSegment];
|
|
|
|
for (let i = objectPath.length - 1; i >= 1; i--) {
|
|
if (Object.keys(objectPath[i]).length > 0) {
|
|
break;
|
|
}
|
|
|
|
const parentObject = objectPath[i - 1];
|
|
const parentKey = parentSegments[i - 1];
|
|
delete parentObject[parentKey];
|
|
}
|
|
|
|
if (Object.keys(getCliNamespaceConfig(smartconfigData)).length === 0) {
|
|
delete smartconfigData[CLI_NAMESPACE];
|
|
}
|
|
|
|
return true;
|
|
};
|