Skip to content

Commit

Permalink
Fix showing files added since the last release (#595)
Browse files Browse the repository at this point in the history
  • Loading branch information
bunysae authored Apr 25, 2021
1 parent 6ad9d1e commit 626aed6
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 36 deletions.
2 changes: 1 addition & 1 deletion integration-test
86 changes: 63 additions & 23 deletions source/npm/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ const ignoreWalker = require('ignore-walk');
const minimatch = require('minimatch');
const {verifyRequirementSatisfied} = require('../version');

// According to https://docs.npmjs.com/files/package.json#files
// npm's default behavior is to ignore these files.
const filesIgnoredByDefault = [
'.*.swp',
'.npmignore',
'.gitignore',
'._*',
'.DS_Store',
'.hg',
'.npmrc',
'.lock-wscript',
'.svn',
'.wafpickle-N',
'*.orig',
'config.gypi',
'CVS',
'node_modules/**/*',
'npm-debug.log',
'package-lock.json',
'.git/**/*',
'.git'
];

exports.checkConnection = () => pTimeout(
(async () => {
try {
Expand Down Expand Up @@ -139,6 +162,19 @@ async function getFilesIgnoredByDotnpmignore(pkg, fileList) {
return fileList.filter(minimatch.filter(getIgnoredFilesGlob(allowList, pkg.directories), {matchBase: true, dot: true}));
}

function filterFileList(globArray, fileList) {
const globString = globArray.length > 1 ? `{${globArray}}` : globArray[0];
return fileList.filter(minimatch.filter(globString, {matchBase: true, dot: true})); // eslint-disable-line unicorn/no-fn-reference-in-iterator
}

async function getFilesIncludedByDotnpmignore(pkg, fileList) {
const allowList = await ignoreWalker({
path: pkgDir.sync(),
ignoreFiles: ['.npmignore']
});
return filterFileList(allowList, fileList);
}

function getFilesNotIncludedInFilesProperty(pkg, fileList) {
const globArrayForFilesAndDirectories = [...pkg.files];
const rootDir = pkgDir.sync();
Expand All @@ -154,6 +190,20 @@ function getFilesNotIncludedInFilesProperty(pkg, fileList) {
return result.filter(minimatch.filter(getDefaultIncludedFilesGlob(pkg.main), {nocase: true, matchBase: true}));
}

function getFilesIncludedInFilesProperty(pkg, fileList) {
const globArrayForFilesAndDirectories = [...pkg.files];
const rootDir = pkgDir.sync();
for (const glob of pkg.files) {
try {
if (fs.statSync(path.resolve(rootDir, glob)).isDirectory()) {
globArrayForFilesAndDirectories.push(`${glob}/**/*`);
}
} catch {}
}

return filterFileList(globArrayForFilesAndDirectories, fileList);
}

function getDefaultIncludedFilesGlob(mainFile) {
// According to https://docs.npmjs.com/files/package.json#files
// npm's default behavior is to always include these files.
Expand All @@ -175,29 +225,6 @@ function getDefaultIncludedFilesGlob(mainFile) {
}

function getIgnoredFilesGlob(globArrayFromFilesProperty, packageDirectories) {
// According to https://docs.npmjs.com/files/package.json#files
// npm's default behavior is to ignore these files.
const filesIgnoredByDefault = [
'.*.swp',
'.npmignore',
'.gitignore',
'._*',
'.DS_Store',
'.hg',
'.npmrc',
'.lock-wscript',
'.svn',
'.wafpickle-N',
'*.orig',
'config.gypi',
'CVS',
'node_modules/**/*',
'npm-debug.log',
'package-lock.json',
'.git/**/*',
'.git'
];

// Test files are assumed not to be part of the package
let testDirectoriesGlob = '';
if (packageDirectories && Array.isArray(packageDirectories.test)) {
Expand All @@ -223,6 +250,19 @@ exports.getNewAndUnpublishedFiles = async (pkg, newFiles = []) => {
}
};

exports.getFirstTimePublishedFiles = async (pkg, newFiles = []) => {
let result;
if (pkg.files) {
result = getFilesIncludedInFilesProperty(pkg, newFiles);
} else if (npmignoreExistsInPackageRootDir()) {
result = await getFilesIncludedByDotnpmignore(pkg, newFiles);
} else {
result = newFiles;
}

return result.filter(minimatch.filter(`!{${filesIgnoredByDefault}}`, {matchBase: true, dot: true})).filter(minimatch.filter(getDefaultIncludedFilesGlob(pkg.main), {nocase: true, matchBase: true}));
};

exports.getRegistryUrl = async (pkgManager, pkg) => {
const args = ['config', 'get', 'registry'];
if (exports.isExternalRegistry(pkg)) {
Expand Down
22 changes: 15 additions & 7 deletions source/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,30 @@ const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch
};
};

const checkIgnoredFiles = async pkg => {
const ignoredFiles = await util.getNewAndUnpublishedFiles(pkg);
if (!ignoredFiles || ignoredFiles.length === 0) {
const checkNewFiles = async pkg => {
const newFiles = await util.getNewFiles(pkg);
if ((!newFiles.unpublished || newFiles.unpublished.length === 0) && (!newFiles.firstTime || newFiles.firstTime.length === 0)) {
return true;
}

const message = `The following new files are not already part of your published package:\n${chalk.reset(ignoredFiles.map(path => `- ${path}`).join('\n'))}`;
const messages = [];
if (newFiles.unpublished.length > 0) {
messages.push(`The following new files will not be part of your published package:\n${chalk.reset(newFiles.unpublished.map(path => `- ${path}`).join('\n'))}`);
}

if (newFiles.firstTime.length > 0) {
messages.push(`The following new files will be published the first time:\n${chalk.reset(newFiles.firstTime.map(path => `- ${path}`).join('\n'))}`);
}

if (!isInteractive()) {
console.log(message);
console.log(messages.join('\n'));
return true;
}

const answers = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: `${message}\nContinue?`,
message: `${messages.join('\n')}\nContinue?`,
default: false
}]);

Expand All @@ -112,7 +120,7 @@ module.exports = async (options, pkg) => {
if (options.runPublish) {
checkIgnoreStrategy(pkg);

const answerIgnoredFiles = await checkIgnoredFiles(pkg);
const answerIgnoredFiles = await checkNewFiles(pkg);
if (!answerIgnoredFiles) {
return {
...options,
Expand Down
4 changes: 2 additions & 2 deletions source/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ exports.getTagVersionPrefix = pMemoize(async options => {
}
});

exports.getNewAndUnpublishedFiles = async pkg => {
exports.getNewFiles = async pkg => {
const listNewFiles = await gitUtil.newFilesSinceLastRelease();
return npmUtil.getNewAndUnpublishedFiles(pkg, listNewFiles);
return {unpublished: await npmUtil.getNewAndUnpublishedFiles(pkg, listNewFiles), firstTime: await npmUtil.getFirstTimePublishedFiles(pkg, listNewFiles)};
};

exports.getPreReleasePrefix = pMemoize(async options => {
Expand Down
Empty file.
66 changes: 63 additions & 3 deletions test/npmignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ test('ignored test files using files attribute and .npmignore', async t => {
t.deepEqual(await testedModule.getNewAndUnpublishedFiles({directories: {test: ['test-tap']}}, newFiles), ['source/ignore.txt', 'test/file.txt']);
});

test('dot files using files attribute', async t => {
test('ignored files - dot files using files attribute', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
Expand All @@ -73,7 +73,7 @@ test('dot files using files attribute', async t => {
t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['source']}, ['test/.dotfile']), []);
});

test('dot files using .npmignore', async t => {
test('ignored files - dot files using .npmignore', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
Expand All @@ -83,7 +83,7 @@ test('dot files using .npmignore', async t => {
t.deepEqual(await testedModule.getNewAndUnpublishedFiles({}, ['test/.dot']), []);
});

test('ignore strategy is not used', async t => {
test('ignored files - ignore strategy is not used', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
Expand All @@ -92,3 +92,63 @@ test('ignore strategy is not used', async t => {
});
t.is(await testedModule.getNewAndUnpublishedFiles({name: 'no ignore strategy'}, newFiles), undefined);
});

test('first time published files using file-attribute in package.json with one file', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures', 'package')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['pay_attention.txt']}, newFiles), ['source/pay_attention.txt']);
});

test('first time published files using file-attribute in package.json with directory', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures', 'package')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['source']}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt']);
});

test('first time published files using .npmignore', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures', 'npmignore')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({name: 'npmignore'}, newFiles), ['source/pay_attention.txt']);
});

test('first time published dot files using files attribute', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures', 'package')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['source']}, ['source/.dotfile']), ['source/.dotfile']);
});

test('first time published dot files using .npmignore', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures', 'npmignore')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({}, ['source/.dotfile']), ['source/.dotfile']);
});

test('first time published files - ignore strategy is not used', async t => {
const testedModule = proxyquire('../source/npm/util', {
'pkg-dir':
{
sync: () => path.resolve('test', 'fixtures')
}
});
t.deepEqual(await testedModule.getFirstTimePublishedFiles({name: 'no ignore strategy'}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt', 'test/file.txt']);
});

0 comments on commit 626aed6

Please sign in to comment.