Hoppa till huvudinnehållet

Prettier 1.0 släpps

· 17 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 →

Detta inlägg skrevs av vjeux och redigerades av jlongster, och publicerades ursprungligen här

Vi tillkännagav prettier officiellt för över två månader sedan som ett sätt att lösa problemet med att slösa tid på att formatera din kod. Det började som ett experiment men det slog tydligt an hos många människor och samlade ~7000 GitHub-stjärnor och över 100 000 månatliga npm downloads på bara två månader.

Om du inte redan vet: prettier är en JavaScript-formaterare som fungerar genom att kompilera din kod till ett AST (Abstract Syntax Tree), och sedan formattera AST:et. Precis som webbläsare bryter text automatiskt kommer prettier också att bryta kodraderna efter en given radlängd. Resultatet är snygg kod som är helt konsekvent oavsett vem som skrev den. Detta löser problemet med att programmerare lägger mycket tid på att manuellt flytta runt kod i sin editor och diskutera om formatering (se gif).

Från början var det inte helt klart om vi kunde få den att alltid generera giltig och snygg kod, men de goda nyheterna är att det inte längre är en öppen fråga. Många projekt (React, Jest, immutable-js, Haul, och fler) och företag (Oculus, Cloudflare) har antagit prettier för att formatera sina JavaScript- kodbaser och insett fördelarna med automatisk formatering (se denna tweet för mer!). Vissa av dem körde prettier på hela sin kodbas (tiotusentals rader kod).

Under de senaste tre veckorna har vi gått igenom alla öppna ärenden och försökt fatta så många beslut som möjligt om hur koden ska formateras samt åtgärdat de flesta buggar vi kände till. Det är inte perfekt och vi kommer att fortsätta förbättra det, men nu är det ett bra tillfälle att släppa version 1.0!

Detta betyder inte att vi aldrig kommer att ändra formateringen igen, men ändringarna blir minimala. Till exempel funderar vi på att justera ternära uttryck, men det kommer att bli väldigt få ändringar av det slaget hädanefter, och när det ändå händer kommer vi att släppa en ny major version.

Det innebär att prettier nu är säker att använda i produktion. Vi har fixat massor av buggar och sett till att resultatet är stabilt och semantiskt korrekt. Prettier är redo att användas – från några enstaka filer i ditt sidoprojekt till att konvertera hela din kodbas. Det är upp till dig att avgöra var du befinner dig på resan att släppa taget om ett visst sätt att formatera din kod.

Låt oss titta på vad 1.0 tillför till prettier!

Tack till alla följande bidragsgivare som har bidragit till denna release, och tack till Ian Storm Taylor för logotypen!

Adam Stankiewicz, Alex Rattray, Alex Stachowiak, Amjad Masad, Andrey Okonetchnikov, António Nuno Monteiro, Artem Sapegin, Benjamin Tan, Bill Mill, Brandon Mills, Brian Holt, Brian Ng, Charles Pick, ChristianHersevoort, Christopher Chedeau, Christopher Moeller, Damien Lebrun, Dan Harper, Dara Hak, David Ascher, David Hong, Davy Duperron, Eric Clemmons, Erick Romero, Esben Petersen, Filipe Fortes, Gregor Adams, Hampus,hlsson, Hawken Rives, Henry Zhu, Ilya Panasenko, James Henry, James Long, Jamie Webb, Jan Kassens, Jed Watson, Jeffrey Horn, Jeremy Morrell, Joe Fiorini, Jon LaBelle, Joseph Savona, Karl Horky, Karl O'Keeffe, Kent C. Dodds, Kevin Gibbons, Kim Joar Bekkelund, Lucas Bento, Lucas Duailibe, MarekMatejkaKCL, Mateusz Zatorski, Michael Ficarra, Michał Pierzchała, Mikael Brevik, Nathan Friedly, Patrick Camacho, Paul,arduner, Rafael Hengles, Raghuvir Kasturi, Rasmus Eneman, Riley Tomasek, Rogelio Guzman, Royce Townsend, Sean Grove, Shigeaki Okazaki, Simen,ekkhus, Simon Lydell, Sorin Muntean, Spencer Dixon, Thai Pangsakulyanont, Tom McKearney, Travis Jefferson, Umidbek Karimov, Vincent Voyer, Vu Tran, Walter Breakell, Yatharth Khatri, azu, daleroy, kalmanb, skratchdot

