diff --git a/commands/publish/command.js b/commands/publish/command.js index 27634a54b7c..510312867ab 100644 --- a/commands/publish/command.js +++ b/commands/publish/command.js @@ -80,7 +80,7 @@ exports.handler = function handler(argv) { }; function composeVersionOptions(yargs) { - versionCommand.addBumpPositional(yargs, ["from-git"]); + versionCommand.addBumpPositional(yargs, ["from-git", "from-package"]); versionCommand.builder(yargs, "publish"); return yargs; diff --git a/commands/publish/index.js b/commands/publish/index.js index bde992529b3..848e7031e16 100644 --- a/commands/publish/index.js +++ b/commands/publish/index.js @@ -25,6 +25,7 @@ const versionCommand = require("@lerna/version"); const createTempLicenses = require("./lib/create-temp-licenses"); const getCurrentSHA = require("./lib/get-current-sha"); const getCurrentTags = require("./lib/get-current-tags"); +const getUnpublishedPackages = require("./lib/get-unpublished-packages"); const getNpmUsername = require("./lib/get-npm-username"); const getTaggedPackages = require("./lib/get-tagged-packages"); const getPackagesWithoutLicense = require("./lib/get-packages-without-license"); @@ -113,8 +114,9 @@ class PublishCommand extends Command { : [this.packagesToPublish]; if (result.needsConfirmation) { - // only confirm for --canary or bump === "from-git", - // as VersionCommand has its own confirmation prompt + // only confirm for --canary, bump === "from-git", + // or bump === "from-package", as VersionCommand + // has its own confirmation prompt return this.confirmPublish(); } @@ -135,6 +137,11 @@ class PublishCommand extends Command { chain = chain.then(() => this.updateCanaryVersions()); } + // TODO need to create git tags + // if (this.options.bump === "from-package") { + + // } + chain = chain.then(() => this.resolveLocalDependencyLinks()); chain = chain.then(() => this.annotateGitHead()); chain = chain.then(() => this.packUpdated()); @@ -161,6 +168,8 @@ class PublishCommand extends Command { if (this.options.bump === "from-git") { chain = chain.then(() => this.detectFromGit()); + } else if (this.options.bump === "from-package") { + chain = chain.then(() => this.detectFromPackage()); } else if (this.options.canary) { chain = chain.then(() => this.detectCanaryVersions()); } else { @@ -206,6 +215,32 @@ class PublishCommand extends Command { }); } + detectFromPackage() { + let chain = Promise.resolve(); + + // attempting to publish a release with local changes is not allowed + chain = chain.then(() => this.verifyWorkingTreeClean()); + + chain = chain.then(() => getUnpublishedPackages(this.project, this.conf)); + chain = chain.then(unpublishedPackages => { + if (!unpublishedPackages.length) { + this.logger.notice("from-package", "No unpublished release found"); + } + + return unpublishedPackages; + }); + + return chain.then(updates => { + const updatesVersions = updates.map(({ pkg }) => [pkg.name, pkg.version]); + + return { + updates, + updatesVersions, + needsConfirmation: true, + }; + }); + } + detectCanaryVersions() { const { bump = "prepatch", diff --git a/commands/publish/lib/get-unpublished-packages.js b/commands/publish/lib/get-unpublished-packages.js new file mode 100644 index 00000000000..5898a3b34af --- /dev/null +++ b/commands/publish/lib/get-unpublished-packages.js @@ -0,0 +1,41 @@ +"use strict"; + +const fetch = require("npm-registry-fetch"); +const log = require("npmlog"); +const pReduce = require("p-reduce"); + +module.exports = getUnpublishedPackages; + +// Only the abbreviated package metadata is needed to +// determine which versions have been published. This +// saves transfer time for packages with a lot of +// history. +const registryOptions = { + Accept: "application/vnd.npm.install-v1+json", +}; + +function getUnpublishedPackages(project, opts) { + log.silly("getPackageVersions"); + + let chain = Promise.resolve(); + + const mapper = (unpublished, pkg) => + fetch(`-/${pkg.name}`, Object.assign({}, opts, registryOptions)).then( + packument => { + if (packument.versions[pkg.version] === undefined) { + unpublished.push(pkg); + } + + return unpublished; + }, + () => { + log.warn("", "Unable to determine published versions, assuming unpublished."); + return unpublished.concat([pkg]); + } + ); + + chain = chain.then(() => project.getPackages()); + chain = chain.then(packages => pReduce(packages, mapper, [])); + + return chain; +}