From caf823e01e9eb5463b452b929a74dbc83ffc5df7 Mon Sep 17 00:00:00 2001 From: Daniel Stockman <5605+evocateur@users.noreply.github.com> Date: Wed, 18 Nov 2020 11:39:00 -0800 Subject: [PATCH] feat(publish): Remove figgy-pudding --- .../__tests__/get-npm-username.test.js | 13 ++----- .../get-two-factor-auth-required.test.js | 10 ++--- .../publish/__tests__/publish-command.test.js | 8 ++-- .../__tests__/publish-from-git.test.js | 2 - .../__tests__/publish-from-package.test.js | 2 - .../verify-npm-package-access.test.js | 20 +++++----- commands/publish/lib/fetch-config.js | 39 +++++++++++-------- commands/publish/lib/get-npm-username.js | 13 +++++-- commands/publish/lib/get-profile-data.js | 19 +++++++++ .../lib/get-two-factor-auth-required.js | 13 +++++-- commands/publish/lib/get-whoami.js | 10 +++++ .../publish/lib/verify-npm-package-access.js | 15 +++++-- commands/publish/package.json | 1 - package-lock.json | 2 - 14 files changed, 100 insertions(+), 67 deletions(-) diff --git a/commands/publish/__tests__/get-npm-username.test.js b/commands/publish/__tests__/get-npm-username.test.js index 551c5ae672..20eedb527d 100644 --- a/commands/publish/__tests__/get-npm-username.test.js +++ b/commands/publish/__tests__/get-npm-username.test.js @@ -8,8 +8,6 @@ const getNpmUsername = require("../lib/get-npm-username"); fetch.json.mockImplementation(() => Promise.resolve({ username: "lerna-test" })); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("getNpmUsername", () => { const origConsoleError = console.error; @@ -29,13 +27,12 @@ describe("getNpmUsername", () => { return Promise.reject(err); }); - const opts = new Map(); - opts.set("registry", "such-config-wow"); + const opts = { registry: "such-config-wow" }; const username = await getNpmUsername(opts); expect(username).toBe("lerna-test"); - expect(fetch.json).toHaveBeenLastCalledWith("/-/whoami", expect.figgyPudding({ "fetch-retries": 0 })); + expect(fetch.json).toHaveBeenLastCalledWith("/-/whoami", expect.objectContaining({ fetchRetries: 0 })); }); test("throws an error when successful fetch yields empty username", async () => { @@ -63,8 +60,7 @@ describe("getNpmUsername", () => { return Promise.reject(err); }); - const opts = new Map(); - opts.set("registry", "https://registry.npmjs.org/"); + const opts = { registry: "https://registry.npmjs.org/" }; await expect(getNpmUsername(opts)).rejects.toThrow( "Authentication error. Use `npm whoami` to troubleshoot." @@ -81,8 +77,7 @@ describe("getNpmUsername", () => { return Promise.reject(err); }); - const opts = new Map(); - opts.set("registry", "http://my-own-private-idaho.com"); + const opts = { registry: "http://my-own-private-idaho.com" }; const username = await getNpmUsername(opts); diff --git a/commands/publish/__tests__/get-two-factor-auth-required.test.js b/commands/publish/__tests__/get-two-factor-auth-required.test.js index bf7ab8c7bf..1e66a6803e 100644 --- a/commands/publish/__tests__/get-two-factor-auth-required.test.js +++ b/commands/publish/__tests__/get-two-factor-auth-required.test.js @@ -8,8 +8,6 @@ const getTwoFactorAuthRequired = require("../lib/get-two-factor-auth-required"); getProfileData.mockImplementation(() => Promise.resolve({ tfa: {} })); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("getTwoFactorAuthRequired", () => { const origConsoleError = console.error; @@ -30,7 +28,7 @@ describe("getTwoFactorAuthRequired", () => { const result = await getTwoFactorAuthRequired(); expect(result).toBe(true); - expect(getProfileData).toHaveBeenLastCalledWith(expect.figgyPudding({ "fetch-retries": 0 })); + expect(getProfileData).toHaveBeenLastCalledWith(expect.objectContaining({ fetchRetries: 0 })); }); it("resolves false if tfa.mode !== 'auth-and-writes'", async () => { @@ -80,14 +78,12 @@ describe("getTwoFactorAuthRequired", () => { return Promise.reject(err); }); - const opts = new Map([["registry", "such-registry-wow"]]); + const opts = { registry: "such-registry-wow" }; const result = await getTwoFactorAuthRequired(opts); expect(result).toBe(false); expect(loggingOutput("warn")).toContain( - `Registry "${opts.get( - "registry" - )}" does not support 'npm profile get', skipping two-factor auth check...` + `Registry "${opts.registry}" does not support 'npm profile get', skipping two-factor auth check...` ); }); diff --git a/commands/publish/__tests__/publish-command.test.js b/commands/publish/__tests__/publish-command.test.js index dbf05d8d7a..68d82e7c3f 100644 --- a/commands/publish/__tests__/publish-command.test.js +++ b/commands/publish/__tests__/publish-command.test.js @@ -39,8 +39,6 @@ const lernaPublish = require("@lerna-test/command-runner")(require("../command") gitCheckout.mockImplementation(() => Promise.resolve()); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("PublishCommand", () => { describe("cli validation", () => { let cwd; @@ -130,20 +128,20 @@ Map { expect(getNpmUsername).toHaveBeenCalled(); expect(getNpmUsername).toHaveBeenLastCalledWith( - expect.figgyPudding({ registry: "https://registry.npmjs.org/" }) + expect.objectContaining({ registry: "https://registry.npmjs.org/" }) ); expect(verifyNpmPackageAccess).toHaveBeenCalled(); expect(verifyNpmPackageAccess).toHaveBeenLastCalledWith( expect.any(Array), "lerna-test", - expect.figgyPudding({ registry: "https://registry.npmjs.org/" }) + expect.objectContaining({ registry: "https://registry.npmjs.org/" }) ); expect(getTwoFactorAuthRequired).toHaveBeenCalled(); expect(getTwoFactorAuthRequired).toHaveBeenLastCalledWith( // extra insurance that @lerna/npm-conf is defaulting things correctly - expect.figgyPudding({ otp: undefined }) + expect.objectContaining({ otp: undefined }) ); expect(gitCheckout).toHaveBeenCalledWith( diff --git a/commands/publish/__tests__/publish-from-git.test.js b/commands/publish/__tests__/publish-from-git.test.js index c237c5aaea..2e864e08f0 100644 --- a/commands/publish/__tests__/publish-from-git.test.js +++ b/commands/publish/__tests__/publish-from-git.test.js @@ -29,8 +29,6 @@ const initFixture = require("@lerna-test/init-fixture")(__dirname); // file under test const lernaPublish = require("@lerna-test/command-runner")(require("../command")); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("publish from-git", () => { it("publishes tagged packages", async () => { const cwd = await initFixture("normal"); diff --git a/commands/publish/__tests__/publish-from-package.test.js b/commands/publish/__tests__/publish-from-package.test.js index dd6f432137..4ce3edc04c 100644 --- a/commands/publish/__tests__/publish-from-package.test.js +++ b/commands/publish/__tests__/publish-from-package.test.js @@ -30,8 +30,6 @@ const initFixture = require("@lerna-test/init-fixture")(__dirname); // file under test const lernaPublish = require("@lerna-test/command-runner")(require("../command")); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("publish from-package", () => { it("publishes unpublished packages", async () => { const cwd = await initFixture("normal"); diff --git a/commands/publish/__tests__/verify-npm-package-access.test.js b/commands/publish/__tests__/verify-npm-package-access.test.js index f4328819e8..dcbd4cb3a3 100644 --- a/commands/publish/__tests__/verify-npm-package-access.test.js +++ b/commands/publish/__tests__/verify-npm-package-access.test.js @@ -15,8 +15,6 @@ access.lsPackages.mockImplementation(() => }) ); -expect.extend(require("@lerna-test/figgy-pudding-matchers")); - describe("verifyNpmPackageAccess", () => { const origConsoleError = console.error; @@ -36,22 +34,22 @@ describe("verifyNpmPackageAccess", () => { test("validates that all packages have read-write permission", async () => { const packages = await getPackages(cwd); - const opts = new Map().set("registry", "https://registry.npmjs.org/"); + const opts = { registry: "https://registry.npmjs.org/" }; await verifyNpmPackageAccess(packages, "lerna-test", opts); expect(access.lsPackages).toHaveBeenLastCalledWith( "lerna-test", - expect.figgyPudding({ + expect.objectContaining({ registry: "https://registry.npmjs.org/", - "fetch-retries": 0, + fetchRetries: 0, }) ); }); test("allows unpublished packages to pass", async () => { const packages = await getPackages(cwd); - const opts = new Map().set("registry", "https://registry.npmjs.org/"); + const opts = { registry: "https://registry.npmjs.org/" }; access.lsPackages.mockImplementationOnce(() => Promise.resolve({ @@ -68,7 +66,7 @@ describe("verifyNpmPackageAccess", () => { test("allows null result to pass with warning", async () => { const packages = await getPackages(cwd); - const opts = new Map().set("registry", "https://registry.npmjs.org/"); + const opts = { registry: "https://registry.npmjs.org/" }; access.lsPackages.mockImplementationOnce(() => // access.lsPackages() returns null when _no_ results returned @@ -85,7 +83,7 @@ describe("verifyNpmPackageAccess", () => { test("throws EACCESS when any package does not have read-write permission", async () => { const packages = await getPackages(cwd); - const opts = new Map().set("registry", "https://registry.npmjs.org/"); + const opts = { registry: "https://registry.npmjs.org/" }; access.lsPackages.mockImplementationOnce(() => Promise.resolve({ @@ -102,7 +100,7 @@ describe("verifyNpmPackageAccess", () => { test("passes when npm Enterprise registry returns E500", async () => { const packages = await getPackages(cwd); const registry = "http://outdated-npm-enterprise.mycompany.com:12345/"; - const opts = new Map().set("registry", registry); + const opts = { registry }; access.lsPackages.mockImplementationOnce(() => { const err = new Error("npm-enterprise-what"); @@ -122,7 +120,7 @@ describe("verifyNpmPackageAccess", () => { test("passes when Artifactory registry returns E404", async () => { const packages = await getPackages(cwd); const registry = "https://artifactory-partial-implementation.corpnet.mycompany.com/"; - const opts = new Map().set("registry", registry); + const opts = { registry }; access.lsPackages.mockImplementationOnce(() => { const err = new Error("artifactory-why"); @@ -141,7 +139,7 @@ describe("verifyNpmPackageAccess", () => { test("logs unexpected failure message before throwing EWHOAMI", async () => { const packages = await getPackages(cwd); - const opts = new Map(); + const opts = {}; access.lsPackages.mockImplementationOnce(() => { const err = new Error("gonna-need-a-bigger-boat"); diff --git a/commands/publish/lib/fetch-config.js b/commands/publish/lib/fetch-config.js index c8df461b19..b24f258218 100644 --- a/commands/publish/lib/fetch-config.js +++ b/commands/publish/lib/fetch-config.js @@ -1,20 +1,27 @@ "use strict"; const log = require("npmlog"); -const figgyPudding = require("figgy-pudding"); -module.exports = figgyPudding( - { - "fetch-retries": {}, - fetchRetries: "fetch-retries", - log: { default: log }, - registry: {}, - username: {}, - }, - { - other() { - // open it up for the sake of tests - return true; - }, - } -); +module.exports.getFetchConfig = getFetchConfig; + +/** + * Create a merged options object suitable for npm-registry-fetch. + * @param {{ [key: string]: unknown }} options + * @param {Partial} [extra] + * @returns {FetchConfig} + */ +function getFetchConfig(options, extra) { + return { + log, + ...options, + ...extra, + }; +} + +/** + * @typedef {object} FetchConfig + * @property {number} [fetchRetries] + * @property {typeof log} log + * @property {string} [registry] + * @property {string} [username] + */ diff --git a/commands/publish/lib/get-npm-username.js b/commands/publish/lib/get-npm-username.js index 280f029461..dec3106bf8 100644 --- a/commands/publish/lib/get-npm-username.js +++ b/commands/publish/lib/get-npm-username.js @@ -1,16 +1,21 @@ "use strict"; const ValidationError = require("@lerna/validation-error"); -const FetchConfig = require("./fetch-config"); +const { getFetchConfig } = require("./fetch-config"); const getProfileData = require("./get-profile-data"); const getWhoAmI = require("./get-whoami"); module.exports = getNpmUsername; -function getNpmUsername(_opts) { - const opts = FetchConfig(_opts, { +/** + * Retrieve username of logged-in user. + * @param {import("./fetch-config").FetchConfig} options + * @returns {Promise} + */ +function getNpmUsername(options) { + const opts = getFetchConfig(options, { // don't wait forever for third-party failures to be dealt with - "fetch-retries": 0, + fetchRetries: 0, }); opts.log.info("", "Verifying npm credentials"); diff --git a/commands/publish/lib/get-profile-data.js b/commands/publish/lib/get-profile-data.js index 901f29c5e7..9e6c12ea60 100644 --- a/commands/publish/lib/get-profile-data.js +++ b/commands/publish/lib/get-profile-data.js @@ -5,6 +5,11 @@ const pulseTillDone = require("@lerna/pulse-till-done"); module.exports = getProfileData; +/** + * Retrieve profile data of logged-in user. + * @param {import("./fetch-config").FetchConfig} opts + * @returns {Promise} + */ function getProfileData(opts) { opts.log.verbose("", "Retrieving npm user profile"); @@ -18,3 +23,17 @@ function getProfileData(opts) { ); }); } + +/** + * @typedef {object} ProfileData + * @property {{ pending: boolean; mode: 'auth-and-writes' | 'auth-only' }} tfa + * @property {string} name + * @property {string} username legacy field alias of `name` + * @property {string} email + * @property {boolean} email_verified + * @property {string} created + * @property {string} updated + * @property {string} [fullname] + * @property {string} [twitter] + * @property {string} [github] + */ diff --git a/commands/publish/lib/get-two-factor-auth-required.js b/commands/publish/lib/get-two-factor-auth-required.js index 2e795778c1..c8d1c51139 100644 --- a/commands/publish/lib/get-two-factor-auth-required.js +++ b/commands/publish/lib/get-two-factor-auth-required.js @@ -1,15 +1,20 @@ "use strict"; const ValidationError = require("@lerna/validation-error"); -const FetchConfig = require("./fetch-config"); +const { getFetchConfig } = require("./fetch-config"); const getProfileData = require("./get-profile-data"); module.exports = getTwoFactorAuthRequired; -function getTwoFactorAuthRequired(_opts) { - const opts = FetchConfig(_opts, { +/** + * Determine if the logged-in user has enabled two-factor auth. + * @param {import("./fetch-config").FetchConfig} options + * @returns {Promise} + */ +function getTwoFactorAuthRequired(options) { + const opts = getFetchConfig(options, { // don't wait forever for third-party failures to be dealt with - "fetch-retries": 0, + fetchRetries: 0, }); opts.log.info("", "Checking two-factor auth mode"); diff --git a/commands/publish/lib/get-whoami.js b/commands/publish/lib/get-whoami.js index f976a38165..d7b79e58dd 100644 --- a/commands/publish/lib/get-whoami.js +++ b/commands/publish/lib/get-whoami.js @@ -5,6 +5,11 @@ const pulseTillDone = require("@lerna/pulse-till-done"); module.exports = getWhoAmI; +/** + * Retrieve logged-in user's username via legacy API. + * @param {import("./fetch-config").FetchConfig} opts + * @returns {WhoIAm} + */ function getWhoAmI(opts) { opts.log.verbose("", "Retrieving npm username"); @@ -15,3 +20,8 @@ function getWhoAmI(opts) { return data; }); } + +/** + * @typedef {object} WhoIAm + * @property {string} username + */ diff --git a/commands/publish/lib/verify-npm-package-access.js b/commands/publish/lib/verify-npm-package-access.js index b90bb81c4d..b733052c31 100644 --- a/commands/publish/lib/verify-npm-package-access.js +++ b/commands/publish/lib/verify-npm-package-access.js @@ -3,14 +3,21 @@ const access = require("libnpmaccess"); const pulseTillDone = require("@lerna/pulse-till-done"); const ValidationError = require("@lerna/validation-error"); -const FetchConfig = require("./fetch-config"); +const { getFetchConfig } = require("./fetch-config"); module.exports = verifyNpmPackageAccess; -function verifyNpmPackageAccess(packages, username, _opts) { - const opts = FetchConfig(_opts, { +/** + * Throw an error if the logged-in user does not have read-write access to all packages. + * @param {{ name: string; }[]} packages + * @param {string} username + * @param {import("./fetch-config").FetchConfig} options + * @returns {Promise} + */ +function verifyNpmPackageAccess(packages, username, options) { + const opts = getFetchConfig(options, { // don't wait forever for third-party failures to be dealt with - "fetch-retries": 0, + fetchRetries: 0, }); opts.log.silly("verifyNpmPackageAccess"); diff --git a/commands/publish/package.json b/commands/publish/package.json index f379fa70a9..4558440b4f 100644 --- a/commands/publish/package.json +++ b/commands/publish/package.json @@ -53,7 +53,6 @@ "@lerna/run-topologically": "file:../../utils/run-topologically", "@lerna/validation-error": "file:../../core/validation-error", "@lerna/version": "file:../version", - "figgy-pudding": "^3.5.1", "fs-extra": "^9.0.1", "libnpmaccess": "^4.0.1", "npm-package-arg": "^8.1.0", diff --git a/package-lock.json b/package-lock.json index d9cf707e19..9e47e766d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -386,7 +386,6 @@ "@lerna/run-topologically": "file:../../utils/run-topologically", "@lerna/validation-error": "file:../../core/validation-error", "@lerna/version": "file:../version", - "figgy-pudding": "^3.5.1", "fs-extra": "^9.0.1", "libnpmaccess": "^4.0.1", "npm-package-arg": "^8.1.0", @@ -15544,7 +15543,6 @@ "@lerna/run-topologically": "file:../../utils/run-topologically", "@lerna/validation-error": "file:../../core/validation-error", "@lerna/version": "file:../version", - "figgy-pudding": "^3.5.1", "fs-extra": "^9.0.1", "libnpmaccess": "^4.0.1", "npm-package-arg": "^8.1.0",