Alternativ

Prettier är en åsiktsdriven kodformaterare. I början av projektet trodde vi att det innebar att ha noll konfiguration som gofmt eller refmt. Men när vi fortsatte insåg vi att detta skulle skada Prettiers anpassning och att människor som kunde ha nytta av den inte skulle använda den eftersom den inte formaterade koden på det sätt de ville.

Istället har vår tolkning av att vara en åsiktsdriven kodformaterare blivit att erbjuda alternativ för grundläggande syntaxaspekter av karaktären "om den inte gör X, oavsett hur bra den är, kommer jag aldrig att använda den". Till exempel kommer jag (@vjeux) aldrig att använda standard eftersom den inte använder semikolon. Detta är absolut inte ett rationellt sätt att tänka, men det är så många människor beter sig och därför har vi "stilkrig".

Vi kommer fortfarande inte att införa alternativ för alla typer av syntax, utan bara för mer inflytelserika saker. Vi har identifierat två huvudalternativ som faller i den kategorin: tabulatorer vs mellanslag och semikolon vs utan semikolon. Så vi lägger till dem i Prettier!

--no-semi (#1129)

Stort tack till rattrayalex som gjorde mycket arbete för att detta skulle bli möjligt!

// Before
console.log();
[1, 2, 3].map(x => x + 1);

// After
console.log()
;[1, 2, 3].map(x => x + 1)

--use-tabs (#1026)

Tack till rhengles för implementeringen och förklaringarna bakom!

// Before
if (1) {
··console.log(); // Two spaces
}

// After
if (1) {
» console.log(); // One Tab!
}

Formatering

Resten av detta inlägg dokumenterar alla mindre förändringar som vi samlat ihop för 1.0. Vi kommer vanligtvis inte att publicera en ändringslogg som ett inlägg, men vi tyckte att det visar hur stabil Prettier blir och ger en god bild av vilka typer av problem vi löser nu för tiden.

Lägg till radbrytning för pilfunktioner utan klammerparenteser som returnerar anrop (#927)

Förmodligen den mest rapporterade frågan i nuvarande version av prettier: vi lade inte till en radbrytning efter pilfunktioner i anrop. Nu gör vi det. Som en sidnot är det ganska spännande att frågorna nu handlar om småsaker som detta och inte längre om "det här ser helt trasigt ut".

// Before
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter));

// After
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter)
);

