diff --git a/eslint.config.js b/eslint.config.js index 42d13a33b..8a22cc627 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -29,8 +29,8 @@ coverage/ dist*/ **/node_modules/** website/build/ -website/static/playground.js website/static/lib/ +website/static/playground/ website/.docusaurus scripts/benchmark/*/ **/.yarn/** @@ -413,6 +413,7 @@ export default [ files: ["src/language-*/**/*.js"], rules: { "prettier-internal-rules/directly-loc-start-end": "error", + "prettier-internal-rules/print-function-parameter-order": "error", }, }, { diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/print-function-parameter-order.js b/scripts/tools/eslint-plugin-prettier-internal-rules/print-function-parameter-order.js new file mode 100644 index 000000000..14033b75b --- /dev/null +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/print-function-parameter-order.js @@ -0,0 +1,55 @@ +const messageId = "print-function-parameter-order"; +const expectedParameters = ["path", "options", "print"]; + +export default { + meta: { + type: "suggestion", + messages: { + [messageId]: + "`{{functionName}}` function parameters should in order of `path`, `options` and `print`.", + }, + }, + create(context) { + const sourceCode = context.getSourceCode(); + + return { + ":function[params.length>=3]"(node) { + const parameterNames = node.params.map((node) => + node.type === "Identifier" ? node.name : "", + ); + + // `embed` function order is `textToDoc, print, path, options` + if (parameterNames.includes("textToDoc")) { + return; + } + + // Only if all three parameters exists + if (expectedParameters.some((name) => !parameterNames.includes(name))) { + return; + } + + // In correct order + if ( + expectedParameters.every( + (name, index) => name === parameterNames[index], + ) + ) { + return; + } + + const firstToken = sourceCode.getFirstToken(node); + const tokenBeforeBody = sourceCode.getTokenBefore(node.body); + const functionName = node.id ? node.id.name : "print*"; + context.report({ + node, + loc: { + start: firstToken.loc.start, + end: tokenBeforeBody.loc.end, + }, + messageId, + data: { functionName }, + }); + }, + }; + }, +}; diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js index 340b967a9..5878aa801 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js @@ -640,6 +640,31 @@ test("prefer-is-non-empty-array", { ], }); +test("print-function-parameter-order", { + valid: [ + "function printFoo(path, options, print) {}", + "function printFoo(path, opts, print) {}", + "function printFoo(path, options, printPath) {}", + "function printFoo() {}", + "function printFoo(path, print) {}", + "function printFoo(path, options) {}", + "function printFoo(print, options) {}", + "function embed(path, print, textToDoc, options) {}", + ], + invalid: [ + "function printFoo(path, print, options) {}", + "function printFoo(path, print, options, args) {}", + "function printFoo(options, print, path) {}", + "const printFoo = function (path, print, options) {}", + "const printFoo = function printFoo(path, print, options) {}", + "const printFoo = (path, print, options) => {}", + ].map((code) => ({ + code, + output: null, + errors: 1, + })), +}); + test("no-empty-flat-contents-for-if-break", { valid: [ "ifBreak('foo', 'bar')", diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js index c360d2312..44ed0f83b 100644 --- a/src/language-handlebars/printer-glimmer.js +++ b/src/language-handlebars/printer-glimmer.js @@ -79,17 +79,17 @@ function print(path, options, print) { if (isElseIfLike(path)) { return [ printElseIfLikeBlock(path, print), - printProgram(path, print, options), - printInverse(path, print, options), + printProgram(path, options, print), + printInverse(path, options, print), ]; } return [ printOpenBlock(path, print), group([ - printProgram(path, print, options), - printInverse(path, print, options), - printCloseBlock(path, print, options), + printProgram(path, options, print), + printInverse(path, options, print), + printCloseBlock(path, options, print), ]), ]; @@ -573,7 +573,7 @@ function printElseIfLikeBlock(path, print) { ]); } -function printCloseBlock(path, print, options) { +function printCloseBlock(path, options, print) { const { node } = path; if (options.htmlWhitespaceSensitivity === "ignore") { @@ -616,7 +616,7 @@ function blockStatementHasElse(node) { return node.type === "BlockStatement" && node.inverse; } -function printProgram(path, print, options) { +function printProgram(path, options, print) { const { node } = path; if (blockStatementHasOnlyWhitespaceInProgram(node)) { @@ -632,7 +632,7 @@ function printProgram(path, print, options) { return indent(program); } -function printInverse(path, print, options) { +function printInverse(path, options, print) { const { node } = path; const inverse = print("inverse"); diff --git a/src/language-js/print/array.js b/src/language-js/print/array.js index dc2625423..fa6c45340 100644 --- a/src/language-js/print/array.js +++ b/src/language-js/print/array.js @@ -135,9 +135,9 @@ function printArray(path, options, print) { printArrayElements( path, options, + print, elementsProperty, node.inexact, - print, ), trailingComma, ], @@ -192,7 +192,7 @@ function isLineAfterElementEmpty({ node }, { originalText: text }) { return isNextLineEmptyAfterIndex(text, skipToComma(locEnd(node))); } -function printArrayElements(path, options, elementsProperty, inexact, print) { +function printArrayElements(path, options, print, elementsProperty, inexact) { const parts = []; path.each(({ node, isLast }) => { diff --git a/src/language-js/print/arrow-function.js b/src/language-js/print/arrow-function.js index 05586c322..27fde8293 100644 --- a/src/language-js/print/arrow-function.js +++ b/src/language-js/print/arrow-function.js @@ -189,8 +189,8 @@ function printArrowFunctionSignature(path, options, print, args) { group([ printFunctionParameters( path, - print, options, + print, expandArg, /* printTypeParams */ true, ), diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index 92a838db0..4cd85975f 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -51,8 +51,8 @@ function printBinaryishExpression(path, options, print) { const parts = printBinaryishExpressions( path, - print, options, + print, /* isNested */ false, isInsideParenthesis, ); @@ -193,8 +193,8 @@ function printBinaryishExpression(path, options, print) { // broken before `+`. function printBinaryishExpressions( path, - print, options, + print, isNested, isInsideParenthesis, ) { @@ -225,8 +225,8 @@ function printBinaryishExpressions( (left) => printBinaryishExpressions( left, - print, options, + print, /* isNested */ true, isInsideParenthesis, ), @@ -283,8 +283,8 @@ function printBinaryishExpressions( (left) => printBinaryishExpressions( left, - print, options, + print, /* isNested */ true, isInsideParenthesis, ), diff --git a/src/language-js/print/component.js b/src/language-js/print/component.js index 777de08f9..cb6597396 100644 --- a/src/language-js/print/component.js +++ b/src/language-js/print/component.js @@ -32,7 +32,7 @@ function printComponent(path, options, print) { parts.push(print("typeParameters")); - const parametersDoc = printComponentParameters(path, print, options); + const parametersDoc = printComponentParameters(path, options, print); if (node.rendersType) { parts.push(group([parametersDoc, " ", print("rendersType")])); } else { @@ -50,7 +50,7 @@ function printComponent(path, options, print) { return parts; } -function printComponentParameters(path, print, options) { +function printComponentParameters(path, options, print) { const { node: componentNode } = path; let parameters = componentNode.params; if (componentNode.rest) { diff --git a/src/language-js/print/enum.js b/src/language-js/print/enum.js index 9e1f6d380..acb1bd832 100644 --- a/src/language-js/print/enum.js +++ b/src/language-js/print/enum.js @@ -1,9 +1,5 @@ import { printDeclareToken } from "./misc.js"; -import { printObject } from "./object.js"; - -function printEnumMembers(path, print, options) { - return printObject(path, options, print); -} +import { printObject as printEnumMembers } from "./object.js"; /* - `EnumBooleanMember`(flow) @@ -48,7 +44,7 @@ function printEnumMember(path, print) { - `EnumStringBody`(flow) - `EnumSymbolBody`(flow) */ -function printEnumBody(path, print, options) { +function printEnumBody(path, options, print) { const { node } = path; let type; @@ -72,7 +68,7 @@ function printEnumBody(path, print, options) { } } - return [type ? `of ${type} ` : "", printEnumMembers(path, print, options)]; + return [type ? `of ${type} ` : "", printEnumMembers(path, options, print)]; } /* @@ -80,7 +76,7 @@ function printEnumBody(path, print, options) { - `EnumDeclaration`(flow) - `TSEnumDeclaration`(TypeScript) */ -function printEnumDeclaration(path, print, options) { +function printEnumDeclaration(path, options, print) { const { node } = path; return [ printDeclareToken(path), @@ -89,7 +85,7 @@ function printEnumDeclaration(path, print, options) { print("id"), " ", node.type === "TSEnumDeclaration" - ? printEnumMembers(path, print, options) + ? printEnumMembers(path, options, print) : print("body"), ]; } diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index 3e5c93882..7f0adfe18 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -173,7 +173,7 @@ function printEstree(path, options, print, args) { return printRestSpread(path, print); case "FunctionDeclaration": case "FunctionExpression": - return printFunction(path, print, options, args); + return printFunction(path, options, print, args); case "ArrowFunctionExpression": return printArrowFunction(path, options, print, args); case "YieldExpression": @@ -632,7 +632,7 @@ function printEstree(path, options, print, args) { case "TemplateElement": return replaceEndOfLine(node.value.raw); case "TemplateLiteral": - return printTemplateLiteral(path, print, options); + return printTemplateLiteral(path, options, print); case "TaggedTemplateExpression": return printTaggedTemplateLiteral(path, print); case "PrivateIdentifier": diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index dd9057977..0005631cd 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -159,14 +159,14 @@ function printFlow(path, options, print) { case "DeclareEnum": case "EnumDeclaration": - return printEnumDeclaration(path, print, options); + return printEnumDeclaration(path, options, print); case "EnumBooleanBody": case "EnumNumberBody": case "EnumBigIntBody": case "EnumStringBody": case "EnumSymbolBody": - return printEnumBody(path, print, options); + return printEnumBody(path, options, print); case "EnumBooleanMember": case "EnumNumberMember": diff --git a/src/language-js/print/function-parameters.js b/src/language-js/print/function-parameters.js index a8e7773db..8c8273dbd 100644 --- a/src/language-js/print/function-parameters.js +++ b/src/language-js/print/function-parameters.js @@ -33,8 +33,8 @@ import { printFunctionTypeParameters } from "./misc.js"; function printFunctionParameters( path, - print, options, + print, expandArg, printTypeParams, ) { diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index 28743fc31..0c71e4b1c 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -52,7 +52,7 @@ const isMethodValue = ({ node, key, parent }) => - "FunctionExpression" - `TSDeclareFunction`(TypeScript) */ -function printFunction(path, print, options, args) { +function printFunction(path, options, print, args) { if (isMethodValue(path)) { return printMethodValue(path, options, print); } @@ -86,8 +86,8 @@ function printFunction(path, print, options, args) { const parametersDoc = printFunctionParameters( path, - print, options, + print, expandArg, ); const returnTypeDoc = printReturnType(path, print); @@ -155,7 +155,7 @@ function printMethod(path, options, print) { function printMethodValue(path, options, print) { const { node } = path; - const parametersDoc = printFunctionParameters(path, print, options); + const parametersDoc = printFunctionParameters(path, options, print); const returnTypeDoc = printReturnType(path, print); const shouldBreakParameters = shouldBreakFunctionParameters(node); const shouldGroupParameters = shouldGroupFunctionParameters( diff --git a/src/language-js/print/hook.js b/src/language-js/print/hook.js index 9da7de100..8f23a342e 100644 --- a/src/language-js/print/hook.js +++ b/src/language-js/print/hook.js @@ -25,8 +25,8 @@ function printHook(path, options, print) { const parametersDoc = printFunctionParameters( path, - print, options, + print, false, true, ); @@ -90,8 +90,8 @@ function printHookTypeAnnotation(path, options, print) { let parametersDoc = printFunctionParameters( path, - print, options, + print, /* expandArg */ false, /* printTypeParams */ true, ); diff --git a/src/language-js/print/template-literal.js b/src/language-js/print/template-literal.js index a2baf1f3f..db81d5a9d 100644 --- a/src/language-js/print/template-literal.js +++ b/src/language-js/print/template-literal.js @@ -22,7 +22,7 @@ import { isMemberExpression, } from "../utils/index.js"; -function printTemplateLiteral(path, print, options) { +function printTemplateLiteral(path, options, print) { const { node } = path; const isTemplateLiteral = node.type === "TemplateLiteral"; diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index 3b736a7a7..f53943c6e 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -298,8 +298,8 @@ function printFunctionType(path, options, print) { let parametersDoc = printFunctionParameters( path, - print, options, + print, /* expandArg */ false, /* printTypeParams */ true, ); diff --git a/src/language-js/print/typescript.js b/src/language-js/print/typescript.js index abd4a51b1..50ca6583c 100644 --- a/src/language-js/print/typescript.js +++ b/src/language-js/print/typescript.js @@ -103,7 +103,7 @@ function printTypescript(path, options, print) { return group([castGroup, print("expression")]); } case "TSDeclareFunction": - return printFunction(path, print, options); + return printFunction(path, options, print); case "TSExportAssignment": return ["export = ", print("expression"), semi]; case "TSModuleBlock": @@ -133,7 +133,7 @@ function printTypescript(path, options, print) { ), ]; case "TSTemplateLiteralType": - return printTemplateLiteral(path, print, options); + return printTemplateLiteral(path, options, print); case "TSNamedTupleMember": return printNamedTupleMember(path, options, print); case "TSRestType": @@ -245,8 +245,8 @@ function printTypescript(path, options, print) { const parametersDoc = printFunctionParameters( path, - print, options, + print, /* expandArg */ false, /* printTypeParams */ true, ); @@ -274,7 +274,7 @@ function printTypescript(path, options, print) { case "TSNamespaceExportDeclaration": return ["export as namespace ", print("id"), options.semi ? ";" : ""]; case "TSEnumDeclaration": - return printEnumDeclaration(path, print, options); + return printEnumDeclaration(path, options, print); case "TSEnumMember": return printEnumMember(path, print); diff --git a/src/language-yaml/print/block.js b/src/language-yaml/print/block.js index 7bd988004..564bd1874 100644 --- a/src/language-yaml/print/block.js +++ b/src/language-yaml/print/block.js @@ -17,7 +17,7 @@ import { } from "../utils.js"; import { alignWithSpaces } from "./misc.js"; -function printBlock(path, print, options) { +function printBlock(path, options, print) { const { node } = path; const parentIndent = path.ancestors.filter( (node) => node.type === "sequence" || node.type === "mapping", diff --git a/src/language-yaml/print/flow-mapping-sequence.js b/src/language-yaml/print/flow-mapping-sequence.js index 36c0dc7f7..4a964743b 100644 --- a/src/language-yaml/print/flow-mapping-sequence.js +++ b/src/language-yaml/print/flow-mapping-sequence.js @@ -8,7 +8,7 @@ import { import { hasEndComments, isEmptyNode } from "../utils.js"; import { alignWithSpaces, printNextEmptyLine } from "./misc.js"; -function printFlowMapping(path, print, options) { +function printFlowMapping(path, options, print) { const { node } = path; const isMapping = node.type === "flowMapping"; const openMarker = isMapping ? "{" : "["; @@ -29,7 +29,7 @@ function printFlowMapping(path, print, options) { openMarker, alignWithSpaces(options.tabWidth, [ bracketSpacing, - printChildren(path, print, options), + printChildren(path, options, print), options.trailingComma === "none" ? "" : ifBreak(","), hasEndComments(node) ? [hardline, join(hardline, path.map(print, "endComments"))] @@ -40,7 +40,7 @@ function printFlowMapping(path, print, options) { ]; } -function printChildren(path, print, options) { +function printChildren(path, options, print) { return path.map( ({ isLast, node, next }) => [ print(), diff --git a/src/language-yaml/print/mapping-item.js b/src/language-yaml/print/mapping-item.js index c9c4b408f..b04c88d7a 100644 --- a/src/language-yaml/print/mapping-item.js +++ b/src/language-yaml/print/mapping-item.js @@ -18,7 +18,7 @@ import { } from "../utils.js"; import { alignWithSpaces } from "./misc.js"; -function printMappingItem(path, print, options) { +function printMappingItem(path, options, print) { const { node, parent } = path; const { key, value } = node; diff --git a/src/language-yaml/printer-yaml.js b/src/language-yaml/printer-yaml.js index 4f71218ab..b1f447c0e 100644 --- a/src/language-yaml/printer-yaml.js +++ b/src/language-yaml/printer-yaml.js @@ -305,7 +305,7 @@ function printNode(path, options, print) { } case "blockFolded": case "blockLiteral": - return printBlock(path, print, options); + return printBlock(path, options, print); case "mapping": case "sequence": @@ -317,12 +317,12 @@ function printNode(path, options, print) { return !node.content ? "" : print("content"); case "mappingItem": case "flowMappingItem": - return printMappingItem(path, print, options); + return printMappingItem(path, options, print); case "flowMapping": - return printFlowMapping(path, print, options); + return printFlowMapping(path, options, print); case "flowSequence": - return printFlowSequence(path, print, options); + return printFlowSequence(path, options, print); case "flowSequenceItem": return print("content"); default: