diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..b460d6f2d --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.12.1 diff --git a/tests/format/js/conditional/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/conditional/__snapshots__/jsfmt.spec.js.snap index 52296ec2f..31cbd272f 100644 --- a/tests/format/js/conditional/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/conditional/__snapshots__/jsfmt.spec.js.snap @@ -94,6 +94,7 @@ test comment comment comment + A newline will be added after this comment, unfortunately – but it can be removed manually, see next statement. */ test ? foo @@ -103,6 +104,23 @@ test */ bar; + +// It is at least possible to delete the extra newline that was +// unfortunately added before the second condition above: +test ? + foo :/* comment + comment + comment + comment + */ +test ? + foo : + /* comment + comment + comment + */ + bar; + test ? foo : /* comment */ @@ -200,6 +218,7 @@ test comment comment comment + A newline will be added after this comment, unfortunately – but it can be removed manually, see next statement. */ test ? foo @@ -209,6 +228,22 @@ test */ bar; +// It is at least possible to delete the extra newline that was +// unfortunately added before the second condition above: +test + ? foo /* comment + comment + comment + comment + */ + : test + ? foo + : /* comment + comment + comment + */ + bar; + test ? foo : /* comment */ bar; test @@ -239,6 +274,699 @@ const testConsole = new TestConsole( ================================================================================ `; +exports[`new-ternary-examples.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts +// EXAMPLES +// mostly taken from https://github.com/prettier/prettier/issues/9561 + +const message = + i % 3 === 0 && i % 5 === 0 ? "fizzbuzz" + : i % 3 === 0 ? "fizz" + : i % 5 === 0 ? "buzz" + : String(i); + +const paymentMessageShort = + state == "success" ? "Payment completed successfully" + : state == "processing" ? "Payment processing" + : state == "invalid_cvc" ? "There was an issue with your CVC number" + : state == "invalid_expiry" ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const paymentMessageWithABreak = + state == "success" ? "Payment completed successfully" + : state == "processing" ? "Payment processing" + : state == "invalid_cvc" ? + "There was an issue with your CVC number, and you need to take a prompt action on it." + : state == "invalid_expiry" ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const typeofExample = + definition.encode ? + definition.encode( + typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null + ) + : typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null + +// (the following is semantically equivalent to the above, but written in a more-confusing style – it'd be hard to grok no matter the formatting) +const typeofExampleFlipped = + definition.encode ? + definition.encode( + typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field] + ) + : typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field]; + + +// JSX Examples: + +const typicalLongConsequentWithNullAlternate = ( +
+ {children && !isEmptyChildren(children) ? + + : null} +
+); + +const reactRouterExampleJSX = ( +
+ {children && !isEmptyChildren(children) ? + children + : props.match ? + component ? React.createElement(component, props) + : render ? render(props) + : null + : null} +
+); + +const reactRouterExampleNonJSX = + children && !isEmptyChildren(children) ? children + : props.match ? + component ? React.createElement(component, props) + : render ? render(props) + : null + : null; + +inJSXExpressionContainer.withLongConditionals.example = ( +
+ {( + isACat() && + (someReallyLongCondition || + moreInThisLongCondition) + ) ? + someReallyLargeExpression + .toMakeMeowNoise() + .willCauseParens() + : ( + someReallyLongCondition || + moreInThisLongCondition + ) ? + bark() + : someReallyLargeExpression + .toMakeMeowNoise() + .willCauseParens() + } +
+); + +inJSXExpressionContainer.withLoops.orBooleans.example = ( +
+ {items ? + items.map((item) => ( + item.display ? + + : + )) + : null} + + {showTheStuff && ( + foo ? + + : + )} +
+); + +inJSXExpressionContainer.withNullConditional = ( +
+ {isACat() ? null : } + {isACat() && (someReallyLongCondition || moreInThisLongCondition) ? null : ( + + )} + {isACat() && (someReallyLongCondition || moreInThisLongCondition || evenMoreInThisExtraLongConditional) ? null : ( + + )} +
+); + +=====================================output===================================== +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts +// EXAMPLES +// mostly taken from https://github.com/prettier/prettier/issues/9561 + +const message = + i % 3 === 0 && i % 5 === 0 + ? "fizzbuzz" + : i % 3 === 0 + ? "fizz" + : i % 5 === 0 + ? "buzz" + : String(i); + +const paymentMessageShort = + state == "success" + ? "Payment completed successfully" + : state == "processing" + ? "Payment processing" + : state == "invalid_cvc" + ? "There was an issue with your CVC number" + : state == "invalid_expiry" + ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const paymentMessageWithABreak = + state == "success" + ? "Payment completed successfully" + : state == "processing" + ? "Payment processing" + : state == "invalid_cvc" + ? "There was an issue with your CVC number, and you need to take a prompt action on it." + : state == "invalid_expiry" + ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const typeofExample = definition.encode + ? definition.encode( + typeof row[field] !== "undefined" + ? row[field] + : typeof definition.default !== "undefined" + ? definition.default + : null + ) + : typeof row[field] !== "undefined" + ? row[field] + : typeof definition.default !== "undefined" + ? definition.default + : null; + +// (the following is semantically equivalent to the above, but written in a more-confusing style – it'd be hard to grok no matter the formatting) +const typeofExampleFlipped = definition.encode + ? definition.encode( + typeof row[field] === "undefined" + ? typeof definition.default === "undefined" + ? null + : definition.default + : row[field] + ) + : typeof row[field] === "undefined" + ? typeof definition.default === "undefined" + ? null + : definition.default + : row[field]; + +// JSX Examples: + +const typicalLongConsequentWithNullAlternate = ( +
+ {children && !isEmptyChildren(children) ? ( + + ) : null} +
+); + +const reactRouterExampleJSX = ( +
+ {children && !isEmptyChildren(children) + ? children + : props.match + ? component + ? React.createElement(component, props) + : render + ? render(props) + : null + : null} +
+); + +const reactRouterExampleNonJSX = + children && !isEmptyChildren(children) + ? children + : props.match + ? component + ? React.createElement(component, props) + : render + ? render(props) + : null + : null; + +inJSXExpressionContainer.withLongConditionals.example = ( +
+ {isACat() && (someReallyLongCondition || moreInThisLongCondition) + ? someReallyLargeExpression.toMakeMeowNoise().willCauseParens() + : someReallyLongCondition || moreInThisLongCondition + ? bark() + : someReallyLargeExpression.toMakeMeowNoise().willCauseParens()} +
+); + +inJSXExpressionContainer.withLoops.orBooleans.example = ( +
+ {items + ? items.map((item) => + item.display ? ( + + ) : ( + + ) + ) + : null} + + {showTheStuff && + (foo ? ( + + ) : ( + + ))} +
+); + +inJSXExpressionContainer.withNullConditional = ( +
+ {isACat() ? null : } + {isACat() && (someReallyLongCondition || moreInThisLongCondition) ? null : ( + + )} + {isACat() && + (someReallyLongCondition || + moreInThisLongCondition || + evenMoreInThisExtraLongConditional) ? null : ( + + )} +
+); + +================================================================================ +`; + +exports[`new-ternary-spec.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts + +// remain on one line if possible: +const short = isLoud() ? makeNoise() : silent(); + +// next, put everything after the = +const lessShort = + isLoudReallyLoud() ? makeNoiseReallyLoudly.omgSoLoud() : silent(); + +// next, indent the consequent: +const andIndented = isLoudReallyReallyReallyReallyLoud() ? + makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud() + : silent(); + +// unless the consequent is short (less than ten characters long): +const shortSoCase = isLoudReallyReallyReallyReallyLoud() ? silent() + : makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud(); + +// if chained, always break and put after the = +const chainedShort = + isCat() ? meow() + : isDog() ? bark() + : silent(); + +// when a consequent breaks in a chain: +const chainedWithLongConsequent = + isCat() ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() ? bark() + : silent(); + +// nested ternary in consequent always breaks: +const chainedWithTernaryConsequent = + isCat() ? + aNestedCondition ? theResult() + : theAlternate() + : isDog() ? bark() + : silent(); + +// consequent and terminal alternate break: +const consequentAndTerminalAlternateBreak = + isCat() ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() ? bark() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// multiline conditions and consequents/alternates: +const multilineConditionsConsequentsAndAlternates = + ( + isAnAdorableKittyCat() && + (someReallyLongCondition || moreInThisLongCondition) + ) ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : ( + isNotAnAdorableKittyCat() && + (someReallyLongCondition || moreInThisLongCondition) + ) ? + bark() + : shortCondition() ? shortConsequent() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// illustrating case of mostly short conditionals +const mostlyShort = + x === 1 ? "one" + : x === 2 ? "two" + : x === 3 ? "three" + : ( + x === 5 && + y === 7 && + someOtherThing.thatIsSoLong.thatItBreaksTheTestCondition() + ) ? + "four" + : x === 6 ? "six" + : "idk"; + +// long conditional, short consequent/alternate, not chained - do indent after ? +const longConditional = ( + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ) ? + "foo" + : "bar"; + +// long conditional, short consequent/alternate, chained +// (break on short consequents iff in chained ternary and its conditional broke) +const longConditionalChained = + ( + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ) ? + "foo" + : anotherCondition ? "bar" + : "baz"; + +// As a function parameter, don't add an extra indent: +definition.encode( + typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null, + typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field] +); + +// In a return, break and over-indent: +const inReturn = () => { + if (short) { + return foo ? 1 : 2; + } + return typeof row[aVeryLongFieldName] !== "undefined" ? + row[aVeryLongFieldName] + : null; +}; + +// Remove current JSX Mode, and replace it with this algorithm: +// When a ternary's parent is a JSXExpressionContainer which is not in a JSXAttribute, +// force the consequent to break, +// and if the alternate breaks, +// add a newline before the closing curly brace. +// Special case when the consequent is \`null\`: +// do not add a line before or after it, +// and wrap the alternate in parens. + +const someJSX = ( +
+ Typical jsx case: + {showFoo ? + + : } + Nested, and with a non-jsx consequent is the same: + {component ? + React.createElement(component, props) + : render ? +
{render(props)}
+ :
Nothing is here
} + As is a non-jsx consequent: + {showTheJSXElement ? +
the stuff
+ : renderOtherStuff()} + But if the alternate breaks, add a newline before the closing curly brace: + {showTheThing || pleaseShowTheThing ? + + : + } + When the consequent is \`null\` and the alternate breaks, + hug it with parens to match boolean behavior: + {!thing ? null : ( + + )} +
+); + +ternaryWithJSXElements.hasNoSpecialCasing = + component ?
{React.createElement(component, props)}
+ : render ?
{render(props)}
+ :
Nothing is here
; + +jsxExpressionContainer.inJSXAttribute.hasNoSpecialCasing = ( + : } + withJSXBroken={ + isRed || isSomeOtherLongCondition.thatBreaksTheLine() ? + + : + } + /> +); + +=====================================output===================================== +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts + +// remain on one line if possible: +const short = isLoud() ? makeNoise() : silent(); + +// next, put everything after the = +const lessShort = isLoudReallyLoud() + ? makeNoiseReallyLoudly.omgSoLoud() + : silent(); + +// next, indent the consequent: +const andIndented = isLoudReallyReallyReallyReallyLoud() + ? makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud() + : silent(); + +// unless the consequent is short (less than ten characters long): +const shortSoCase = isLoudReallyReallyReallyReallyLoud() + ? silent() + : makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud(); + +// if chained, always break and put after the = +const chainedShort = isCat() ? meow() : isDog() ? bark() : silent(); + +// when a consequent breaks in a chain: +const chainedWithLongConsequent = isCat() + ? someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() + ? bark() + : silent(); + +// nested ternary in consequent always breaks: +const chainedWithTernaryConsequent = isCat() + ? aNestedCondition + ? theResult() + : theAlternate() + : isDog() + ? bark() + : silent(); + +// consequent and terminal alternate break: +const consequentAndTerminalAlternateBreak = isCat() + ? someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() + ? bark() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// multiline conditions and consequents/alternates: +const multilineConditionsConsequentsAndAlternates = + isAnAdorableKittyCat() && (someReallyLongCondition || moreInThisLongCondition) + ? someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isNotAnAdorableKittyCat() && + (someReallyLongCondition || moreInThisLongCondition) + ? bark() + : shortCondition() + ? shortConsequent() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// illustrating case of mostly short conditionals +const mostlyShort = + x === 1 + ? "one" + : x === 2 + ? "two" + : x === 3 + ? "three" + : x === 5 && + y === 7 && + someOtherThing.thatIsSoLong.thatItBreaksTheTestCondition() + ? "four" + : x === 6 + ? "six" + : "idk"; + +// long conditional, short consequent/alternate, not chained - do indent after ? +const longConditional = + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ? "foo" + : "bar"; + +// long conditional, short consequent/alternate, chained +// (break on short consequents iff in chained ternary and its conditional broke) +const longConditionalChained = + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ? "foo" + : anotherCondition + ? "bar" + : "baz"; + +// As a function parameter, don't add an extra indent: +definition.encode( + typeof row[field] !== "undefined" + ? row[field] + : typeof definition.default !== "undefined" + ? definition.default + : null, + typeof row[field] === "undefined" + ? typeof definition.default === "undefined" + ? null + : definition.default + : row[field] +); + +// In a return, break and over-indent: +const inReturn = () => { + if (short) { + return foo ? 1 : 2; + } + return typeof row[aVeryLongFieldName] !== "undefined" + ? row[aVeryLongFieldName] + : null; +}; + +// Remove current JSX Mode, and replace it with this algorithm: +// When a ternary's parent is a JSXExpressionContainer which is not in a JSXAttribute, +// force the consequent to break, +// and if the alternate breaks, +// add a newline before the closing curly brace. +// Special case when the consequent is \`null\`: +// do not add a line before or after it, +// and wrap the alternate in parens. + +const someJSX = ( +
+ Typical jsx case: + {showFoo ? : } + Nested, and with a non-jsx consequent is the same: + {component ? ( + React.createElement(component, props) + ) : render ? ( +
{render(props)}
+ ) : ( +
Nothing is here
+ )} + As is a non-jsx consequent: + {showTheJSXElement ?
the stuff
: renderOtherStuff()} + But if the alternate breaks, add a newline before the closing curly brace: + {showTheThing || pleaseShowTheThing ? ( + + ) : ( + + )} + When the consequent is \`null\` and the alternate breaks, hug it with parens + to match boolean behavior: + {!thing ? null : ( + + )} +
+); + +ternaryWithJSXElements.hasNoSpecialCasing = component ? ( +
{React.createElement(component, props)}
+) : render ? ( +
{render(props)}
+) : ( +
Nothing is here
+); + +jsxExpressionContainer.inJSXAttribute.hasNoSpecialCasing = ( + : } + withJSXBroken={ + isRed || isSomeOtherLongCondition.thatBreaksTheLine() ? ( + + ) : ( + + ) + } + /> +); + +================================================================================ +`; + exports[`no-confusing-arrow.js format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] @@ -256,3 +984,301 @@ var x = a <= 1 ? 2 : 3; ================================================================================ `; + +exports[`postfix-ternary-regressions.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== + +// concatened string in consequent should be visually distinguishable from alternate +// … or maybe this is okay, because the colon is enough? +const avatar = has_ordered ? + 'https://marmelab.com/posters/avatar/longer-word-that-breaks-consequent-' + + numberOfCustomers + + '.jpeg' + : undefined; + +// Similarly, in the alternate: +const redirectUrl = pathName ? + pathName + : nextPathName + nextSearch || + defaultAuthParams.afterLoginUrl.makeThisLongerSoItBreaks; + +// And another, more pathological case of the above: +const isEmpty = obj => + obj instanceof Date ? + false + : obj === '' || + obj === null || + obj === undefined || + obj === somethingThatIsLonger || + shallowEqual(obj, {}); + + +// Again, this case is a bit hard to distinguish the alternate. +const eventsFromOrders = + orderIds && orders ? + orderIds.map(id => ({ + type: 'order', + date: orders[id].date, + data: orders[id], + })) + : []; + +// Kinda weird to have dedents to the level of "return" in a function. +function foo() { + return !linkTo ? + false + : typeof linkTo === 'function' + ? linkTo(record, reference) + : linkToRecord(rootPath, sourceId, linkTo_as_string); +} +function foo2() { + return React.isValidElement(emptyText) + ? React.cloneElement(emptyText) + : emptyText === '' + ? ' ' // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }); +} + +// Function call ideally wouldnt break break +const matchingReferencesError = isMatchingReferencesError( + matchingReferences +) + ? translate(matchingReferences.error, { + _: matchingReferences.error, + }) + : null; + +// This one is kinda confusing any way you slice it… +const obj = { + error: + matchingReferencesError && + (!input.value || + (input.value && + selectedReferencesDataStatus === REFERENCES_STATUS_EMPTY)) + ? translate('ra.input.references.all_missing', { + _: 'ra.input.references.all_missing', + }) + : null, +} + +// I think we should indent after the inner || on this, and do better wtih the parens around the && +const obj2 = { + warning: + matchingReferencesError || + (input.value && selectedReferencesDataStatus !== REFERENCES_STATUS_READY) + ? matchingReferencesError || + translate('ra.input.references.many_missing', { + _: 'ra.input.references.many_missing', + }) + : null, +} + +// The boolean conditions in the test should look cohesive. +const selectedReferencesDataStatus = + !isEmpty(value) && typeof value === 'string' && !pattern.test(value) + ? getMessage(message, { pattern }, value, values) + : undefined + + +// Would be nice if these two nested ternaries didn't look like a single one. +resolveRedirectTo( + redirectTo, + basePath, + payload + ? payload.id || (payload.data ? payload.data.id : null) + : requestPayload + ? requestPayload.id + : null, + payload && payload.data + ? payload.data + : requestPayload && requestPayload.data + ? requestPayload.data + : null +) + +const delayedDataProvider = new Proxy(restProvider, { + get: (target, name, self) => + name === 'then' ? // as we await for the dataProvider, JS calls then on it. We must trap that call or else the dataProvider will be called with the then method + self + : ( + (resource, params) => + new Promise(resolve => + setTimeout( + () => + resolve( + restProvider[name](resource, params) + ), + 500 + ) + ) + ), +}); + +function foo4() { + return !match || match.length < 5 ? line : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo5() { + return !match || match.length < 5 ? foo(line) : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo6() { + return !match || match.length < 5 ? linethatisverylongandbreaksthelinehooray : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo7() { + return !match || match.length < 5 ? linethatisverylongandbreaksthelinehoorayjustabitlonger : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +=====================================output===================================== +// concatened string in consequent should be visually distinguishable from alternate +// … or maybe this is okay, because the colon is enough? +const avatar = has_ordered + ? "https://marmelab.com/posters/avatar/longer-word-that-breaks-consequent-" + + numberOfCustomers + + ".jpeg" + : undefined; + +// Similarly, in the alternate: +const redirectUrl = pathName + ? pathName + : nextPathName + nextSearch || + defaultAuthParams.afterLoginUrl.makeThisLongerSoItBreaks; + +// And another, more pathological case of the above: +const isEmpty = (obj) => + obj instanceof Date + ? false + : obj === "" || + obj === null || + obj === undefined || + obj === somethingThatIsLonger || + shallowEqual(obj, {}); + +// Again, this case is a bit hard to distinguish the alternate. +const eventsFromOrders = + orderIds && orders + ? orderIds.map((id) => ({ + type: "order", + date: orders[id].date, + data: orders[id], + })) + : []; + +// Kinda weird to have dedents to the level of "return" in a function. +function foo() { + return !linkTo + ? false + : typeof linkTo === "function" + ? linkTo(record, reference) + : linkToRecord(rootPath, sourceId, linkTo_as_string); +} +function foo2() { + return React.isValidElement(emptyText) + ? React.cloneElement(emptyText) + : emptyText === "" + ? " " // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }); +} + +// Function call ideally wouldnt break break +const matchingReferencesError = isMatchingReferencesError(matchingReferences) + ? translate(matchingReferences.error, { + _: matchingReferences.error, + }) + : null; + +// This one is kinda confusing any way you slice it… +const obj = { + error: + matchingReferencesError && + (!input.value || + (input.value && selectedReferencesDataStatus === REFERENCES_STATUS_EMPTY)) + ? translate("ra.input.references.all_missing", { + _: "ra.input.references.all_missing", + }) + : null, +}; + +// I think we should indent after the inner || on this, and do better wtih the parens around the && +const obj2 = { + warning: + matchingReferencesError || + (input.value && selectedReferencesDataStatus !== REFERENCES_STATUS_READY) + ? matchingReferencesError || + translate("ra.input.references.many_missing", { + _: "ra.input.references.many_missing", + }) + : null, +}; + +// The boolean conditions in the test should look cohesive. +const selectedReferencesDataStatus = + !isEmpty(value) && typeof value === "string" && !pattern.test(value) + ? getMessage(message, { pattern }, value, values) + : undefined; + +// Would be nice if these two nested ternaries didn't look like a single one. +resolveRedirectTo( + redirectTo, + basePath, + payload + ? payload.id || (payload.data ? payload.data.id : null) + : requestPayload + ? requestPayload.id + : null, + payload && payload.data + ? payload.data + : requestPayload && requestPayload.data + ? requestPayload.data + : null +); + +const delayedDataProvider = new Proxy(restProvider, { + get: (target, name, self) => + name === "then" // as we await for the dataProvider, JS calls then on it. We must trap that call or else the dataProvider will be called with the then method + ? self + : (resource, params) => + new Promise((resolve) => + setTimeout(() => resolve(restProvider[name](resource, params)), 500) + ), +}); + +function foo4() { + return !match || match.length < 5 + ? line + : match[1] + match[2] + match[3] + match[4]; +} + +function foo5() { + return !match || match.length < 5 + ? foo(line) + : match[1] + match[2] + match[3] + match[4]; +} + +function foo6() { + return !match || match.length < 5 + ? linethatisverylongandbreaksthelinehooray + : match[1] + match[2] + match[3] + match[4]; +} + +function foo7() { + return !match || match.length < 5 + ? linethatisverylongandbreaksthelinehoorayjustabitlonger + : match[1] + match[2] + match[3] + match[4]; +} + +================================================================================ +`; diff --git a/tests/format/js/conditional/comments.js b/tests/format/js/conditional/comments.js index 84d735742..d6cdc984d 100644 --- a/tests/format/js/conditional/comments.js +++ b/tests/format/js/conditional/comments.js @@ -86,6 +86,7 @@ test comment comment comment + A newline will be added after this comment, unfortunately – but it can be removed manually, see next statement. */ test ? foo @@ -95,6 +96,23 @@ test */ bar; + +// It is at least possible to delete the extra newline that was +// unfortunately added before the second condition above: +test ? + foo :/* comment + comment + comment + comment + */ +test ? + foo : + /* comment + comment + comment + */ + bar; + test ? foo : /* comment */ diff --git a/tests/format/js/conditional/new-ternary-examples.js b/tests/format/js/conditional/new-ternary-examples.js new file mode 100644 index 000000000..8c319faf9 --- /dev/null +++ b/tests/format/js/conditional/new-ternary-examples.js @@ -0,0 +1,138 @@ +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts +// EXAMPLES +// mostly taken from https://github.com/prettier/prettier/issues/9561 + +const message = + i % 3 === 0 && i % 5 === 0 ? "fizzbuzz" + : i % 3 === 0 ? "fizz" + : i % 5 === 0 ? "buzz" + : String(i); + +const paymentMessageShort = + state == "success" ? "Payment completed successfully" + : state == "processing" ? "Payment processing" + : state == "invalid_cvc" ? "There was an issue with your CVC number" + : state == "invalid_expiry" ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const paymentMessageWithABreak = + state == "success" ? "Payment completed successfully" + : state == "processing" ? "Payment processing" + : state == "invalid_cvc" ? + "There was an issue with your CVC number, and you need to take a prompt action on it." + : state == "invalid_expiry" ? "Expiry must be sometime in the past." + : "There was an issue with the payment. Please contact support."; + +const typeofExample = + definition.encode ? + definition.encode( + typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null + ) + : typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null + +// (the following is semantically equivalent to the above, but written in a more-confusing style – it'd be hard to grok no matter the formatting) +const typeofExampleFlipped = + definition.encode ? + definition.encode( + typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field] + ) + : typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field]; + + +// JSX Examples: + +const typicalLongConsequentWithNullAlternate = ( +
+ {children && !isEmptyChildren(children) ? + + : null} +
+); + +const reactRouterExampleJSX = ( +
+ {children && !isEmptyChildren(children) ? + children + : props.match ? + component ? React.createElement(component, props) + : render ? render(props) + : null + : null} +
+); + +const reactRouterExampleNonJSX = + children && !isEmptyChildren(children) ? children + : props.match ? + component ? React.createElement(component, props) + : render ? render(props) + : null + : null; + +inJSXExpressionContainer.withLongConditionals.example = ( +
+ {( + isACat() && + (someReallyLongCondition || + moreInThisLongCondition) + ) ? + someReallyLargeExpression + .toMakeMeowNoise() + .willCauseParens() + : ( + someReallyLongCondition || + moreInThisLongCondition + ) ? + bark() + : someReallyLargeExpression + .toMakeMeowNoise() + .willCauseParens() + } +
+); + +inJSXExpressionContainer.withLoops.orBooleans.example = ( +
+ {items ? + items.map((item) => ( + item.display ? + + : + )) + : null} + + {showTheStuff && ( + foo ? + + : + )} +
+); + +inJSXExpressionContainer.withNullConditional = ( +
+ {isACat() ? null : } + {isACat() && (someReallyLongCondition || moreInThisLongCondition) ? null : ( + + )} + {isACat() && (someReallyLongCondition || moreInThisLongCondition || evenMoreInThisExtraLongConditional) ? null : ( + + )} +
+); diff --git a/tests/format/js/conditional/new-ternary-spec.js b/tests/format/js/conditional/new-ternary-spec.js new file mode 100644 index 000000000..74e7a362a --- /dev/null +++ b/tests/format/js/conditional/new-ternary-spec.js @@ -0,0 +1,189 @@ +// from https://gist.github.com/rattrayalex/dacbf5838571a47f22d0ae1f8b960268 +// Input and output should match (for 2-space indent formatting). +// TypeScript is here: prettier/tests/format/typescript/conditional-types/new-ternary-spec.ts + +// remain on one line if possible: +const short = isLoud() ? makeNoise() : silent(); + +// next, put everything after the = +const lessShort = + isLoudReallyLoud() ? makeNoiseReallyLoudly.omgSoLoud() : silent(); + +// next, indent the consequent: +const andIndented = isLoudReallyReallyReallyReallyLoud() ? + makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud() + : silent(); + +// unless the consequent is short (less than ten characters long): +const shortSoCase = isLoudReallyReallyReallyReallyLoud() ? silent() + : makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud(); + +// if chained, always break and put after the = +const chainedShort = + isCat() ? meow() + : isDog() ? bark() + : silent(); + +// when a consequent breaks in a chain: +const chainedWithLongConsequent = + isCat() ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() ? bark() + : silent(); + +// nested ternary in consequent always breaks: +const chainedWithTernaryConsequent = + isCat() ? + aNestedCondition ? theResult() + : theAlternate() + : isDog() ? bark() + : silent(); + +// consequent and terminal alternate break: +const consequentAndTerminalAlternateBreak = + isCat() ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : isDog() ? bark() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// multiline conditions and consequents/alternates: +const multilineConditionsConsequentsAndAlternates = + ( + isAnAdorableKittyCat() && + (someReallyLongCondition || moreInThisLongCondition) + ) ? + someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens() + : ( + isNotAnAdorableKittyCat() && + (someReallyLongCondition || moreInThisLongCondition) + ) ? + bark() + : shortCondition() ? shortConsequent() + : someReallyLargeExpression + .thatWouldCauseALineBreak() + .willCauseAnIndentButNotParens(); + +// illustrating case of mostly short conditionals +const mostlyShort = + x === 1 ? "one" + : x === 2 ? "two" + : x === 3 ? "three" + : ( + x === 5 && + y === 7 && + someOtherThing.thatIsSoLong.thatItBreaksTheTestCondition() + ) ? + "four" + : x === 6 ? "six" + : "idk"; + +// long conditional, short consequent/alternate, not chained - do indent after ? +const longConditional = ( + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ) ? + "foo" + : "bar"; + +// long conditional, short consequent/alternate, chained +// (break on short consequents iff in chained ternary and its conditional broke) +const longConditionalChained = + ( + bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans && + glimseGlyphsHazardNoopsTieTie >= + averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol() + ) ? + "foo" + : anotherCondition ? "bar" + : "baz"; + +// As a function parameter, don't add an extra indent: +definition.encode( + typeof row[field] !== "undefined" ? row[field] + : typeof definition.default !== "undefined" ? definition.default + : null, + typeof row[field] === "undefined" ? + typeof definition.default === "undefined" ? null + : definition.default + : row[field] +); + +// In a return, break and over-indent: +const inReturn = () => { + if (short) { + return foo ? 1 : 2; + } + return typeof row[aVeryLongFieldName] !== "undefined" ? + row[aVeryLongFieldName] + : null; +}; + +// Remove current JSX Mode, and replace it with this algorithm: +// When a ternary's parent is a JSXExpressionContainer which is not in a JSXAttribute, +// force the consequent to break, +// and if the alternate breaks, +// add a newline before the closing curly brace. +// Special case when the consequent is `null`: +// do not add a line before or after it, +// and wrap the alternate in parens. + +const someJSX = ( +
+ Typical jsx case: + {showFoo ? + + : } + Nested, and with a non-jsx consequent is the same: + {component ? + React.createElement(component, props) + : render ? +
{render(props)}
+ :
Nothing is here
} + As is a non-jsx consequent: + {showTheJSXElement ? +
the stuff
+ : renderOtherStuff()} + But if the alternate breaks, add a newline before the closing curly brace: + {showTheThing || pleaseShowTheThing ? + + : + } + When the consequent is `null` and the alternate breaks, + hug it with parens to match boolean behavior: + {!thing ? null : ( + + )} +
+); + +ternaryWithJSXElements.hasNoSpecialCasing = + component ?
{React.createElement(component, props)}
+ : render ?
{render(props)}
+ :
Nothing is here
; + +jsxExpressionContainer.inJSXAttribute.hasNoSpecialCasing = ( + : } + withJSXBroken={ + isRed || isSomeOtherLongCondition.thatBreaksTheLine() ? + + : + } + /> +); diff --git a/tests/format/js/conditional/postfix-ternary-regressions.js b/tests/format/js/conditional/postfix-ternary-regressions.js new file mode 100644 index 000000000..3d1f9ce7e --- /dev/null +++ b/tests/format/js/conditional/postfix-ternary-regressions.js @@ -0,0 +1,150 @@ + +// concatened string in consequent should be visually distinguishable from alternate +// … or maybe this is okay, because the colon is enough? +const avatar = has_ordered ? + 'https://marmelab.com/posters/avatar/longer-word-that-breaks-consequent-' + + numberOfCustomers + + '.jpeg' + : undefined; + +// Similarly, in the alternate: +const redirectUrl = pathName ? + pathName + : nextPathName + nextSearch || + defaultAuthParams.afterLoginUrl.makeThisLongerSoItBreaks; + +// And another, more pathological case of the above: +const isEmpty = obj => + obj instanceof Date ? + false + : obj === '' || + obj === null || + obj === undefined || + obj === somethingThatIsLonger || + shallowEqual(obj, {}); + + +// Again, this case is a bit hard to distinguish the alternate. +const eventsFromOrders = + orderIds && orders ? + orderIds.map(id => ({ + type: 'order', + date: orders[id].date, + data: orders[id], + })) + : []; + +// Kinda weird to have dedents to the level of "return" in a function. +function foo() { + return !linkTo ? + false + : typeof linkTo === 'function' + ? linkTo(record, reference) + : linkToRecord(rootPath, sourceId, linkTo_as_string); +} +function foo2() { + return React.isValidElement(emptyText) + ? React.cloneElement(emptyText) + : emptyText === '' + ? ' ' // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }); +} + +// Function call ideally wouldnt break break +const matchingReferencesError = isMatchingReferencesError( + matchingReferences +) + ? translate(matchingReferences.error, { + _: matchingReferences.error, + }) + : null; + +// This one is kinda confusing any way you slice it… +const obj = { + error: + matchingReferencesError && + (!input.value || + (input.value && + selectedReferencesDataStatus === REFERENCES_STATUS_EMPTY)) + ? translate('ra.input.references.all_missing', { + _: 'ra.input.references.all_missing', + }) + : null, +} + +// I think we should indent after the inner || on this, and do better wtih the parens around the && +const obj2 = { + warning: + matchingReferencesError || + (input.value && selectedReferencesDataStatus !== REFERENCES_STATUS_READY) + ? matchingReferencesError || + translate('ra.input.references.many_missing', { + _: 'ra.input.references.many_missing', + }) + : null, +} + +// The boolean conditions in the test should look cohesive. +const selectedReferencesDataStatus = + !isEmpty(value) && typeof value === 'string' && !pattern.test(value) + ? getMessage(message, { pattern }, value, values) + : undefined + + +// Would be nice if these two nested ternaries didn't look like a single one. +resolveRedirectTo( + redirectTo, + basePath, + payload + ? payload.id || (payload.data ? payload.data.id : null) + : requestPayload + ? requestPayload.id + : null, + payload && payload.data + ? payload.data + : requestPayload && requestPayload.data + ? requestPayload.data + : null +) + +const delayedDataProvider = new Proxy(restProvider, { + get: (target, name, self) => + name === 'then' ? // as we await for the dataProvider, JS calls then on it. We must trap that call or else the dataProvider will be called with the then method + self + : ( + (resource, params) => + new Promise(resolve => + setTimeout( + () => + resolve( + restProvider[name](resource, params) + ), + 500 + ) + ) + ), +}); + +function foo4() { + return !match || match.length < 5 ? line : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo5() { + return !match || match.length < 5 ? foo(line) : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo6() { + return !match || match.length < 5 ? linethatisverylongandbreaksthelinehooray : ( + match[1] + match[2] + match[3] + match[4] + ) +} + +function foo7() { + return !match || match.length < 5 ? linethatisverylongandbreaksthelinehoorayjustabitlonger : ( + match[1] + match[2] + match[3] + match[4] + ) +} diff --git a/tests/format/js/ternaries/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/ternaries/__snapshots__/jsfmt.spec.js.snap index 64a356b85..3de99ae7a 100644 --- a/tests/format/js/ternaries/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/ternaries/__snapshots__/jsfmt.spec.js.snap @@ -179,10 +179,11 @@ fn( bifornCringerMoshedPerplexSawder, askTrovenaBeenaDependsRowans, glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl && - anodyneCondosMalateOverateRetinol - ? annularCooeedSplicesWalksWayWay - : kochabCooieGameOnOboleUnweave + anodyneCondosMalateOverateRetinol + ? annularCooeedSplicesWalksWayWay + : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. =====================================output===================================== fn( @@ -193,6 +194,7 @@ fn( ? annularCooeedSplicesWalksWayWay : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. ================================================================================ `; @@ -209,10 +211,11 @@ fn( bifornCringerMoshedPerplexSawder, askTrovenaBeenaDependsRowans, glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl && - anodyneCondosMalateOverateRetinol - ? annularCooeedSplicesWalksWayWay - : kochabCooieGameOnOboleUnweave + anodyneCondosMalateOverateRetinol + ? annularCooeedSplicesWalksWayWay + : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. =====================================output===================================== fn( @@ -223,6 +226,7 @@ fn( ? annularCooeedSplicesWalksWayWay : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. ================================================================================ `; @@ -238,10 +242,11 @@ fn( bifornCringerMoshedPerplexSawder, askTrovenaBeenaDependsRowans, glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl && - anodyneCondosMalateOverateRetinol - ? annularCooeedSplicesWalksWayWay - : kochabCooieGameOnOboleUnweave + anodyneCondosMalateOverateRetinol + ? annularCooeedSplicesWalksWayWay + : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. =====================================output===================================== fn( @@ -252,6 +257,7 @@ fn( ? annularCooeedSplicesWalksWayWay : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. ================================================================================ `; @@ -266,10 +272,11 @@ fn( bifornCringerMoshedPerplexSawder, askTrovenaBeenaDependsRowans, glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl && - anodyneCondosMalateOverateRetinol - ? annularCooeedSplicesWalksWayWay - : kochabCooieGameOnOboleUnweave + anodyneCondosMalateOverateRetinol + ? annularCooeedSplicesWalksWayWay + : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. =====================================output===================================== fn( @@ -280,6 +287,7 @@ fn( ? annularCooeedSplicesWalksWayWay : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. ================================================================================ `; @@ -1816,6 +1824,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -2160,6 +2169,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -2468,6 +2478,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -2812,6 +2823,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -3119,6 +3131,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -3463,6 +3476,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -3769,6 +3783,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -4113,6 +4128,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && @@ -4315,6 +4331,34 @@ a ? line : softline +const config = { + onFailure: onFailure !== undefined ? onFailure : ( + error => { + notify( + typeof error === 'string' ? + error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' ? error + : error && error.message ? error.message + : undefined, + } + ); + refresh(); + } + ) +} + +showNotification( + typeof error === 'string' ? error : error.message || body, + level || 'warning', + { + messageArgs, + undoable: false, + } +) =====================================output===================================== let icecream = what == "cone" @@ -4413,6 +4457,38 @@ a ? line : softline; +const config = { + onFailure: + onFailure !== undefined + ? onFailure + : (error) => { + notify( + typeof error === "string" + ? error + : error.message || "ra.notification.http_error", + "warning", + { + _: + typeof error === "string" + ? error + : error && error.message + ? error.message + : undefined, + } + ); + refresh(); + }, +}; + +showNotification( + typeof error === "string" ? error : error.message || body, + level || "warning", + { + messageArgs, + undoable: false, + } +); + ================================================================================ `; @@ -4520,6 +4596,34 @@ a ? line : softline +const config = { + onFailure: onFailure !== undefined ? onFailure : ( + error => { + notify( + typeof error === 'string' ? + error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' ? error + : error && error.message ? error.message + : undefined, + } + ); + refresh(); + } + ) +} + +showNotification( + typeof error === 'string' ? error : error.message || body, + level || 'warning', + { + messageArgs, + undoable: false, + } +) =====================================output===================================== let icecream = what == "cone" @@ -4618,6 +4722,38 @@ a ? line : softline; +const config = { + onFailure: + onFailure !== undefined + ? onFailure + : (error) => { + notify( + typeof error === "string" + ? error + : error.message || "ra.notification.http_error", + "warning", + { + _: + typeof error === "string" + ? error + : error && error.message + ? error.message + : undefined, + } + ); + refresh(); + }, +}; + +showNotification( + typeof error === "string" ? error : error.message || body, + level || "warning", + { + messageArgs, + undoable: false, + } +); + ================================================================================ `; @@ -4724,6 +4860,34 @@ a ? line : softline +const config = { + onFailure: onFailure !== undefined ? onFailure : ( + error => { + notify( + typeof error === 'string' ? + error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' ? error + : error && error.message ? error.message + : undefined, + } + ); + refresh(); + } + ) +} + +showNotification( + typeof error === 'string' ? error : error.message || body, + level || 'warning', + { + messageArgs, + undoable: false, + } +) =====================================output===================================== let icecream = what == "cone" @@ -4822,6 +4986,38 @@ a ? line : softline; +const config = { + onFailure: + onFailure !== undefined + ? onFailure + : (error) => { + notify( + typeof error === "string" + ? error + : error.message || "ra.notification.http_error", + "warning", + { + _: + typeof error === "string" + ? error + : error && error.message + ? error.message + : undefined, + } + ); + refresh(); + }, +}; + +showNotification( + typeof error === "string" ? error : error.message || body, + level || "warning", + { + messageArgs, + undoable: false, + } +); + ================================================================================ `; @@ -4927,6 +5123,34 @@ a ? line : softline +const config = { + onFailure: onFailure !== undefined ? onFailure : ( + error => { + notify( + typeof error === 'string' ? + error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' ? error + : error && error.message ? error.message + : undefined, + } + ); + refresh(); + } + ) +} + +showNotification( + typeof error === 'string' ? error : error.message || body, + level || 'warning', + { + messageArgs, + undoable: false, + } +) =====================================output===================================== let icecream = what == "cone" @@ -5025,6 +5249,38 @@ a ? line : softline; +const config = { + onFailure: + onFailure !== undefined + ? onFailure + : (error) => { + notify( + typeof error === "string" + ? error + : error.message || "ra.notification.http_error", + "warning", + { + _: + typeof error === "string" + ? error + : error && error.message + ? error.message + : undefined, + } + ); + refresh(); + }, +}; + +showNotification( + typeof error === "string" ? error : error.message || body, + level || "warning", + { + messageArgs, + undoable: false, + } +); + ================================================================================ `; diff --git a/tests/format/js/ternaries/func-call.js b/tests/format/js/ternaries/func-call.js index a87c7289e..527237c7c 100644 --- a/tests/format/js/ternaries/func-call.js +++ b/tests/format/js/ternaries/func-call.js @@ -2,7 +2,8 @@ fn( bifornCringerMoshedPerplexSawder, askTrovenaBeenaDependsRowans, glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl && - anodyneCondosMalateOverateRetinol - ? annularCooeedSplicesWalksWayWay - : kochabCooieGameOnOboleUnweave + anodyneCondosMalateOverateRetinol + ? annularCooeedSplicesWalksWayWay + : kochabCooieGameOnOboleUnweave ); +// TODO(rattrayalex): try to indent consequent/alternate here. diff --git a/tests/format/js/ternaries/indent-after-paren.js b/tests/format/js/ternaries/indent-after-paren.js index f4c0bdbf9..eacc4a6fa 100644 --- a/tests/format/js/ternaries/indent-after-paren.js +++ b/tests/format/js/ternaries/indent-after-paren.js @@ -199,6 +199,7 @@ bifornCringerMoshedPerplexSawder = kochabCooieGameOnOboleUnweave === Math.PI ? averredBathersBoxroomBuggyNurl : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); +// TODO(rattrayalex): try to fix this case bifornCringerMoshedPerplexSawder = ( glimseGlyphsHazardNoopsTieTie === 0 && diff --git a/tests/format/js/ternaries/nested.js b/tests/format/js/ternaries/nested.js index bfc1cba44..ce77c6750 100644 --- a/tests/format/js/ternaries/nested.js +++ b/tests/format/js/ternaries/nested.js @@ -93,3 +93,32 @@ a } ? line : softline + +const config = { + onFailure: onFailure !== undefined ? onFailure : ( + error => { + notify( + typeof error === 'string' ? + error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' ? error + : error && error.message ? error.message + : undefined, + } + ); + refresh(); + } + ) +} + +showNotification( + typeof error === 'string' ? error : error.message || body, + level || 'warning', + { + messageArgs, + undoable: false, + } +) \ No newline at end of file diff --git a/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap index e8776bdc0..a1dd16f69 100644 --- a/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap @@ -1102,6 +1102,13 @@ jsxModeFromElementBreaking ? ( {a ? "a" : b ? "b" : "c"} ; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo ? "a" : b ? "b" : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -1243,6 +1250,17 @@ jsxModeFromElementBreaking ? ( // non-breaking.
{a ? "a" : b ? "b" : "c"}
; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo + ? "a" + : b + ? "b" + : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -1401,6 +1419,13 @@ jsxModeFromElementBreaking ? ( {a ? "a" : b ? "b" : "c"} ; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo ? "a" : b ? "b" : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -1542,6 +1567,17 @@ jsxModeFromElementBreaking ? ( // non-breaking.
{a ? "a" : b ? "b" : "c"}
; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo + ? "a" + : b + ? "b" + : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -1700,6 +1736,13 @@ jsxModeFromElementBreaking ? ( {a ? "a" : b ? "b" : "c"} ; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo ? "a" : b ? "b" : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -1841,6 +1884,17 @@ jsxModeFromElementBreaking ? ( // non-breaking.
{a ? 'a' : b ? 'b' : 'c'}
; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo + ? 'a' + : b + ? 'b' + : 'c'} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? 'satellite' : isPublic ? 'affairs' : network ? : 'dun'; @@ -1999,6 +2053,13 @@ jsxModeFromElementBreaking ? ( {a ? "a" : b ? "b" : "c"} ; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo ? "a" : b ? "b" : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; @@ -2140,6 +2201,17 @@ jsxModeFromElementBreaking ? ( // non-breaking.
{a ? 'a' : b ? 'b' : 'c'}
; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo + ? 'a' + : b + ? 'b' + : 'c'} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? 'satellite' : isPublic ? 'affairs' : network ? : 'dun'; diff --git a/tests/format/jsx/jsx/conditional-expression.js b/tests/format/jsx/jsx/conditional-expression.js index 3283d9c6a..703ce4d95 100644 --- a/tests/format/jsx/jsx/conditional-expression.js +++ b/tests/format/jsx/jsx/conditional-expression.js @@ -84,6 +84,13 @@ jsxModeFromElementBreaking ? ( {a ? "a" : b ? "b" : "c"} ; +// This chain of ConditionalExpressions prints in JSX mode because the parent of +// the outermost ConditionalExpression is a JSXExpressionContainer. It is +// breaking. +
+ {thisIsASongAboutYourPoorSickPenguinHeHasAFeverAndHisToesAreBlueButIfISingToYourPoorSickPenguinHeWillFeelBetterInADayOrTwo ? "a" : b ? "b" : "c"} +
; + // This chain of ConditionalExpressions prints in JSX mode because there is a // JSX element somewhere in the chain. It is non-breaking. cable ? "satellite" : isPublic ? "affairs" : network ? : "dun"; diff --git a/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap index 7231c3da4..e598947c2 100644 --- a/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap @@ -347,3 +347,87 @@ type Foo3 = ( ================================================================================ `; + +exports[`new-ternary-spec.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +// TypeScript has the same behavior, including a line break after =, but no parens around "conditional": +type KnownKeys = + { + [K in keyof T]: string extends K ? never + : number extends K ? never + : K; + } extends { [_ in keyof T]: infer U } ? + {} extends U ? never + : U + : never; + +type KnownKeysWithLongExtends = + { + [K in keyof T]: string extends K ? never + : number extends K ? never + : K; + } extends { + [_ in keyof T]: SomeReallyLongThingThatBreaksTheLine + } ? U + : never; + +// TypeScript examples: +type TypeName = + T extends string ? "string" + : T extends number ? "number" + : T extends boolean ? "boolean" + : T extends undefined ? "undefined" + : T extends Function ? "function" + : "object"; + +type Unpacked = + T extends (infer U)[] ? U + : T extends (...args: any[]) => infer U ? + SomeReallyLongThingThatBreaksTheLine + : T extends Promise ? U + : T; +=====================================output===================================== +// TypeScript has the same behavior, including a line break after =, but no parens around "conditional": +type KnownKeys = { + [K in keyof T]: string extends K ? never : number extends K ? never : K; +} extends { [_ in keyof T]: infer U } + ? {} extends U + ? never + : U + : never; + +type KnownKeysWithLongExtends = { + [K in keyof T]: string extends K ? never : number extends K ? never : K; +} extends { + [_ in keyof T]: SomeReallyLongThingThatBreaksTheLine; +} + ? U + : never; + +// TypeScript examples: +type TypeName = T extends string + ? "string" + : T extends number + ? "number" + : T extends boolean + ? "boolean" + : T extends undefined + ? "undefined" + : T extends Function + ? "function" + : "object"; + +type Unpacked = T extends (infer U)[] + ? U + : T extends (...args: any[]) => infer U + ? SomeReallyLongThingThatBreaksTheLine + : T extends Promise + ? U + : T; + +================================================================================ +`; diff --git a/tests/format/typescript/conditional-types/new-ternary-spec.ts b/tests/format/typescript/conditional-types/new-ternary-spec.ts new file mode 100644 index 000000000..972d3e935 --- /dev/null +++ b/tests/format/typescript/conditional-types/new-ternary-spec.ts @@ -0,0 +1,36 @@ +// TypeScript has the same behavior, including a line break after =, but no parens around "conditional": +type KnownKeys = + { + [K in keyof T]: string extends K ? never + : number extends K ? never + : K; + } extends { [_ in keyof T]: infer U } ? + {} extends U ? never + : U + : never; + +type KnownKeysWithLongExtends = + { + [K in keyof T]: string extends K ? never + : number extends K ? never + : K; + } extends { + [_ in keyof T]: SomeReallyLongThingThatBreaksTheLine + } ? U + : never; + +// TypeScript examples: +type TypeName = + T extends string ? "string" + : T extends number ? "number" + : T extends boolean ? "boolean" + : T extends undefined ? "undefined" + : T extends Function ? "function" + : "object"; + +type Unpacked = + T extends (infer U)[] ? U + : T extends (...args: any[]) => infer U ? + SomeReallyLongThingThatBreaksTheLine + : T extends Promise ? U + : T; \ No newline at end of file