diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f085574..2f185de80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# 0.22.0 + +[link](https://github.com/jlongster/prettier/compare/0.21.0...0.22.0) + +* Run 0.21.0 (#876) +* Fix paren removal on UnionTypeAnnotation (#878) +* Fix typo (#891) +* Ensure no parens for JSX inside of an ArrayExpression (#895) +* Fix object expression in arrow function expression (#897) +* Fix unprinted comments in destructuring (#898) +* Fix bug with importing empty type (#904) +* Fix broken export comment (#899) +* Add CLI Example to Readme (#909) +* Fix 0.5e0 (#911) +* Fix binary expression instanceof in arrow function expression (#913) +* Improve readme CLI usage example (#910) +* Do not break long it/test calls when template literal (#893) +* Update lint-staged docs to use husky for less config. (#923) +* Fix files with comments only (#813) +* Update README.md (#928) +* Fix binary op as body in arrow expression (#921) +* cleanup needsParens (#935) +* [JSX] Break if opening element breaks (#942) +* Parenthesize function expressions in expression position (#941) +* update the README to add a pre-commit hook (#944) +* Fix #951: properly parenthesize ** expressions (#952) +* [WIP] TypeScript Parser (#915) +* Do not break long `describe` calls (#953) +* Recursively find leading comments inside ReturnStatements (#955) +* Fix `in` inside of a for in a nested way (#954) +* Make comments around empty parenthesis be inside (#957) +* Stabilize comment after object label (#958) +* Inline BinaryExpressions inside JSXExpression (#965) +* Only allow same-line arrow-less body for explicit nodes (#966) + # 0.21.0 [link](https://github.com/jlongster/prettier/compare/0.20.0...0.21.0) diff --git a/docs/prettier.min.js b/docs/prettier.min.js index 8e55747c9..7914d770f 100644 --- a/docs/prettier.min.js +++ b/docs/prettier.min.js @@ -8987,7 +8987,8 @@ var PRECEDENCE = {}; ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], - ["*", "/", "%", "**"] + ["*", "/", "%"], + ["**"] ].forEach(function(tier, i) { tier.forEach(function(op) { PRECEDENCE[op] = i; @@ -9078,7 +9079,7 @@ function getSortedChildNodes(node, text, resultArray) { if (!resultArray) { Object.defineProperty(node, childNodesCacheKey, { - value: resultArray = [], + value: (resultArray = []), enumerable: false }); } @@ -9152,24 +9153,38 @@ function attach(comments, ast, text, options) { var tiesToBreak = []; - comments.forEach(function(comment) { + comments.forEach((comment, i) => { decorateComment(ast, comment, text); const precedingNode = comment.precedingNode; const enclosingNode = comment.enclosingNode; const followingNode = comment.followingNode; + const isLastComment = comments.length - 1 === i; + if (util.hasNewline(text, locStart(comment), { backwards: true })) { // If a comment exists on its own line, prefer a leading comment. // We also need to check if it's the first line of the file. if ( - handleLastFunctionArgComments(precedingNode, enclosingNode, followingNode, comment) || + handleLastFunctionArgComments( + precedingNode, + enclosingNode, + followingNode, + comment + ) || handleMemberExpressionComments(enclosingNode, followingNode, comment) || handleIfStatementComments(enclosingNode, followingNode, comment) || handleTryStatementComments(enclosingNode, followingNode, comment) || - handleImportSpecifierComments(enclosingNode, comment) || handleClassComments(enclosingNode, comment) || - handleUnionTypeComments(precedingNode, enclosingNode, followingNode, comment) + handleImportSpecifierComments(enclosingNode, comment) || + handleObjectPropertyComments(enclosingNode, comment) || + handleUnionTypeComments( + precedingNode, + enclosingNode, + followingNode, + comment + ) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) ) { // We're good } else if (followingNode) { @@ -9194,8 +9209,11 @@ function attach(comments, ast, text, options) { ) || handleImportSpecifierComments(enclosingNode, comment) || handleTemplateLiteralComments(enclosingNode, comment) || + handleClassComments(enclosingNode, comment) || handleCallExpressionComments(precedingNode, enclosingNode, comment) || - handleClassComments(enclosingNode, comment) + handlePropertyComments(enclosingNode, comment) || + handleExportNamedDeclarationComments(enclosingNode, comment) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) ) { // We're good } else if (precedingNode) { @@ -9213,9 +9231,10 @@ function attach(comments, ast, text, options) { } else { if ( handleIfStatementComments(enclosingNode, followingNode, comment) || - handleObjectProperty(enclosingNode, precedingNode, comment) || + handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || handleTemplateLiteralComments(enclosingNode, comment) || - handleFunctionDeclarationComments(enclosingNode, comment) + handleCommentInEmptyParens(enclosingNode, comment) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) ) { // We're good } else if (precedingNode && followingNode) { @@ -9439,7 +9458,7 @@ function handleConditionalExpressionComments( return false; } -function handleObjectProperty(enclosingNode, precedingNode, comment) { +function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { if ( enclosingNode && (enclosingNode.type === "ObjectProperty" || @@ -9468,35 +9487,61 @@ function handleTemplateLiteralComments(enclosingNode, comment) { return false; } -function handleFunctionDeclarationComments(enclosingNode, comment) { +function handleCommentInEmptyParens(enclosingNode, comment) { // Only add dangling comments to fix the case when no params are present, // i.e. a function without any argument. if ( enclosingNode && - enclosingNode.type === "FunctionDeclaration" && - enclosingNode.params.length === 0 + (((enclosingNode.type === "FunctionDeclaration" || + enclosingNode.type === "FunctionExpression" || + enclosingNode.type === "ArrowFunctionExpression" || + enclosingNode.type === "ClassMethod" || + enclosingNode.type === "ObjectMethod") && + enclosingNode.params.length === 0) || + (enclosingNode.type === "CallExpression" && + enclosingNode.arguments.length === 0)) ) { addDanglingComment(enclosingNode, comment); return true; } + if (enclosingNode && + (enclosingNode.type === "MethodDefinition" && + enclosingNode.value.params.length === 0)) { + addDanglingComment(enclosingNode.value, comment); + return true; + } return false; } -function handleLastFunctionArgComments(precedingNode, enclosingNode, followingNode, comment) { +function handleLastFunctionArgComments( + precedingNode, + enclosingNode, + followingNode, + comment +) { // Type definitions functions - if (precedingNode && precedingNode.type === 'FunctionTypeParam' && - enclosingNode && enclosingNode.type === 'FunctionTypeAnnotation' && - followingNode && followingNode.type !== 'FunctionTypeParam') { + if ( + precedingNode && + precedingNode.type === "FunctionTypeParam" && + enclosingNode && + enclosingNode.type === "FunctionTypeAnnotation" && + followingNode && + followingNode.type !== "FunctionTypeParam" + ) { addTrailingComment(precedingNode, comment); return true; } // Real functions - if (precedingNode && precedingNode.type === 'Identifier' && - enclosingNode && ( - enclosingNode.type === 'ArrowFunctionExpression' || - enclosingNode.type === 'FunctionExpression') && - followingNode && followingNode.type !== 'Identifier') { + if ( + precedingNode && + precedingNode.type === "Identifier" && + enclosingNode && + (enclosingNode.type === "ArrowFunctionExpression" || + enclosingNode.type === "FunctionExpression") && + followingNode && + followingNode.type !== "Identifier" + ) { addTrailingComment(precedingNode, comment); return true; } @@ -9523,26 +9568,93 @@ function handleImportSpecifierComments(enclosingNode, comment) { return false; } +function handleObjectPropertyComments(enclosingNode, comment) { + if (enclosingNode && enclosingNode.type === "ObjectProperty") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + function handleCallExpressionComments(precedingNode, enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "CallExpression" && - precedingNode && enclosingNode.callee === precedingNode && - enclosingNode.arguments.length > 0) { + if ( + enclosingNode && + enclosingNode.type === "CallExpression" && + precedingNode && + enclosingNode.callee === precedingNode && + enclosingNode.arguments.length > 0 + ) { addLeadingComment(enclosingNode.arguments[0], comment); return true; } return false; } -function handleUnionTypeComments(precedingNode, enclosingNode, followingNode, comment) { - if (enclosingNode && enclosingNode.type === 'UnionTypeAnnotation' && - precedingNode && precedingNode.type === 'ObjectTypeAnnotation' && - followingNode && followingNode.type === 'ObjectTypeAnnotation') { +function handleUnionTypeComments( + precedingNode, + enclosingNode, + followingNode, + comment +) { + if ( + enclosingNode && + enclosingNode.type === "UnionTypeAnnotation" && + precedingNode && + precedingNode.type === "ObjectTypeAnnotation" && + followingNode && + followingNode.type === "ObjectTypeAnnotation" + ) { addTrailingComment(precedingNode, comment); return true; } return false; } +function handlePropertyComments(enclosingNode, comment) { + if ( + enclosingNode && ( + enclosingNode.type === "Property" || + enclosingNode.type === "ObjectProperty" + ) + ) { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleExportNamedDeclarationComments(enclosingNode, comment) { + if (enclosingNode && enclosingNode.type === "ExportNamedDeclaration") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { + // With Flow the enclosingNode is undefined so use the AST instead. + if (ast && ast.body && ast.body.length === 0) { + if (isLastComment) { + addDanglingComment(ast, comment); + } else { + addLeadingComment(ast, comment); + } + return true; + } else if ( + enclosingNode && enclosingNode.type === 'Program' && + enclosingNode.body.length === 0 && enclosingNode.directives && + enclosingNode.directives.length === 0 + ) { + if (isLastComment) { + addDanglingComment(enclosingNode, comment); + } else { + addLeadingComment(enclosingNode, comment); + } + return true; + } + return false; +} + function printComment(commentPath) { const comment = commentPath.getValue(); comment.printed = true; @@ -9716,7 +9828,7 @@ function printComments(path, print, options) { var comments$1 = { attach, printComments, printDanglingComments }; var name = "prettier"; -var version$2 = "0.21.0"; +var version$2 = "0.22.0"; var description = "Prettier is an opinionated JavaScript formatter"; var bin = {"prettier":"./bin/prettier.js"}; var repository = {"type":"git","url":"git+https://github.com/jlongster/prettier.git"}; @@ -9725,7 +9837,7 @@ var license = "MIT"; var bugs = {"url":"https://github.com/jlongster/prettier/issues"}; var main$2 = "./index.js"; var dependencies = {"ast-types":"0.9.4","babel-code-frame":"6.22.0","babylon":"6.15.0","chalk":"1.1.3","esutils":"2.0.2","flow-parser":"0.40.0","get-stdin":"5.0.1","glob":"7.1.1","jest-validate":"19.0.0","minimist":"1.2.0"}; -var devDependencies = {"diff":"3.2.0","jest":"19.0.1","rollup":"0.41.1","rollup-plugin-commonjs":"7.0.0","rollup-plugin-json":"2.1.0","rollup-plugin-node-builtins":"2.0.0","rollup-plugin-node-globals":"1.1.0","rollup-plugin-node-resolve":"2.0.0"}; +var devDependencies = {"diff":"3.2.0","jest":"19.0.1","rollup":"0.41.1","rollup-plugin-commonjs":"7.0.0","rollup-plugin-json":"2.1.0","rollup-plugin-node-builtins":"2.0.0","rollup-plugin-node-globals":"1.1.0","rollup-plugin-node-resolve":"2.0.0","typescript":"2.2.1","typescript-eslint-parser":"git://github.com/eslint/typescript-eslint-parser.git#215a012ec4d272939fa5a57d0231d22fb7f7a9e0"}; var scripts = {"test":"jest","format":"./bin/prettier.js --write","format:single":"npm run format -- src/printer.js","format:all":"npm run format -- index.js src/*.js bin/*.js","build:docs":"rollup -c docs/rollup.config.js"}; var jest = {"setupFiles":["/tests_config/run_spec.js"],"testRegex":"jsfmt\\.spec\\.js$","testPathIgnorePatterns":["tests/new_react","tests/more_react"]}; var _package = { @@ -9787,7 +9899,7 @@ FastPath$1.from = function(obj) { // lightweight FastPath [..., name, value] stacks. var copy = Object.create(FastPath$1.prototype); var stack = [obj.value]; - for (var pp; pp = obj.parentPath; obj = pp) + for (var pp; (pp = obj.parentPath); obj = pp) stack.push(obj.name, pp.value); copy.stack = stack.reverse(); return copy; @@ -9994,20 +10106,17 @@ FPp.needsParens = function(assumeExpressionContext) { return true; } - // The left-hand side of the ** exponentiation operator must always - // be parenthesized unless it's an ident or literal if ( - parent.type === "BinaryExpression" && - parent.operator === "**" && - parent.left === node && - node.type !== "Identifier" && - node.type !== "Literal" && - node.type !== "NumericLiteral" + parent.type === "ArrowFunctionExpression" && parent.body === node && startsWithNoLookaheadToken(node, /* forbidFunctionAndClass */ false) + || parent.type === "ExpressionStatement" && startsWithNoLookaheadToken(node, /* forbidFunctionAndClass */ true) ) { return true; } switch (node.type) { + case "CallExpression": + return false; + case "SpreadElement": case "SpreadProperty": return parent.type === "MemberExpression" && @@ -10015,26 +10124,12 @@ FPp.needsParens = function(assumeExpressionContext) { parent.object === node; case "UpdateExpression": - switch (parent.type) { - case "MemberExpression": - return name === "object" && parent.object === node; - - case "TaggedTemplateExpression": - case "NewExpression": - return true; - - case "UnaryExpression": - if ( - node.prefix && - ((node.operator === "++" && parent.operator === "+") || - (node.operator === "--" && parent.operator === "-")) - ) { - return true; - } - - return false; + if (parent.type === "UnaryExpression") { + return node.prefix && + ((node.operator === "++" && parent.operator === "+") || + (node.operator === "--" && parent.operator === "-")); } - + // else fall through case "UnaryExpression": switch (parent.type) { case "UnaryExpression": @@ -10043,21 +10138,40 @@ FPp.needsParens = function(assumeExpressionContext) { case "MemberExpression": return name === "object" && parent.object === node; + + case "TaggedTemplateExpression": + return true; + + case "NewExpression": + case "CallExpression": + return name === "callee" && parent.callee == node; + + case "BinaryExpression": + return parent.operator === "**" && name === "left"; + + default: + return false; } case "BinaryExpression": - if ( - node.operator === "in" && - parent.type === "ForStatement" && - parent.init === node - ) { + const isLeftOfAForStatement = node => { + let i = 0; + while (node) { + let parent = this.getParentNode(i++); + if (!parent) { + return false; + } + if (parent.type === "ForStatement" && parent.init === node) { + return true; + } + node = parent; + } + return false; + }; + if (node.operator === "in" && isLeftOfAForStatement(node)) { return true; } - - if (node.operator === "in" && parent.type === "AssignmentExpression") { - return true; - } - + // else fall through case "LogicalExpression": switch (parent.type) { case "CallExpression": @@ -10088,6 +10202,10 @@ FPp.needsParens = function(assumeExpressionContext) { return true; } + if (no === "**" && po === "**") { + return name === "left"; + } + if (pp === np && name === "right") { assert$6.strictEqual(parent.right, node); return true; @@ -10122,6 +10240,7 @@ FPp.needsParens = function(assumeExpressionContext) { if (parent.type === "UnaryExpression") { return true; } + // else fall through case "AwaitExpression": switch (parent.type) { case "TaggedTemplateExpression": @@ -10148,7 +10267,8 @@ FPp.needsParens = function(assumeExpressionContext) { case "IntersectionTypeAnnotation": case "UnionTypeAnnotation": - return parent.type === "NullableTypeAnnotation" || + return parent.type === "ArrayTypeAnnotation" || + parent.type === "NullableTypeAnnotation" || parent.type === "IntersectionTypeAnnotation" || parent.type === "UnionTypeAnnotation"; @@ -10167,21 +10287,15 @@ FPp.needsParens = function(assumeExpressionContext) { parent.object === node; case "AssignmentExpression": - if ( - parent.type === "ArrowFunctionExpression" && - parent.body === node - ) { + if (parent.type === "ArrowFunctionExpression" && parent.body === node) { return node.left.type === "ObjectPattern"; - } - if (parent.type === "ForStatement" && - (parent.init === node || parent.update === node)) { - return false; - } - if (parent.type === "ExpressionStatement") { - if (node.left.type === "ObjectPattern") { - return true; - } + } else if ( + parent.type === "ForStatement" && + (parent.init === node || parent.update === node) + ) { return false; + } else if (parent.type === "ExpressionStatement") { + return node.left.type === "ObjectPattern"; } return true; @@ -10209,108 +10323,64 @@ FPp.needsParens = function(assumeExpressionContext) { return name === "object" && parent.object === node; default: - return n$2.ObjectPattern.check(node.left) && this.firstInStatement(); + return false; } case "FunctionExpression": - case "ArrowFunctionExpression": - if (parent.type === "CallExpression" && name === "callee") { - return true; + switch (parent.type) { + case "CallExpression": + return name === "callee"; // Not strictly necessary, but it's clearer to the reader if IIFEs are wrapped in parentheses. + case "TaggedTemplateExpression": + return true; // This is basically a kind of IIFE. + case "ExportDefaultDeclaration": + return true; + default: + return false; } + case "ArrowFunctionExpression": switch (parent.type) { - case "ConditionalExpression": - if (parent.test === node) { - return true; - } - return false; - - case "ExportDefaultDeclaration": - if ( - node.type === "ArrowFunctionExpression" || - node.type === "FunctionDeclaration" - ) { - return false; - } - return true; - - case "ExpressionStatement": - case "MemberExpression": - case "TaggedTemplateExpression": - case "UnaryExpression": - return true; + case "CallExpression": + return name === "callee"; case "NewExpression": return name === "callee"; + case "MemberExpression": + return name === "object"; + + case "BindExpression": + case "TaggedTemplateExpression": + case "UnaryExpression": case "LogicalExpression": - return node.type === "ArrowFunctionExpression"; + case "BinaryExpression": + return true; + + case "ConditionalExpression": + return name === "test"; default: - return isBinary(parent); + return false; } case "ClassExpression": - switch (parent.type) { - case "TaggedTemplateExpression": - case "BinaryExpression": - case "ExportDefaultDeclaration": - case "ExpressionStatement": - return true; - case "CallExpression": - if (parent.callee === node) { - return true; - } - case "MemberExpression": - return name === "object" && parent.object === node; - case "ConditionalExpression": - if (parent.test === node) { - return true; - } - } - - return false; - - case "ObjectExpression": - if (parent.type === "ArrowFunctionExpression" && name === "body") { - return true; - } - if (parent.type === "TaggedTemplateExpression") { - return true; - } - if (parent.type === "MemberExpression") { - return name === "object" && parent.object === node; - } + return parent.type === "ExportDefaultDeclaration"; case "StringLiteral": - if (parent.type === "ExpressionStatement") { - return true; - } - - default: - if ( - parent.type === "NewExpression" && - name === "callee" && - parent.callee === node - ) { - return containsCallExpression(node); - } + return parent.type === "ExpressionStatement"; // To avoid becoming a directive } if ( - assumeExpressionContext !== true && - !this.canBeFirstInStatement() && - this.firstInStatement() - ) - return true; + parent.type === "NewExpression" && + name === "callee" && + parent.callee === node + ) { + return containsCallExpression(node); + } return false; }; -function isBinary(node) { - return n$2.BinaryExpression.check(node) || n$2.LogicalExpression.check(node); -} - function containsCallExpression(node) { if (n$2.CallExpression.check(node)) { return true; @@ -10329,86 +10399,50 @@ function containsCallExpression(node) { return false; } -FPp.canBeFirstInStatement = function() { - var node = this.getNode(); - return !n$2.FunctionExpression.check(node) && - !n$2.ObjectExpression.check(node) && - !n$2.ClassExpression.check(node) && - !(n$2.AssignmentExpression.check(node) && n$2.ObjectPattern.check(node.left)); -}; - -FPp.firstInStatement = function() { - var s = this.stack; - var parentName, parent; - var childName, child; - - for (var i = s.length - 1; i >= 0; i -= 2) { - if (n$2.Node.check(s[i])) { - childName = parentName; - child = parent; - parentName = s[i - 1]; - parent = s[i]; - } - - if (!parent || !child) { - continue; - } - - if ( - n$2.BlockStatement.check(parent) && parentName === "body" && childName === 0 - ) { - assert$6.strictEqual(parent.body[0], child); +// Tests if an expression starts with `{`, or (if forbidFunctionAndClass holds) `function` or `class`. +// Will be overzealous if there's already necessary grouping parentheses. +function startsWithNoLookaheadToken(node, forbidFunctionAndClass) { + node = getLeftMost(node); + switch (node.type) { + case "FunctionExpression": + case "ClassExpression": + return forbidFunctionAndClass; + case "ObjectExpression": return true; - } - - if (n$2.ExpressionStatement.check(parent) && childName === "expression") { - assert$6.strictEqual(parent.expression, child); - return true; - } - - if ( - n$2.SequenceExpression.check(parent) && - parentName === "expressions" && - childName === 0 - ) { - assert$6.strictEqual(parent.expressions[0], child); - continue; - } - - if (n$2.CallExpression.check(parent) && childName === "callee") { - assert$6.strictEqual(parent.callee, child); - continue; - } - - if (n$2.MemberExpression.check(parent) && childName === "object") { - assert$6.strictEqual(parent.object, child); - continue; - } - - if (n$2.ConditionalExpression.check(parent) && childName === "test") { - assert$6.strictEqual(parent.test, child); - continue; - } - - if (isBinary(parent) && childName === "left") { - assert$6.strictEqual(parent.left, child); - continue; - } - - if ( - n$2.UnaryExpression.check(parent) && - !parent.prefix && - childName === "argument" - ) { - assert$6.strictEqual(parent.argument, child); - continue; - } - - return false; + case "MemberExpression": + return startsWithNoLookaheadToken(node.object, forbidFunctionAndClass); + case "TaggedTemplateExpression": + if (node.tag.type === "FunctionExpression") { + // IIFEs are always already parenthesized + return false; + } + return startsWithNoLookaheadToken(node.tag, forbidFunctionAndClass); + case "CallExpression": + if (node.callee.type === "FunctionExpression") { + // IIFEs are always already parenthesized + return false; + } + return startsWithNoLookaheadToken(node.callee, forbidFunctionAndClass); + case "ConditionalExpression": + return startsWithNoLookaheadToken(node.test, forbidFunctionAndClass); + case "UpdateExpression": + return !node.prefix && startsWithNoLookaheadToken(node.argument, forbidFunctionAndClass); + case "BindExpression": + return node.object && startsWithNoLookaheadToken(node.object, forbidFunctionAndClass); + case "SequenceExpression": + return startsWithNoLookaheadToken(node.expressions[0], forbidFunctionAndClass) + default: + return false; } +} - return true; -}; +function getLeftMost(node) { + if (node.left) { + return getLeftMost(node.left); + } else { + return node; + } +} var fastPath = FastPath$1; @@ -10751,25 +10785,18 @@ function genericPrintNoParens(path, options, print) { n.body.type === "ArrayExpression" || n.body.type === "ObjectExpression" || n.body.type === "JSXElement" || - n.body.type === "BlockStatement" + n.body.type === "BlockStatement" || + n.body.type === "TaggedTemplateExpression" || + n.body.type === "TemplateElement" ) { return group$1(collapsed); } - // These nested groups are a little wonky, but because - // `conditionalGroup` suppresses break propagation, we want to - // re-propagate it. We still want to allow the printer to choose - // the more collapsed version, but still break parents if there - // are any hard breaks in the content. return group$1( - conditionalGroup$1([ - collapsed, - concat$2([ - concat$2(parts), - group$1(indent$2(options.tabWidth, concat$2([line$1, body]))) - ]) - ]), - { shouldBreak: willBreak$2(body) } + concat$2([ + concat$2(parts), + group$1(indent$2(options.tabWidth, concat$2([line$1, body]))) + ]) ); case "MethodDefinition": if (n.static) { @@ -10935,6 +10962,8 @@ function genericPrintNoParens(path, options, print) { } fromParts.push(grouped.length === 0 ? line$1 : " ", "from "); + } else if (n.importKind && n.importKind === "type") { + parts.push("{} from "); } fromParts.push(path.call(print, "source"), ";"); @@ -11013,24 +11042,22 @@ function genericPrintNoParens(path, options, print) { case "ReturnStatement": parts.push("return"); - if ( - n.argument && - n.argument.comments && - n.argument.comments.some(comment => comment.leading) - ) { - parts.push( - concat$2([ - " (", - indent$2( - options.tabWidth, - concat$2([softline$1, path.call(print, "argument")]) - ), - line$1, - ")" - ]) - ); - } else if (n.argument) { - parts.push(" ", path.call(print, "argument")); + if (n.argument) { + if (returnArgumentHasLeadingComment(options, n.argument)) { + parts.push( + concat$2([ + " (", + indent$2( + options.tabWidth, + concat$2([softline$1, path.call(print, "argument")]) + ), + line$1, + ")" + ]) + ); + } else { + parts.push(" ", path.call(print, "argument")); + } } parts.push(";"); @@ -11039,13 +11066,20 @@ function genericPrintNoParens(path, options, print) { case "CallExpression": { if ( // We want to keep require calls as a unit - n.callee.type === "Identifier" && n.callee.name === "require" || - // `it('long name', () => {` should not break - (n.callee.type === "Identifier" && ( - n.callee.name === "it" || n.callee.name === "test") && + (n.callee.type === "Identifier" && n.callee.name === "require") || + // Keep test declarations on a single line + // e.g. `it('long name', () => {` + (n.callee.type === "Identifier" && + (n.callee.name === "it" || + n.callee.name === "test" || + n.callee.name === "describe") && n.arguments.length === 2 && - (n.arguments[0].type === "StringLiteral" || n.arguments[0].type === "Literal" && typeof n.arguments[0].value === "string") && - (n.arguments[1].type === "FunctionExpression" || n.arguments[1].type === "ArrowFunctionExpression") && + (n.arguments[0].type === "StringLiteral" || + n.arguments[0].type === "TemplateLiteral" || + (n.arguments[0].type === "Literal" && + typeof n.arguments[0].value === "string")) && + (n.arguments[1].type === "FunctionExpression" || + n.arguments[1].type === "ArrowFunctionExpression") && n.arguments[1].params.length <= 1) ) { return concat$2([ @@ -11669,7 +11703,7 @@ function genericPrintNoParens(path, options, print) { n.expression.type === "JSXEmptyExpression" || (parent.type === "JSXElement" && (n.expression.type === "ConditionalExpression" || - n.expression.type === "LogicalExpression")); + isBinaryish(n.expression))); if (shouldInline) { return group$1( @@ -11737,7 +11771,11 @@ function genericPrintNoParens(path, options, print) { case "JSXText": throw new Error("JSXTest should be handled by JSXElement"); case "JSXEmptyExpression": - return comments$3.printDanglingComments(path, options, /* sameIndent */ true); + return comments$3.printDanglingComments( + path, + options, + /* sameIndent */ true + ); case "TypeAnnotatedIdentifier": return concat$2([ path.call(print, "annotation"), @@ -12030,7 +12068,7 @@ function genericPrintNoParens(path, options, print) { const parent = path.getParentNode(); // If there's a leading comment, the parent is doing the indentation - const shouldIndent = !(parent.type === 'TypeAlias' && + const shouldIndent = !(parent.type === "TypeAlias" && hasLeadingOwnLineComment(options.originalText, n)); const token = isIntersection ? "&" : "|"; @@ -12355,7 +12393,11 @@ function printArgumentsList(path, options, print) { var printed = path.map(print, "arguments"); if (printed.length === 0) { - return "()"; + return concat$2([ + "(", + comments$3.printDanglingComments(path, options, /* sameIndent */ true), + ")" + ]); } const args = path.getValue().arguments; @@ -12443,8 +12485,7 @@ function printFunctionParams(path, print, options) { const lastParam = util$4.getLast(path.getValue().params); const canHaveTrailingComma = !(lastParam && - lastParam.type === "RestElement") && - !fun.rest; + lastParam.type === "RestElement") && !fun.rest; // If the parent is a call with the last argument expansion and this is the // params of the last argument, we dont want the arguments to break and instead @@ -12457,9 +12498,11 @@ function printFunctionParams(path, print, options) { // }) ) => { // }) const parent = path.getParentNode(); - if ((parent.type === "CallExpression" || parent.type === "NewExpression") && + if ( + (parent.type === "CallExpression" || parent.type === "NewExpression") && util$4.getLast(parent.arguments) === path.getValue() && - shouldGroupLastArg(parent.arguments)) { + shouldGroupLastArg(parent.arguments) + ) { return concat$2(["(", join$2(", ", printed), ")"]); } @@ -13009,12 +13052,13 @@ function printJSXElement(path, options, print) { assert$5.ok(!n.closingElement); return openingLines; } + // Record any breaks. Should never go from true to false, only false to true. + let forcedBreak = willBreak$2(openingLines); const jsxWhitespace = options.singleQuote ? ifBreak$1("{' '}", " ") : ifBreak$1('{" "}', " "); const children = printJSXChildren(path, options, print, jsxWhitespace); - let forcedBreak = false; // Trim trailing lines, recording if there was a hardline let numTrailingHard = 0; @@ -13119,6 +13163,7 @@ function maybeWrapJSXElementInParens(path, elem, options) { if (!parent) return elem; const NO_WRAP_PARENTS = { + ArrayExpression: true, JSXElement: true, JSXExpressionContainer: true, ExpressionStatement: true, @@ -13170,10 +13215,12 @@ function printBinaryishExpressions(path, parts, print, options, isNested) { // is where the rest of the expression will exist. Binary // expressions on the right side mean they have a difference // precedence level and should be treated as a separate group, so - // print them normally. + // print them normally. (This doesn't hold for the `**` operator, + // which is unique in that it is right-associative.) if ( util$4.getPrecedence(node.left.operator) === util$4.getPrecedence(node.operator) + && node.operator !== "**" ) { // Flatten them out by recursively calling this function. The // printed values will all be appended to `parts`. @@ -13201,8 +13248,7 @@ function printBinaryishExpressions(path, parts, print, options, isNested) { // If there's only a single binary expression, we want to create a group // in order to avoid having a small right part like -1 be on its own line. const parent = path.getParentNode(); - const shouldGroup = - parent.type !== node.type && + const shouldGroup = parent.type !== node.type && node.left.type !== node.type && node.right.type !== node.type; @@ -13323,7 +13369,9 @@ function printNumber(rawNumber) { return rawNumber .toLowerCase() // Remove unnecessary plus and zeroes from scientific notation. - .replace(/^([\d.]+e)(?:\+|(-))?0*/, "$1$2") + .replace(/^([\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3") + // Remove unnecessary scientific notation (1e0). + .replace(/^([\d.]+)e[+-]?0+$/, "$1") // Make sure numbers always start with a digit. .replace(/^\./, "0.") // Remove trailing dot. @@ -13348,6 +13396,44 @@ function hasLeadingOwnLineComment(text, node) { return res; } +// This recurses the return argument, looking for the first token +// (the leftmost leaf node) and, if it (or its parents) has any +// leadingComments, returns true (so it can be wrapped in parens). +function returnArgumentHasLeadingComment(options, argument) { + if (hasLeadingOwnLineComment(options.originalText, argument)) { + return true; + } + + const hasCommentableLeftSide = argument.type === "BinaryExpression" || + argument.type === "LogicalExpression" || + argument.type === "ConditionalExpression" || + argument.type === "CallExpression" || + argument.type === "MemberExpression" || + argument.type === "SequenceExpression" || + argument.type === "TaggedTemplateExpression"; + + if (hasCommentableLeftSide) { + const getLeftSide = (node) => { + if (node.expressions) { + return node.expressions[0]; + } + return node.left || node.test || node.callee || node.object || node.tag; + }; + + let leftMost = argument; + let newLeftMost; + while (newLeftMost = getLeftSide(leftMost)) { + leftMost = newLeftMost; + + if (hasLeadingOwnLineComment(options.originalText, leftMost)) { + return true; + } + } + } + + return false; +} + // Hack to differentiate between the following two which have the same ast // type T = { method: () => void }; // type T = { method(): void }; @@ -28957,7 +29043,20 @@ function parseWithBabylon(text) { }); } -var parser$1 = { parseWithFlow, parseWithBabylon }; +function parseWithTypeScript(text) { + // const parser = require('typescript-eslint-parser') + return parser.parse(text, { + loc: true, + range: true, + tokens: true, + attachComment: true, + ecmaFeatures: { + jsx: true, + } + }) +} + +var parser_1 = { parseWithFlow, parseWithBabylon, parseWithTypeScript }; function flattenDoc(doc) { if (doc.type === "concat") { @@ -29071,7 +29170,7 @@ const version = require$$2$11.version; const printAstToDoc = printer.printAstToDoc; const printDocToString = docPrinter.printDocToString; const normalizeOptions = options.normalize; -const parser = parser$1; +const parser$1 = parser_1; const printDocToDebug = docDebug.printDocToDebug; function guessLineEnding(text) { @@ -29083,9 +29182,15 @@ function guessLineEnding(text) { } function parse(text, opts) { - const parseFunction = opts.parser === "flow" - ? parser.parseWithFlow - : parser.parseWithBabylon; + let parseFunction; + + if (opts.parser === 'flow') { + parseFunction = parser$1.parseWithFlow; + } else if (opts.parser === 'typescript') { + parseFunction = parser$1.parseWithTypeScript; + } else { + parseFunction = parser$1.parseWithBabylon; + } try { return parseFunction(text); diff --git a/package.json b/package.json index af9334c90..51a54e5d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prettier", - "version": "0.21.0", + "version": "0.22.0", "description": "Prettier is an opinionated JavaScript formatter", "bin": { "prettier": "./bin/prettier.js"