UNPKG

@pnpm/plugin-commands-publishing

Version:
316 lines 14 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.commandNames = void 0; exports.rcOptionsTypes = rcOptionsTypes; exports.cliOptionsTypes = cliOptionsTypes; exports.help = help; exports.handler = handler; exports.api = api; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const zlib_1 = require("zlib"); const error_1 = require("@pnpm/error"); const config_1 = require("@pnpm/config"); const cli_utils_1 = require("@pnpm/cli-utils"); const exportable_manifest_1 = require("@pnpm/exportable-manifest"); const fs_packlist_1 = require("@pnpm/fs.packlist"); const package_bins_1 = require("@pnpm/package-bins"); const tinyglobby_1 = require("tinyglobby"); const pick_1 = __importDefault(require("ramda/src/pick")); const realpath_missing_1 = __importDefault(require("realpath-missing")); const render_help_1 = __importDefault(require("render-help")); const tar_stream_1 = __importDefault(require("tar-stream")); const publish_js_1 = require("./publish.js"); const chalk_1 = __importDefault(require("chalk")); const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name")); const p_limit_1 = __importDefault(require("p-limit")); const common_cli_options_help_1 = require("@pnpm/common-cli-options-help"); const sort_packages_1 = require("@pnpm/sort-packages"); const logger_1 = require("@pnpm/logger"); const LICENSE_GLOB = 'LICEN{S,C}E{,.*}'; // cspell:disable-line function rcOptionsTypes() { return { ...cliOptionsTypes(), ...(0, pick_1.default)([ 'npm-path', ], config_1.types), }; } function cliOptionsTypes() { return { out: String, recursive: Boolean, ...(0, pick_1.default)([ 'dry-run', 'pack-destination', 'pack-gzip-level', 'json', 'workspace-concurrency', ], config_1.types), }; } exports.commandNames = ['pack']; function help() { return (0, render_help_1.default)({ description: 'Create a tarball from a package', usages: ['pnpm pack'], descriptionLists: [ { title: 'Options', list: [ { description: 'Does everything `pnpm pack` would do except actually writing the tarball to disk.', name: '--dry-run', }, { description: 'Directory in which `pnpm pack` will save tarballs. The default is the current working directory.', name: '--pack-destination <dir>', }, { description: 'Prints the packed tarball and contents in the json format.', name: '--json', }, { description: 'Customizes the output path for the tarball. Use `%s` and `%v` to include the package name and version, e.g., `%s.tgz` or `some-dir/%s-%v.tgz`. By default, the tarball is saved in the current working directory with the name `<package-name>-<version>.tgz`.', name: '--out <path>', }, { description: 'Pack all packages from the workspace', name: '--recursive', shortAlias: '-r', }, { description: `Set the maximum number of concurrency. Default is ${(0, config_1.getDefaultWorkspaceConcurrency)()}. For unlimited concurrency use Infinity.`, name: '--workspace-concurrency <number>', }, ], }, common_cli_options_help_1.FILTERING, ], }); } async function handler(opts) { const packedPackages = []; if (opts.recursive) { const selectedProjectsGraph = opts.selectedProjectsGraph; const pkgsToPack = []; for (const { package: pkg } of Object.values(selectedProjectsGraph)) { if (pkg.manifest.name && pkg.manifest.version) { pkgsToPack.push(pkg); } } const packedPkgDirs = new Set(pkgsToPack.map(({ rootDir }) => rootDir)); if (packedPkgDirs.size === 0) { logger_1.logger.info({ message: 'There are no packages that should be packed', prefix: opts.dir, }); } const chunks = (0, sort_packages_1.sortPackages)(selectedProjectsGraph); const limitPack = (0, p_limit_1.default)((0, config_1.getWorkspaceConcurrency)(opts.workspaceConcurrency)); const resolvedOpts = { ...opts }; if (opts.out) { resolvedOpts.out = path_1.default.resolve(opts.dir, opts.out); } else if (opts.packDestination) { resolvedOpts.packDestination = path_1.default.resolve(opts.dir, opts.packDestination); } else { resolvedOpts.packDestination = path_1.default.resolve(opts.dir); } for (const chunk of chunks) { // eslint-disable-next-line no-await-in-loop await Promise.all(chunk.map(pkgDir => limitPack(async () => { if (!packedPkgDirs.has(pkgDir)) return; const pkg = selectedProjectsGraph[pkgDir].package; const packResult = await api({ ...resolvedOpts, dir: pkg.rootDir, }); packedPackages.push(toPackResultJson(packResult)); }))); } } else { const packResult = await api(opts); packedPackages.push(toPackResultJson(packResult)); } if (opts.json) { return JSON.stringify(packedPackages.length > 1 ? packedPackages : packedPackages[0], null, 2); } return packedPackages.map(({ name, version, filename, files }) => `${opts.unicode ? '📦 ' : 'package:'} ${name}@${version} ${chalk_1.default.blueBright('Tarball Contents')} ${files.map(({ path }) => path).join('\n')} ${chalk_1.default.blueBright('Tarball Details')} ${filename}`).join('\n\n'); } async function api(opts) { const { manifest: entryManifest, fileName: manifestFileName } = await (0, cli_utils_1.readProjectManifest)(opts.dir, opts); preventBundledDependenciesWithoutHoistedNodeLinker(opts.nodeLinker, entryManifest); const _runScriptsIfPresent = publish_js_1.runScriptsIfPresent.bind(null, { depPath: opts.dir, extraBinPaths: opts.extraBinPaths, extraEnv: opts.extraEnv, pkgRoot: opts.dir, rawConfig: opts.rawConfig, rootModulesDir: await (0, realpath_missing_1.default)(path_1.default.join(opts.dir, 'node_modules')), stdio: 'inherit', unsafePerm: true, // when running scripts explicitly, assume that they're trusted. }); if (!opts.ignoreScripts) { await _runScriptsIfPresent([ 'prepack', 'prepare', ], entryManifest); } const dir = entryManifest.publishConfig?.directory ? path_1.default.join(opts.dir, entryManifest.publishConfig.directory) : opts.dir; // always read the latest manifest, as "prepack" or "prepare" script may modify package manifest. const { manifest } = await (0, cli_utils_1.readProjectManifest)(dir, opts); preventBundledDependenciesWithoutHoistedNodeLinker(opts.nodeLinker, manifest); if (!manifest.name) { throw new error_1.PnpmError('PACKAGE_NAME_NOT_FOUND', `Package name is not defined in the ${manifestFileName}.`); } if (!(0, validate_npm_package_name_1.default)(manifest.name).validForOldPackages) { throw new error_1.PnpmError('INVALID_PACKAGE_NAME', `Invalid package name "${manifest.name}".`); } if (!manifest.version) { throw new error_1.PnpmError('PACKAGE_VERSION_NOT_FOUND', `Package version is not defined in the ${manifestFileName}.`); } let tarballName; let packDestination; const normalizedName = manifest.name.replace('@', '').replace('/', '-'); if (opts.out) { if (opts.packDestination) { throw new error_1.PnpmError('INVALID_OPTION', 'Cannot use --pack-destination and --out together'); } const preparedOut = opts.out.replaceAll('%s', normalizedName).replaceAll('%v', manifest.version); const parsedOut = path_1.default.parse(preparedOut); packDestination = parsedOut.dir ? parsedOut.dir : opts.packDestination; tarballName = parsedOut.base; } else { tarballName = `${normalizedName}-${manifest.version}.tgz`; packDestination = opts.packDestination; } const publishManifest = await createPublishManifest({ projectDir: dir, modulesDir: path_1.default.join(opts.dir, 'node_modules'), manifest, embedReadme: opts.embedReadme, catalogs: opts.catalogs ?? {}, hooks: opts.hooks, }); const files = await (0, fs_packlist_1.packlist)(dir, { packageJsonCache: { [path_1.default.join(dir, 'package.json')]: publishManifest, }, }); const filesMap = Object.fromEntries(files.map((file) => [`package/${file}`, path_1.default.join(dir, file)])); // cspell:disable-next-line if (opts.workspaceDir != null && dir !== opts.workspaceDir && !files.some((file) => /LICEN[CS]E(?:\..+)?/i.test(file))) { const licenses = await (0, tinyglobby_1.glob)([LICENSE_GLOB], { cwd: opts.workspaceDir, expandDirectories: false }); for (const license of licenses) { filesMap[`package/${license}`] = path_1.default.join(opts.workspaceDir, license); } } const destDir = packDestination ? (path_1.default.isAbsolute(packDestination) ? packDestination : path_1.default.join(dir, packDestination ?? '.')) : dir; if (!opts.dryRun) { await fs_1.default.promises.mkdir(destDir, { recursive: true }); await packPkg({ destFile: path_1.default.join(destDir, tarballName), filesMap, modulesDir: path_1.default.join(opts.dir, 'node_modules'), packGzipLevel: opts.packGzipLevel, manifest: publishManifest, bins: [ ...(await (0, package_bins_1.getBinsFromPackageManifest)(publishManifest, dir)).map(({ path }) => path), ...(manifest.publishConfig?.executableFiles ?? []) .map((executableFile) => path_1.default.join(dir, executableFile)), ], }); if (!opts.ignoreScripts) { await _runScriptsIfPresent(['postpack'], entryManifest); } } let packedTarballPath; if (opts.dir !== destDir) { packedTarballPath = path_1.default.join(destDir, tarballName); } else { packedTarballPath = path_1.default.relative(opts.dir, path_1.default.join(dir, tarballName)); } const packedContents = files.sort((a, b) => a.localeCompare(b, 'en')); return { publishedManifest: publishManifest, contents: packedContents, tarballPath: packedTarballPath, }; } function preventBundledDependenciesWithoutHoistedNodeLinker(nodeLinker, manifest) { if (nodeLinker === 'hoisted') return; for (const key of ['bundledDependencies', 'bundleDependencies']) { const bundledDependencies = manifest[key]; if (bundledDependencies) { throw new error_1.PnpmError('BUNDLED_DEPENDENCIES_WITHOUT_HOISTED', `${key} does not work with "nodeLinker: ${nodeLinker}"`, { hint: `Add "nodeLinker: hoisted" to pnpm-workspace.yaml or delete ${key} from the root package.json to resolve this error`, }); } } } async function readReadmeFile(projectDir) { const files = await fs_1.default.promises.readdir(projectDir); const readmePath = files.find(name => /readme\.md$/i.test(name)); const readmeFile = readmePath ? await fs_1.default.promises.readFile(path_1.default.join(projectDir, readmePath), 'utf8') : undefined; return readmeFile; } async function packPkg(opts) { const { destFile, filesMap, bins, manifest, } = opts; const mtime = new Date('1985-10-26T08:15:00.000Z'); const pack = tar_stream_1.default.pack(); await Promise.all(Object.entries(filesMap).map(async ([name, source]) => { const isExecutable = bins.some((bin) => path_1.default.relative(bin, source) === ''); const mode = isExecutable ? 0o755 : 0o644; if (/^package\/package\.(?:json|json5|yaml)$/.test(name)) { pack.entry({ mode, mtime, name: 'package/package.json' }, JSON.stringify(manifest, null, 2)); return; } pack.entry({ mode, mtime, name }, fs_1.default.readFileSync(source)); })); const tarball = fs_1.default.createWriteStream(destFile); pack.pipe((0, zlib_1.createGzip)({ level: opts.packGzipLevel })).pipe(tarball); pack.finalize(); return new Promise((resolve, reject) => { tarball.on('close', () => { resolve(); }).on('error', reject); }); } async function createPublishManifest(opts) { const { projectDir, embedReadme, modulesDir, manifest, catalogs, hooks } = opts; const readmeFile = embedReadme ? await readReadmeFile(projectDir) : undefined; return (0, exportable_manifest_1.createExportableManifest)(projectDir, manifest, { catalogs, hooks, readmeFile, modulesDir, }); } function toPackResultJson(packResult) { const { publishedManifest, contents, tarballPath } = packResult; return { name: publishedManifest.name, version: publishedManifest.version, filename: tarballPath, files: contents.map((file) => ({ path: file })), }; } //# sourceMappingURL=pack.js.map