fix(lint): re-arrange scripts (#36511)
This commit is contained in:
11
tools/scripts/lint/.markdownlintrc.js
Normal file
11
tools/scripts/lint/.markdownlintrc.js
Normal file
@ -0,0 +1,11 @@
|
||||
// JS rather than JSON so comments can be included
|
||||
|
||||
module.exports = {
|
||||
"default": true, // include all rules, with exceptions below
|
||||
"MD002": false, // first heading should not be a top level heading
|
||||
"MD013": false, // lines can be any length
|
||||
"MD022": false, // headings don't need surrounding by newlines
|
||||
"MD031": true, // fenced blocks do need surrounding by newlines
|
||||
"MD033": false, // inline html is required
|
||||
"whitespace": false // extra whitespace is ignored, so we don't enforce it.
|
||||
}
|
38
tools/scripts/lint/fixtures/badFencing.md
Normal file
38
tools/scripts/lint/fixtures/badFencing.md
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
id: ''
|
||||
title: ''
|
||||
challengeType: 0
|
||||
videoUrl: ''
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
```yml
|
||||
tests:
|
||||
- text: text
|
||||
testString: testString
|
||||
|
||||
```
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='html-seed'>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
</section>
|
40
tools/scripts/lint/fixtures/badYML.md
Normal file
40
tools/scripts/lint/fixtures/badYML.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
id: ''
|
||||
title: ''
|
||||
challengeType: 0
|
||||
videoUrl: ''
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: text
|
||||
testString: testString
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='html-seed'>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
</section>
|
40
tools/scripts/lint/fixtures/good.md
Normal file
40
tools/scripts/lint/fixtures/good.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
id: ''
|
||||
title: ''
|
||||
challengeType: 0
|
||||
videoUrl: ''
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: text
|
||||
testString: testString
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='html-seed'>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
</section>
|
12
tools/scripts/lint/index.js
Normal file
12
tools/scripts/lint/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
const lintRules = require('./.markdownlintrc');
|
||||
const linter = require('./linter');
|
||||
const argv = require('yargs').argv;
|
||||
|
||||
const isMDRE = /.*\.md$/;
|
||||
|
||||
const lint = linter(lintRules);
|
||||
|
||||
const files = argv._.filter(arg => isMDRE.test(arg));
|
||||
files.forEach(path => lint({ path: path }));
|
||||
|
||||
module.exports = lint;
|
57
tools/scripts/lint/index.test.js
Normal file
57
tools/scripts/lint/index.test.js
Normal file
@ -0,0 +1,57 @@
|
||||
/* global describe it expect jest beforeEach */
|
||||
const path = require('path');
|
||||
|
||||
const lint = require('.');
|
||||
|
||||
describe('markdown linter', () => {
|
||||
let good = { path: path.join(__dirname, './fixtures/good.md') };
|
||||
let badYML = { path: path.join(__dirname, './fixtures/badYML.md') };
|
||||
let badFencing = { path: path.join(__dirname, './fixtures/badFencing.md') };
|
||||
beforeEach(() => {
|
||||
console.log = jest.fn();
|
||||
// the linter signals that a file failed by setting
|
||||
// exitCode to 1, so it needs (re)setting to 0
|
||||
process.exitCode = 0;
|
||||
});
|
||||
afterEach(() => {
|
||||
process.exitCode = 0;
|
||||
});
|
||||
|
||||
it('should pass `good` markdown', done => {
|
||||
function callback() {
|
||||
expect(process.exitCode).toBe(0);
|
||||
done();
|
||||
}
|
||||
lint(good, callback);
|
||||
});
|
||||
|
||||
it('should fail invalid YML blocks', done => {
|
||||
function callback() {
|
||||
expect(process.exitCode).toBe(1);
|
||||
done();
|
||||
}
|
||||
lint(badYML, callback);
|
||||
});
|
||||
|
||||
it('should fail when code fences are not surrounded by newlines', done => {
|
||||
function callback() {
|
||||
expect(process.exitCode).toBe(1);
|
||||
done();
|
||||
}
|
||||
lint(badFencing, callback);
|
||||
});
|
||||
|
||||
it('should write to the console describing the problem', done => {
|
||||
function callback() {
|
||||
const expected =
|
||||
// eslint-disable-next-line max-len
|
||||
'badYML.md: 19: yaml-linter YAML code blocks should be valid [bad indentation of a mapping entry at line 3, column 17:\n testString: testString\n ^] [Context: "```yml"]';
|
||||
expect(console.log.mock.calls.length).toBe(1);
|
||||
expect(console.log.mock.calls[0][0]).toEqual(
|
||||
expect.stringContaining(expected)
|
||||
);
|
||||
done();
|
||||
}
|
||||
lint(badYML, callback);
|
||||
});
|
||||
});
|
25
tools/scripts/lint/linter/index.js
Normal file
25
tools/scripts/lint/linter/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const markdownlint = require('markdownlint');
|
||||
|
||||
const lintPrism = require('./markdown-prism');
|
||||
const lintYAML = require('./markdown-yaml');
|
||||
|
||||
function linter(rules) {
|
||||
const lint = (file, next) => {
|
||||
const options = {
|
||||
files: [file.path],
|
||||
config: rules,
|
||||
customRules: [lintYAML, lintPrism]
|
||||
};
|
||||
markdownlint(options, function callback(err, result) {
|
||||
const resultString = (result || '').toString();
|
||||
if (resultString) {
|
||||
process.exitCode = 1;
|
||||
console.log(resultString);
|
||||
}
|
||||
if (next) next(err, file);
|
||||
});
|
||||
};
|
||||
return lint;
|
||||
}
|
||||
|
||||
module.exports = linter;
|
44
tools/scripts/lint/linter/markdown-prism.js
Normal file
44
tools/scripts/lint/linter/markdown-prism.js
Normal file
@ -0,0 +1,44 @@
|
||||
const components = require(`prismjs/components`);
|
||||
|
||||
module.exports = {
|
||||
names: ['prism-langs'],
|
||||
description: 'Code block languages should be supported by PrismJS',
|
||||
tags: ['prism'],
|
||||
function: function rule(params, onError) {
|
||||
params.tokens
|
||||
.filter(param => param.type === 'fence')
|
||||
.forEach(codeBlock => {
|
||||
// whitespace around the language is ignored by the parser, as is case:
|
||||
const baseLang = codeBlock.info.trim().toLowerCase();
|
||||
const lang = getBaseLanguageName(baseLang);
|
||||
// Rule MD040 checks if the block has a language, so this rule only
|
||||
// comes into play if a language has been specified.
|
||||
if (baseLang && !lang) {
|
||||
onError({
|
||||
lineNumber: codeBlock.lineNumber,
|
||||
detail: `\'${baseLang}\' is not recognised.`
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the method used by https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-prismjs/src/load-prism-language.js
|
||||
*/
|
||||
|
||||
// Get the real name of a language given it or an alias
|
||||
const getBaseLanguageName = nameOrAlias => {
|
||||
if (components.languages[nameOrAlias]) {
|
||||
return nameOrAlias;
|
||||
}
|
||||
return Object.keys(components.languages).find(language => {
|
||||
const { alias } = components.languages[language];
|
||||
if (!alias) return false;
|
||||
if (Array.isArray(alias)) {
|
||||
return alias.includes(nameOrAlias);
|
||||
} else {
|
||||
return alias === nameOrAlias;
|
||||
}
|
||||
});
|
||||
};
|
24
tools/scripts/lint/linter/markdown-yaml.js
Normal file
24
tools/scripts/lint/linter/markdown-yaml.js
Normal file
@ -0,0 +1,24 @@
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
module.exports = {
|
||||
names: ['yaml-linter'],
|
||||
description: 'YAML code blocks should be valid',
|
||||
tags: ['yaml'],
|
||||
function: function rule(params, onError) {
|
||||
params.tokens
|
||||
.filter(param => param.type === 'fence')
|
||||
.filter(param => param.info === 'yml' || param.info === 'yaml')
|
||||
// TODO since the parser only looks for yml, should we reject yaml blocks?
|
||||
.forEach(codeBlock => {
|
||||
try {
|
||||
yaml.safeLoad(codeBlock.content);
|
||||
} catch (e) {
|
||||
onError({
|
||||
lineNumber: codeBlock.lineNumber,
|
||||
detail: e.message,
|
||||
context: codeBlock.line
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user