From 66062eb542ded987b8e48ccf8ba7201846498c61 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 14 Apr 2026 14:01:48 -0700 Subject: [PATCH] feat!: make author prompt even when already specified BREAKING CHANGE: The author prompt will now be shown even if an author is already specified in the existing package.json. If the user leaves the prompt blank, the author field will be omitted from the resulting package.json instead of being set to an empty string. --- lib/default-input.js | 32 ++++++++++++----- lib/init-package-json.js | 5 +++ test/author.js | 76 ++++++++++++++++++++++++++++++++++++++++ test/dependencies.js | 1 - test/license.js | 1 - test/name-spaces.js | 2 -- test/name-uppercase.js | 1 - test/repository.js | 1 - test/scope-in-config.js | 1 - test/yes-defaults.js | 2 +- 10 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 test/author.js diff --git a/lib/default-input.js b/lib/default-input.js index 7ce2ccb..a20217b 100644 --- a/lib/default-input.js +++ b/lib/default-input.js @@ -204,15 +204,31 @@ if (!package.keywords) { }) } -if (!package.author) { +let author +if (package.author) { + if (typeof package.author === 'string') { + author = package.author + } else { + const authorName = package.author.name + const authorEmail = package.author.email || package.author.mail + const authorUrl = package.author.url || package.author.web + author = `${authorName}${authorEmail ? ` <${authorEmail}>` : ''}${authorUrl ? ` (${authorUrl})` : ''}` + } +} else { const authorName = getConfig('author.name') - exports.author = authorName - ? { - name: authorName, - email: getConfig('author.email'), - url: getConfig('author.url'), - } - : yes ? '' : prompt('author') + if (authorName) { + const authorEmail = getConfig('author.email') + const authorUrl = getConfig('author.url') + author = `${authorName}${authorEmail ? ` <${authorEmail}>` : ''}${authorUrl ? ` (${authorUrl})` : ''}` + } +} + +if (yes) { + if (author) { + exports.author = author + } +} else { + exports.author = prompt('author', author || undefined) } const configLicense = getConfig('license') diff --git a/lib/init-package-json.js b/lib/init-package-json.js index a560ae3..257bd5d 100644 --- a/lib/init-package-json.js +++ b/lib/init-package-json.js @@ -105,6 +105,11 @@ async function init (dir, delete pkg.content.license } + // if no author was explicitly provided, don't include one + if (!pzData.author) { + delete pkg.content.author + } + // readJson filters out empty descriptions, but init-package-json // traditionally leaves them alone if (!pkg.content.description) { diff --git a/test/author.js b/test/author.js new file mode 100644 index 0000000..84b76e6 --- /dev/null +++ b/test/author.js @@ -0,0 +1,76 @@ +const t = require('tap') +const { setup, child, isChild } = require('./fixtures/setup') + +if (isChild()) { + return child() +} + +t.test('author from config used as default', async (t) => { + const { data } = await setup(t, __filename, { + inputs: { + name: 'the-name', + author: [ + [/author: \(npmbot \(https:\/\/npmjs\.com\)\) $/, ''], + ], + }, + config: { + 'init-author-name': 'npmbot', + 'init-author-email': 'npmbot@npmjs.com', + 'init-author-url': 'https://npmjs.com', + }, + }) + + t.equal(data.author, 'npmbot (https://npmjs.com)', 'uses config author as default') +}) + +t.test('author from config in yes mode', async (t) => { + const { data } = await setup(t, __filename, { + config: { + yes: 'yes', + 'init-author-name': 'npmbot', + 'init-author-email': 'npmbot@npmjs.com', + 'init-author-url': 'https://npmjs.com', + }, + }) + + t.equal(data.author, 'npmbot (https://npmjs.com)', 'uses config author in yes mode') +}) + +t.test('author omitted when left blank', async (t) => { + const { data } = await setup(t, __filename, { + inputs: { + name: 'the-name', + author: [ + [/author: $/, ''], + ], + }, + }) + + t.equal(data.author, undefined, 'author is omitted when left blank') +}) + +t.test('author preserved from existing package.json', async (t) => { + const { data } = await setup(t, __filename, { + testdir: { + 'package.json': JSON.stringify({ + name: 'existing-package', + version: '1.0.0', + author: 'Existing Author ', + }), + }, + config: { yes: 'yes' }, + }) + + t.equal(data.author, 'Existing Author ', 'preserves existing author') +}) + +t.test('author name only from config', async (t) => { + const { data } = await setup(t, __filename, { + config: { + yes: 'yes', + 'init-author-name': 'npmbot', + }, + }) + + t.equal(data.author, 'npmbot', 'uses author name only when no email/url configured') +}) diff --git a/test/dependencies.js b/test/dependencies.js index 803a9ea..72c8059 100644 --- a/test/dependencies.js +++ b/test/dependencies.js @@ -28,7 +28,6 @@ t.test('existing dependencies', async (t) => { version: '1.0.0', type: 'commonjs', description: '', - author: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, main: 'index.js', keywords: [], diff --git a/test/license.js b/test/license.js index 8abaa9d..15c8404 100644 --- a/test/license.js +++ b/test/license.js @@ -22,7 +22,6 @@ t.test('license', async (t) => { description: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, license: 'Apache-2.0', - author: '', main: 'index.js', } t.has(data, wanted) diff --git a/test/name-spaces.js b/test/name-spaces.js index c5f6cff..75ca363 100644 --- a/test/name-spaces.js +++ b/test/name-spaces.js @@ -20,7 +20,6 @@ t.test('single space', async t => { version: '1.0.0', description: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, - author: '', main: 'index.js', } t.has(data, wanted) @@ -41,7 +40,6 @@ t.test('multiple spaces', async t => { version: '1.0.0', description: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, - author: '', main: 'index.js', } t.has(data, wanted) diff --git a/test/name-uppercase.js b/test/name-uppercase.js index 170474a..dc4f075 100644 --- a/test/name-uppercase.js +++ b/test/name-uppercase.js @@ -20,7 +20,6 @@ t.test('uppercase', async (t) => { version: '1.0.0', description: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, - author: '', main: 'index.js', } t.has(data, EXPECT) diff --git a/test/repository.js b/test/repository.js index f87e410..cc744bf 100644 --- a/test/repository.js +++ b/test/repository.js @@ -15,7 +15,6 @@ t.test('license', async (t) => { version: '1.0.0', description: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, - author: '', repository: { type: 'git', url: 'git+https://github.com/npm/cli.git', diff --git a/test/scope-in-config.js b/test/scope-in-config.js index cf244da..df1e3d5 100644 --- a/test/scope-in-config.js +++ b/test/scope-in-config.js @@ -10,7 +10,6 @@ t.test('--yes with scope', async (t) => { name: '@scoped/tap-testdir-scope-in-config---yes-with-scope', version: '1.0.0', description: '', - author: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, main: 'index.js', keywords: [], diff --git a/test/yes-defaults.js b/test/yes-defaults.js index add7177..b220b8b 100644 --- a/test/yes-defaults.js +++ b/test/yes-defaults.js @@ -10,7 +10,6 @@ t.test('--yes defaults', async (t) => { name: 'tap-testdir-yes-defaults---yes-defaults', version: '1.0.0', description: '', - author: '', scripts: { test: 'echo "Error: no test specified" && exit 1' }, main: 'index.js', keywords: [], @@ -21,5 +20,6 @@ t.test('--yes defaults', async (t) => { }) t.has(data, EXPECT, 'used the default data') + t.equal(data.author, undefined, 'author is omitted by default') t.equal(data.license, undefined, 'license is omitted by default') })