@visulima/package
Version:
One Package to rule them all, finds your root-dir, monorepo, or package manager.
465 lines (456 loc) • 15.6 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const node_fs = require('node:fs');
const installPkg = require('@antfu/install-pkg');
const confirm = require('@inquirer/confirm');
const fs = require('@visulima/fs');
const error = require('@visulima/fs/error');
const utils = require('@visulima/fs/utils');
const path = require('@visulima/path');
const normalizeData = require('normalize-package-data');
const _interopDefaultCompat = e => e && typeof e === 'object' && 'default' in e ? e.default : e;
const confirm__default = /*#__PURE__*/_interopDefaultCompat(confirm);
const normalizeData__default = /*#__PURE__*/_interopDefaultCompat(normalizeData);
var __defProp$1 = Object.defineProperty;
var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true });
const isObject = /* @__PURE__ */ __name$1((value) => {
const type = typeof value;
return value !== null && (type === "object" || type === "function");
}, "isObject");
const isEmptyObject = /* @__PURE__ */ __name$1((value) => isObject(value) && Object.keys(value).length === 0, "isEmptyObject");
const disallowedKeys = /* @__PURE__ */ new Set([
"__proto__",
"prototype",
"constructor"
]);
const digits = new Set("0123456789");
function getPathSegments(path) {
const parts = [];
let currentSegment = "";
let currentPart = "start";
let isIgnoring = false;
for (const character of path) {
switch (character) {
case "\\": {
if (currentPart === "index") {
throw new Error("Invalid character in an index");
}
if (currentPart === "indexEnd") {
throw new Error("Invalid character after an index");
}
if (isIgnoring) {
currentSegment += character;
}
currentPart = "property";
isIgnoring = !isIgnoring;
break;
}
case ".": {
if (currentPart === "index") {
throw new Error("Invalid character in an index");
}
if (currentPart === "indexEnd") {
currentPart = "property";
break;
}
if (isIgnoring) {
isIgnoring = false;
currentSegment += character;
break;
}
if (disallowedKeys.has(currentSegment)) {
return [];
}
parts.push(currentSegment);
currentSegment = "";
currentPart = "property";
break;
}
case "[": {
if (currentPart === "index") {
throw new Error("Invalid character in an index");
}
if (currentPart === "indexEnd") {
currentPart = "index";
break;
}
if (isIgnoring) {
isIgnoring = false;
currentSegment += character;
break;
}
if (currentPart === "property") {
if (disallowedKeys.has(currentSegment)) {
return [];
}
parts.push(currentSegment);
currentSegment = "";
}
currentPart = "index";
break;
}
case "]": {
if (currentPart === "index") {
parts.push(Number.parseInt(currentSegment, 10));
currentSegment = "";
currentPart = "indexEnd";
break;
}
if (currentPart === "indexEnd") {
throw new Error("Invalid character after an index");
}
}
default: {
if (currentPart === "index" && !digits.has(character)) {
throw new Error("Invalid character in an index");
}
if (currentPart === "indexEnd") {
throw new Error("Invalid character after an index");
}
if (currentPart === "start") {
currentPart = "property";
}
if (isIgnoring) {
isIgnoring = false;
currentSegment += "\\";
}
currentSegment += character;
}
}
}
if (isIgnoring) {
currentSegment += "\\";
}
switch (currentPart) {
case "property": {
if (disallowedKeys.has(currentSegment)) {
return [];
}
parts.push(currentSegment);
break;
}
case "index": {
throw new Error("Index was not closed");
}
case "start": {
parts.push("");
break;
}
}
return parts;
}
__name$1(getPathSegments, "getPathSegments");
function isStringIndex(object, key) {
if (typeof key !== "number" && Array.isArray(object)) {
const index = Number.parseInt(key, 10);
return Number.isInteger(index) && object[index] === object[key];
}
return false;
}
__name$1(isStringIndex, "isStringIndex");
function assertNotStringIndex(object, key) {
if (isStringIndex(object, key)) {
throw new Error("Cannot use string index");
}
}
__name$1(assertNotStringIndex, "assertNotStringIndex");
function getProperty(object, path, value) {
if (!isObject(object) || typeof path !== "string") {
return value === void 0 ? object : value;
}
const pathArray = getPathSegments(path);
if (pathArray.length === 0) {
return value;
}
for (let index = 0; index < pathArray.length; index++) {
const key = pathArray[index];
if (isStringIndex(object, key)) {
object = index === pathArray.length - 1 ? void 0 : null;
} else {
object = object[key];
}
if (object === void 0 || object === null) {
if (index !== pathArray.length - 1) {
return value;
}
break;
}
}
return object === void 0 ? value : object;
}
__name$1(getProperty, "getProperty");
function setProperty(object, path, value) {
if (!isObject(object) || typeof path !== "string") {
return object;
}
const root = object;
const pathArray = getPathSegments(path);
for (let index = 0; index < pathArray.length; index++) {
const key = pathArray[index];
assertNotStringIndex(object, key);
if (index === pathArray.length - 1) {
object[key] = value;
} else if (!isObject(object[key])) {
object[key] = typeof pathArray[index + 1] === "number" ? [] : {};
}
object = object[key];
}
return root;
}
__name$1(setProperty, "setProperty");
function deleteProperty(object, path) {
if (!isObject(object) || typeof path !== "string") {
return false;
}
const pathArray = getPathSegments(path);
for (let index = 0; index < pathArray.length; index++) {
const key = pathArray[index];
assertNotStringIndex(object, key);
if (index === pathArray.length - 1) {
delete object[key];
return true;
}
object = object[key];
if (!isObject(object)) {
return false;
}
}
}
__name$1(deleteProperty, "deleteProperty");
function hasProperty(object, path) {
if (!isObject(object) || typeof path !== "string") {
return false;
}
const pathArray = getPathSegments(path);
if (pathArray.length === 0) {
return false;
}
for (const key of pathArray) {
if (!isObject(object) || !(key in object) || isStringIndex(object, key)) {
return false;
}
object = object[key];
}
return true;
}
__name$1(hasProperty, "hasProperty");
function escapePath(path) {
if (typeof path !== "string") {
throw new TypeError("Expected a string");
}
return path.replaceAll(/[\\.[]/g, "\\$&");
}
__name$1(escapePath, "escapePath");
function entries(value) {
const result = Object.entries(value);
if (Array.isArray(value)) {
return result.map(([key, value2]) => [Number(key), value2]);
}
return result;
}
__name$1(entries, "entries");
function stringifyPath(pathSegments) {
let result = "";
for (let [index, segment] of entries(pathSegments)) {
if (typeof segment === "number") {
result += `[${segment}]`;
} else {
segment = escapePath(segment);
result += index === 0 ? segment : `.${segment}`;
}
}
return result;
}
__name$1(stringifyPath, "stringifyPath");
function* deepKeysIterator(object, currentPath = []) {
if (!isObject(object) || isEmptyObject(object)) {
if (currentPath.length > 0) {
yield stringifyPath(currentPath);
}
return;
}
for (const [key, value] of entries(object)) {
yield* deepKeysIterator(value, [...currentPath, key]);
}
}
__name$1(deepKeysIterator, "deepKeysIterator");
function deepKeys(object) {
return [...deepKeysIterator(object)];
}
__name$1(deepKeys, "deepKeys");
const isNode = typeof process.stdout < "u" && !process.versions.deno && !globalThis.window;
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
const PackageJsonFileCache = /* @__PURE__ */ new Map();
class PackageJsonValidationError extends Error {
static {
__name(this, "PackageJsonValidationError");
}
constructor(warnings) {
super(`The following warnings were encountered while normalizing package data:
- ${warnings.join("\n- ")}`);
this.name = "PackageJsonValidationError";
}
}
const normalizeInput = /* @__PURE__ */ __name((input, strict, ignoreWarnings = []) => {
const warnings = [];
normalizeData__default(
input,
(message) => {
warnings.push(message);
},
strict
);
if (strict && warnings.length > 0) {
const filteredWarnings = warnings.filter(
(warning) => !ignoreWarnings.some((pattern) => {
if (pattern instanceof RegExp) {
return pattern.test(warning);
}
return pattern === warning;
})
);
if (filteredWarnings.length > 0) {
throw new PackageJsonValidationError(filteredWarnings);
}
}
return input;
}, "normalizeInput");
const findPackageJson = /* @__PURE__ */ __name(async (cwd, options = {}) => {
const findUpConfig = {
type: "file"
};
if (cwd) {
findUpConfig.cwd = cwd;
}
const filePath = await fs.findUp("package.json", findUpConfig);
if (!filePath) {
throw new error.NotFoundError("No such file or directory, for package.json found.");
}
const cache = options.cache && typeof options.cache !== "boolean" ? options.cache : PackageJsonFileCache;
if (options.cache && cache.has(filePath)) {
return cache.get(filePath);
}
const packageJson = await fs.readJson(filePath);
normalizeInput(packageJson, options.strict ?? false, options.ignoreWarnings);
const output = {
packageJson,
path: filePath
};
cache.set(filePath, output);
return output;
}, "findPackageJson");
const findPackageJsonSync = /* @__PURE__ */ __name((cwd, options = {}) => {
const findUpConfig = {
type: "file"
};
if (cwd) {
findUpConfig.cwd = cwd;
}
const filePath = fs.findUpSync("package.json", findUpConfig);
if (!filePath) {
throw new error.NotFoundError("No such file or directory, for package.json found.");
}
const cache = options.cache && typeof options.cache !== "boolean" ? options.cache : PackageJsonFileCache;
if (options.cache && cache.has(filePath)) {
return cache.get(filePath);
}
const packageJson = fs.readJsonSync(filePath);
normalizeInput(packageJson, options.strict ?? false, options.ignoreWarnings);
const output = {
packageJson,
path: filePath
};
cache.set(filePath, output);
return output;
}, "findPackageJsonSync");
const writePackageJson = /* @__PURE__ */ __name(async (data, options = {}) => {
const { cwd, ...writeOptions } = options;
const directory = utils.toPath(options.cwd ?? process.cwd());
await fs.writeJson(path.join(directory, "package.json"), data, writeOptions);
}, "writePackageJson");
const writePackageJsonSync = /* @__PURE__ */ __name((data, options = {}) => {
const { cwd, ...writeOptions } = options;
const directory = utils.toPath(options.cwd ?? process.cwd());
fs.writeJsonSync(path.join(directory, "package.json"), data, writeOptions);
}, "writePackageJsonSync");
const parsePackageJson = /* @__PURE__ */ __name((packageFile, options) => {
const isObject = packageFile !== null && typeof packageFile === "object" && !Array.isArray(packageFile);
const isString = typeof packageFile === "string";
if (!isObject && !isString) {
throw new TypeError("`packageFile` should be either an `object` or a `string`.");
}
const json = isObject ? structuredClone(packageFile) : (
// eslint-disable-next-line security/detect-non-literal-fs-filename
node_fs.existsSync(packageFile) ? fs.readJsonSync(packageFile) : utils.parseJson(packageFile)
);
normalizeInput(json, options?.strict ?? false, options?.ignoreWarnings);
return json;
}, "parsePackageJson");
const getPackageJsonProperty = /* @__PURE__ */ __name((packageJson, property, defaultValue) => getProperty(packageJson, property, defaultValue), "getPackageJsonProperty");
const hasPackageJsonProperty = /* @__PURE__ */ __name((packageJson, property) => hasProperty(packageJson, property), "hasPackageJsonProperty");
const hasPackageJsonAnyDependency = /* @__PURE__ */ __name((packageJson, arguments_, options) => {
const dependencies = getProperty(packageJson, "dependencies", {});
const devDependencies = getProperty(packageJson, "devDependencies", {});
const peerDependencies = getProperty(packageJson, "peerDependencies", {});
const allDependencies = { ...dependencies, ...devDependencies, ...options?.peerDeps === false ? {} : peerDependencies };
for (const argument of arguments_) {
if (hasProperty(allDependencies, argument)) {
return true;
}
}
return false;
}, "hasPackageJsonAnyDependency");
const ensurePackages = /* @__PURE__ */ __name(async (packageJson, packages, installKey = "dependencies", options = {}) => {
if (process.env.CI || isNode && !process.stdout?.isTTY) {
console.warn("Skipping package installation because the process is not interactive.");
return;
}
const dependencies = getProperty(packageJson, "dependencies", {});
const devDependencies = getProperty(packageJson, "devDependencies", {});
const peerDependencies = getProperty(packageJson, "peerDependencies", {});
const nonExistingPackages = [];
const config = {
deps: true,
devDeps: true,
peerDeps: false,
...options
};
for (const packageName of packages) {
if (config.deps && hasProperty(dependencies, packageName) || config.devDeps && hasProperty(devDependencies, packageName) || config.peerDeps && hasProperty(peerDependencies, packageName)) {
continue;
}
nonExistingPackages.push(packageName);
}
if (typeof config.confirm?.message === "function") {
config.confirm.message = config.confirm.message(nonExistingPackages);
}
if (config.confirm?.message === void 0) {
const message = `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?`;
if (config.confirm === void 0) {
config.confirm = {
message
};
} else {
config.confirm.message = message;
}
}
const answer = await confirm__default(config.confirm);
if (!answer) {
return;
}
await installPkg.installPackage(nonExistingPackages, {
...config.installPackage,
cwd: config.cwd ? utils.toPath(config.cwd) : void 0,
dev: installKey === "devDependencies"
});
}, "ensurePackages");
exports.ensurePackages = ensurePackages;
exports.findPackageJson = findPackageJson;
exports.findPackageJsonSync = findPackageJsonSync;
exports.getPackageJsonProperty = getPackageJsonProperty;
exports.hasPackageJsonAnyDependency = hasPackageJsonAnyDependency;
exports.hasPackageJsonProperty = hasPackageJsonProperty;
exports.parsePackageJson = parsePackageJson;
exports.writePackageJson = writePackageJson;
exports.writePackageJsonSync = writePackageJsonSync;