op-pkg
Version:
operating package
153 lines (148 loc) • 4.32 kB
JavaScript
import fs, { existsSync } from 'node:fs';
import { resolve, dirname, join, win32 } from 'node:path';
import process from 'node:process';
import { x } from 'tinyexec';
import { detect } from 'package-manager-detector/detect';
import fsp from 'node:fs/promises';
import { createRequire } from 'node:module';
import { findUp } from 'find-up';
import { interopDefault, resolvePathSync } from 'mlly';
async function detectPackageManager(cwd = process.cwd()) {
const result = await detect({
cwd,
onUnknown(packageManager) {
console.warn("[op-pkg] Unknown packageManager:", packageManager);
return void 0;
}
});
return result?.agent || null;
}
async function installPackage(names, options = {}) {
const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || "npm";
const [agent] = detectedAgent.split("@");
if (!Array.isArray(names))
names = [names];
const args = (typeof options.additionalArgs === "function" ? options.additionalArgs(agent, detectedAgent) : options.additionalArgs) || [];
if (options.preferOffline) {
if (detectedAgent === "yarn@berry")
args.unshift("--cached");
else
args.unshift("--prefer-offline");
}
if (agent === "pnpm" && existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) {
args.unshift(
"-w",
/**
* Prevent pnpm from removing installed devDeps while `NODE_ENV` is `production`
* @see https://pnpm.io/cli/install#--prod--p
*/
"--prod=false"
);
}
return x(
agent,
[
agent === "yarn" ? "add" : "install",
options.dev ? "-D" : "",
...args,
...names
].filter(Boolean),
{
nodeOptions: {
stdio: options.silent ? "ignore" : "inherit",
cwd: options.cwd
},
throwOnError: true
}
);
}
function _resolve(path, options = {}) {
if (options.platform === "auto" || !options.platform)
options.platform = process.platform === "win32" ? "win32" : "posix";
if (process.versions.pnp) {
const paths = options.paths || [];
if (paths.length === 0)
paths.push(process.cwd());
const targetRequire = createRequire(import.meta.url);
try {
return targetRequire.resolve(path, { paths });
} catch {
}
}
const modulePath = resolvePathSync(path, {
url: options.paths
});
if (options.platform === "win32")
return win32.normalize(modulePath);
return modulePath;
}
function resolveModule(name, options = {}) {
try {
return _resolve(name, options);
} catch {
return void 0;
}
}
async function importModule(path) {
const i = await import(path);
if (i)
return interopDefault(i);
return i;
}
function isPackageExists(name, options = {}) {
return !!resolvePackage(name, options);
}
async function getPackageInfo(name, options = {}) {
const packageJsonPath = getPackageJsonPath(name, options);
if (!packageJsonPath)
return;
const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf8"));
return {
name,
version: packageJson.version,
rootPath: dirname(packageJsonPath),
packageJsonPath,
packageJson
};
}
async function loadPackageJSON(cwd = process.cwd()) {
const path = await findUp("package.json", { cwd });
if (!path || !fs.existsSync(path))
return null;
return JSON.parse(await fsp.readFile(path, "utf-8"));
}
function getPackageJsonPath(name, options = {}) {
const entry = resolvePackage(name, options);
if (!entry)
return;
return searchPackageJSON(entry);
}
function searchPackageJSON(dir) {
let packageJsonPath;
while (true) {
if (!dir)
return;
const newDir = dirname(dir);
if (newDir === dir)
return;
dir = newDir;
packageJsonPath = join(dir, "package.json");
if (fs.existsSync(packageJsonPath))
break;
}
return packageJsonPath;
}
function resolvePackage(name, options = {}) {
try {
return _resolve(`${name}/package.json`, options);
} catch {
}
try {
return _resolve(name, options);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND" && e.code !== "ERR_MODULE_NOT_FOUND")
console.error(e);
return false;
}
}
export { detectPackageManager, getPackageInfo, importModule, installPackage, isPackageExists, loadPackageJSON, resolveModule };