Hoppa till huvudinnehållet

Prettier 1.3

· 11 min att läsa
Inofficiell Beta-översättning

Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet. Hittade du ett fel? Rapportera problem →

Det här inlägget ger en uppdatering om Facebooks anammande av Prettier, redogör för våra framsteg med TypeScript och går in på detaljer om några förbättringar och korrigeringar i Prettier 1.3.0.

Uppdatering om Facebooks anammande

Anledningen till att jag (@vjeux) påbörjade den här resan med Prettier har alltid varit att konvertera hela Facebooks kodbas. Jag vill ge en uppdatering om hur det går och vad processen ser ut.

De första projekten som antog Prettier var Jest, React och immutable-js. Dessa är mindre kodbaser i storleksordningen hundratals filer med egen infrastruktur. Ett fåtal personer arbetar på dem heltid.

Sedan konverterade Oculus och Nuclide sina kodbaser. Skalan är större med några tusen filer och tiotals heltidsbidragsgivare, men liknar de första projekten. Konverteringarna gjordes i en stor kodmodifiering och sedan var det klart.

Hela Facebooks kodbas är dock mycket större än så och det är inte genomförbart att konvertera allt på en gång eller att övertyga alla att deras kodbas kommer omformateras under deras fötter. Därför behöver vi en mer gradvis strategi.

Skalbar implementering

Att köra Prettier på kod är en relativt kostsam operation, den gör din pull request ful på grund av många irrelevanta ändringar och orsakar merge-konflikter för alla pågående pull requests. Därför bör du göra allt för att säkerställa att en fil förblir formaterad när den väl är det.

  • När du formaterar en fil, lägg till @format i det första blockkommentaren som @flow.

  • Skapa en lint-regel med autofix som kontrollerar om filen är korrekt formaterad när @format finns.

    • I Nuclide visas detta som en inline-varning med en fixa-knapp.
    • Vid pull request visas ett misslyckat lint med en [Yn]-prompt där du bara trycker enter.
  • Uppdatera standardkodmallarna för att lägga till @format i huvudet.

  • När du kör kodformatering via cmd-shift-c i Nuclide, lägg automatiskt till @format-huvudet.

  • Inaktivera alla stilregler som max-len när @format finns i huvudet.

  • Ha ett skript för att köra Prettier på en hel mapp med allt konfigurerat som en enradsoperation.

  • Ta fram en bra guide som hjälper dem som vill konvertera sin kodbas med instruktioner och bästa praxis.

  • När du släpper en ny version av Prettier, kör den också på alla filer med @format för att undvika varningar efteråt.

  • Lägg till spårning av antalet filer med @format över tid.

Vi fick äntligen allt detta på plats för 1,5 vecka sedan och mottagandet har varit fantastiskt. Många från olika team har konverterat sina kodbaser till Prettier på egen hand. Idag har 15% av Facebooks kodbas konverterats!

När jag började arbeta med Prettier hade jag en känsla av att folk var sugna på verktyg för formatering. Men jag anade inte att när verktygen väl fanns på plats skulle folk rusa för att konvertera sina kodbaser! Detta är en fin bekräftelse på att projektet är användbart och inte bara ett prydligt verktyg.

Framsteg med TypeScript-stöd

@despairblue, @azz och @JamesHenry har arbetat hårt för att få TypeScript-stöd i Prettier eftersom det är den mest efterfrågade funktionen. 2000 av 11000 filer i TypeScript-testpaketet är inte korrekt utskrivna än. Du kan följa framstegen på https://github.com/prettier/prettier/issues/1480 och tveka inte att hjälpa till!

Flow

