UNPKG

eslint-config-chain-able

Version:
1,812 lines (1,608 loc) 407 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var require$$0 = _interopDefault(require('assert')); var path = _interopDefault(require('path')); function assertDoc(val) { if ( !(typeof val === "string" || (val != null && typeof val.type === "string")) ) { throw new Error( "Value " + JSON.stringify(val) + " is not a valid document" ); } } function concat$1(parts) { parts.forEach(assertDoc); // We cannot do this until we change `printJSXElement` to not // access the internals of a document directly. // if(parts.length === 1) { // // If it's a single document, no need to concat it. // return parts[0]; // } return { type: "concat", parts }; } function indent$1(contents) { assertDoc(contents); return { type: "indent", contents }; } function align(n, contents) { assertDoc(contents); return { type: "align", contents, n }; } function group(contents, opts) { opts = opts || {}; assertDoc(contents); return { type: "group", contents: contents, break: !!opts.shouldBreak, expandedStates: opts.expandedStates }; } function conditionalGroup(states, opts) { return group( states[0], Object.assign(opts || {}, { expandedStates: states }) ); } function fill(parts) { parts.forEach(assertDoc); return { type: "fill", parts }; } function ifBreak(breakContents, flatContents) { if (breakContents) { assertDoc(breakContents); } if (flatContents) { assertDoc(flatContents); } return { type: "if-break", breakContents, flatContents }; } function lineSuffix$1(contents) { assertDoc(contents); return { type: "line-suffix", contents }; } const lineSuffixBoundary = { type: "line-suffix-boundary" }; const breakParent$1 = { type: "break-parent" }; const line = { type: "line" }; const softline = { type: "line", soft: true }; const hardline$1 = concat$1([{ type: "line", hard: true }, breakParent$1]); const literalline = concat$1([ { type: "line", hard: true, literal: true }, breakParent$1 ]); const cursor$1 = { type: "cursor", placeholder: Symbol() }; function join$1(sep, arr) { const res = []; for (let i = 0; i < arr.length; i++) { if (i !== 0) { res.push(sep); } res.push(arr[i]); } return concat$1(res); } function addAlignmentToDoc(doc, size, tabWidth) { let aligned = doc; if (size > 0) { // Use indent to add tabs for all the levels of tabs we need for (let i = 0; i < Math.floor(size / tabWidth); ++i) { aligned = indent$1(aligned); } // Use align for all the spaces that are needed aligned = align(size % tabWidth, aligned); // size is absolute from 0 and not relative to the current // indentation, so we use -Infinity to reset the indentation to 0 aligned = align(-Infinity, aligned); } return aligned; } var docBuilders$1 = { concat: concat$1, join: join$1, line, softline, hardline: hardline$1, literalline, group, conditionalGroup, fill, lineSuffix: lineSuffix$1, lineSuffixBoundary, cursor: cursor$1, breakParent: breakParent$1, ifBreak, indent: indent$1, align, addAlignmentToDoc }; function isExportDeclaration(node) { if (node) { switch (node.type) { case "ExportDeclaration": case "ExportDefaultDeclaration": case "ExportDefaultSpecifier": case "DeclareExportDeclaration": case "ExportNamedDeclaration": case "ExportAllDeclaration": return true; } } return false; } function getParentExportDeclaration(path$$1) { const parentNode = path$$1.getParentNode(); if (path$$1.getName() === "declaration" && isExportDeclaration(parentNode)) { return parentNode; } return null; } function getPenultimate(arr) { if (arr.length > 1) { return arr[arr.length - 2]; } return null; } function getLast(arr) { if (arr.length > 0) { return arr[arr.length - 1]; } return null; } function skip(chars) { return (text, index, opts) => { const backwards = opts && opts.backwards; // Allow `skip` functions to be threaded together without having // to check for failures (did someone say monads?). if (index === false) { return false; } const length = text.length; let cursor = index; while (cursor >= 0 && cursor < length) { const c = text.charAt(cursor); if (chars instanceof RegExp) { if (!chars.test(c)) { return cursor; } } else if (chars.indexOf(c) === -1) { return cursor; } backwards ? cursor-- : cursor++; } if (cursor === -1 || cursor === length) { // If we reached the beginning or end of the file, return the // out-of-bounds cursor. It's up to the caller to handle this // correctly. We don't want to indicate `false` though if it // actually skipped valid characters. return cursor; } return false; }; } const skipWhitespace = skip(/\s/); const skipSpaces = skip(" \t"); const skipToLineEnd = skip(",; \t"); const skipEverythingButNewLine = skip(/[^\r\n]/); function skipInlineComment(text, index) { if (index === false) { return false; } if (text.charAt(index) === "/" && text.charAt(index + 1) === "*") { for (let i = index + 2; i < text.length; ++i) { if (text.charAt(i) === "*" && text.charAt(i + 1) === "/") { return i + 2; } } } return index; } function skipTrailingComment(text, index) { if (index === false) { return false; } if (text.charAt(index) === "/" && text.charAt(index + 1) === "/") { return skipEverythingButNewLine(text, index); } return index; } // This one doesn't use the above helper function because it wants to // test \r\n in order and `skip` doesn't support ordering and we only // want to skip one newline. It's simple to implement. function skipNewline(text, index, opts) { const backwards = opts && opts.backwards; if (index === false) { return false; } const atIndex = text.charAt(index); if (backwards) { if (text.charAt(index - 1) === "\r" && atIndex === "\n") { return index - 2; } if ( atIndex === "\n" || atIndex === "\r" || atIndex === "\u2028" || atIndex === "\u2029" ) { return index - 1; } } else { if (atIndex === "\r" && text.charAt(index + 1) === "\n") { return index + 2; } if ( atIndex === "\n" || atIndex === "\r" || atIndex === "\u2028" || atIndex === "\u2029" ) { return index + 1; } } return index; } function hasNewline(text, index, opts) { opts = opts || {}; const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts); const idx2 = skipNewline(text, idx, opts); return idx !== idx2; } function hasNewlineInRange(text, start, end) { for (let i = start; i < end; ++i) { if (text.charAt(i) === "\n") { return true; } } return false; } // Note: this function doesn't ignore leading comments unlike isNextLineEmpty function isPreviousLineEmpty(text, node) { let idx = locStart$1(node) - 1; idx = skipSpaces(text, idx, { backwards: true }); idx = skipNewline(text, idx, { backwards: true }); idx = skipSpaces(text, idx, { backwards: true }); const idx2 = skipNewline(text, idx, { backwards: true }); return idx !== idx2; } function isNextLineEmpty(text, node) { let oldIdx = null; let idx = locEnd$1(node); while (idx !== oldIdx) { // We need to skip all the potential trailing inline comments oldIdx = idx; idx = skipToLineEnd(text, idx); idx = skipInlineComment(text, idx); idx = skipSpaces(text, idx); } idx = skipTrailingComment(text, idx); idx = skipNewline(text, idx); return hasNewline(text, idx); } function getNextNonSpaceNonCommentCharacter$1(text, node) { let oldIdx = null; let idx = locEnd$1(node); while (idx !== oldIdx) { oldIdx = idx; idx = skipSpaces(text, idx); idx = skipInlineComment(text, idx); idx = skipTrailingComment(text, idx); idx = skipNewline(text, idx); } return text.charAt(idx); } function hasSpaces(text, index, opts) { opts = opts || {}; const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts); return idx !== index; } function locStart$1(node) { // Handle nodes with decorators. They should start at the first decorator if ( node.declaration && node.declaration.decorators && node.declaration.decorators.length > 0 ) { return locStart$1(node.declaration.decorators[0]); } if (node.decorators && node.decorators.length > 0) { return locStart$1(node.decorators[0]); } if (node.__location) { return node.__location.startOffset; } if (node.range) { return node.range[0]; } if (typeof node.start === "number") { return node.start; } if (node.source) { return lineColumnToIndex(node.source.start, node.source.input.css) - 1; } if (node.loc) { return node.loc.start; } } function locEnd$1(node) { const endNode = node.nodes && getLast(node.nodes); if (endNode && node.source && !node.source.end) { node = endNode; } let loc; if (node.range) { loc = node.range[1]; } else if (typeof node.end === "number") { loc = node.end; } else if (node.source) { loc = lineColumnToIndex(node.source.end, node.source.input.css); } if (node.__location) { return node.__location.endOffset; } if (node.typeAnnotation) { return Math.max(loc, locEnd$1(node.typeAnnotation)); } if (node.loc && !loc) { return node.loc.end; } return loc; } // Super inefficient, needs to be cached. function lineColumnToIndex(lineColumn, text) { let index = 0; for (let i = 0; i < lineColumn.line - 1; ++i) { index = text.indexOf("\n", index) + 1; if (index === -1) { return -1; } } return index + lineColumn.column; } function setLocStart(node, index) { if (node.range) { node.range[0] = index; } else { node.start = index; } } function setLocEnd(node, index) { if (node.range) { node.range[1] = index; } else { node.end = index; } } const PRECEDENCE = {}; [ ["||"], ["&&"], ["|"], ["^"], ["&"], ["==", "===", "!=", "!=="], ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"], ["**"] ].forEach((tier, i) => { tier.forEach(op => { PRECEDENCE[op] = i; }); }); function getPrecedence(op) { return PRECEDENCE[op]; } // 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) { // Hack. Remove after https://github.com/eslint/typescript-eslint-parser/issues/331 case "ObjectPattern": return !forbidFunctionAndClass; case "FunctionExpression": case "ClassExpression": return forbidFunctionAndClass; case "ObjectExpression": return true; 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 ); case "TSAsExpression": return startsWithNoLookaheadToken( node.expression, forbidFunctionAndClass ); default: return false; } } function getLeftMost(node) { if (node.left) { return getLeftMost(node.left); } return node; } function hasBlockComments(node) { return node.comments && node.comments.some(isBlockComment); } function isBlockComment(comment) { return comment.type === "Block" || comment.type === "CommentBlock"; } function getAlignmentSize(value, tabWidth, startIndex) { startIndex = startIndex || 0; let size = 0; for (let i = startIndex; i < value.length; ++i) { if (value[i] === "\t") { // Tabs behave in a way that they are aligned to the nearest // multiple of tabWidth: // 0 -> 4, 1 -> 4, 2 -> 4, 3 -> 4 // 4 -> 8, 5 -> 8, 6 -> 8, 7 -> 8 ... size = size + tabWidth - size % tabWidth; } else { size++; } } return size; } var util$2 = { getPrecedence, isExportDeclaration, getParentExportDeclaration, getPenultimate, getLast, getNextNonSpaceNonCommentCharacter: getNextNonSpaceNonCommentCharacter$1, skipWhitespace, skipSpaces, skipNewline, isNextLineEmpty, isPreviousLineEmpty, hasNewline, hasNewlineInRange, hasSpaces, locStart: locStart$1, locEnd: locEnd$1, setLocStart, setLocEnd, startsWithNoLookaheadToken, hasBlockComments, isBlockComment, getAlignmentSize }; const assert = require$$0; const docBuilders = docBuilders$1; const concat = docBuilders.concat; const hardline = docBuilders.hardline; const breakParent = docBuilders.breakParent; const indent = docBuilders.indent; const lineSuffix = docBuilders.lineSuffix; const join = docBuilders.join; const cursor = docBuilders.cursor; const util$1 = util$2; const childNodesCacheKey = Symbol("child-nodes"); const locStart = util$1.locStart; const locEnd = util$1.locEnd; const getNextNonSpaceNonCommentCharacter = util$1.getNextNonSpaceNonCommentCharacter; function getSortedChildNodes(node, text, resultArray) { if (!node) { return; } if (resultArray) { if ( node && ((node.type && node.type !== "CommentBlock" && node.type !== "CommentLine" && node.type !== "Line" && node.type !== "Block" && node.type !== "EmptyStatement" && node.type !== "TemplateElement") || (node.kind && node.kind !== "Comment")) ) { // This reverse insertion sort almost always takes constant // time because we almost always (maybe always?) append the // nodes in order anyway. let i; for (i = resultArray.length - 1; i >= 0; --i) { if ( locStart(resultArray[i]) <= locStart(node) && locEnd(resultArray[i]) <= locEnd(node) ) { break; } } resultArray.splice(i + 1, 0, node); return; } } else if (node[childNodesCacheKey]) { return node[childNodesCacheKey]; } let names; if (node && typeof node === "object") { names = Object.keys(node).filter( n => n !== "enclosingNode" && n !== "precedingNode" && n !== "followingNode" ); } else { return; } if (!resultArray) { Object.defineProperty(node, childNodesCacheKey, { value: (resultArray = []), enumerable: false }); } for (let i = 0, nameCount = names.length; i < nameCount; ++i) { getSortedChildNodes(node[names[i]], text, resultArray); } return resultArray; } // As efficiently as possible, decorate the comment object with // .precedingNode, .enclosingNode, and/or .followingNode properties, at // least one of which is guaranteed to be defined. function decorateComment(node, comment, text) { const childNodes = getSortedChildNodes(node, text); let precedingNode, followingNode; // Time to dust off the old binary search robes and wizard hat. let left = 0, right = childNodes.length; while (left < right) { const middle = (left + right) >> 1; const child = childNodes[middle]; if ( locStart(child) - locStart(comment) <= 0 && locEnd(comment) - locEnd(child) <= 0 ) { // The comment is completely contained by this child node. comment.enclosingNode = child; decorateComment(child, comment, text); return; // Abandon the binary search at this level. } if (locEnd(child) - locStart(comment) <= 0) { // This child node falls completely before the comment. // Because we will never consider this node or any nodes // before it again, this node must be the closest preceding // node we have encountered so far. precedingNode = child; left = middle + 1; continue; } if (locEnd(comment) - locStart(child) <= 0) { // This child node falls completely after the comment. // Because we will never consider this node or any nodes after // it again, this node must be the closest following node we // have encountered so far. followingNode = child; right = middle; continue; } throw new Error("Comment location overlaps with node location"); } // We don't want comments inside of different expressions inside of the same // template literal to move to another expression. if ( comment.enclosingNode && comment.enclosingNode.type === "TemplateLiteral" ) { const quasis = comment.enclosingNode.quasis; const commentIndex = findExpressionIndexForComment(quasis, comment); if ( precedingNode && findExpressionIndexForComment(quasis, precedingNode) !== commentIndex ) { precedingNode = null; } if ( followingNode && findExpressionIndexForComment(quasis, followingNode) !== commentIndex ) { followingNode = null; } } if (precedingNode) { comment.precedingNode = precedingNode; } if (followingNode) { comment.followingNode = followingNode; } } function attach(comments, ast, text) { if (!Array.isArray(comments)) { return; } const tiesToBreak = []; 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$1.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( text, precedingNode, enclosingNode, followingNode, comment ) || handleMemberExpressionComments(enclosingNode, followingNode, comment) || handleIfStatementComments( text, precedingNode, enclosingNode, followingNode, comment ) || handleTryStatementComments(enclosingNode, followingNode, comment) || handleClassComments(enclosingNode, comment) || handleImportSpecifierComments(enclosingNode, comment) || handleObjectPropertyComments(enclosingNode, comment) || handleForComments(enclosingNode, precedingNode, comment) || handleUnionTypeComments( precedingNode, enclosingNode, followingNode, comment ) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || handleImportDeclarationComments( text, enclosingNode, precedingNode, comment ) || handleAssignmentPatternComments(enclosingNode, comment) ) { // We're good } else if (followingNode) { // Always a leading comment. addLeadingComment(followingNode, comment); } else if (precedingNode) { addTrailingComment(precedingNode, comment); } else if (enclosingNode) { addDanglingComment(enclosingNode, comment); } else { // There are no nodes, let's attach it to the root of the ast addDanglingComment(ast, comment); } } else if (util$1.hasNewline(text, locEnd(comment))) { if ( handleLastFunctionArgComments( text, precedingNode, enclosingNode, followingNode, comment ) || handleConditionalExpressionComments( enclosingNode, precedingNode, followingNode, comment, text ) || handleImportSpecifierComments(enclosingNode, comment) || handleIfStatementComments( text, precedingNode, enclosingNode, followingNode, comment ) || handleClassComments(enclosingNode, comment) || handleLabeledStatementComments(enclosingNode, comment) || handleCallExpressionComments(precedingNode, enclosingNode, comment) || handlePropertyComments(enclosingNode, comment) || handleExportNamedDeclarationComments(enclosingNode, comment) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || handleClassMethodComments(enclosingNode, comment) || handleTypeAliasComments(enclosingNode, followingNode, comment) || handleVariableDeclaratorComments(enclosingNode, followingNode, comment) ) { // We're good } else if (precedingNode) { // There is content before this comment on the same line, but // none after it, so prefer a trailing comment of the previous node. addTrailingComment(precedingNode, comment); } else if (followingNode) { addLeadingComment(followingNode, comment); } else if (enclosingNode) { addDanglingComment(enclosingNode, comment); } else { // There are no nodes, let's attach it to the root of the ast addDanglingComment(ast, comment); } } else { if ( handleIfStatementComments( text, precedingNode, enclosingNode, followingNode, comment ) || handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || handleCommentInEmptyParens(text, enclosingNode, comment) || handleMethodNameComments(enclosingNode, precedingNode, comment) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) ) { // We're good } else if (precedingNode && followingNode) { // Otherwise, text exists both before and after the comment on // the same line. If there is both a preceding and following // node, use a tie-breaking algorithm to determine if it should // be attached to the next or previous node. In the last case, // simply attach the right node; const tieCount = tiesToBreak.length; if (tieCount > 0) { const lastTie = tiesToBreak[tieCount - 1]; if (lastTie.followingNode !== comment.followingNode) { breakTies(tiesToBreak, text); } } tiesToBreak.push(comment); } else if (precedingNode) { addTrailingComment(precedingNode, comment); } else if (followingNode) { addLeadingComment(followingNode, comment); } else if (enclosingNode) { addDanglingComment(enclosingNode, comment); } else { // There are no nodes, let's attach it to the root of the ast addDanglingComment(ast, comment); } } }); breakTies(tiesToBreak, text); comments.forEach(comment => { // 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; }); } function breakTies(tiesToBreak, text) { const tieCount = tiesToBreak.length; if (tieCount === 0) { return; } const precedingNode = tiesToBreak[0].precedingNode; const followingNode = tiesToBreak[0].followingNode; let gapEndPos = locStart(followingNode); // Iterate backwards through tiesToBreak, examining the gaps // between the tied comments. In order to qualify as leading, a // comment must be separated from followingNode by an unbroken series of // whitespace-only gaps (or other comments). let indexOfFirstLeadingComment; for ( indexOfFirstLeadingComment = tieCount; indexOfFirstLeadingComment > 0; --indexOfFirstLeadingComment ) { const comment = tiesToBreak[indexOfFirstLeadingComment - 1]; assert.strictEqual(comment.precedingNode, precedingNode); assert.strictEqual(comment.followingNode, followingNode); const gap = text.slice(locEnd(comment), gapEndPos); if (/\S/.test(gap)) { // The gap string contained something other than whitespace. break; } gapEndPos = locStart(comment); } tiesToBreak.forEach((comment, i) => { if (i < indexOfFirstLeadingComment) { addTrailingComment(precedingNode, comment); } else { addLeadingComment(followingNode, comment); } }); tiesToBreak.length = 0; } function addCommentHelper(node, comment) { const comments = node.comments || (node.comments = []); comments.push(comment); comment.printed = false; // For some reason, TypeScript parses `// x` inside of JSXText as a comment // We already "print" it via the raw text, we don't need to re-print it as a // comment if (node.type === "JSXText") { comment.printed = true; } } function addLeadingComment(node, comment) { comment.leading = true; comment.trailing = false; addCommentHelper(node, comment); } function addDanglingComment(node, comment) { comment.leading = false; comment.trailing = false; addCommentHelper(node, comment); } function addTrailingComment(node, comment) { comment.leading = false; comment.trailing = true; addCommentHelper(node, comment); } function addBlockStatementFirstComment(node, comment) { const body = node.body.filter(n => n.type !== "EmptyStatement"); if (body.length === 0) { addDanglingComment(node, comment); } else { addLeadingComment(body[0], comment); } } function addBlockOrNotComment(node, comment) { if (node.type === "BlockStatement") { addBlockStatementFirstComment(node, comment); } else { addLeadingComment(node, comment); } } // There are often comments before the else clause of if statements like // // if (1) { ... } // // comment // else { ... } // // They are being attached as leading comments of the BlockExpression which // is not well printed. What we want is to instead move the comment inside // of the block and make it leadingComment of the first element of the block // or dangling comment of the block if there is nothing inside // // if (1) { ... } // else { // // comment // ... // } function handleIfStatementComments( text, precedingNode, enclosingNode, followingNode, comment ) { if ( !enclosingNode || enclosingNode.type !== "IfStatement" || !followingNode ) { return false; } // We unfortunately have no way using the AST or location of nodes to know // if the comment is positioned before or after the condition parenthesis: // if (a /* comment */) {} // if (a) /* comment */ {} // The only workaround I found is to look at the next character to see if // it is a ). if (getNextNonSpaceNonCommentCharacter(text, comment) === ")") { addTrailingComment(precedingNode, comment); return true; } if (followingNode.type === "BlockStatement") { addBlockStatementFirstComment(followingNode, comment); return true; } if (followingNode.type === "IfStatement") { addBlockOrNotComment(followingNode.consequent, comment); return true; } return false; } // Same as IfStatement but for TryStatement function handleTryStatementComments(enclosingNode, followingNode, comment) { if ( !enclosingNode || enclosingNode.type !== "TryStatement" || !followingNode ) { return false; } if (followingNode.type === "BlockStatement") { addBlockStatementFirstComment(followingNode, comment); return true; } if (followingNode.type === "TryStatement") { addBlockOrNotComment(followingNode.finalizer, comment); return true; } if (followingNode.type === "CatchClause") { addBlockOrNotComment(followingNode.body, comment); return true; } return false; } function handleMemberExpressionComments(enclosingNode, followingNode, comment) { if ( enclosingNode && enclosingNode.type === "MemberExpression" && followingNode && followingNode.type === "Identifier" ) { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleConditionalExpressionComments( enclosingNode, precedingNode, followingNode, comment, text ) { const isSameLineAsPrecedingNode = precedingNode && !util$1.hasNewlineInRange(text, locEnd(precedingNode), locStart(comment)); if ( (!precedingNode || !isSameLineAsPrecedingNode) && enclosingNode && enclosingNode.type === "ConditionalExpression" && followingNode ) { addLeadingComment(followingNode, comment); return true; } return false; } function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { if ( enclosingNode && (enclosingNode.type === "ObjectProperty" || enclosingNode.type === "Property") && enclosingNode.shorthand && enclosingNode.key === precedingNode && enclosingNode.value.type === "AssignmentPattern" ) { addTrailingComment(enclosingNode.value.left, comment); return true; } return false; } function handleMethodNameComments(enclosingNode, precedingNode, comment) { // This is only needed for estree parsers (flow, typescript) to attach // after a method name: // obj = { fn /*comment*/() {} }; if ( enclosingNode && precedingNode && (enclosingNode.type === "Property" || enclosingNode.type === "MethodDefinition") && precedingNode.type === "Identifier" && enclosingNode.key === precedingNode ) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleCommentInEmptyParens(text, enclosingNode, comment) { if (getNextNonSpaceNonCommentCharacter(text, comment) !== ")") { return false; } // 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.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( text, precedingNode, enclosingNode, followingNode, comment ) { // Type definitions functions 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" || precedingNode.type === "AssignmentPattern") && enclosingNode && (enclosingNode.type === "ArrowFunctionExpression" || enclosingNode.type === "FunctionExpression" || enclosingNode.type === "FunctionDeclaration" || enclosingNode.type === "ObjectMethod" || enclosingNode.type === "ClassMethod") && getNextNonSpaceNonCommentCharacter(text, comment) === ")" ) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleClassComments(enclosingNode, comment) { if ( enclosingNode && (enclosingNode.type === "ClassDeclaration" || enclosingNode.type === "ClassExpression") ) { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleImportSpecifierComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "ImportSpecifier") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleObjectPropertyComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "ObjectProperty") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleLabeledStatementComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "LabeledStatement") { 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 ) { addLeadingComment(enclosingNode.arguments[0], comment); return true; } return false; } function handleUnionTypeComments( precedingNode, enclosingNode, followingNode, comment ) { if ( enclosingNode && (enclosingNode.type === "UnionTypeAnnotation" || enclosingNode.type === "TSUnionType") ) { 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 handleForComments(enclosingNode, precedingNode, comment) { if ( enclosingNode && (enclosingNode.type === "ForInStatement" || enclosingNode.type === "ForOfStatement") ) { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleImportDeclarationComments( text, enclosingNode, precedingNode, comment ) { if ( precedingNode && enclosingNode && enclosingNode.type === "ImportDeclaration" && util$1.hasNewline(text, util$1.locEnd(comment)) ) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleAssignmentPatternComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "AssignmentPattern") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleClassMethodComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "ClassMethod") { addTrailingComment(enclosingNode, comment); return true; } return false; } function handleTypeAliasComments(enclosingNode, followingNode, comment) { if (enclosingNode && enclosingNode.type === "TypeAlias") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleVariableDeclaratorComments( enclosingNode, followingNode, comment ) { if ( enclosingNode && enclosingNode.type === "VariableDeclarator" && followingNode && (followingNode.type === "ObjectExpression" || followingNode.type === "ArrayExpression") ) { addLeadingComment(followingNode, comment); return true; } return false; } function printComment(commentPath, options) { const comment = commentPath.getValue(); comment.printed = true; switch (comment.type || comment.kind) { case "Comment": return "#" + comment.value; case "CommentBlock": case "Block": return "/*" + comment.value + "*/"; case "CommentLine": case "Line": // Print shebangs with the proper comment characters if (options.originalText.slice(util$1.locStart(comment)).startsWith("#!")) { return "#!" + comment.value; } return "//" + comment.value; default: throw new Error("Not a comment: " + JSON.stringify(comment)); } } function findExpressionIndexForComment(quasis, comment) { const startPos = locStart(comment) - 1; for (let i = 1; i < quasis.length; ++i) { if (startPos < getQuasiRange(quasis[i]).start) { return i - 1; } } // We haven't found it, it probably means that some of the locations are off. // Let's just return the first one. return 0; } function getQuasiRange(expr) { if (expr.start !== undefined) { // Babylon return { start: expr.start, end: expr.end }; } // Flow return { start: expr.range[0], end: expr.range[1] }; } function printLeadingComment(commentPath, print, options) { const comment = commentPath.getValue(); const contents = printComment(commentPath, options); if (!contents) { return ""; } const isBlock = util$1.isBlockComment(comment); // Leading block comments should see if they need to stay on the // same line or not. if (isBlock) { return concat([ contents, util$1.hasNewline(options.originalText, locEnd(comment)) ? hardline : " " ]); } return concat([contents, hardline]); } function printTrailingComment(commentPath, print, options) { const comment = commentPath.getValue(); const contents = printComment(commentPath, options); if (!contents) { return ""; } const isBlock = util$1.isBlockComment(comment); if ( util$1.hasNewline(options.originalText, locStart(comment), { backwards: true }) ) { // This allows comments at the end of nested structures: // { // x: 1, // y: 2 // // A comment // } // Those kinds of comments are almost always leading comments, but // here it doesn't go "outside" the block and turns it into a // trailing comment for `2`. We can simulate the above by checking // if this a comment on its own line; normal trailing comments are // always at the end of another expression. const isLineBeforeEmpty = util$1.isPreviousLineEmpty( options.originalText, comment ); return lineSuffix( concat([hardline, isLineBeforeEmpty ? hardline : "", contents]) ); } else if (isBlock) { // Trailing block comments never need a newline return concat([" ", contents]); } return concat([lineSuffix(" " + contents), !isBlock ? breakParent : ""]); } function printDanglingComments(path$$1, options, sameIndent) { const parts = []; const node = path$$1.getValue(); if (!node || !node.comments) { return ""; } path$$1.each(commentPath => { const comment = commentPath.getValue(); if (comment && !comment.leading && !comment.trailing) { parts.push(printComment(commentPath, options)); } }, "comments"); if (parts.length === 0) { return ""; } if (sameIndent) { return join(hardline, parts); } return indent(concat([hardline, join(hardline, parts)])); } function prependCursorPlaceholder(path$$1, options, printed) { if (path$$1.getNode() === options.cursorNode && path$$1.getValue()) { return concat([cursor, printed]); } return printed; } function printComments(path$$1, print, options, needsSemi) { const value = path$$1.getValue(); const printed = print(path$$1); const comments = value && value.comments; if (!comments || comments.length === 0) { return prependCursorPlaceholder(path$$1, options, printed); } const leadingParts = []; const trailingParts = [needsSemi ? ";" : "", printed]; path$$1.each(commentPath => { const comment = commentPath.getValue(); const leading = comment.leading; const trailing = comment.trailing; if (leading) { const contents = printLeadingComment(commentPath, print, options); if (!contents) { return; } leadingParts.push(contents); const text = options.originalText; if (util$1.hasNewline(text, util$1.skipNewline(text, util$1.locEnd(comment)))) { leadingParts.push(hardline); } } else if (trailing) { trailingParts.push(printTrailingComment(commentPath, print, options)); } }, "comments"); return prependCursorPlaceholder( path$$1, options, concat(leadingParts.concat(trailingParts)) ); } var comments$1 = { attach, printComments, printDanglingComments, getSortedChildNodes }; var name = "prettier"; var version$1 = "1.5.2"; var description = "Prettier is an opinionated JavaScript formatter"; var bin = {"prettier":"./bin/prettier.js"}; var repository = "prettier/prettier"; var author = "James Long"; var license = "MIT"; var main = "./index.js"; var dependencies = {"babel-code-frame":"7.0.0-alpha.12","babylon":"7.0.0-beta.13","chalk":"1.1.3","diff":"3.2.0","esutils":"2.0.2","flow-parser":"0.47.0","get-stream":"3.0.0","glob":"7.1.2","graphql":"0.10.1","jest-validate":"20.0.3","json-to-ast":"2.0.0-alpha1.2","minimist":"1.2.0","parse5":"3.0.2","postcss":"^6.0.1","postcss-less":"^1.0.0","postcss-media-query-parser":"0.2.3","postcss-scss":"1.0.0","postcss-selector-parser":"2.2.3","postcss-values-parser":"git://github.com/shellscape/postcss-values-parser.git#5e351360479116f3fe309602cdd15b0a233bc29f","typescript":"2.5.0-dev.20170617","typescript-eslint-parser":"git://github.com/eslint/typescript-eslint-parser.git#cfddbfe3ebf550530aef2f1c6c4ea1d9e738d9c1"}; var devDependencies = {"babel-cli":"6.24.1","babel-preset-es2015":"6.24.1","cross-spawn":"5.1.0","eslint":"3.19.0","eslint-friendly-formatter":"3.0.0","eslint-plugin-prettier":"2.1.1","jest":"20.0.0","mkdirp":"^0.5.1","prettier":"1.4.2","rimraf":"2.6.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","rollup-plugin-replace":"1.1.1","sw-toolbox":"3.6.0","uglify-es":"3.0.15","webpack":"2.6.1"}; var scripts = {"test":"jest","test-integration":"jest tests_integration","lint":"EFF_NO_LINK_RULES=true eslint . --format 'node_modules/eslint-friendly-formatter'","build":"./scripts/build/build.sh"}; var jest = {"setupFiles":["<rootDir>/tests_config/run_spec.js"],"snapshotSerializers":["<rootDir>/tests_config/raw-serializer.js"],"testRegex":"jsfmt\\.spec\\.js$|__tests__/.*\\.js$","testPathIgnorePatterns":["tests/new_react","tests/more_react"]}; var _package = { name: name, version: version$1, description: description, bin: bin, repository: repository, author: author, license: license, main: main, dependencies: dependencies, devDependencies: devDependencies, scripts: scripts, jest: jest }; var _package$1 = Object.freeze({ name: name, version: version$1, description: description, bin: bin, repository: repository, author: author, license: license, main: main, dependencies: dependencies, devDependencies: devDependencies, scripts: scripts, jest: jest, default: _package }); const assert$2 = require$$0; const util$5 = util$2; const startsWithNoLookaheadToken$1 = util$5.startsWithNoLookaheadToken; function FastPath$1(value) { assert$2.ok(this instanceof FastPath$1); this.stack = [value]; } // The name of the current property is always the penultimate element of // this.stack, and always a String. FastPath$1.prototype.getName = function getName() { const s = this.stack; const len = s.length; if (len > 1) { return s[len - 2]; } // Since the name is always a string, null is a safe sentinel value to // return if we do not know the name of the (root) value. return null; }; // The value of the current property is always the final element of // this.stack. FastPath$1.prototype.getValue = function getValue() { const s = this.stack; return s[s.length - 1]; }; function getNodeHelper(path$$1, count) { const s = path$$1.stack; for (let i = s.length - 1; i >= 0; i -= 2) { const value = s[i]; if (value && !Array.isArray(value) && --count < 0) { return value; } } return null; } FastPath$1.prototype.getNode = function getNode(count) { return getNodeHelper(this, ~~count); }; FastPath$1.prototype.getParentNode = function getParentNode(count) { return getNodeHelper(this, ~~count + 1); }; // Temporarily push properties named by string arguments given after the // callback function onto this.stack, then call the callback with a // reference to this (modified) FastPath object. Note that the stack will // be restored to its original state after the callback is finished, so it // is probably a mistake to retain a reference to the path. FastPath$1.prototype.call = function call(callback /*, name1, name2, ... */) { const s = this.stack; const origLen = s.length; let value = s[origLen - 1]; const argc = arguments.length; for (let i = 1; i < argc; ++i) { const name = arguments[i]; value = value[name]; s.push(name, value); } const result = callback(this); s.length = origLen; return result; }; // Similar to FastPath.prototype.call, except that the value obtained by // accessing this.getValue()[name1][name2]... should be array-like. The // callback will be called with a reference to this path object for each // element of the array. FastPath$1.prototype.each = function each(callback /*, name1, name2, ... */) { const s = this.stack; const origLen = s.length; let value = s[origLen - 1]; const argc = arguments.length; for (let i = 1; i < argc; ++i) { const name = arguments[i]; value = value[name]; s.push(name, value); } for (let i = 0; i < value.length; ++i) { if (i in value) { s.push(i, value[i]); // If the callback needs to know the value of i, call // path.getName(), assuming path is the parameter name. callback(this); s.length -= 2; } } s.length = origLen; }; // Similar to FastPath.prototype.each, except that the results of the // callback function invocations are stored in an array and returned at // the end of the iteration. FastPath$1.prototype.map = function map(callback /*, name1, name2, ... */) { const s = this.stack; const origLen = s.length; let value = s[origLen - 1]; const argc = arguments.length; for (let i = 1; i < argc; ++i) { const name = arguments[i]; value = value[name]; s.push(name, value); } const result = new Array(value.length); for (let i = 0; i < value.length; ++i) { if (i in value) { s.push(i, value[i]); result[i] = callback(this, i); s.length -= 2; } } s.length = origLen; return result; }; FastPath$1.prototype.needsParens = function(options) { const parent = this.getParentNode(); if (!parent) { return false; } const name = this.getName(); const node = this.getNode(); // If the value of this path is some child of a Node and not a Node // itself, then it doesn't need parentheses. Only Node objects (in // fact, only Expression nodes) need parentheses. if (this.getValue() !== node) { return false; } // Only statements don't need parentheses. if (isStatement(node)) { return false; } // Identifiers never need parentheses. if (node.type === "Identifier") { return false; } if (parent.type === "ParenthesizedExpression") { return false; } // Add parens around the extends clause of a class. It is needed for almost // all expressions. if ( (parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && parent.superClass === node && (node.type === "ArrowFunctionExpression" || node.type === "AssignmentExpression" || node.type === "AwaitExpression" || node.type === "BinaryExpression" || node.type === "ConditionalExpression" || node.type === "LogicalExpression" || node.type === "NewExpression" || node.type === "ObjectExpression" || node.type === "ParenthesizedExpression" || node.type === "SequenceExpression" || node.type === "TaggedTemplateExpression" || node.type === "UnaryExpression" || node.type === "UpdateExpression" || node.type === "YieldExpression") ) { return true; } if ( (parent.type === "ArrowFunctionExpression" && parent.body === node && startsWithNoLookaheadToken$1(node, /* forbidFunctionAndClass */ false)) ||