Skip to content

Commit

Permalink
Add multiple rules, update test structure, and require Node.js 8 (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
transitive-bullshit authored and sindresorhus committed Aug 10, 2018
1 parent 1d0bc31 commit a5bf993
Show file tree
Hide file tree
Showing 57 changed files with 1,692 additions and 50 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -2,4 +2,3 @@ language: node_js
node_js:
- '10'
- '8'
- '6'
48 changes: 43 additions & 5 deletions cli.js
@@ -1,11 +1,49 @@
#!/usr/bin/env node
'use strict';
const gitClone = require('git-clone');
const isUrl = require('is-url-superb');
const meow = require('meow');
const pify = require('pify');
const rmfr = require('rmfr');
const tempy = require('tempy');

const findReadmeFile = require('./lib/find-readme-file');
const awesomeLint = require('.');

const cli = meow(`
Usage
$ awesome-lint
`);
const main = async () => {
const cli = meow(`
Usage
$ awesome-lint
`);

const options = { };
const input = cli.input[0];
let temp = null;

if (input) {
if (isUrl(input)) {
temp = tempy.directory();
await pify(gitClone)(input, temp);

const readme = findReadmeFile(temp);
if (!readme) {
await rmfr(temp);
throw new Error(`unable to find valid readme for "${input}"`);
}

options.filename = readme;
} else {
options.filename = input;
}
} else {
options.filename = 'readme.md';
}

await awesomeLint.report(options);

if (temp) {
await rmfr(temp);
}
};

awesomeLint.report({filename: cli.input[0] || 'readme.md'});
main();
19 changes: 8 additions & 11 deletions config.js
Expand Up @@ -5,23 +5,21 @@ exports.plugins = [

// Official plugins
[require('remark-lint-blockquote-indentation'), 2],
[require('remark-lint-checkbox-character-style'), {
checked: 'x',
unchecked: ' '
}],
[require('remark-lint-checkbox-character-style'), 'consistent'],
require('remark-lint-checkbox-content-indent'),
[require('remark-lint-code-block-style'), 'fenced'],
require('remark-lint-definition-case'),
require('remark-lint-definition-spacing'),
[require('remark-lint-emphasis-marker'), '*'],
[require('remark-lint-emphasis-marker'), 'consistent'],
[require('remark-lint-fenced-code-marker'), '`'],
require('remark-lint-file-extension'),
require('remark-lint-final-newline'),
require('remark-lint-hard-break-spaces'),
[require('remark-lint-heading-style'), 'atx'],
[require('remark-lint-link-title-style'), '\''],
require('remark-lint-list-item-bullet-indent'),
require('remark-lint-list-item-content-indent'),
// TODO: this rule doesn't properly handle tab indents
// require('remark-lint-list-item-content-indent'),
[require('remark-lint-list-item-indent'), 'space'],
require('remark-lint-no-auto-link-without-protocol'),
require('remark-lint-no-blockquote-without-marker'),
Expand All @@ -41,18 +39,17 @@ exports.plugins = [
require('remark-lint-no-table-indentation'),
require('remark-lint-no-undefined-references'),
require('remark-lint-no-unused-definitions'),
[require('remark-lint-ordered-list-marker-style'), '.'],
[require('remark-lint-ordered-list-marker-style'), 'consistent'],
[require('remark-lint-ordered-list-marker-value'), 'ordered'],
[require('remark-lint-rule-style'), '---'],
[require('remark-lint-strong-marker'), '*'],
[require('remark-lint-table-cell-padding'), 'padded'],
[require('remark-lint-strong-marker'), 'consistent'],
[require('remark-lint-table-cell-padding'), 'consistent'],
require('remark-lint-table-pipe-alignment'),
require('remark-lint-table-pipes'),
[require('remark-lint-unordered-list-marker-style'), '-'],
[require('remark-lint-unordered-list-marker-style'), 'consistent'],

// Third-party plugins
require('remark-lint-no-empty-sections'),
require('remark-lint-no-url-trailing-slash'),

// Custom plugins
...require('./rules')
Expand Down
19 changes: 12 additions & 7 deletions index.js
@@ -1,4 +1,5 @@
'use strict';
const path = require('path');
const remark = require('remark');
const globby = require('globby');
const pify = require('pify');
Expand All @@ -7,23 +8,26 @@ const vfileReporterPretty = require('vfile-reporter-pretty');
const config = require('./config');

const m = options => {
options = Object.assign({
filename: 'readme.md'
}, options);
options = {
config,
filename: 'readme.md',
...options
};

const readmeFile = globby.sync(options.filename, {nocase: true})[0];

if (!readmeFile) {
return Promise.reject(new Error(`Couldn't find the file ${options.filename}`));
}

const run = remark().use(config).process;
const file = toVfile.readSync(readmeFile);
const run = remark().use(options.config).process;
const file = toVfile.readSync(path.resolve(readmeFile));

return pify(run)(file);
};

m.report = options => m(options).then(file => {
m.report = async options => {
const file = await m(options);
const {messages} = file;

if (messages.length === 0) {
Expand All @@ -36,7 +40,8 @@ m.report = options => m(options).then(file => {

process.exitCode = 1;

file.path = path.basename(file.path);
console.log(vfileReporterPretty([file]));
});
};

module.exports = m;
14 changes: 14 additions & 0 deletions lib/find-readme-file.js
@@ -0,0 +1,14 @@
'use strict';

const fs = require('fs');
const path = require('path');

module.exports = dir => {
const readmeFile = fs.readdirSync(dir).find(filename => (
/readme|readme\.md|readme\.markdown|readme.txt/i.test(filename)
));

if (readmeFile) {
return path.join(fs.realpathSync(dir), readmeFile);
}
};
5 changes: 5 additions & 0 deletions lib/identifier-whitelist.js
@@ -0,0 +1,5 @@
'use strict';

module.exports = new Set([
'npm'
]);
File renamed without changes
22 changes: 18 additions & 4 deletions package.json
Expand Up @@ -11,7 +11,7 @@
},
"bin": "cli.js",
"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"test": "xo && ava"
Expand All @@ -34,7 +34,14 @@
"cli"
],
"dependencies": {
"case": "^1.5.5",
"emoji-regex": "^7.0.0",
"execa": "^0.10.0",
"git-clone": "^0.1.0",
"github-slugger": "^1.2.0",
"globby": "^8.0.1",
"is-url-superb": "^2.0.0",
"mdast-util-to-string": "^1.0.4",
"meow": "^5.0.0",
"pify": "^3.0.0",
"remark": "^9.0.0",
Expand All @@ -58,7 +65,7 @@
"remark-lint-no-auto-link-without-protocol": "^1.0.2",
"remark-lint-no-blockquote-without-marker": "^2.0.2",
"remark-lint-no-emphasis-as-heading": "^1.0.2",
"remark-lint-no-empty-sections": "^2.0.0",
"remark-lint-no-empty-sections": "^3.0.0",
"remark-lint-no-file-name-articles": "^1.0.2",
"remark-lint-no-file-name-consecutive-dashes": "^1.0.2",
"remark-lint-no-file-name-irregular-characters": "^1.0.2",
Expand All @@ -83,14 +90,21 @@
"remark-lint-table-pipe-alignment": "^1.0.2",
"remark-lint-table-pipes": "^1.0.2",
"remark-lint-unordered-list-marker-style": "^1.0.2",
"rmfr": "^2.0.0",
"tempy": "^0.2.1",
"to-vfile": "^5.0.0",
"unified-lint-rule": "^1.0.3",
"unist-util-visit": "^1.1.0",
"unist-util-find": "^1.0.1",
"unist-util-find-all-after": "^1.0.2",
"unist-util-find-all-before": "^2.0.2",
"unist-util-find-all-between": "^1.0.1",
"unist-util-inspect": "^4.1.3",
"unist-util-visit": "^1.4.0",
"vfile-reporter-pretty": "^1.0.2"
},
"devDependencies": {
"ava": "*",
"execa": "^0.10.0",
"sinon": "^6.1.4",
"xo": "*"
}
}
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -14,7 +14,7 @@ Intended to make it easier to create and maintain Awesome lists.

Includes a bunch of [general Markdown rules](https://github.com/sindresorhus/awesome-lint/blob/master/config.js) and some [Awesome specific rules](https://github.com/sindresorhus/awesome-lint/tree/master/rules).

![](screenshot.png)
![](media/screenshot.png)


## CLI
Expand Down
27 changes: 21 additions & 6 deletions rules/badge.js
Expand Up @@ -2,26 +2,33 @@
const rule = require('unified-lint-rule');
const visit = require('unist-util-visit');

const badgeUrlWhitelist = new Set([
'https://awesome.re',
'https://github.com/sindresorhus/awesome'
]);

const badgeSrcUrlWhitelist = new Set([
'https://awesome.re/badge.svg',
'https://awesome.re/badge-flat.svg'
]);

module.exports = rule('remark-lint:awesome/badge', (ast, file) => {
visit(ast, 'heading', (node, index) => {
if (index > 0) {
return;
}

const badgeUrl = 'https://awesome.re';
const badgeSrcUrl = 'https://awesome.re/badge.svg';

let hasBadge = false;

for (const child of node.children) {
if (node.depth !== 1 || child.type !== 'link' || child.url !== badgeUrl) {
if (node.depth !== 1 || child.type !== 'link' || !isValidBadgeUrl(child.url)) {
continue;
}

for (const child2 of child.children) {
if (child2.type === 'image') {
if (child2.url !== badgeSrcUrl) {
file.message('Incorrect badge source', child2);
if (!isValidBadgeSrcUrl(child2.url)) {
file.message('Invalid badge source', child2);
return;
}

Expand All @@ -35,3 +42,11 @@ module.exports = rule('remark-lint:awesome/badge', (ast, file) => {
}
});
});

function isValidBadgeUrl(url) {
return badgeUrlWhitelist.has(url);
}

function isValidBadgeSrcUrl(url) {
return badgeSrcUrlWhitelist.has(url);
}
23 changes: 23 additions & 0 deletions rules/contributing.js
@@ -0,0 +1,23 @@
'use strict';
const fs = require('fs');
const path = require('path');
const globby = require('globby');
const rule = require('unified-lint-rule');

module.exports = rule('remark-lint:awesome/contributing', (ast, file) => {
const {dirname} = file;

const contributingFile = globby.sync('contributing.md', {case: false, cwd: dirname})[0];

if (!contributingFile) {
file.message('Missing file contributing.md');
return;
}

const contributingPath = path.resolve(dirname, contributingFile);
const contributing = fs.readFileSync(contributingPath, 'utf8').trim();

if (!contributing) {
file.message('contributing.md file must not be empty');
}
});
42 changes: 42 additions & 0 deletions rules/git-repo-age.js
@@ -0,0 +1,42 @@
'use strict';
const execa = require('execa');
const rule = require('unified-lint-rule');

const minGitRepoAgeDays = 30;
const minGitRepoAgeMs = minGitRepoAgeDays * 24 * 60 * 60 * 1000;

module.exports = rule('remark-lint:awesome/git-repo-age', async (ast, file) => {
const {dirname} = file;

try {
const firstCommitHash = await execa.stdout('git', [
'rev-list',
'--max-parents=0',
'HEAD'
], {
cwd: dirname
});

const firstCommitDate = await execa.stdout('git', [
'show',
'-s',
'--format=%ci',
firstCommitHash
], {
cwd: dirname
});

const date = new Date(firstCommitDate);
const now = new Date();

if (now - date < minGitRepoAgeMs) {
file.message(`Git repository must be at least ${minGitRepoAgeDays} days old`);
}
} catch (err) {
// Most likely not a git repository
file.message(`Awesome list must reside in a valid git repository`);
}
});

// For stubbing
module.exports.execa = execa;
8 changes: 7 additions & 1 deletion rules/index.js
@@ -1,5 +1,11 @@
'use strict';

module.exports = [
require('./badge')
require('./badge'),
require('./contributing'),
require('./git-repo-age'),
require('./license'),
require('./list-item'),
require('./no-ci-badge'),
require('./toc')
];

0 comments on commit a5bf993

Please sign in to comment.