diff --git a/.eslintrc.yml b/.eslintrc.yml index 45720dab9..a7135b083 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -69,6 +69,7 @@ rules: unicorn/better-regex: error unicorn/explicit-length-check: error unicorn/new-for-builtins: error + unicorn/no-array-for-each: error unicorn/no-array-push-push: error unicorn/no-useless-undefined: error unicorn/prefer-includes: error diff --git a/scripts/check-deps.js b/scripts/check-deps.js index c892a1063..d6b56650f 100644 --- a/scripts/check-deps.js +++ b/scripts/check-deps.js @@ -7,7 +7,7 @@ validateDependencyObject(pkg.dependencies); validateDependencyObject(pkg.devDependencies); function validateDependencyObject(object) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { if (object[key][0] === "^" || object[key][0] === "~") { console.error( chalk.red("error"), @@ -15,5 +15,5 @@ function validateDependencyObject(object) { ); process.exitCode = 1; } - }); + } } diff --git a/scripts/sync-flow-tests.js b/scripts/sync-flow-tests.js index a42309b59..7705c78b8 100644 --- a/scripts/sync-flow-tests.js +++ b/scripts/sync-flow-tests.js @@ -50,13 +50,13 @@ function syncTests(syncDir) { rimraf.sync(FLOW_TESTS_DIR); - filesToCopy.forEach((file) => { + for (const file of filesToCopy) { const content = fs.readFileSync(file, "utf8"); const parseError = tryParse(file, content); if (parseError) { skipped.push(parseError); - return; + continue; } const newFile = path.join(FLOW_TESTS_DIR, path.relative(syncDir, file)); @@ -67,7 +67,7 @@ function syncTests(syncDir) { fs.mkdirSync(dirname, { recursive: true }); fs.writeFileSync(newFile, content); fs.writeFileSync(specFile, specContent); - }); + } return skipped; } diff --git a/src/config/resolve-config.js b/src/config/resolve-config.js index 6bce18398..a8c55977a 100644 --- a/src/config/resolve-config.js +++ b/src/config/resolve-config.js @@ -85,7 +85,7 @@ function _resolveConfig(filePath, opts, sync) { ...mergeOverrides(result, filePath), }; - ["plugins", "pluginSearchDirs"].forEach((optionName) => { + for (const optionName of ["plugins", "pluginSearchDirs"]) { if (Array.isArray(merged[optionName])) { merged[optionName] = merged[optionName].map((value) => typeof value === "string" && value.startsWith(".") // relative path @@ -93,7 +93,7 @@ function _resolveConfig(filePath, opts, sync) { : value ); } - }); + } if (!result && !editorConfigured) { return null; diff --git a/src/document/doc-builders.js b/src/document/doc-builders.js index 8d04efe5f..a875f9be0 100644 --- a/src/document/doc-builders.js +++ b/src/document/doc-builders.js @@ -42,7 +42,9 @@ function assertDoc(val) { */ function concat(parts) { if (process.env.NODE_ENV !== "production") { - parts.forEach(assertDoc); + for (const part of parts) { + assertDoc(part); + } } // We cannot do this until we change `printJSXElement` to not @@ -140,7 +142,9 @@ function conditionalGroup(states, opts) { */ function fill(parts) { if (process.env.NODE_ENV !== "production") { - parts.forEach(assertDoc); + for (const part of parts) { + assertDoc(part); + } } return { type: "fill", parts }; diff --git a/src/language-html/parser-html.js b/src/language-html/parser-html.js index 9607e81f0..748c3cf7a 100644 --- a/src/language-html/parser-html.js +++ b/src/language-html/parser-html.js @@ -184,7 +184,7 @@ function ngHtmlParser( const restoreNameAndValue = (node) => { if (node instanceof Element) { restoreName(node); - node.attrs.forEach((attr) => { + for (const attr of node.attrs) { restoreName(attr); if (!attr.valueSpan) { attr.value = null; @@ -194,7 +194,7 @@ function ngHtmlParser( attr.value = attr.value.slice(1, -1); } } - }); + } } else if (node instanceof Comment) { node.value = node.sourceSpan .toString() @@ -225,7 +225,7 @@ function ngHtmlParser( if (normalizeAttributeName) { const CURRENT_HTML_ELEMENT_ATTRIBUTES = HTML_ELEMENT_ATTRIBUTES[node.name] || Object.create(null); - node.attrs.forEach((attr) => { + for (const attr of node.attrs) { if (!attr.namespace) { attr.name = lowerCaseIfFn( attr.name, @@ -235,7 +235,7 @@ function ngHtmlParser( lowerCasedAttrName in CURRENT_HTML_ELEMENT_ATTRIBUTES) ); } - }); + } } } }; diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js index d72cc68b3..d4863e233 100644 --- a/src/language-html/printer-html.js +++ b/src/language-html/printer-html.js @@ -1147,7 +1147,7 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) { const value = getValue(); if (interpolationRegex.test(value)) { const parts = []; - value.split(interpolationRegex).forEach((part, index) => { + for (const [index, part] of value.split(interpolationRegex).entries()) { if (index % 2 === 0) { parts.push(replaceEndOfLineWith(part, literalline)); } else { @@ -1170,7 +1170,7 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) { parts.push("{{", replaceEndOfLineWith(part, literalline), "}}"); } } - }); + } return group(parts); } } diff --git a/src/language-js/clean.js b/src/language-js/clean.js index 9ff115bc1..5604e5b7b 100644 --- a/src/language-js/clean.js +++ b/src/language-js/clean.js @@ -107,7 +107,9 @@ function clean(ast, newObj, parent) { [] ); - quasis.forEach((q) => delete q.value); + for (const q of quasis) { + delete q.value; + } } // CSS template literals in css prop @@ -117,7 +119,9 @@ function clean(ast, newObj, parent) { ast.value.type === "JSXExpressionContainer" && ast.value.expression.type === "TemplateLiteral" ) { - newObj.value.expression.quasis.forEach((q) => delete q.value); + for (const q of newObj.value.expression.quasis) { + delete q.value; + } } // We change quotes @@ -139,7 +143,10 @@ function clean(ast, newObj, parent) { expression.arguments.length === 1 ) { const astProps = ast.expression.arguments[0].properties; - newObj.expression.arguments[0].properties.forEach((prop, index) => { + for (const [ + index, + prop, + ] of newObj.expression.arguments[0].properties.entries()) { let templateLiteral = null; switch (astProps[index].key.name) { @@ -156,9 +163,11 @@ function clean(ast, newObj, parent) { } if (templateLiteral) { - templateLiteral.quasis.forEach((q) => delete q.value); + for (const q of templateLiteral.quasis) { + delete q.value; + } } - }); + } } // styled-components, graphql, markdown @@ -174,7 +183,9 @@ function clean(ast, newObj, parent) { ast.tag.name === "html")) || ast.tag.type === "CallExpression") ) { - newObj.quasi.quasis.forEach((quasi) => delete quasi.value); + for (const quasi of newObj.quasi.quasis) { + delete quasi.value; + } } if (ast.type === "TemplateLiteral") { // This checks for a leading comment that is exactly `/* GraphQL */` @@ -195,17 +206,19 @@ function clean(ast, newObj, parent) { hasLanguageComment || (parent.type === "CallExpression" && parent.callee.name === "graphql") ) { - newObj.quasis.forEach((quasi) => delete quasi.value); + for (const quasi of newObj.quasis) { + delete quasi.value; + } } // TODO: check parser // `flow` and `typescript` don't have `leadingComments` if (!ast.leadingComments) { - newObj.quasis.forEach((quasi) => { + for (const quasi of newObj.quasis) { if (quasi.value) { delete quasi.value.cooked; } - }); + } } } diff --git a/src/language-js/embed/graphql.js b/src/language-js/embed/graphql.js index 895545f44..80d9d4764 100644 --- a/src/language-js/embed/graphql.js +++ b/src/language-js/embed/graphql.js @@ -82,25 +82,24 @@ function printGraphqlComments(lines) { const parts = []; let seenComment = false; - lines - .map((textLine) => textLine.trim()) - .forEach((textLine, i, array) => { - // Lines are either whitespace only, or a comment (with potential whitespace - // around it). Drop whitespace-only lines. - if (textLine === "") { - return; - } + const array = lines.map((textLine) => textLine.trim()); + for (const [i, textLine] of array.entries()) { + // Lines are either whitespace only, or a comment (with potential whitespace + // around it). Drop whitespace-only lines. + if (textLine === "") { + continue; + } - if (array[i - 1] === "" && seenComment) { - // If a non-first comment is preceded by a blank (whitespace only) line, - // add in a blank line. - parts.push([hardline, textLine]); - } else { - parts.push(textLine); - } + if (array[i - 1] === "" && seenComment) { + // If a non-first comment is preceded by a blank (whitespace only) line, + // add in a blank line. + parts.push([hardline, textLine]); + } else { + parts.push(textLine); + } - seenComment = true; - }); + seenComment = true; + } // If `lines` was whitespace only, return `null`. return parts.length === 0 ? null : join(hardline, parts); diff --git a/src/language-js/parser-babel.js b/src/language-js/parser-babel.js index c778462b3..dad0eded4 100644 --- a/src/language-js/parser-babel.js +++ b/src/language-js/parser-babel.js @@ -178,7 +178,9 @@ function shouldRethrowRecoveredError(error) { function parseJson(text, parsers, opts) { const ast = parseExpression(text, parsers, opts); - ast.comments.forEach(assertJsonNode); + for (const comment of ast.comments) { + assertJsonNode(comment); + } assertJsonNode(ast); return ast; @@ -187,9 +189,15 @@ function parseJson(text, parsers, opts) { function assertJsonNode(node, parent) { switch (node.type) { case "ArrayExpression": - return node.elements.forEach(assertJsonChildNode); + for (const element of node.elements) { + assertJsonChildNode(element); + } + return; case "ObjectExpression": - return node.properties.forEach(assertJsonChildNode); + for (const property of node.properties) { + assertJsonChildNode(property); + } + return; case "ObjectProperty": if (node.computed) { throw createJsonError("computed"); @@ -198,7 +206,10 @@ function assertJsonNode(node, parent) { if (node.shorthand) { throw createJsonError("shorthand"); } - return [node.key, node.value].forEach(assertJsonChildNode); + + assertJsonChildNode(node.key); + assertJsonChildNode(node.value); + return; case "UnaryExpression": switch (node.operator) { case "+": diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js index 41fc4ce1f..1d619a6f6 100644 --- a/src/language-js/print/jsx.js +++ b/src/language-js/print/jsx.js @@ -195,7 +195,7 @@ function printJsxElementInternal(path, options, print) { // Tweak how we format children if outputting this element over multiple lines. // Also detect whether we will force this element to output over multiple lines. const multilineChildren = []; - children.forEach((child, i) => { + for (const [i, child] of children.entries()) { // There are a number of situations where we need to ensure we display // whitespace as `{" "}` when outputting this element over multiple lines. if (child === jsxWhitespace) { @@ -203,19 +203,19 @@ function printJsxElementInternal(path, options, print) { if (children.length === 2) { // Solitary whitespace multilineChildren.push(rawJsxWhitespace); - return; + continue; } // Leading whitespace multilineChildren.push([rawJsxWhitespace, hardline]); - return; + continue; } else if (i === children.length - 1) { // Trailing whitespace multilineChildren.push(rawJsxWhitespace); - return; + continue; } else if (children[i - 1] === "" && children[i - 2] === hardline) { // Whitespace after line break multilineChildren.push(rawJsxWhitespace); - return; + continue; } } @@ -224,7 +224,7 @@ function printJsxElementInternal(path, options, print) { if (willBreak(child)) { forcedBreak = true; } - }); + } // If there is text we use `fill` to fit as much onto each line as possible. // When there is no text (just tags and expressions) we use `group` @@ -315,13 +315,13 @@ function printJsxChildren( return; } - words.forEach((word, i) => { + for (const [i, word] of words.entries()) { if (i % 2 === 1) { parts.push(line); } else { parts.push(word); } - }); + } if (endWhitespace !== undefined) { if (/\n/.test(endWhitespace)) { diff --git a/src/language-js/print/object.js b/src/language-js/print/object.js index c84307f46..e5c2b387d 100644 --- a/src/language-js/print/object.js +++ b/src/language-js/print/object.js @@ -97,7 +97,7 @@ function printObject(path, options, print) { // interleaved in the source code. So we need to reorder them before // printing them. const propsAndLoc = []; - fields.forEach((field) => { + for (const field of fields) { path.each((childPath) => { const node = childPath.getValue(); propsAndLoc.push({ @@ -106,7 +106,7 @@ function printObject(path, options, print) { loc: locStart(node), }); }, field); - }); + } /** @type {Doc[]} */ let separatorParts = []; diff --git a/src/language-js/print/template-literal.js b/src/language-js/print/template-literal.js index ad185cffb..23e5d5692 100644 --- a/src/language-js/print/template-literal.js +++ b/src/language-js/print/template-literal.js @@ -164,12 +164,12 @@ function printJestEachTemplateLiteral(path, options, print) { ...tableBody.filter((row) => row.cells.length > 0), ]; for (const { cells } of table.filter((row) => !row.hasLineBreak)) { - cells.forEach((cell, index) => { + for (const [index, cell] of cells.entries()) { maxColumnWidths[index] = Math.max( maxColumnWidths[index], getStringWidth(cell) ); - }); + } } parts.push( diff --git a/src/language-js/utils.js b/src/language-js/utils.js index 415816d3d..313631e60 100644 --- a/src/language-js/utils.js +++ b/src/language-js/utils.js @@ -1119,7 +1119,7 @@ function shouldFlatten(parentOp, nodeOp) { } const PRECEDENCE = {}; -[ +for (const [i, tier] of [ ["|>"], ["??"], ["||"], @@ -1133,11 +1133,11 @@ const PRECEDENCE = {}; ["+", "-"], ["*", "/", "%"], ["**"], -].forEach((tier, i) => { - tier.forEach((op) => { +].entries()) { + for (const op of tier) { PRECEDENCE[op] = i; - }); -}); + } +} function getPrecedence(op) { return PRECEDENCE[op]; diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 3f008dac9..0b1b02fb1 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -645,7 +645,7 @@ function printRoot(path, options, print) { let ignoreStart = null; const { children } = path.getValue(); - children.forEach((childNode, index) => { + for (const [index, childNode] of children.entries()) { switch (isPrettierIgnore(childNode)) { case "start": if (ignoreStart === null) { @@ -665,7 +665,7 @@ function printRoot(path, options, print) { // do nothing break; } - }); + } return printChildren(path, options, print, { processor: (childPath, index) => { diff --git a/src/language-markdown/utils.js b/src/language-markdown/utils.js index 166fc37f4..5520e7574 100644 --- a/src/language-markdown/utils.js +++ b/src/language-markdown/utils.js @@ -40,7 +40,6 @@ const punctuationRegex = new RegExp(punctuationPattern); /** * split text into whitespaces and words * @param {string} text - * @return {Array<{ type: "whitespace", value: " " | "\n" | "" } | { type: "word", value: string }>} */ function splitText(text, options) { const KIND_NON_CJK = "non-cjk"; @@ -48,77 +47,72 @@ function splitText(text, options) { const KIND_K_LETTER = "k-letter"; const KIND_CJK_PUNCTUATION = "cjk-punctuation"; + /** @type {Array<{ type: "whitespace", value: " " | "\n" | "" } | { type: "word", value: string }>} */ const nodes = []; - (options.proseWrap === "preserve" + const tokens = (options.proseWrap === "preserve" ? text : text.replace(new RegExp(`(${cjkPattern})\n(${cjkPattern})`, "g"), "$1$2") - ) - .split(/([\t\n ]+)/) - .forEach((token, index, tokens) => { - // whitespace - if (index % 2 === 1) { - nodes.push({ - type: "whitespace", - value: /\n/.test(token) ? "\n" : " ", - }); - return; + ).split(/([\t\n ]+)/); + for (const [index, token] of tokens.entries()) { + // whitespace + if (index % 2 === 1) { + nodes.push({ + type: "whitespace", + value: /\n/.test(token) ? "\n" : " ", + }); + continue; + } + + // word separated by whitespace + + if ((index === 0 || index === tokens.length - 1) && token === "") { + continue; + } + + const innerTokens = token.split(new RegExp(`(${cjkPattern})`)); + for (const [innerIndex, innerToken] of innerTokens.entries()) { + if ( + (innerIndex === 0 || innerIndex === innerTokens.length - 1) && + innerToken === "" + ) { + continue; } - // word separated by whitespace - - if ((index === 0 || index === tokens.length - 1) && token === "") { - return; + // non-CJK word + if (innerIndex % 2 === 0) { + if (innerToken !== "") { + appendNode({ + type: "word", + value: innerToken, + kind: KIND_NON_CJK, + hasLeadingPunctuation: punctuationRegex.test(innerToken[0]), + hasTrailingPunctuation: punctuationRegex.test(getLast(innerToken)), + }); + } + continue; } - token - .split(new RegExp(`(${cjkPattern})`)) - .forEach((innerToken, innerIndex, innerTokens) => { - if ( - (innerIndex === 0 || innerIndex === innerTokens.length - 1) && - innerToken === "" - ) { - return; - } - - // non-CJK word - if (innerIndex % 2 === 0) { - if (innerToken !== "") { - appendNode({ - type: "word", - value: innerToken, - kind: KIND_NON_CJK, - hasLeadingPunctuation: punctuationRegex.test(innerToken[0]), - hasTrailingPunctuation: punctuationRegex.test( - getLast(innerToken) - ), - }); + // CJK character + appendNode( + punctuationRegex.test(innerToken) + ? { + type: "word", + value: innerToken, + kind: KIND_CJK_PUNCTUATION, + hasLeadingPunctuation: true, + hasTrailingPunctuation: true, } - return; - } - - // CJK character - appendNode( - punctuationRegex.test(innerToken) - ? { - type: "word", - value: innerToken, - kind: KIND_CJK_PUNCTUATION, - hasLeadingPunctuation: true, - hasTrailingPunctuation: true, - } - : { - type: "word", - value: innerToken, - kind: kRegex.test(innerToken) - ? KIND_K_LETTER - : KIND_CJ_LETTER, - hasLeadingPunctuation: false, - hasTrailingPunctuation: false, - } - ); - }); - }); + : { + type: "word", + value: innerToken, + kind: kRegex.test(innerToken) ? KIND_K_LETTER : KIND_CJ_LETTER, + hasLeadingPunctuation: false, + hasTrailingPunctuation: false, + } + ); + } + } return nodes; diff --git a/src/main/comments.js b/src/main/comments.js index 15dc38dc0..a0476f577 100644 --- a/src/main/comments.js +++ b/src/main/comments.js @@ -70,9 +70,9 @@ function getSortedChildNodes(node, options, resultArray) { childNodesCache.set(node, resultArray); } - childNodes.forEach((childNode) => { + for (const childNode of childNodes) { getSortedChildNodes(childNode, options, resultArray); - }); + } return resultArray; } @@ -185,7 +185,7 @@ function attach(comments, ast, text, options) { isLastComment: comments.length - 1 === index, })); - decoratedComments.forEach((context, index) => { + for (const [index, context] of decoratedComments.entries()) { const { comment, precedingNode, @@ -205,11 +205,11 @@ function attach(comments, ast, text, options) { ) { if (locStart(comment) - locStart(ast) <= 0) { addLeadingComment(ast, comment); - return; + continue; } if (locEnd(comment) - locEnd(ast) >= 0) { addTrailingComment(ast, comment); - return; + continue; } } @@ -285,19 +285,19 @@ function attach(comments, ast, text, options) { addDanglingComment(ast, comment); } } - }); + } breakTies(tiesToBreak, text, options); if (!avoidAstMutation) { - comments.forEach((comment) => { + for (const comment of comments) { // These node references were useful for breaking ties, but we // don't need them anymore, and they create cycles in the AST that // may lead to infinite recursion if we don't delete them here. delete comment.precedingNode; delete comment.enclosingNode; delete comment.followingNode; - }); + } } } @@ -400,13 +400,13 @@ function breakTies(tiesToBreak, text, options) { } } - tiesToBreak.forEach(({ comment }, i) => { + for (const [i, { comment }] of tiesToBreak.entries()) { if (i < indexOfFirstLeadingComment) { addTrailingComment(precedingNode, comment); } else { addLeadingComment(followingNode, comment); } - }); + } for (const node of [precedingNode, followingNode]) { if (node.comments && node.comments.length > 1) { @@ -593,7 +593,7 @@ function ensureAllCommentsPrinted(astComments) { return; } - astComments.forEach((comment) => { + for (const comment of astComments) { if (!comment.printed) { throw new Error( 'Comment "' + @@ -602,7 +602,7 @@ function ensureAllCommentsPrinted(astComments) { ); } delete comment.printed; - }); + } } module.exports = { diff --git a/src/main/options.js b/src/main/options.js index 386e9eb99..544f31e26 100644 --- a/src/main/options.js +++ b/src/main/options.js @@ -86,11 +86,11 @@ function normalize(options, opts) { const mixedDefaults = { ...defaults, ...pluginDefaults }; - Object.keys(mixedDefaults).forEach((k) => { + for (const k of Object.keys(mixedDefaults)) { if (rawOptions[k] == null) { rawOptions[k] = mixedDefaults[k]; } - }); + } if (rawOptions.parser === "json") { rawOptions.trailingComma = "none"; diff --git a/tests_integration/__tests__/help-options.js b/tests_integration/__tests__/help-options.js index a1dc6ae44..a4db9f040 100644 --- a/tests_integration/__tests__/help-options.js +++ b/tests_integration/__tests__/help-options.js @@ -6,7 +6,7 @@ const constant = require("../../src/cli/constant"); const core = require("../../src/cli/core"); const arrayify = require("../../src/utils/arrayify"); -arrayify( +for (const option of arrayify( { ...core.createDetailedOptionMap( prettier.getSupportInfo({ @@ -18,17 +18,17 @@ arrayify( ...core.normalizeDetailedOptionMap(constant.options), }, "name" -).forEach((option) => { +)) { const optionNames = [ option.description ? option.name : null, option.oppositeDescription ? `no-${option.name}` : null, ].filter(Boolean); - optionNames.forEach((optionName) => { + for (const optionName of optionNames) { describe(`show detailed usage with --help ${optionName}`, () => { runPrettier("cli", ["--help", optionName]).test({ status: 0, }); }); - }); -}); + } +} diff --git a/tests_integration/__tests__/loglevel.js b/tests_integration/__tests__/loglevel.js index 8aba3aaf0..04b32154a 100644 --- a/tests_integration/__tests__/loglevel.js +++ b/tests_integration/__tests__/loglevel.js @@ -44,9 +44,9 @@ function runPrettierWithLogLevel(logLevel, patterns) { const stderr = stripAnsi(result.stderr); if (patterns) { - patterns.forEach((pattern) => { + for (const pattern of patterns) { expect(stderr).toMatch(pattern); - }); + } } else { expect(stderr).toMatch(/^\s*$/); } diff --git a/tests_integration/runPrettier.js b/tests_integration/runPrettier.js index ce568c462..8440f1157 100644 --- a/tests_integration/runPrettier.js +++ b/tests_integration/runPrettier.js @@ -119,7 +119,7 @@ function runPrettier(dir, args, options) { const testResult = (testOptions) => { testOptions = testOptions || {}; - Object.keys(result).forEach((name) => { + for (const name of Object.keys(result)) { test(`(${name})`, () => { const value = // \r is trimmed from jest snapshots by default; @@ -140,7 +140,7 @@ function runPrettier(dir, args, options) { expect(value).toMatchSnapshot(); } }); - }); + } return result; };