Lägg till mjuk radbrytning vid tilldelning och parenteser kring return för binära uttryck (#871 & #870)

Långa kedjor av logiska uttryck ser konstiga ut när första raden är på samma rad som variabeldeklarationen eller return. Istället är det bättre att flytta den till nästa rad och lägga till parenteser vid return för att göra det giltigt.

// Before
const computedDescriptionLines = (showConfirm &&
descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;

// After
const computedDescriptionLines =
(showConfirm && descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;


// Before
return !filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`);

// After
return (
!filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`)
);

Gruppera första argument för inline-funktioner (#947)

Det första stora anpassade mönstret vi lagt till i prettier är att skriva funktioner som sista argument inline. Ett mindre vanligt är att göra samma sak för första argumentet. Det är ovanligt för moderna bibliotek att använda den stilen, men vissa grundläggande JavaScript-funktioner som setTimeout gör det, så det är rimligt att stödja det i prettier.

// Before
setTimeout(
function() {
thing();
},
500
);

// After
setTimeout(function() {
thing();
}, 500);

Förbättra JSX-utdata för stilkomponenter (#1144)

Malliteraler är väldigt knepiga att formatera eftersom blanktecken är signifikanta. Med prettier vill vi inte ändra programmets semantik genom omformatering, så vi lämnar dem som de är. Vårt nuvarande sätt att hantera dem är inte optimalt, och vi har idéer om hur man kan fixa det generellt. Men tills vidare kan vi göra några riktade fixar som denna för JSX.

// Before
<style>
{
`
color: red;
`
}
</style>

// After
<style>{`
color: red;
`}</style>

Lägg till stöd för punktnotation på samma rad i dekoratorer (#1029)

MobX 3 använder nu medlemsuttryck som ska skrivas inline - den tidigare heuristiken var för restriktiv. Det är nu fixat.

class X {
// Before
@action.bound
setPrice(price) {
this.price = price;
}

// After
@action.bound setPrice(price) {
this.price = price;
}
}

Klamma samma rad för enkelobjekt-destrukturering i funktioner (#1022)

React-komponenter utan tillstånd (stateless functional components) är väldigt populära just nu, och det är meningsfullt att klamma destruktureringen av första argumentet för att ta mindre plats.

// Before
export default function StatelessFunctionalComponent(
{
searchFilters,
title,
items
}
) {
return <div />;
}

// After
export default function StatelessFunctionalComponent({
searchFilters,
title,
items,
}) {
return <div />
}

Lägg till stöd för currying (#1066)

Redux förespråkar starkt att skriva funktioner i curried-form där varje argument är en nästlad funktion. Istället för att indentera dem placerar vi dem nu inline.

// Before
const mw = store =>
next =>
action => {
return next(action)
};

// After
const mw = store => next => action => {
return next(action)
};

Respektera escapning för JSX-strängar (#1056)

För strängar har vi testat flera olika lösningar gällande escapning: escape inget, escape allt, escape en vitlista... Men vi hittade ingen heuristik som fungerade rimligt för alla användningsfall. Så vi beslöt att lämna strängarna escapade som de matades in. Nu gör vi detta för JSX-strängar.

// Before
<a href="https://foo.bar?q1=foo&amp;q2=bar" />

// After
<a href="https://foo.bar?q1=foo&q2=bar" />

Parenteser

En välsignelse och förbannelse med utskrift från AST är att vi måste skriva ut alla programmets parenteser. Vårt tidigare förhållningssätt var att endast skriva ut det minsta antalet parenteser som behövdes för att programmet skulle vara giltigt och exekveras på samma sätt. Nu är vi villiga att lägga till parenteser som inte är strikt nödvändiga men som hjälper folk att förstå koden.

Lägg till parenteser för binära operatorer (#1153)

// Before
var sizeIndex = index - 1 >>> level & MASK;

// After
var sizeIndex = ((index - 1) >>> level) & MASK;

Lägg till parenteser för no-confusing-arrow-regeln (#1182)

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

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

Ta bort parenteser från kedjade tilldelningar (#967)

// Before
this.size = (this._origin = (this._capacity = 0));

// After
this.size = this._origin = this._capacity = 0;

Flow

I Prettiers tidiga dagar fokuserade vi tungt på att få kärnfunktionaliteten för JavaScript väl understödd. Nu när den är i bra skick har vi mer tid att formatera Flow-konstruktioner på ett polerat sätt.

Möjlighet att bryta flödesgeneriska typer (#1041)

// Before
type _ReactElement<DefaultProps, Props, Config: $Diff<Props, DefaultProps>, C: $React.Component<DefaultProps, Props, any>> = $React.Element<Config>;

// After
type _ReactElement<
DefaultProps,
Props,
Config: $Diff<Props, DefaultProps>,
C: $React.Component<DefaultProps, Props, any>
> = $React.Element<Config>;

Förbättrad utskrift av Flow-intersektioner (#1018 & #1155)

// Before
type Props =
& {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
}
& FooterProps;

// After
type Props = {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
} & FooterProps;

Stöd för radbrytningar i TupleTypeAnnotation (#1003)

// Before
export type FileMetaData = [/* id */ string, /* mtime */ number, /* visited */
| 0
| 1, /* dependencies */ Array<string>];

// After
export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0|1,
/* dependencies */ Array<string>,
];

Inga parenteser för Flow-shorthand med ett argument (#972)

// Before
type T = { method: (a) => void };

// After
type T = { method: a => void };

Möjlighet att bryta interface (#1060)

// Before
export interface Environment1 extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}

// After
export interface Environment1
extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}

Fixat utskrift av Flow-typliteraler för nummer (#1132)

// Before
type Hex = {n: 1};

// After
type Hex = {n: 0x01};

Brytpunkter

Det finns fortfarande fall där Prettier genererar kod > 80 kolumner trots att det finns möjligheter att skriva den kompaktare. Lösningen är att noggrant identifiera brytpunkter som inte påverkar vanliga fall negativt.

Möjlighet att bryta efter = för strängar och medlemsuttryck (#1142 & #1188)

// Before
elements[0].innerHTML = '<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered = obj.exampleOfOrderOfGetterAndSetterReordered;

// After
elements[0].innerHTML =
'<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered =
obj.exampleOfOrderOfGetterAndSetterReordered;

Brytmöjlighet för toppnivå-medlemsuttryck (#1036)

// Before
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0].style.paddingRight
).toBe("1000px");

// After
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0]
.style.paddingRight
).toBe("1000px");

Diverse förbättringar

Infogning av klassuttryck för pilfunktioner utan klammerparenteser (#1023)

En liten men signifikant ändring: vi infogar nu klasser som returneras från pilfunktioner.

// Before
jest.mock(
'../SearchSource',
() =>
class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);

// After
jest.mock(
'../SearchSource',
() => class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);

Respektera inte radbrytningar vid objekt-destrukturering (#981)

För objekt har vi specialhantering där vi behåller expansion om originalkoden innehåller \n. Denna logik applicerades av misstag även på destrukturering, vilket nu är korrigerat.

// Before
const Component2 = ({
props
}) => <Text>Test</Text>;

// After
const Component1 = ({ props }) => <Text>Test</Text>;

Språkstöd

För att Prettier ska vara användbar måste den kunna formatera all kod ni skriver. Vårt mål är att stödja allt som våra underliggande parsers (babylon och flow) kan tolka.

Stöd för ForOfStatement med await-flagga (#1073)

async function f() {
for await (const line of readLines(\\"/path/to/file\\")) {
(line: void); // error: string ~> void
}
}

Stöd för flödestyp-spridning (#1064)

type TypeB = { ...TypeA };

Korrekthet

Prettiers absolut viktigaste krav är att generera giltig kod med samma beteende som originalet. Ni ska kunna köra den över hela kodbasen utan oro. För att garantera detta gör vi flera saker:

  • Automatiserad validering av utdatans giltighet

  • Testning mot stora kodbaser som Facebook, CDNjs och communityns användare

  • Omfattande fuzzing med eslump

  • Prioriterar och åtgärdar korrekthetsrapporter omedelbart

Om du är så oturlig att stöta på ett, vänligen rapportera det så att vi kan åtgärda det. Du kan använda // prettier-ignore för att komma vidare under tiden.

Korrekt hantering av \r\n i JSXText (#1170)

// Before
<div>
{" "}
Text{" "}
</div>;

// After
<div>
Text
</div>;

Fixa parenteser vid funktionsanrop inuti new-uttryck (#1169)

// Before
new factory()();

// After
new (factory())();

Lägg till parenteser runt returtyp för arrow-funktioner (#1152)

// Before
const f = (): string => string => {};

// After
const f = (): (string => string) => {};

Fixa utskrift av exact object-typer i Flow (#1114)

// Before
type Props = {};

// After
type Props = {||};

Skriv ut kommentarer efter return-satser korrekt (#1178)

// Before
function f() {
return;
}

// After
function f() {
return /* a */;
}

Skriv ut kommentarer efter objektnycklar (#1131)

// Before
let a = {
"a": () => 1
};

// After
let a = {
"a" /* comment */: () => 1
};

Fixa inledande kommentarer i returnerade SequenceExpression (#1133)

// Before
function sequenceExpressionInside() {
return;
// Reason for a
a, b;
}

// After
function sequenceExpressionInside() {
return ( // Reason for a
a, b
);
}

Fixa utskrift av enskild valfri parameter i arrow-funktioner (#1002)

// Before
a = b? => c;

// After
a = (b?) => c;

Fixa default exports (#998)

// Before
export { foo, bar } from "./baz";

// After
export foo, {bar} from './baz';

Skriv rad-/blandade kommentarer på nya rader inuti JSXEmptyExpression (#985)

// Before
<div>
{
// single line comment}
</div>;

// After
<div>
{
// single line comment
}
</div>;

Kommentarer

Prettier fungerar genom att skriva ut AST-trädet, vilket gör kommentarhantering knepigt eftersom de kan placeras var som helst mellan tokens. Prettier hanterar detta genom att koppla en kommentar till en AST-nod, antingen före eller efter den. Vi har en generisk heuristik för att hitta rätt plats som fungerar i de flesta fall, men det finns många edge cases som kräver manuell hantering.

Prettier kommer aldrig kunna hantera alla galna platser där kommentarer kan placeras, men vi strävar efter att göra ett rimligt jobb i de flesta fall. Om du stöter på konstigheter relaterade till kommentarer, skapa gärna issues så kan vi lösa det.

Stöd för breakParent med willBreak (#674)

// Before
runtimeAgent.getProperties(objectId, false, false, false, ( // ownProperties // accessorPropertiesOnly // generatePreview
error,
properties,
internalProperties
) => {
return 1;
});

// After
runtimeAgent.getProperties(
objectId,
false, // ownProperties
false, // accessorPropertiesOnly
false, // generatePreview
(error, properties, internalProperties) => {
return 1
},
);

Fixa extra tom rad vid kommentarer i switch-case (#936)

// Before
switch (foo) {
case "bar":
doThing()


// no default
}

// After
switch (foo) {
case "bar":
doThing()

// no default
}

Fixa kommentarer i import-deklarationer (#1030)

// Before
import {
FN1,
FN2,
// FN3,
FN4
} from // FN4,
// FN5
"./module";

// After
import {
FN1,
FN2,
// FN3,
FN4,
// FN4,
// FN5
} from './module';

Fixa kommentarer för sista argumentet i funktioner (#1176 & #905)

// Before
function f(
a: number
)// some comment here
: number {
return a + 1;
}

// After
function f(
a: number
// some comment here
): number {
return a + 1;
}

Avrestriktion för kommentarer i flow union-typer (#1040)

// Before
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
| // The upload timed out
B
| // Failed somewhere on the line
C;

// After
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
// The upload timed out
| B
// Failed somewhere on the line
| C;

Fixa kommentarer inuti ObjectPattern (#1045)

// Before
export default (
{
foo,
bar
// comment
// comment 2
}: {
foo?: Object,
bar?: Object
}
) => {};

// After
export default (
{
foo,
bar
}: {
// comment
foo?: Object,
// comment 2
bar?: Object,
},
) => {}

Fixa sorteringsposition för kommentarer (#1038)

// Before
let {
// comment
a = b
} = c;

// After
let {
a = b // comment
} = c;

Fixa kommentarposition för binära uttryck (#1043)

// Before
a = Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange// Comment
-
offset;

// After
a =
// Comment
Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange -
offset;

Fixa kommentarer för klassmetoder (#1074)

// Before
class x {
focus() // do nothing
{
// do nothing
}
}

// After
class x {
focus() {
// do nothing
// do nothing
}
}

Stöd för "// prettier-ignore" i kommentarsblock (#1125)

// Before
module.exports = {
// Some comment
// prettier-ignore
m: matrix(1, 0, 0, 0, 1, 0, 0, 0, 1)
};

// After
module.exports = {
// Some comment
// prettier-ignore
m: matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
)
};

Stabilisera kommentarer för VariableDeclarator (#1130)

// Before
let obj = [ // Comment
'val'
];

// After
let obj = [
// Comment
'val'
];

Åtgärda identifiering av kommentarer före kommatecken (#1127)

// Before
const foo = {
a: 'a' /* comment for this line */,

/* Section B */
b: 'b',
};

// After
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};

Åtgärda sista kommentaren i ett if-test (#1042)

// Before
if (isIdentifierStart(code) || code === 92) {
/* '\' */
}

// After
if (isIdentifierStart(code) || code === 92 /* '\' */) {}