UNPKG

@visulima/package

Version:

One Package to rule them all, finds your root-dir, monorepo, or package manager.

465 lines (456 loc) 15.6 kB
'use strict'; 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;