Lägg till efterföljande kommatecken i flow-generics (#1381)

Alternativet --trailing-comma=all är tänkt att lägga till efterföljande kommatecken överallt det går, men som en förbiseelse glömde vi göra det för flow-generics.

// Before
type Errors = Immutable.Map<
Ahohohhohohohohohohohohohohooh,
Fbt | Immutable.Map<ErrorIndex, Fbt>
>;

// After
type Errors = Immutable.Map<
Ahohohhohohohohohohohohohohooh,
Fbt | Immutable.Map<ErrorIndex, Fbt>,
>;

Infoga nullable inline i flow-generics (#1426)

Fasen efter att ha skrivit ut koden korrekt är att finjustera utdatan för att få den mer lik hur utvecklare skriver kod i praktiken. Att infoga valfria flow-typer inline är en liten förbättring som gör skillnad.

// Before
type Cursor = Promise<
?{
newCursor?: number,
formatted: string,
}
>;

// After
type Cursor = Promise<?{
newCursor?: number,
formatted: string,
}>;

Tillåt att flow-deklarationer bryts vid StringLiteralTypeAnnotations (#1437)

Vi kan alltid hitta fler ställen att lägga till radbrytningar när innehållet inte får plats på 80 kolumner. Den här gången gäller det att deklarera en typ som en konstant sträng.

// Before
export type AdamPlacementValidationSingleErrorKey = 'SOME_FANCY_TARGETS.GLOBAL_TARGET';

// After
export type AdamPlacementValidationSingleErrorKey =
'SOME_FANCY_TARGETS.GLOBAL_TARGET';

Lägg till mellanrum runt = för standardargument i flow-generics (#1476)

Ytterligare ett exempel på en liten förbättring för visningen av flow-kod. För funktioners standardargument lade vi till mellanrum runt = men inte för flow-generics.

// Before
class PolyDefault<T=string> {}

// After
class PolyDefault<T = string> {}

Bryt inte för oparenteserade flow-funktioner med ett argument (#1452)

Jag försöker hitta något vettigt att skriva här, men... det ser bara konstigt ut!

// Before
const selectorByPath:
Path
=> SomethingSelector<
SomethingUEditorContextType,
SomethingUEditorContextType,
SomethingBulkValue<string>
> = memoizeWithArgs(/* ... */)

// After
const selectorByPath: Path => SomethingSelector<
SomethingUEditorContextType,
SomethingUEditorContextType,
SomethingBulkValue<string>
> = memoizeWithArgs(/* ... */);

Åtgärda parenteser för valfria flow-typer (#1357)

Vi var lite för slapphänta med parenteser för valfria flow-typer. I ett enskilt fall i hela Facebooks kodbas genererades kod med annan semantik. Som del av denna fix har vi skärpt reglerna för vilka typer som kan skrivas utan parenteser.

// Before
type X = ?(number, number) => number => void;

// After
type X = (?(number, number) => number) => void;

Hoppa över efterföljande kommatecken med FlowShorthandWithOneArg (#1364)

Det är ett tolkningsfel att lägga till efterföljande kommatecken utan parenteser för argument i pilsyntax-funktionstyper. Vi hittade ett fall i Facebooks kodbas där detta inträffade - det är mycket ovanligt.

// Before
type IdeConnectionFactory =
child_process$ChildProcess,
=> FlowIDEConnection = defaultIDEConnectionFactory;

// After
type IdeConnectionFactory =
child_process$ChildProcess
=> FlowIDEConnection = defaultIDEConnectionFactory;

Ändra ordning på flow-objekts egenskaper (#1451)

Här är ett exempel där AST-strukturen inte är i vår favör. Istället för att ha en elementlista inuti en typ är AST strukturerat så att vanliga nycklar och array-nycklar har egna grupper. För att återställa ursprungsordningen läser vi nu direkt från källkoden :(

// Before
type Foo = {
[key: string]: void,
alpha: "hello",
beta: 10
};

// After
type Foo = {
alpha: 'hello',
[key: string]: void,
beta: 10
}

Malliteraler

Korrekt indrag för malliteraler (#1385)

Ett långvarigt problem med malliteraler och prettier har varit indragen av kod inuti ${}. Tidigare användes indraget från bakåtklicken, men det gav dåliga resultat. Utvecklare föredrar istället indraget från ${. Vi ändrade detta beteende och magiskt började GraphQL-frågor se snygga ut!

// Before
Relay.createContainer({
nodes: ({ solution_type, time_frame }) => Relay.QL`
fragment {
__typename
${OptimalSolutionsSection.getFragment("node", {
solution_type,
time_frame
})}
}
`
})
// After
Relay.createContainer({
nodes: ({ solution_type, time_frame }) => Relay.QL`
fragment {
__typename
${OptimalSolutionsSection.getFragment("node", {
solution_type,
time_frame
})}
}
`
})

Indra inte anrop med ett enda malliteral-argument (#873)

Malliteraler är svåra att hantera för en kodformaterare eftersom mellanrummen inuti är meningsbärande och inte kan omindras. Vi visste inte hur vi skulle hantera anrop med ett enda malliteral-argument så vi gjorde inget, men fick fortsatta rapporter om felaktigt indrag. Nu infogar vi dem inline - vi håller tummarna att detta täcker de flesta fall.

// Before
insertRule(
`*, *:before, *:after {
box-sizing: inherit;
}`,
);

// After
insertRule(`*, *:before, *:after {
box-sizing: inherit;
}`);

Åtgärda radbrytningar för Windows i malliteraler (#1439)

Vi hanterar radslut på många ställen i Prettier och har lagt stor vård vid att hantera både \n och \r\n utom för malliteraler där vi glömde. Nu är detta åtgärdat!

// Before
const aLongString = `

Line 1

Line 2

Line 3

`;

// After
const aLongString = `
Line 1
Line 2
Line 3
`;

Infogade malliteraler som pilkropp (#1485)

Vi infogar redan malliteraler som är taggade (t.ex. graphql`query`) men gjorde inte det för vanliga malliteraler. Som kuriositet visade det sig att koden var tänkt att stödja det men använde TemplateElement istället för TemplateLiteral :(

// Before
const inlineStore = preloadedState =>
`
<script>
window.preloadedState = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
</script>
`

// After
const inlineStore = preloadedState => `
<script>
window.preloadedState = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
</script>
`

Ternära uttryck

Lägg till parenteser för ovanliga kapslade ternära uttryck (#1386)

När vi arbetade med utskrift av kapslade ternära uttryck fokuserade alla på de med if-then-else-struktur cond1 ? elem1_if : cond2 ? elem2_if : elem_else som är vanligast. Men om du flyttar om ? och : kan du skapa ett annat mönster. Det ser nästan likadant ut men har annan betydelse. För att minska förvirring lägger vi nu till parenteser runt den ovanliga formen.

// Before
cond1 ? cond2 ? elem2_if : elem2_else : elem1_else

// After
cond1 ? (cond2 ? elem2_if : elem2_else) : elem1_else

Lägg endast till parenteser på ternära uttryck i pilfunktioner om det löser förvirring (#1450)

Det finns en eslint-regel no-confusing-arrows som föreslår parenteser för ternära uttryck i pilfunktioner utan klammerparenteser. Följande två kodsnuttar är förvirrande:

var x = a => 1 ? 2 : 3;
var x = a <= 1 ? 2 : 3;

Det är förståeligt när koden är på en rad, men när den delas på flera rader blir parenteserna onödiga med indrag som vägledning, så vi lägger nu bara till dem när de tjänar sitt syfte att förtydliga.

// Before
var x = a => (1 ? 2 : 3);
var x = a =>
(1
? 2
: 3);

// After
var x = a => (1 ? 2 : 3);
var x = a =>
1
? 2
: 3;

Allmänna JavaScript-förbättringar

Infoga funktionsdeklaration med ett enda objekt som argument (#1173)

Denna förbättring har ofta efterfrågats för React Stateless Functional Components (SFC). Om du använder många sådana kommer detta sannolikt bli en stor förändring för dig.

// Before
const X = (
props: {
a: boolean,
},
) => <div />;

// After
const X = (props: {
a: boolean,
}) => <div />;

Bryt infogat objekt först i funktionsargument (#1453)

Vi upptäckte tidigt att användare vanligtvis bryter funktionens argument före returtypen. Tyvärr bröt koden för enskilda destruktureringsargument mot detta och skapade oestetisk kod som i detta exempel. Goda nyheten är att detta möjliggör infogning för enskilda argument med objekttyp.

// Before
class X {
async onDidInsertSuggestion({editor, triggerPosition, suggestion}): Promise<
void
> {
}
}

// After
class X {
async onDidInsertSuggestion({
editor,
triggerPosition,
suggestion
}): Promise<void> {
}
}

Bryt inte på tomma arrayer och objekt (#1440)

Detta har länge varit ett problem och är en enkel fix, men fungerade som värdefullt verktyg: när någon rapporterade att [] eller {} bröts kunde vi lösa genom att fixa annat. Det var ett bra sätt att hitta hörnfall. Lyckligtvis är denna källa nu uttömd och alla nya exempel ser bara dåliga ut utan annan orsak än brytningen. Dags att fixa det!

// Before
const a = someVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeLong.Expression || [
];

// After
const a = someVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeLong.Expression || [];

Bryt inte på [0] (#1441)

Vi har många problem där kod bryts vid arrayåtkomst när det ser dåligt ut. Vi har ännu ingen generisk lösning, men kan fixa en vanlig situation: [0].

// Before
queryThenMutateDOM(() => {
title = SomeThing.call(root, "someLongStringThatPushesThisTextReall")[
0
];
});

// After
queryThenMutateDOM(() => {
title = SomeThing.call(
root,
"someLongStringThatPushesThisTextReall",
)[0];
});

Indentera do-while-villkor (#1373)

Vi använde inte rätt indentering för do-while-villkor men någon upptäckte det, nu gör vi det!

// Before
do {}
while (someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringD);

// After
do {}
while (
someVeryLongStringA &&
someVeryLongStringB &&
someVeryLongStringC &&
someVeryLongStringD
);

Bevara infogad kommentar som sista argument (#1390)

Vi glömde att hantera ett specialfall i vår kommentarsdetektionskod när kommentarer placeras sist i JSX-attribut eller funktionsargument, vilket fick dem att hamna efter stängningstaggen. För JSX genererade detta kod med annan betydelse. Som tur är påverkade detta inte produktionskod eftersom vi sällan checkar in utkommenterad kod, men det skapar en dålig upplevelse under kodning.

// Before
const x = (
<div
attr1={1}>
// attr3={3}
{children}
</div>
);

// After
const x = (
<div
attr1={1}
// attr3={3}
>
{children}
</div>
);

Bryt klassuttryck returnerat av arrow-anrop (#1464)

I version 1.0 gjorde vi klasser inline i arrow-funktioner. Det visade sig inte fungera bra när klassen är komplex, så vi återgår till tidigare stil. Vi försöker verkligen undvika sådana pendlande stilförändringar, men tillåter oss ibland att rätta misstag!

// Before
export default (ViewComponent: Function, ContainerComponent: Function) =>
class extends React.Component {
static propTypes = {};
};

// After
export default (ViewComponent: Function, ContainerComponent: Function) =>
class extends React.Component {
static propTypes = {};
};

Åtgärda tom rad i block med EmptyStatement (#1375)

Denna bugg hittades genom fuzzing. Du kommer troligen inte stöta på den i verklig kod, men det är bra att veta att den är fixad!

// Input
if (a) {
b;


;
}

// Before
if (a) {
b;

}

// After
if (a) {
b;
}