diff --git a/source/ui.js b/source/ui.js index 29f1c980..6eec35df 100644 --- a/source/ui.js +++ b/source/ui.js @@ -93,7 +93,7 @@ const checkNewFilesAndDependencies = async (pkg, rootDir) => { const messages = []; if (newFiles.unpublished.length > 0) { - messages.push(`The following new files will not be part of your published package:\n${util.joinList(newFiles.unpublished)}\n\nIf you intended to publish them, add them to the \`files\` field in package.json.`); + messages.push(`The following new files will not be part of your published package:\n${util.autoGroupList(newFiles.unpublished)}\n\nIf you intended to publish them, add them to the \`files\` field in package.json.`); } if (newFiles.firstTime.length > 0) { diff --git a/source/util.js b/source/util.js index 8d31e6ec..b58fa1b9 100644 --- a/source/util.js +++ b/source/util.js @@ -76,6 +76,28 @@ export const getTagVersionPrefix = pMemoize(async options => { export const joinList = list => chalk.reset(list.map(item => `- ${item}`).join('\n')); +export const groupFilesInFolders = (files, groupingMinimumDepth = 1, groupingThresholdCount = 5) => { + const groups = {}; + for (const file of files) { + const groupKey = path.join(...file.split(path.sep).slice(0, groupingMinimumDepth)); + groups[groupKey] = [...groups[groupKey] ?? [], file]; + } + + const lines = []; + for (const [folder, filesInFolder] of Object.entries(groups)) { + if (filesInFolder.length > groupingThresholdCount) { + lines.push(`- ${folder}/* ${chalk.bold.white(`(${filesInFolder.length} files)`)}`); + continue; + } + + for (const file of filesInFolder) { + lines.push(`- ${file}`); + } + } + + return chalk.reset(lines.join('\n')); +}; + export const getNewFiles = async rootDir => { const listNewFiles = await git.newFilesSinceLastRelease(rootDir); const listPkgFiles = await npm.getFilesToBePacked(rootDir); diff --git a/test/util/auto-group-list.js b/test/util/auto-group-list.js new file mode 100644 index 00000000..331d1b28 --- /dev/null +++ b/test/util/auto-group-list.js @@ -0,0 +1,33 @@ +import test from 'ava'; +import stripAnsi from 'strip-ansi'; +import {groupFilesInFolders} from '../../source/util.js'; + +const testJoinList = test.macro((t, {list, expected}) => { + const output = groupFilesInFolders(list); + t.is(stripAnsi(output), expected); +}); + +test('one item', testJoinList, { + list: [ + 'scripts/a.sh', + ], + expected: '- scripts/a.sh', +}); + +test('mix of collapsed and expanded folders', testJoinList, { + list: [ + 'scripts/a.sh', + 'scripts/b.sh', + 'scripts/c.sh', + 'test/_utils-1.js', + 'test/_utils-2.js', + 'test/_utils-3.js', + 'test/_utils-4.js', + 'test/_utils-5.js', + 'test/_utils-6.js', + ], + expected: `- scripts/a.sh +- scripts/b.sh +- scripts/c.sh +- test/* (6 files)`, +});