Enable ESLint rule unicorn/no-array-for-each, auto-fix all violations, manually address new issues for ~4% time reduction measured via profile-fixture.mjs on Apple Silicon M1.

This commit is contained in:
David Anson 2022-06-08 22:10:27 -07:00
parent 15efcb4282
commit b6471fba31
34 changed files with 414 additions and 389 deletions

View file

@ -350,7 +350,7 @@ test.cb("customRulesNpmPackage", (t) => {
test("customRulesBadProperty", (t) => {
t.plan(27);
[
for (const testCase of [
{
"propertyName": "names",
"propertyValues":
@ -377,9 +377,9 @@ test("customRulesBadProperty", (t) => {
"propertyName": "function",
"propertyValues": [ null, "string", [] ]
}
].forEach(function forTestCase(testCase) {
]) {
const { propertyName, propertyValues } = testCase;
propertyValues.forEach(function forPropertyValue(propertyValue) {
for (const propertyValue of propertyValues) {
const badRule = { ...customRules.anyBlockquote };
badRule[propertyName] = propertyValue;
const options = {
@ -395,8 +395,8 @@ test("customRulesBadProperty", (t) => {
},
"Did not get correct exception for missing property."
);
});
});
}
}
});
test.cb("customRulesUsedNameName", (t) => {
@ -639,7 +639,7 @@ test("customRulesOnErrorNullSync", (t) => {
test("customRulesOnErrorBad", (t) => {
t.plan(21);
[
for (const testCase of [
{
"propertyName": "lineNumber",
"subPropertyName": null,
@ -685,9 +685,9 @@ test("customRulesOnErrorBad", (t) => {
"subPropertyName": "insertText",
"propertyValues": [ 10, [] ]
}
].forEach(function forTestCase(testCase) {
]) {
const { propertyName, subPropertyName, propertyValues } = testCase;
propertyValues.forEach(function forPropertyValue(propertyValue) {
for (const propertyValue of propertyValues) {
const badObject = {
"lineNumber": 1
};
@ -725,13 +725,13 @@ test("customRulesOnErrorBad", (t) => {
},
"Did not get correct exception for bad object."
);
});
});
}
}
});
test("customRulesOnErrorInvalid", (t) => {
t.plan(17);
[
for (const testCase of [
{
"propertyName": "lineNumber",
"subPropertyName": null,
@ -757,9 +757,9 @@ test("customRulesOnErrorInvalid", (t) => {
"subPropertyName": "deleteCount",
"propertyValues": [ -2, 5 ]
}
].forEach(function forTestCase(testCase) {
]) {
const { propertyName, subPropertyName, propertyValues } = testCase;
propertyValues.forEach(function forPropertyValue(propertyValue) {
for (const propertyValue of propertyValues) {
const badObject = {
"lineNumber": 1
};
@ -797,13 +797,13 @@ test("customRulesOnErrorInvalid", (t) => {
},
"Did not get correct exception for invalid object."
);
});
});
}
}
});
test("customRulesOnErrorValid", (t) => {
t.plan(24);
[
for (const testCase of [
{
"propertyName": "lineNumber",
"subPropertyName": null,
@ -835,9 +835,9 @@ test("customRulesOnErrorValid", (t) => {
"propertyValues":
[ "", "1", "123456", "\n", "\nText", "Text\n", "\nText\n" ]
}
].forEach(function forTestCase(testCase) {
]) {
const { propertyName, subPropertyName, propertyValues } = testCase;
propertyValues.forEach(function forPropertyValue(propertyValue) {
for (const propertyValue of propertyValues) {
const goodObject = {
"lineNumber": 1
};
@ -864,8 +864,8 @@ test("customRulesOnErrorValid", (t) => {
};
markdownlint.sync(options);
t.truthy(true);
});
});
}
}
});
test.cb("customRulesOnErrorLazy", (t) => {
@ -1424,7 +1424,7 @@ const stringScenarios = [
]
];
[
for (const flavor of [
[
"customRulesThrowString",
() => {
@ -1437,7 +1437,7 @@ const stringScenarios = [
throw new Error(errorMessage);
}
]
].forEach((flavor) => {
]) {
const [ name, func ] = flavor;
const customRule = [
{
@ -1461,7 +1461,7 @@ const stringScenarios = [
}
]
};
stringScenarios.forEach((inputs) => {
for (const inputs of stringScenarios) {
const [ subname, files, strings ] = inputs;
test.cb(`${name}${subname}UnhandledAsync`, (t) => {
@ -1530,10 +1530,10 @@ const stringScenarios = [
});
t.deepEqual(actualResult, expectedResult, "Undetected issues.");
});
});
});
}
}
[
for (const flavor of [
[
"customRulesAsyncExceptionString",
() => {
@ -1570,7 +1570,7 @@ const stringScenarios = [
"customRulesAsyncRejectError",
() => Promise.reject(new Error(errorMessage))
]
].forEach((flavor) => {
]) {
const [ name, func ] = flavor;
const customRule = {
"names": [ "name" ],
@ -1579,7 +1579,7 @@ const stringScenarios = [
"asynchronous": true,
"function": func
};
stringScenarios.forEach((inputs) => {
for (const inputs of stringScenarios) {
const [ subname, files, strings ] = inputs;
test.cb(`${name}${subname}Unhandled`, (t) => {
@ -1630,5 +1630,5 @@ const stringScenarios = [
t.end();
});
});
});
});
}
}

View file

@ -8,8 +8,10 @@ const test = require("ava").default;
const markdownlint = require("../lib/markdownlint");
// Simulates typing each test file to validate handling of partial input
const files = fs.readdirSync("./test");
files.filter((file) => /\.md$/.test(file)).forEach((file) => {
const files = fs
.readdirSync("./test")
.filter((file) => /\.md$/.test(file));
for (const file of files) {
const strings = {};
let content = fs.readFileSync(path.join("./test", file), "utf8");
while (content) {
@ -25,4 +27,4 @@ files.filter((file) => /\.md$/.test(file)).forEach((file) => {
});
t.pass();
});
});
}

View file

@ -219,11 +219,11 @@ bar`
"_"
]
];
testCases.forEach(function forTestCase(testCase) {
for (const testCase of testCases) {
const [ markdown, expected, replacement ] = testCase;
const actual = helpers.unescapeMarkdown(markdown, replacement);
t.is(actual, expected);
});
}
});
test("isBlankLine", (t) => {
@ -253,7 +253,9 @@ test("isBlankLine", (t) => {
"text --> <!--text--> <!--text--> <!-- text",
"text --> --> <!--text--> <!--text--> <!-- <!-- text"
];
blankLines.forEach((line) => t.true(helpers.isBlankLine(line), line || ""));
for (const line of blankLines) {
t.true(helpers.isBlankLine(line), line || "");
}
const nonBlankLines = [
"text",
" text ",
@ -266,7 +268,9 @@ test("isBlankLine", (t) => {
"text --> <!--text--> text <!--text--> <!-- text",
"text --> --> <!--text--> text <!--text--> <!-- <!-- text"
];
nonBlankLines.forEach((line) => t.true(!helpers.isBlankLine(line), line));
for (const line of nonBlankLines) {
t.true(!helpers.isBlankLine(line), line);
}
});
test("includesSorted", (t) => {
@ -280,11 +284,11 @@ test("includesSorted", (t) => {
[ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 ],
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
];
inputs.forEach((input) => {
for (const input of inputs) {
for (let i = 0; i <= 21; i++) {
t.is(helpers.includesSorted(input, i), input.includes(i));
}
});
}
});
test("forEachInlineCodeSpan", (t) => {
@ -377,7 +381,7 @@ test("forEachInlineCodeSpan", (t) => {
"expecteds": [ [ "code", 1, 1, 1 ] ]
}
];
testCases.forEach((testCase) => {
for (const testCase of testCases) {
const { input, expecteds } = testCase;
helpers.forEachInlineCodeSpan(input, (code, line, column, ticks) => {
const [ expectedCode, expectedLine, expectedColumn, expectedTicks ] =
@ -388,7 +392,7 @@ test("forEachInlineCodeSpan", (t) => {
t.is(ticks, expectedTicks, input);
});
t.is(expecteds.length, 0, "length");
});
}
});
test("getPreferredLineEnding", (t) => {
@ -412,11 +416,11 @@ test("getPreferredLineEnding", (t) => {
[ "t\nt\r\nt\nt", "\n" ],
[ "t\rt\t\rt", "\r" ]
];
testCases.forEach((testCase) => {
for (const testCase of testCases) {
const [ input, expected ] = testCase;
const actual = helpers.getPreferredLineEnding(input);
t.is(actual, expected, "Incorrect line ending returned.");
});
}
t.is(helpers.getPreferredLineEnding("", null), "\n");
t.is(helpers.getPreferredLineEnding("", { "EOL": "\n" }), "\n");
t.is(helpers.getPreferredLineEnding("", { "EOL": "\r\n" }), "\r\n");
@ -463,12 +467,12 @@ test("applyFix", (t) => {
"Hello world.\r\n"
]
];
testCases.forEach((testCase) => {
for (const testCase of testCases) {
const [ line, fixInfo, lineEnding, expected ] = testCase;
// @ts-ignore
const actual = helpers.applyFix(line, fixInfo, lineEnding);
t.is(actual, expected, "Incorrect fix applied.");
});
}
});
test("applyFixes", (t) => {
@ -941,12 +945,12 @@ test("applyFixes", (t) => {
"Hello world"
]
];
testCases.forEach((testCase) => {
for (const testCase of testCases) {
const [ input, errors, expected ] = testCase;
// @ts-ignore
const actual = helpers.applyFixes(input, errors);
t.is(actual, expected, "Incorrect fix applied.");
});
}
});
test("deepFreeze", (t) => {
@ -962,7 +966,7 @@ test("deepFreeze", (t) => {
}
};
t.is(helpers.deepFreeze(obj), obj, "Did not return object.");
[
for (const scenario of [
() => {
obj.prop = false;
},
@ -978,9 +982,9 @@ test("deepFreeze", (t) => {
() => {
obj.sub.sub.prop = "zero";
}
].forEach((scenario) => {
]) {
t.throws(scenario, null, "Assigned to frozen object.");
});
}
});
test("forEachLink", (t) => {

View file

@ -36,18 +36,18 @@ function createTestForFile(file) {
.then((params) => {
const [ config, content, results ] = params;
// Canonicalize version number
const errors = results[file];
errors
.filter((error) => !!error.ruleInformation)
.forEach((error) => {
error.ruleInformation =
const errors = results[file]
.filter((error) => !!error.ruleInformation);
for (const error of errors) {
error.ruleInformation =
error.ruleInformation.replace(/v\d+\.\d+\.\d+/, "v0.0.0");
});
}
// Match identified issues by MD### markers
const marker = /\{(MD\d+)(?::(\d+))?\}/g;
const lines = content.split(helpers.newLineRe);
const expected = {};
lines.forEach((line, index) => {
// @ts-ignore
for (const [ index, line ] of lines.entries()) {
let match = null;
while ((match = marker.exec(line))) {
const rule = match[1];
@ -55,17 +55,17 @@ function createTestForFile(file) {
const indices = expected[rule] = expected[rule] || [];
indices.push(match[2] ? Number.parseInt(match[2], 10) : index + 1);
}
});
}
if (Object.keys(expected).length > 0) {
const actual = {};
errors.forEach((error) => {
for (const error of errors) {
const rule = error.ruleNames[0];
// eslint-disable-next-line no-multi-assign
const indices = actual[rule] = actual[rule] || [];
if (indices[indices.length - 1] !== error.lineNumber) {
indices.push(error.lineNumber);
}
});
}
t.deepEqual(actual, expected, "Too few or too many issues found.");
}
// Create snapshot
@ -91,10 +91,13 @@ function createTestForFile(file) {
};
}
require("fs").readdirSync("./test")
.filter((file) => /\.md$/.test(file))
const files = require("fs")
.readdirSync("./test")
.filter((file) => /\.md$/.test(file));
for (const file of files) {
// @ts-ignore
.forEach((file) => test(
test(
file,
createTestForFile(path.join("./test", file))
));
);
}

View file

@ -433,9 +433,9 @@ test.cb("styleFiles", (t) => {
t.plan(4);
fs.readdir("./style", function readdir(err, files) {
t.falsy(err);
files.forEach(function forFile(file) {
for (const file of files) {
t.truthy(require(path.join("../style", file)), "Unable to load/parse.");
});
}
t.end();
});
});
@ -847,13 +847,13 @@ test.cb("customFileSystemAsync", (t) => {
test.cb("readme", (t) => {
t.plan(125);
const tagToRules = {};
rules.forEach(function forRule(rule) {
rule.tags.forEach(function forTag(tag) {
for (const rule of rules) {
for (const tag of rule.tags) {
const tagRules = tagToRules[tag] || [];
tagRules.push(rule.names[0]);
tagToRules[tag] = tagRules;
});
});
}
}
fs.readFile("README.md", "utf8",
function readFile(err, contents) {
t.falsy(err);
@ -864,7 +864,7 @@ test.cb("readme", (t) => {
let seenTags = false;
let inTags = false;
// @ts-ignore
md.parse(contents, {}).forEach(function forToken(token) {
for (const token of md.parse(contents, {})) {
if (
(token.type === "bullet_list_open") &&
(token.level === 0)
@ -909,7 +909,7 @@ test.cb("readme", (t) => {
delete tagToRules[tag];
}
}
});
}
const ruleLeft = rulesLeft.shift();
t.true(!ruleLeft,
"Missing rule documentation for " +
@ -943,7 +943,7 @@ test.cb("rules", (t) => {
"Missing parameters for rule " + r.names + ".");
};
// @ts-ignore
md.parse(contents, {}).forEach(function forToken(token) {
for (const token of md.parse(contents, {})) {
if ((token.type === "heading_open") && (token.tag === "h2")) {
inHeading = true;
} else if (token.type === "heading_close") {
@ -995,7 +995,7 @@ test.cb("rules", (t) => {
ruleUsesParams = null;
}
}
});
}
const ruleLeft = rulesLeft.shift();
t.true(!ruleLeft,
"Missing rule documentation for " +
@ -1013,13 +1013,15 @@ test("validateJsonUsingConfigSchemaStrict", (t) => {
const jsConfigFileRe = /^jsconfig\.json$/i;
const wrongTypesFileRe = /wrong-types-in-config-file.json$/i;
const testDirectory = __dirname;
const testFiles = fs.readdirSync(testDirectory);
testFiles.filter(function filterFile(file) {
return jsonFileRe.test(file) &&
!resultsFileRe.test(file) &&
!jsConfigFileRe.test(file) &&
!wrongTypesFileRe.test(file);
}).forEach(function forFile(file) {
const testFiles = fs
.readdirSync(testDirectory)
.filter(function filterFile(file) {
return jsonFileRe.test(file) &&
!resultsFileRe.test(file) &&
!jsConfigFileRe.test(file) &&
!wrongTypesFileRe.test(file);
});
for (const file of testFiles) {
const data = fs.readFileSync(
path.join(testDirectory, file),
"utf8"
@ -1028,7 +1030,7 @@ test("validateJsonUsingConfigSchemaStrict", (t) => {
// @ts-ignore
tv4.validate(JSON.parse(data), configSchemaStrict),
file + "\n" + JSON.stringify(tv4.error, null, 2));
});
}
});
test("validateConfigSchemaAllowsUnknownProperties", (t) => {
@ -1043,7 +1045,7 @@ test("validateConfigSchemaAllowsUnknownProperties", (t) => {
}
}
];
testCases.forEach((testCase) => {
for (const testCase of testCases) {
t.true(
// @ts-ignore
tv4.validate(testCase, configSchema),
@ -1052,7 +1054,7 @@ test("validateConfigSchemaAllowsUnknownProperties", (t) => {
// @ts-ignore
tv4.validate(testCase, configSchemaStrict),
"Unknown property allowed when strict: " + JSON.stringify(testCase));
});
}
});
test("validateConfigSchemaAppliesToUnknownProperties", (t) => {
@ -1099,20 +1101,23 @@ test("validateConfigExampleJson", async(t) => {
test("allBuiltInRulesHaveValidUrl", (t) => {
t.plan(147);
rules.forEach(function forRule(rule) {
for (const rule of rules) {
// @ts-ignore
t.truthy(rule.information);
// @ts-ignore
t.true(Object.getPrototypeOf(rule.information) === URL.prototype);
const name = rule.names[0].toLowerCase();
t.is(
// @ts-ignore
rule.information.href,
`${homepage}/blob/v${version}/doc/Rules.md#${name}`
);
});
}
});
test("someCustomRulesHaveValidUrl", (t) => {
t.plan(8);
customRules.all.forEach(function forRule(rule) {
for (const rule of customRules.all) {
t.true(!rule.information ||
(Object.getPrototypeOf(rule.information) === URL.prototype));
if (rule === customRules.anyBlockquote) {
@ -1126,7 +1131,7 @@ test("someCustomRulesHaveValidUrl", (t) => {
`${homepage}/blob/main/test/rules/letters-E-X.js`
);
}
});
}
});
test.cb("markdownItPluginsSingle", (t) => {

View file

@ -11,12 +11,12 @@ module.exports = {
),
"tags": [ "test" ],
"function": function rule(params, onError) {
params.tokens.filter(function filterToken(token) {
for (const inline of params.tokens.filter(function filterToken(token) {
return token.type === "inline";
}).forEach(function forToken(inline) {
inline.children.filter(function filterChild(child) {
})) {
for (const text of inline.children.filter(function filterChild(child) {
return child.type === "text";
}).forEach(function forChild(text) {
})) {
const index = text.content.toLowerCase().indexOf("ex");
if (index !== -1) {
onError({
@ -24,7 +24,7 @@ module.exports = {
"context": text.content.substr(index - 1, 4)
});
}
});
});
}
}
}
};

View file

@ -36,14 +36,14 @@ module.exports = {
.then((config) => {
config = cleanJsdocRulesFromEslintConfig(config);
const results = linter.verify(fence.content, config);
results.forEach((result) => {
for (const result of results) {
const lineNumber = fence.lineNumber + result.line;
onError({
"lineNumber": lineNumber,
"detail": result.message,
"context": params.lines[lineNumber - 1]
});
});
}
});
}
return Promise.resolve();

View file

@ -7,13 +7,13 @@ module.exports = {
"description": "Sample rule",
"tags": [ "sample" ],
"function": function rule(params, onError) {
params.tokens.forEach((token) => {
for (const token of params.tokens) {
if (token.type === "hr") {
onError({
"lineNumber": token.lineNumber,
"detail": "Sample error for hr"
});
}
});
}
}
};