Skip to content

Commit bfee077

Browse files
satazorAndré Cruz
authored and
André Cruz
committed
feat: improve readmeSize, badges and linters in mono-repos
1 parent 6cb749f commit bfee077

File tree

9 files changed

+173
-12089
lines changed

9 files changed

+173
-12089
lines changed

lib/analyze/collect/metadata.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,7 @@ function metadata(data, packageJson) {
322322
hasSelectiveFiles: Array.isArray(packageJson.files) && packageJson.files.length > 0 ? true : null,
323323

324324
// Need to use typeof because there's some old packages in which the README is an object, e.g.: `flatsite`
325-
readme: (typeof data.readme === 'string' && data.readme.indexOf('No README data') === -1) ?
326-
data.readme : null,
325+
readme: (typeof data.readme === 'string' && data.readme) ? data.readme : null,
327326
});
328327
})
329328
.tap(() => log.debug(`The metadata collector for ${packageJson.name} completed successfully`));

lib/analyze/collect/source.js

+35-33
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ const detectReadmeBadges = require('detect-readme-badges');
77
const detectRepoChangelog = require('detect-repo-changelog');
88
const fetchCoverage = require('fetch-coverage');
99
const loadJsonFile = require('load-json-file');
10-
const readFile = Promise.promisify(require('fs').readFile);
1110
const deepCompact = require('deep-compact');
1211
const isRegularFile = require('is-regular-file');
1312
const got = require('got');
1413
const fileSize = require('./util/fileSize');
1514
const promisePropsSettled = require('./util/promisePropsSettled');
1615
const exec = require('../util/exec');
1716
const gotRetry = require('../util/gotRetry');
17+
const fileContents = require('./util/fileContents');
18+
const uniqWith = require('lodash/uniqWith');
19+
const isEqual = require('lodash/isEqual');
20+
const { uniq } = require('lodash');
1821

