From 008a83642e04360e461f56da74b5557d5248a726 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Wed, 18 Apr 2018 14:01:24 -0700 Subject: [PATCH] init: use npx for extended initializers (#20303) PR-URL: https://github.com/npm/npm/pull/20303 Credit: @jdalton Reviewed-By: @zkat --- doc/cli/npm-init.md | 35 ++++++++++++++++------- lib/config/cmd-list.js | 2 ++ lib/init.js | 24 +++++++++++++++- test/tap/init-create.js | 61 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 test/tap/init-create.js diff --git a/doc/cli/npm-init.md b/doc/cli/npm-init.md index ec4c25bedaab4..c6d4aaeb4087c 100644 --- a/doc/cli/npm-init.md +++ b/doc/cli/npm-init.md @@ -3,23 +3,38 @@ npm-init(1) -- Interactively create a package.json file ## SYNOPSIS - npm init [-f|--force|-y|--yes] + npm init [--force|-f|--yes|-y] + npm init <@scope> (same as `npx <@scope>/create`) + npm init [<@scope>/] (same as `npx [<@scope>/]create-`) ## DESCRIPTION -This will ask you a bunch of questions, and then write a package.json for you. +* `npm init [--force|-f|--yes|-y]`: -It attempts to make reasonable guesses about what you want things to be set to, -and then writes a package.json file with the options you've selected. + This will ask you a bunch of questions, and then write a package.json for + you. -If you already have a package.json file, it'll read that first, and default to -the options in there. + It attempts to make reasonable guesses about what you want things to be set + to, and then writes a package.json file with the options you've selected. -It is strictly additive, so it does not delete options from your package.json -without a really good reason to do so. + If you already have a package.json file, it'll read that first, and default + to the options in there. -If you invoke it with `-f`, `--force`, `-y`, or `--yes`, it will use only -defaults and not prompt you for any options. + It is strictly additive, so it does not delete options from your + package.json without a really good reason to do so. + + If you invoke it with `--force`, `-f`, `--yes`, or `-y`, it will use only + defaults and not prompt you for any options. + +* `npm init <@scope>` (same as `npx <@scope>/create`): + + Run `<@scope>/create` as the package initializer instead of + [`npm-init`](https://www.npmjs.com/package/init-package-json). + +* `npm init [<@scope>/]` (same as `npx [<@scope>/]create-`): + + Run `[<@scope>/]create-` as the package initializer instead of + [`npm-init`](https://www.npmjs.com/package/init-package-json). ## CONFIGURATION diff --git a/lib/config/cmd-list.js b/lib/config/cmd-list.js index ed5d80759941e..1fd5c296ddb89 100644 --- a/lib/config/cmd-list.js +++ b/lib/config/cmd-list.js @@ -4,6 +4,7 @@ var shorthands = { 'rb': 'rebuild', 'list': 'ls', 'ln': 'link', + 'create': 'init', 'i': 'install', 'it': 'install-test', 'cit': 'install-ci-test', @@ -24,6 +25,7 @@ var affordances = { 'll': 'ls', 'verison': 'version', 'ic': 'ci', + 'innit': 'init', 'isntall': 'install', 'dist-tags': 'dist-tag', 'apihelp': 'help', diff --git a/lib/init.js b/lib/init.js index 000fa1a5b689e..32e73298fbc77 100644 --- a/lib/init.js +++ b/lib/init.js @@ -2,15 +2,37 @@ module.exports = init +var path = require('path') var log = require('npmlog') var npm = require('./npm.js') +var npx = require('libnpx') var initJson = require('init-package-json') var output = require('./utils/output.js') var noProgressTillDone = require('./utils/no-progress-while-running').tillDone +var usage = require('./utils/usage') -init.usage = 'npm init [--force|-f|--yes|-y]' +init.usage = usage( + 'init', + '\nnpm init [--force|-f|--yes|-y]' + + '\nnpm init <@scope> (same as `npx <@scope>/create`)' + + '\nnpm init [<@scope>/] (same as `npx [<@scope>/]create-`)' +) function init (args, cb) { + if (args.length) { + var NPM_PATH = path.resolve(__dirname, '../bin/npm-cli.js') + var initerName = args[0] + var packageName = /^@[^/]+$/.test(initerName) + ? initerName + '/create' + : initerName.replace(/^(@[^/]+\/)?/, '$1create-') + + var npxArgs = [process.argv0, '[fake arg]', '--always-spawn', packageName, ...process.argv.slice(4)] + var parsed = npx.parseArgs(npxArgs, NPM_PATH) + + return npx(parsed) + .then(() => cb()) + .catch(cb) + } var dir = process.cwd() log.pause() var initFile = npm.config.get('init-module') diff --git a/test/tap/init-create.js b/test/tap/init-create.js new file mode 100644 index 0000000000000..ee946f58f60c4 --- /dev/null +++ b/test/tap/init-create.js @@ -0,0 +1,61 @@ +/* eslint-disable standard/no-callback-literal */ +var test = require('tap').test +var requireInject = require('require-inject') + +var npm = require('../../lib/npm.js') + +test('npm init ', function (t) { + var initJsonMock = function () { + t.ok(false, 'should not run initJson()') + } + initJsonMock.yes = function () { + t.ok(false, 'should not run initJson.yes()') + return false + } + var libnpxMock = function () { + return Promise.resolve() + } + libnpxMock.parseArgs = function (argv, defaultNpm) { + t.ok(argv[0].includes('node'), 'node is the first arg') + t.equals(argv[2], '--always-spawn', 'set npx opts.alwaysSpawn') + t.equals(argv[3], 'create-pkg-name', 'expands pkg-name') + t.ok(defaultNpm.endsWith('npm-cli.js'), 'passes npx bin path') + } + + npm.load({ loglevel: 'silent' }, function () { + var init = requireInject('../../lib/init', { + 'init-package-json': initJsonMock, + 'libnpx': libnpxMock + }) + + init(['pkg-name'], function () {}) + + t.end() + }) +}) + +test('npm init with scoped packages', function (t) { + var libnpxMock = function () { + return Promise.resolve() + } + + npm.load({ loglevel: 'silent' }, function () { + var init = requireInject('../../lib/init', { + 'libnpx': libnpxMock + }) + + libnpxMock.parseArgs = function (argv) { + t.equals(argv[3], '@scope/create', 'expands @scope') + } + + init(['@scope'], function () {}) + + libnpxMock.parseArgs = function (argv) { + t.equals(argv[3], '@scope/create-pkg-name', 'expands @scope/pkg-name') + } + + init(['@scope/pkg-name'], function () {}) + + t.end() + }) +})