UNPKG

@pnpm/fs.packlist

Version:

Get a list of the files to add from a directory into an npm package

138 lines 4.91 kB
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