@pnpm/fs.packlist
Version:
Get a list of the files to add from a directory into an npm package
138 lines • 4.91 kB
JavaScript
import fs from 'node:fs';
import path from 'node:path';
import util from 'node:util';
import { isSubdir } from 'is-subdir';
import npmPacklist from 'npm-packlist';
export async function packlist(pkgDir, opts) {
const resolvedPkgDir = path.resolve(pkgDir);
const workspaceDir = opts?.workspaceDir == null ? undefined : path.resolve(opts.workspaceDir);
const pkg = opts?.manifest ?? readPackageJson(resolvedPkgDir);
const tree = buildRootTree(resolvedPkgDir, pkg);
const hasWorkspaceContext = workspaceDir != null && workspaceDir !== resolvedPkgDir && isSubdir(workspaceDir, resolvedPkgDir);
let hasNpmIgnore = false;
if (hasWorkspaceContext) {
try {
hasNpmIgnore = (await fs.promises.stat(path.join(resolvedPkgDir, '.npmignore'))).isFile();
}
catch (err) {
if (!util.types.isNativeError(err) || !('code' in err) || err.code !== 'ENOENT')
throw err;
}
}
const packlistOpts = hasWorkspaceContext && !hasNpmIgnore
? { prefix: workspaceDir, workspaces: [resolvedPkgDir] }
: undefined;
const files = await npmPacklist(tree, packlistOpts);
return files.map((file) => file.replace(/^\.[/\\]/, ''));
}
function buildRootTree(pkgDir, pkg) {
const bundledDeps = getRootBundledDeps(pkg);
// npm-packlist's gatherBundles() iterates package.bundleDependencies directly,
// so the field must be an array. Normalize true/undefined to an explicit list.
const normalizedPkg = normalizePackage(pkg);
normalizedPkg.bundleDependencies = bundledDeps;
delete normalizedPkg.bundledDependencies;
const root = makeNode(pkgDir, normalizedPkg, true);
const seen = new Map([[pkgDir, root]]);
populateEdges(root, bundledDeps, seen);
return root;
}
function buildBundledTree(pkgDir, seen) {
const cached = seen.get(pkgDir);
if (cached)
return cached;
const pkg = readPackageJson(pkgDir);
const node = makeNode(pkgDir, normalizePackage(pkg), false);
seen.set(pkgDir, node);
populateEdges(node, getNestedBundledDeps(pkg), seen);
return node;
}
function populateEdges(node, deps, seen) {
for (const dep of deps) {
const depDir = resolveDependency(dep, node.path);
if (!depDir)
continue;
const depNode = buildBundledTree(depDir, seen);
node.edgesOut.set(dep, { to: depNode, peer: false, dev: false });
}
}
function makeNode(pkgDir, pkg, isProjectRoot) {
const node = {
path: pkgDir,
package: pkg,
isProjectRoot,
isLink: false,
edgesOut: new Map(),
};
node.target = node;
return node;
}
function getRootBundledDeps(pkg) {
const bundle = pkg.bundleDependencies ?? pkg.bundledDependencies;
if (Array.isArray(bundle))
return bundle;
if (bundle === true) {
return Object.keys((pkg.dependencies ?? {}));
}
return [];
}
function getNestedBundledDeps(pkg) {
const dependencies = (pkg.dependencies ?? {});
const optionalDependencies = (pkg.optionalDependencies ?? {});
return [...Object.keys(dependencies), ...Object.keys(optionalDependencies)];
}
function resolveDependency(depName, fromDir) {
let currentDir = fromDir;
while (true) {
const candidate = path.join(currentDir, 'node_modules', depName);
try {
const stat = fs.statSync(path.join(candidate, 'package.json'));
if (stat.isFile())
return candidate;
}
catch (err) {
if (!util.types.isNativeError(err) || !('code' in err) || err.code !== 'ENOENT') {
throw err;
}
}
const parent = path.dirname(currentDir);
if (parent === currentDir)
return undefined;
currentDir = parent;
}
}
function readPackageJson(dir) {
try {
return JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
}
catch (err) {
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
return {};
}
throw err;
}
}
function stripDotSlash(p) {
return p.replace(/^\.[/\\]/, '');
}
function normalizePackage(pkg) {
const normalized = { ...pkg };
if (typeof normalized.main === 'string') {
normalized.main = stripDotSlash(normalized.main);
}
if (typeof normalized.browser === 'string') {
normalized.browser = stripDotSlash(normalized.browser);
}
if (typeof normalized.bin === 'string') {
normalized.bin = stripDotSlash(normalized.bin);
}
else if (normalized.bin != null && typeof normalized.bin === 'object') {
const bin = {};
for (const [key, value] of Object.entries(normalized.bin)) {
bin[key] = stripDotSlash(value);
}
normalized.bin = bin;
}
return normalized;
}
//# sourceMappingURL=index.js.map