1922
const davidBin = path.normalize(`${__dirname}/bin/david-json`);
2023
const log = logger.child({ module: 'collect/source' });
@@ -28,8 +31,11 @@ const log = logger.child({ module: 'collect/source' });
2831
* @returns {Promise} The promise for the inspection result.
2932
*/
3033
function inspectFiles(data, downloaded) {
31-
// Readme must be located in the package dir
32-
const readmeSize = data.readmeFilename ? fileSize(`${downloaded.packageDir}/${data.readmeFilename}`) : 0;
34+
// Sum readme file sizes of the one in the package dir with the root
35+
const readmeSize = fileSize([
36+
`${downloaded.packageDir}/${data.readmeFilename || 'README.md'}`,
37+
downloaded.dir !== downloaded.packageDir ? `${downloaded.DIR}/${data.readmeFilename || 'README.md'}` : '',
38+
].filter((path) => path));
3339
// Prefer tests located in the package dir and fallback to the root
3440
const testsSize = (
3541
detectRepoTestFiles(downloaded.packageDir)
@@ -64,20 +70,17 @@ function inspectFiles(data, downloaded) {
6470
* @returns {Promise} The promise for the badges result.
6571
*/
6672
function getReadmeBadges(data, downloaded) {
67-
// Prefer README badges from the package dir but usually badges are at the root README
68-
// Need to use typeof because there's some old packages in which the README is an object, e.g.: `flatsite`
69-
return Promise.try(() => typeof data.readme === 'string' ? detectReadmeBadges(data.readme) : [])
70-
.then((badges) => {
71-
if (!badges.length && downloaded.dir !== downloaded.packageDir && data.readmeFilename) {
72-
return readFile(`${downloaded.dir}/${data.readmeFilename}`)
73-
.then((readme) => detectReadmeBadges(readme.toString()))
74-
// Ignore if file does not exist or is actually a directory
75-
.catch({ code: 'ENOENT' }, () => [])
76-
.catch({ code: 'EISDIR' }, () => []);
77-
}
78-
79-
return badges;
80-
});
73+
// Use badges from both the package dir and root README
74+
return Promise.props({
75+
onPackageDir: typeof data.readme === 'string' && data.readme ? data.readme : fileContents(`${downloaded.packageDir}/${data.readmeFilename || 'README.md'}`),
76+
onRoot: downloaded.dir !== downloaded.packageDir ? fileContents(`${downloaded.dir}/${data.readmeFilename || 'README.md'}`) : '',
77+
})
78+
.then((readmes) => Promise.props({
79+
onRoot: detectReadmeBadges(readmes.onRoot || ''),
80+
onPackageDir: detectReadmeBadges(readmes.onPackageDir || ''),
81+
}))
82+
// Cleanup duplicates
83+
.then((badges) => uniqWith([...badges.onPackageDir, ...badges.onRoot], isEqual));
8184
}
8285

8386
/**
@@ -88,23 +91,22 @@ function getReadmeBadges(data, downloaded) {
8891
* @returns {Promise} The promise for the linters result.
8992
*/
9093
function getRepoLinters(downloaded) {
91-
// Linters usually are at the root but prefer the ones within the package just in case..
92-
return detectRepoLinters(downloaded.packageDir)
93-
.then((linters) => {
94-
if (linters.length || downloaded.dir === downloaded.packageDir) {
95-
return linters;
96-
}
97-
98-
return detectRepoLinters(downloaded.dir)
99-
// A JSON error might occur if `detect-repo-linters`fails to parse `package.json` as JSON
100-
// Since the `package.json` at the root was not validated, it can have errors
101-
// If that's the case, we want to skip them here
102-
.catch({ name: 'JSONError' }, () => {
103-
log.warn({ dir: downloaded.dir }, 'Error reading downloaded package.json when scanning for linters');
94+
// Linters usually are at the root but we consider both just in case..
95+
return Promise.props({
96+
onPackageDir: detectRepoLinters(downloaded.packageDir),
97+
onRootDir: downloaded.dir !== downloaded.packageDir ?
98+
detectRepoLinters(downloaded.dir)
99+
// A JSON error might occur if `detect-repo-linters`fails to parse `package.json` as JSON
100+
// Since the `package.json` at the root was not validated, it can have errors
101+
// If that's the case, we want to skip them here
102+
.catch({ name: 'JSONError' }, () => {
103+
log.warn({ dir: downloaded.dir }, 'Error reading downloaded package.json when scanning for linters');
104104

105-
return [];
106-
});
107-
});
105+
return [];
106+
}) :
107+
[],
108+
})
109+
.then((linters) => uniq([...linters.onPackageDir, ...linters.onRootDir]));
108110
}
109111

110112
/**
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const readFile = Promise.promisify(require('fs').readFile);
4+
5+
const log = logger.child({ module: 'util/file-contents' });
6+
7+
/**
8+
* Gets the file contents of a file.
9+
*
10+
* @param {String} path - The path.
11+
*
12+
* @returns {Promise} A promise that fulfills when done.
13+
*/
14+
function fileContents(path) {
15+
return readFile(path)
16+
.then((buffer) => buffer.toString())
17+
// Return 0 if path does not exist
18+
.catch({ code: 'ENOENT' }, () => null)
19+
// Return 0 if path is directory
20+
.catch({ code: 'EISDIR' }, () => null)
21+
// Return 0 if too many symlinks are being followed, e.g.: `condensation`
22+
.catch({ code: 'ELOOP' }, (err) => {
23+
log.warn({ err }, `ELOOP while getting file size of ${path}, returning 0..`);
24+
25+
return null;
26+
})
27+
// Ignore errors of packages that have large nested paths.. e.g.: `cordova-plugin-forcetouch`
28+
.catch({ code: 'ENAMETOOLONG' }, (err) => {
29+
/* istanbul ignore next */
30+
log.warn({ err }, `ENAMETOOLONG while getting file size of ${path}, returning 0..`);
31+
/* istanbul ignore next */
32+
33+
return null;
34+
});
35+
}
36+
37+
module.exports = fileContents;

lib/analyze/evaluate/maintenance.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ function isPackageFinished(collected) {
183183
const isStable = semver.gte(collected.metadata.version, '1.0.0', true); // `true` = loose semver
184184
const isNotDeprecated = !collected.metadata.deprecated;
185185
const hasFewIssues = get(collected, 'github.issues.openCount', Infinity) < 15;
186-
const hasREADME = !!collected.metadata.readme;
186+
const hasREADME = !!collected.metadata.readme || get(collected, 'source.files.readmeSize', 0) > 0;
187187
const hasTests = !!collected.metadata.hasTestScript;
188188

189189
const isFinished = isStable && isNotDeprecated && hasFewIssues && hasREADME && hasTests;

0 commit comments

Comments
 (0)