From 0cff2b4680710ad78bb8abe1cb431dd1b06d6284 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Thu, 29 Oct 2020 10:38:43 -0400 Subject: [PATCH] Prefer local install (#572) --- package.json | 1 + readme.md | 2 +- source/cli-implementation.js | 147 +++++++++++++++++++++++++++++++++ source/cli.js | 152 +++-------------------------------- 4 files changed, 160 insertions(+), 142 deletions(-) create mode 100755 source/cli-implementation.js diff --git a/package.json b/package.json index f2ca454d..56fd5fbe 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "github-url-from-git": "^1.5.0", "has-yarn": "^2.1.0", "hosted-git-info": "^3.0.0", + "import-local": "^3.0.2", "inquirer": "^7.0.0", "is-installed-globally": "^0.3.1", "is-scoped": "^2.1.0", diff --git a/readme.md b/readme.md index 2f4fc658..73e723b9 100644 --- a/readme.md +++ b/readme.md @@ -80,7 +80,7 @@ Run `np` without arguments to launch the interactive UI that guides you through ## Config -`np` can be configured both locally and globally. When using the global `np` binary, you can configure any of the CLI flags in either a `.np-config.js` or `.np-config.json` file in the home directory. When using the local `np` binary, for example, in a `npm run` script, you can configure `np` by setting the flags in either a top-level `np` field in `package.json` or in a `.np-config.js` or `.np-config.json` file in the project directory. +`np` can be configured both locally and globally. When using the global `np` binary, you can configure any of the CLI flags in either a `.np-config.js` or `.np-config.json` file in the home directory. When using the local `np` binary, for example, in a `npm run` script, you can configure `np` by setting the flags in either a top-level `np` field in `package.json` or in a `.np-config.js` or `.np-config.json` file in the project directory. If it exists, the local installation will always take precedence. This ensures any local config matches the version of `np` it was designed for. Currently, these are the flags you can configure: diff --git a/source/cli-implementation.js b/source/cli-implementation.js new file mode 100755 index 00000000..eb9e0190 --- /dev/null +++ b/source/cli-implementation.js @@ -0,0 +1,147 @@ +#!/usr/bin/env node +'use strict'; +// eslint-disable-next-line import/no-unassigned-import +require('symbol-observable'); // Important: This needs to be first to prevent weird Observable incompatibilities +const logSymbols = require('log-symbols'); +const meow = require('meow'); +const updateNotifier = require('update-notifier'); +const hasYarn = require('has-yarn'); +const config = require('./config'); +const {isPackageNameAvailable} = require('./npm/util'); +const version = require('./version'); +const util = require('./util'); +const ui = require('./ui'); +const np = require('.'); + +const cli = meow(` + Usage + $ np + + Version can be: + ${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3 + + Options + --any-branch Allow publishing from any branch + --branch Name of the release branch (default: master) + --no-cleanup Skips cleanup of node_modules + --no-tests Skips tests + --yolo Skips cleanup and testing + --no-publish Skips publishing + --preview Show tasks without actually executing them + --tag Publish under a given dist-tag + --no-yarn Don't use Yarn + --contents Subdirectory to publish + --no-release-draft Skips opening a GitHub release draft + --test-script Name of npm run script to run tests before publishing (default: test) + --no-2fa Don't enable 2FA on new packages (not recommended) + + Examples + $ np + $ np patch + $ np 1.0.2 + $ np 1.0.2-beta.3 --tag=beta + $ np 1.0.2-beta.3 --tag=beta --contents=dist +`, { + booleanDefault: undefined, + flags: { + anyBranch: { + type: 'boolean' + }, + branch: { + type: 'string' + }, + cleanup: { + type: 'boolean' + }, + tests: { + type: 'boolean' + }, + yolo: { + type: 'boolean' + }, + publish: { + type: 'boolean' + }, + releaseDraft: { + type: 'boolean' + }, + tag: { + type: 'string' + }, + yarn: { + type: 'boolean' + }, + contents: { + type: 'string' + }, + preview: { + type: 'boolean' + }, + testScript: { + type: 'string' + }, + '2fa': { + type: 'boolean' + } + } +}); + +updateNotifier({pkg: cli.pkg}).notify(); + +(async () => { + const pkg = util.readPkg(); + + const defaultFlags = { + cleanup: true, + tests: true, + publish: true, + releaseDraft: true, + yarn: hasYarn(), + '2fa': true + }; + + const localConfig = await config(); + + const flags = { + ...defaultFlags, + ...localConfig, + ...cli.flags + }; + + // Workaround for unintended auto-casing behavior from `meow`. + if ('2Fa' in flags) { + flags['2fa'] = flags['2Fa']; + } + + const runPublish = flags.publish && !pkg.private; + + const availability = flags.publish ? await isPackageNameAvailable(pkg) : { + isAvailable: false, + isUnknown: false + }; + + const version = cli.input.length > 0 ? cli.input[0] : false; + + const options = await ui({ + ...flags, + availability, + version, + runPublish + }, pkg); + + if (!options.confirm) { + process.exit(0); + } + + console.log(); // Prints a newline for readability + const newPkg = await np(options.version, options); + + if (options.preview) { + return; + } + + console.log(`\n ${newPkg.name} ${newPkg.version} published 🎉`); +})().catch(error => { + console.error(`\n${logSymbols.error} ${error.message}`); + process.exit(1); +}); diff --git a/source/cli.js b/source/cli.js index eb9e0190..3bab28fc 100755 --- a/source/cli.js +++ b/source/cli.js @@ -1,147 +1,17 @@ #!/usr/bin/env node 'use strict'; -// eslint-disable-next-line import/no-unassigned-import -require('symbol-observable'); // Important: This needs to be first to prevent weird Observable incompatibilities -const logSymbols = require('log-symbols'); -const meow = require('meow'); -const updateNotifier = require('update-notifier'); -const hasYarn = require('has-yarn'); -const config = require('./config'); -const {isPackageNameAvailable} = require('./npm/util'); -const version = require('./version'); -const util = require('./util'); -const ui = require('./ui'); -const np = require('.'); +const util = require('util'); +const importLocal = require('import-local'); +const isInstalledGlobally = require('is-installed-globally'); -const cli = meow(` - Usage - $ np +const debuglog = util.debuglog('np'); - Version can be: - ${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3 - - Options - --any-branch Allow publishing from any branch - --branch Name of the release branch (default: master) - --no-cleanup Skips cleanup of node_modules - --no-tests Skips tests - --yolo Skips cleanup and testing - --no-publish Skips publishing - --preview Show tasks without actually executing them - --tag Publish under a given dist-tag - --no-yarn Don't use Yarn - --contents Subdirectory to publish - --no-release-draft Skips opening a GitHub release draft - --test-script Name of npm run script to run tests before publishing (default: test) - --no-2fa Don't enable 2FA on new packages (not recommended) - - Examples - $ np - $ np patch - $ np 1.0.2 - $ np 1.0.2-beta.3 --tag=beta - $ np 1.0.2-beta.3 --tag=beta --contents=dist -`, { - booleanDefault: undefined, - flags: { - anyBranch: { - type: 'boolean' - }, - branch: { - type: 'string' - }, - cleanup: { - type: 'boolean' - }, - tests: { - type: 'boolean' - }, - yolo: { - type: 'boolean' - }, - publish: { - type: 'boolean' - }, - releaseDraft: { - type: 'boolean' - }, - tag: { - type: 'string' - }, - yarn: { - type: 'boolean' - }, - contents: { - type: 'string' - }, - preview: { - type: 'boolean' - }, - testScript: { - type: 'string' - }, - '2fa': { - type: 'boolean' - } - } -}); - -updateNotifier({pkg: cli.pkg}).notify(); - -(async () => { - const pkg = util.readPkg(); - - const defaultFlags = { - cleanup: true, - tests: true, - publish: true, - releaseDraft: true, - yarn: hasYarn(), - '2fa': true - }; - - const localConfig = await config(); - - const flags = { - ...defaultFlags, - ...localConfig, - ...cli.flags - }; - - // Workaround for unintended auto-casing behavior from `meow`. - if ('2Fa' in flags) { - flags['2fa'] = flags['2Fa']; - } - - const runPublish = flags.publish && !pkg.private; - - const availability = flags.publish ? await isPackageNameAvailable(pkg) : { - isAvailable: false, - isUnknown: false - }; - - const version = cli.input.length > 0 ? cli.input[0] : false; - - const options = await ui({ - ...flags, - availability, - version, - runPublish - }, pkg); - - if (!options.confirm) { - process.exit(0); - } - - console.log(); // Prints a newline for readability - const newPkg = await np(options.version, options); - - if (options.preview) { - return; +// Prefer the local installation +if (!importLocal(__filename)) { + if (isInstalledGlobally) { + debuglog('Using global install of np.'); } - console.log(`\n ${newPkg.name} ${newPkg.version} published 🎉`); -})().catch(error => { - console.error(`\n${logSymbols.error} ${error.message}`); - process.exit(1); -}); + // eslint-disable-next-line import/no-unassigned-import + require('./cli-implementation'); +}