build(aio): create noMarkdownHeadings content rule (#22759)
This content rule can check what markdown headings appear in content properties. PR Close #22759
This commit is contained in:
parent
e0ae74d40e
commit
7a8c58162c
50
aio/tools/transforms/angular-api-package/content-rules/noMarkdownHeadings.js
vendored
Normal file
50
aio/tools/transforms/angular-api-package/content-rules/noMarkdownHeadings.js
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* A factory for creating a rule for the `checkContentRules` processor, which disallows markdown
|
||||
* headings in a content property.
|
||||
*
|
||||
* @param {...number|string} disallowedHeadings
|
||||
* Each parameter identifies heading levels that are not allowed. They can be in the form of:
|
||||
*
|
||||
* - a number (e.g. 1), which implies that the specified heading is not allowed
|
||||
* - a range (e.g. '2,3'), which implies the range of headings that are not allowed
|
||||
*
|
||||
* (A range can be open ended on the upper bound by not specifying a value after the comma.)
|
||||
*
|
||||
* @example
|
||||
* To create a rule that will only allow level 3 headings:
|
||||
*
|
||||
* ```
|
||||
* const rule = createNoMarkdownHeadingRule(1, 2, '4,');
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
module.exports = function createrNoMarkdownHeadingRule() {
|
||||
const args = Array.prototype.slice.apply(arguments);
|
||||
const disallowedHeadings = args.map(arg => `#{${arg}}`);
|
||||
if (!disallowedHeadings.length) {
|
||||
disallowedHeadings.push('#{1,}');
|
||||
}
|
||||
const regex = new RegExp(`^ {0,3}(${disallowedHeadings.join('|')}) +.*$`, 'mg');
|
||||
return (doc, prop, value) => {
|
||||
let match, matches = [];
|
||||
while(match = regex.exec(value)) { // eslint-disable-line no-cond-assign
|
||||
matches.push(match[0]);
|
||||
}
|
||||
if (matches.length) {
|
||||
const list = listify(matches.map(match => `"${match}"`));
|
||||
return `Invalid headings found in "${prop}" property: ${list}.`;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convert an array of strings in to a human list - e.g separated by commas and the word `and`.
|
||||
* @param {string[]} values The strings to convert to a list
|
||||
*/
|
||||
function listify(values) {
|
||||
if (values.length <= 1) return values;
|
||||
const last = values[values.length - 1];
|
||||
const rest = values.slice(0, values.length - 1);
|
||||
return [rest.join(', '), last].join(' and ');
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
const createNoMarkdownHeadings = require('./noMarkdownHeadings');
|
||||
|
||||
describe('createNoMarkdownHeadings rule', () => {
|
||||
|
||||
const noMarkdownHeadings = createNoMarkdownHeadings();
|
||||
|
||||
it('should return `undefined` if there is no heading in a value', () => {
|
||||
expect(noMarkdownHeadings({}, 'description', 'some ## text'))
|
||||
.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return an error message if there is a markdown heading in a single line value', () => {
|
||||
expect(noMarkdownHeadings({}, 'description', '# heading 1'))
|
||||
.toEqual('Invalid headings found in "description" property: "# heading 1".');
|
||||
});
|
||||
|
||||
it('should return an error message if there is a markdown heading in a multiline value', () => {
|
||||
expect(noMarkdownHeadings({}, 'description', 'some text\n# heading 1'))
|
||||
.toEqual('Invalid headings found in "description" property: "# heading 1".');
|
||||
});
|
||||
|
||||
it('should cope with up to 3 spaces before the heading marker', () => {
|
||||
expect(noMarkdownHeadings({}, 'description', ' # heading 1'))
|
||||
.toEqual('Invalid headings found in "description" property: " # heading 1".');
|
||||
expect(noMarkdownHeadings({}, 'description', ' # heading 1'))
|
||||
.toEqual('Invalid headings found in "description" property: " # heading 1".');
|
||||
expect(noMarkdownHeadings({}, 'description', ' # heading 1'))
|
||||
.toEqual('Invalid headings found in "description" property: " # heading 1".');
|
||||
});
|
||||
|
||||
it('should return an error message for each heading found', () => {
|
||||
expect(noMarkdownHeadings({}, 'description', '# heading 1\nsome text\n## heading 2\nmore text\n### heading 3'))
|
||||
.toEqual('Invalid headings found in "description" property: "# heading 1", "## heading 2" and "### heading 3".');
|
||||
});
|
||||
|
||||
describe('(specified heading levels)', () => {
|
||||
it('should take heading levels into account', () => {
|
||||
const noTopLevelHeadings = createNoMarkdownHeadings(1);
|
||||
expect(noTopLevelHeadings({}, 'description', '# top level'))
|
||||
.toEqual('Invalid headings found in "description" property: "# top level".');
|
||||
expect(noTopLevelHeadings({}, 'description', '## second level'))
|
||||
.toBeUndefined();
|
||||
expect(noTopLevelHeadings({}, 'description', '### third level'))
|
||||
.toBeUndefined();
|
||||
expect(noTopLevelHeadings({}, 'description', '#### fourth level'))
|
||||
.toBeUndefined();
|
||||
|
||||
const allowLevel3Headings = createNoMarkdownHeadings(1, 2, '4,');
|
||||
expect(allowLevel3Headings({}, 'description', '# top level'))
|
||||
.toEqual('Invalid headings found in "description" property: "# top level".');
|
||||
expect(allowLevel3Headings({}, 'description', '## second level'))
|
||||
.toEqual('Invalid headings found in "description" property: "## second level".');
|
||||
expect(allowLevel3Headings({}, 'description', '### third level'))
|
||||
.toBeUndefined();
|
||||
expect(allowLevel3Headings({}, 'description', '#### fourth level'))
|
||||
.toEqual('Invalid headings found in "description" property: "#### fourth level".');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue