import { __exportAll } from "../_virtual/_rolldown/runtime.js";
import path from "node:path";
import is from "@sindresorhus/is";
import { replaceCore } from "case-police";
import abbreviates from "case-police/dict/abbreviates.json" with { type: "json" };
import brands from "case-police/dict/brands.json" with { type: "json" };
import general from "case-police/dict/general.json" with { type: "json" };
import products from "case-police/dict/products.json" with { type: "json" };
import softwares from "case-police/dict/softwares.json" with { type: "json" };
import { titleCase } from "scule";
//#region src/lib/utilities/template-helpers.ts
var template_helpers_exports = /* @__PURE__ */ __exportAll({
	REPLACEMENTS: () => REPLACEMENTS,
	collectArrayField: () => collectArrayField,
	collectField: () => collectField,
	dependencyNames: () => dependencyNames,
	ensureArray: () => ensureArray,
	firstOf: () => firstOf,
	hasDependencyWithId: () => hasDependencyWithId,
	isAuthoredBy: () => isAuthoredBy,
	isOnGithubAccountOf: () => isOnGithubAccountOf,
	isValidUrl: () => isValidUrl,
	mixedStringsToArray: () => mixedStringsToArray,
	nonEmpty: () => nonEmpty,
	splitCommaSeparated: () => splitCommaSeparated,
	stripNamespace: () => stripNamespace,
	stripUndefined: () => stripUndefined,
	toAlias: () => toAlias,
	toBasicLicense: () => toBasicLicense,
	toBasicLicenses: () => toBasicLicenses,
	toBasicNames: () => toBasicNames,
	toDelimitedString: () => toDelimitedString,
	toLocalUrl: () => toLocalUrl,
	toMarkdownLink: () => toMarkdownLink,
	toMb: () => toMb,
	toStatus: () => toStatus,
	toStatusLegacy: () => toStatusLegacy,
	usesPnpm: () => usesPnpm
});
const casePoliceDict = {
	...abbreviates,
	...brands,
	...general,
	...products,
	...softwares
};
/**
* Extract the first element from a `OneOrMany` value.
*
* Metadata sources may return a single record or an array of records. This
* helper normalizes access so you can safely retrieve the first record
* regardless of cardinality.
*/
function firstOf(value) {
	if (value === void 0) return void 0;
	return Array.isArray(value) ? value[0] : value;
}
/**
* Wrap a value in an array if it isn't one already.
* Returns an empty array for `undefined` or `null`.
*/
function ensureArray(value) {
	if (value === void 0 || value === null) return [];
	return Array.isArray(value) ? value : [value];
}
/**
* Collect values from all records in a `OneOrMany<SourceRecord<D>>` source.
* Runs the accessor on each record's `.data` and returns all non-undefined results.
*
* Useful for extracting a specific field from sources that may contain multiple records
* (e.g. multiple Cargo.toml files in a workspace).
*/
function collectField(source, accessor) {
	if (source === void 0) return [];
	return (Array.isArray(source) ? source : [source]).map((record) => accessor(record.data)).filter((value) => value !== void 0);
}
/**
* Collect and flatten array values from all records in a `OneOrMany<SourceRecord<D>>` source.
* Runs the accessor on each record's `.data` and flattens the resulting arrays.
*/
function collectArrayField(source, accessor) {
	if (source === void 0) return [];
	return (Array.isArray(source) ? source : [source]).flatMap((record) => accessor(record.data) ?? []);
}
/**
* Return the array if non-empty, otherwise undefined.
* Useful for converting empty collection results to undefined before `stripUndefined`.
*/
function nonEmpty(array) {
	return array.length > 0 ? array : void 0;
}
/**
* Split a string on commas, trimming each part and filtering out empty strings.
* Returns an empty array for `undefined` or empty input.
*/
function splitCommaSeparated(value) {
	if (value === void 0) return [];
	return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
}
/**
* Join an array of strings with a delimiter, filtering out undefined values.
*/
function toDelimitedString(source, delimiter = ", ") {
	if (source === void 0) return void 0;
	if (Array.isArray(source)) {
		const filtered = source.filter((s) => s !== void 0);
		return filtered.length > 0 ? filtered.join(delimiter) : void 0;
	}
	return source;
}
/**
* Convert a URL or path to a markdown link using its basename as the label.
*/
function toMarkdownLink(value) {
	if (is.nonEmptyStringAndNotWhitespace(value)) return `[${path.basename(value)}](${value})`;
}
/**
* Convert bytes to megabytes (rounded).
* (MB, not MiB.)
*/
function toMb(bytes) {
	if (is.positiveNumber(bytes)) return Math.round(bytes / 1e3 / 1e3 * 100) / 100;
}
/**
* Strip the namespace or directory prefix from a package name or path.
*/
function stripNamespace(value) {
	return path.basename(value);
}
/**
* Convert a package name to a human-friendly alias using title case
* and brand-name corrections.
*/
function toAlias(value) {
	if (is.nonEmptyString(value)) {
		const result = titleCase(stripNamespace(value)).replaceAll(/ {2,}/g, " ").trim();
		return replaceCore(result, casePoliceDict) ?? result;
	}
}
/**
* Escape a string for use in a regular expression.
*/
function escapeRegExp(string_) {
	return string_.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
}
const REPLACEMENTS = new Map([["javascript", "JavaScript"], ["typescript", "TypeScript"]]);
/**
* Takes any value and extracts all strings from it.
* Returns string[] if any strings were found, or undefined otherwise.
* Optionally performs case-insensitive string replacement with `replacements`.
*/
function mixedStringsToArray(value, replacements) {
	if (value === void 0) return;
	const filtered = ensureArray(value).filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => {
		if (!replacements) return item;
		let result = item;
		for (const [search, replace] of replacements) {
			const pattern = new RegExp(escapeRegExp(search), "gi");
			result = result.replace(pattern, replace);
		}
		return result;
	});
	return filtered.length > 0 ? filtered : void 0;
}
/**
* Convert a filename or relative path to a local `file://` URL rooted at `repoPath`.
*/
function toLocalUrl(value, repoPath) {
	if (value === void 0 || repoPath === void 0) return;
	const relativePath = path.join(repoPath, path.basename(value)).replaceAll("\\", "/");
	return `file://${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
}
/**
* Recursively removes `undefined` values and empty objects from an object.
* Only recurses into plain objects and arrays. Non-plain objects (like Date)
* are preserved as-is.
* Array elements that are `undefined` are also removed.
* Returns `undefined` if the entire input becomes empty after stripping.
*/
function stripUndefined(value) {
	if (Array.isArray(value)) {
		const filtered = value.filter((item) => item !== void 0).map((item) => stripUndefined(item)).filter((item) => item !== void 0);
		return filtered.length > 0 ? filtered : void 0;
	}
	if (is.plainObject(value)) {
		const result = {};
		let hasKeys = false;
		for (const [key, theValue] of Object.entries(value)) if (theValue !== void 0) {
			const stripped = stripUndefined(theValue);
			if (stripped !== void 0) {
				result[key] = stripped;
				hasKeys = true;
			}
		}
		if (!hasKeys) return void 0;
		return result;
	}
	return value;
}
/**
* Strip the SPDX license URL prefix, returning just the license identifier.
*/
function toBasicLicense(source) {
	if (source === void 0) return;
	return source.replace("http://spdx.org/licenses/", "").replace("https://spdx.org/licenses/", "");
}
/**
* Normalize one or more license values to plain SPDX identifiers, stripping URL prefixes.
*/
function toBasicLicenses(...sources) {
	const result = sources.flat().filter((value) => value !== void 0).map((value) => toBasicLicense(value)).filter((value) => value !== void 0);
	return result.length === 0 ? void 0 : result;
}
/**
* Extract a display name from a `CodeMetaPersonOrOrg`.
*/
function toBasicName(basicPersonOrOrg) {
	if (basicPersonOrOrg === void 0) return;
	if (basicPersonOrOrg.name !== void 0) return basicPersonOrOrg.name;
	return toDelimitedString([basicPersonOrOrg.givenName, basicPersonOrOrg.familyName], " ");
}
/**
* Extract display names from an array of `CodeMetaPersonOrOrg`.
*/
function toBasicNames(source) {
	if (source === void 0) return;
	const result = source.map((basicPersonOrOrg) => toBasicName(basicPersonOrOrg)).filter((value) => value !== void 0);
	return result.length > 0 ? result : void 0;
}
/**
* Check id a project uses a specific dependency
*/
function hasDependencyWithId(id, codemeta) {
	return [...codemeta.softwareSuggestions ?? [], ...codemeta.softwareRequirements ?? []].some(({ identifier, name }) => identifier === id || name === id);
}
/**
* Simple list of all dependencies
*/
function dependencyNames(codemeta, filter = "all") {
	return nonEmpty([...new Set([...filter === "prod" ? [] : codemeta.softwareSuggestions ?? [], ...filter === "dev" ? [] : codemeta.softwareRequirements ?? []].map((value) => value.name ?? value.identifier ?? void 0).filter((value) => value !== void 0))].toSorted());
}
/**
* Check if the project uses pnpm as its package manager.
*/
function usesPnpm(packageJson) {
	const first = firstOf(packageJson);
	if (!first) return false;
	return first.data.packageManager?.toLowerCase().startsWith("pnpm") ?? Object.hasOwn(first.data.engines ?? {}, "pnpm");
}
/**
* True if project was authored by specific person(s).
*/
function isAuthoredBy(codemetaAuthorName, expectedAuthorName) {
	if (codemetaAuthorName === void 0 || expectedAuthorName === void 0 || is.emptyArray(codemetaAuthorName) || is.emptyArray(expectedAuthorName)) return;
	const authors = new Set(ensureArray(expectedAuthorName).map((name) => name.toLocaleLowerCase().trim()));
	return (toBasicNames(ensureArray(codemetaAuthorName))?.map((name) => name.toLocaleLowerCase().trim()))?.some((name) => authors.has(name));
}
/**
* True if project is on a specific GitHub account(s).
*/
function isOnGithubAccountOf(codeRepository, githubUserName) {
	if (codeRepository === void 0 || githubUserName === void 0) return;
	const cleanRepo = codeRepository.toLocaleLowerCase().trim();
	if (!cleanRepo.includes("github.com/")) return false;
	return ensureArray(githubUserName).some((userName) => cleanRepo.includes(`/${userName.toLocaleLowerCase().trim()}/`));
}
/**
* Legacy heuristic project status based on authorship and GitHub account.
*/
function toStatusLegacy(codeRepository, codemetaAuthorName, authorName, githubUserName) {
	if (codeRepository === void 0 || authorName === void 0 || githubUserName === void 0) return;
	const isAuthoredByAuthorName = isAuthoredBy(codemetaAuthorName, authorName);
	const isOnGithub = isOnGithubAccountOf(codeRepository, githubUserName);
	if (isAuthoredByAuthorName === void 0 || isOnGithub === void 0) return;
	return !isAuthoredByAuthorName && isOnGithub ? "fork" : isAuthoredByAuthorName ? "source" : "unmaintained";
}
/**
* Heuristic project status based on authorship and GitHub account.
*/
function toStatus(codeRepository, codemetaAuthorName, codemetaContributorOrMaintainerName, isGitHubFork, myAuthorName, myGithubUserName) {
	const author = isAuthoredBy(codemetaAuthorName, myAuthorName);
	const maintainer = isAuthoredBy(codemetaContributorOrMaintainerName, myAuthorName);
	const githubStatus = isOnGithubAccountOf(codeRepository, myGithubUserName);
	const github = githubStatus === void 0 ? "missing" : githubStatus ? isGitHubFork ? "my-fork" : "my-source" : isGitHubFork ? "their-fork" : "their-source";
	const me = author === true ? "author" : maintainer === true ? "maintainer" : "missing";
	if (github === "missing" && me === "author") return "author";
	if (github === "missing" && me === "maintainer") return "maintainer";
	if (github === "missing" && me === "missing") return "unknown";
	if (github === "my-fork" && me === "author") return "maintainer";
	if (github === "my-fork" && me === "maintainer") return "maintainer";
	if (github === "my-fork" && me === "missing") return "observer";
	if (github === "my-source" && me === "author") return "author";
	if (github === "my-source" && me === "maintainer") return "maintainer";
	if (github === "my-source" && me === "missing") return "author";
	if (github === "their-fork" && me === "author") return "maintainer";
	if (github === "their-fork" && me === "maintainer") return "maintainer";
	if (github === "their-fork" && me === "missing") return "observer";
	if (github === "their-source" && me === "author") return "author";
	if (github === "their-source" && me === "maintainer") return "maintainer";
	if (github === "their-source" && me === "missing") return "observer";
	return "unknown";
}
/**
* True if valid url
*/
function isValidUrl(value) {
	return is.urlString(value);
}
//#endregion
export { REPLACEMENTS, collectArrayField, collectField, dependencyNames, ensureArray, firstOf, hasDependencyWithId, isAuthoredBy, isOnGithubAccountOf, isValidUrl, mixedStringsToArray, nonEmpty, splitCommaSeparated, stripUndefined, template_helpers_exports, toAlias, toBasicLicenses, toBasicNames, toLocalUrl, toMb, toStatus, toStatusLegacy, usesPnpm };
