@settlemint/sdk-utils
Version:
Shared utilities and helper functions for SettleMint SDK modules
216 lines (211 loc) • 6.65 kB
JavaScript
import { dirname, join } from "node:path";
import { findUp } from "find-up";
import { readFile, stat } from "node:fs/promises";
import { glob } from "glob";
//#region src/filesystem/project-root.ts
/**
* Finds the root directory of the current project by locating the nearest package.json file
*
* @param fallbackToCwd - If true, will return the current working directory if no package.json is found
* @param cwd - The directory to start searching for the package.json file from (defaults to process.cwd())
* @returns Promise that resolves to the absolute path of the project root directory
* @throws Will throw an error if no package.json is found in the directory tree
* @example
* import { projectRoot } from "@settlemint/sdk-utils/filesystem";
*
* // Get project root path
* const rootDir = await projectRoot();
* console.log(`Project root is at: ${rootDir}`);
*/
async function projectRoot(fallbackToCwd = false, cwd) {
const packageJsonPath = await findUp("package.json", { cwd });
if (!packageJsonPath) {
if (fallbackToCwd) {
return process.cwd();
}
throw new Error("Unable to find project root (no package.json found)");
}
return dirname(packageJsonPath);
}
//#endregion
//#region src/filesystem/exists.ts
/**
* Checks if a file or directory exists at the given path
*
* @param path - The file system path to check for existence
* @returns Promise that resolves to true if the path exists, false otherwise
* @example
* import { exists } from "@settlemint/sdk-utils/filesystem";
*
* // Check if file exists before reading
* if (await exists('/path/to/file.txt')) {
* // File exists, safe to read
* }
*/
async function exists(path) {
try {
await stat(path);
return true;
} catch {
return false;
}
}
//#endregion
//#region src/json.ts
/**
* Attempts to parse a JSON string into a typed value, returning a default value if parsing fails.
*
* @param value - The JSON string to parse
* @param defaultValue - The value to return if parsing fails or results in null/undefined
* @returns The parsed JSON value as type T, or the default value if parsing fails
*
* @example
* import { tryParseJson } from "@settlemint/sdk-utils";
*
* const config = tryParseJson<{ port: number }>(
* '{"port": 3000}',
* { port: 8080 }
* );
* // Returns: { port: 3000 }
*
* const invalid = tryParseJson<string[]>(
* 'invalid json',
* []
* );
* // Returns: []
*/
function tryParseJson(value, defaultValue = null) {
try {
const parsed = JSON.parse(value);
if (parsed === undefined || parsed === null) {
return defaultValue;
}
return parsed;
} catch (_err) {
return defaultValue;
}
}
/**
* Extracts a JSON object from a string.
*
* @param value - The string to extract the JSON object from
* @returns The parsed JSON object, or null if no JSON object is found
* @throws {Error} If the input string is too long (longer than 5000 characters)
* @example
* import { extractJsonObject } from "@settlemint/sdk-utils";
*
* const json = extractJsonObject<{ port: number }>(
* 'port info: {"port": 3000}',
* );
* // Returns: { port: 3000 }
*/
function extractJsonObject(value) {
if (value.length > 5e3) {
throw new Error("Input too long");
}
const result = /\{([\s\S]*)\}/.exec(value);
if (!result) {
return null;
}
return tryParseJson(result[0]);
}
/**
* Converts a value to a JSON stringifiable format.
*
* @param value - The value to convert
* @returns The JSON stringifiable value
*
* @example
* import { makeJsonStringifiable } from "@settlemint/sdk-utils";
*
* const json = makeJsonStringifiable<{ amount: bigint }>({ amount: BigInt(1000) });
* // Returns: '{"amount":"1000"}'
*/
function makeJsonStringifiable(value) {
if (value === undefined || value === null) {
return value;
}
return tryParseJson(JSON.stringify(value, (_, value$1) => typeof value$1 === "bigint" ? value$1.toString() : value$1));
}
//#endregion
//#region src/filesystem/mono-repo.ts
/**
* Finds the root directory of a monorepo
*
* @param startDir - The directory to start searching from
* @returns The root directory of the monorepo or null if not found
* @example
* import { findMonoRepoRoot } from "@settlemint/sdk-utils/filesystem";
*
* const root = await findMonoRepoRoot("/path/to/your/project");
* console.log(root); // Output: /path/to/your/project/packages/core
*/
async function findMonoRepoRoot(startDir) {
const lockFilePath = await findUp([
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"bun.lockb",
"bun.lock"
], { cwd: startDir });
if (lockFilePath) {
const packageJsonPath = join(dirname(lockFilePath), "package.json");
const hasWorkSpaces = await packageJsonHasWorkspaces(packageJsonPath);
return hasWorkSpaces ? dirname(lockFilePath) : null;
}
let currentDir = startDir;
while (currentDir !== "/") {
const packageJsonPath = join(currentDir, "package.json");
if (await packageJsonHasWorkspaces(packageJsonPath)) {
return currentDir;
}
const parentDir = dirname(currentDir);
if (parentDir === currentDir) {
break;
}
currentDir = parentDir;
}
return null;
}
/**
* Finds all packages in a monorepo
*
* @param projectDir - The directory to start searching from
* @returns An array of package directories
* @example
* import { findMonoRepoPackages } from "@settlemint/sdk-utils/filesystem";
*
* const packages = await findMonoRepoPackages("/path/to/your/project");
* console.log(packages); // Output: ["/path/to/your/project/packages/core", "/path/to/your/project/packages/ui"]
*/
async function findMonoRepoPackages(projectDir) {
try {
const monoRepoRoot = await findMonoRepoRoot(projectDir);
if (!monoRepoRoot) {
return [projectDir];
}
const packageJsonPath = join(monoRepoRoot, "package.json");
const packageJson = tryParseJson(await readFile(packageJsonPath, "utf-8"));
const workspaces = packageJson?.workspaces ?? [];
const packagePaths = await Promise.all(workspaces.map(async (workspace) => {
const matches = await glob(join(monoRepoRoot, workspace, "package.json"));
return matches.map((match) => join(match, ".."));
}));
const allPaths = packagePaths.flat();
return allPaths.length === 0 ? [projectDir] : [monoRepoRoot, ...allPaths];
} catch (_error) {
return [projectDir];
}
}
async function packageJsonHasWorkspaces(packageJsonPath) {
if (await exists(packageJsonPath)) {
const packageJson = tryParseJson(await readFile(packageJsonPath, "utf-8"));
if (packageJson?.workspaces && Array.isArray(packageJson?.workspaces) && packageJson?.workspaces.length > 0) {
return true;
}
}
return false;
}
//#endregion
export { exists, findMonoRepoPackages, findMonoRepoRoot, projectRoot };
//# sourceMappingURL=filesystem.js.map