Skip to content

Instantly share code, notes, and snippets.

@rattrayalex
Last active January 16, 2022 06:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rattrayalex/dacbf5838571a47f22d0ae1f8b960268 to your computer and use it in GitHub Desktop.
Save rattrayalex/dacbf5838571a47f22d0ae1f8b960268 to your computer and use it in GitHub Desktop.
Desired Prettier formatting of (nested) ternaries
// reflected in github.com/rattrayalex/prettier/tests/format/js/conditional/new-ternary-spec.js
// Input and output should match (for 2-space indent formatting).
// remain on one line if possible:
const short = isLoud() ? makeNoise() : silent();
// next, put everything after the =
// ⚠️⚠️⚠️ NOT WORKING
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 ?
// (TODO: might be nice to have a newline before the opening paren)
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 = (
<div>
Typical jsx case:
{showFoo ?
<Foo attribute="such and such stuff here" />
: <Bar short />}
Nested, and with a non-jsx consequent is the same:
{component ?
React.createElement(component, props)
: render ?
<div>{render(props)}</div>
: <div>Nothing is here</div>}
As is a non-jsx consequent:
{showTheJSXElement ?
<div>the stuff</div>
: renderOtherStuff()}
But if the alternate breaks, add a newline before the closing curly brace:
{showTheThing || pleaseShowTheThing ?
<Foo attribute="such and such stuff here" />
: <Bar
attribute="such and such stuff here"
another="more stuff here"
third="and even more, hooray!"
/>
}
When the consequent is `null` and the alternate breaks,
hug it with parens to match boolean behavior:
{!thing ? null : (
<TheThing
thing={thing}
someVeryLongPropertyThatBreaksTheAlternate="hello"
/>
)}
</div>
);
ternaryWithJSXElements.hasNoSpecialCasing =
component ? <div>{React.createElement(component, props)}</div>
: render ? <div>{render(props)}</div>
: <div>Nothing is here</div>;
jsxExpressionContainer.inJSXAttribute.hasNoSpecialCasing = (
<Foo
withJSX={isRed ? <RedColorThing /> : <GreenColorThing />}
withJSXBroken={
isRed || isSomeOtherLongCondition.thatBreaksTheLine() ?
<RedColorThing />
: <GreenColorThing />
}
/>
);
@rattrayalex
Copy link
Author

rattrayalex commented May 14, 2021

Benefits:

  1. Similar behavior to JSX when the consequent is large, just without the parens
  2. Always has ? right after a conditional, which matches natural language
  3. Always has : at the beginning of an else
  4. Nice and concise when it can be, which will look especially nice w/ TS.
  5. Definitely looks like ternaries, not too wild/crazy/different.
  6. Close to the "case/table style" that was popular with the community, a lot of people really like this idea (I just didnt think we could make it work)
  7. Works much better with useTabs, no 2-space indents.

@rattrayalex
Copy link
Author

rattrayalex commented May 16, 2021

The stream-of-consciousness reasoning for why to adopt this new JSX formatting is here: https://gist.github.com/rattrayalex/6c32515457d5c1b3667050c895ec4510#file-what-if-we-removed-jsx-mode-jsx

@rattrayalex
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment