Hoppa till huvudinnehållet

Motivering

Inofficiell Beta-översättning

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

Prettier är en åsiktsdriven kodformaterare. Det här dokumentet förklarar några av dess designval.

Vad Prettier fokuserar på

Korrekthet

Prettiers främsta krav är att generera giltig kod med exakt samma beteende som före formateringen. Rapportera gärna kod där Prettier inte följer dessa korrekthetsregler – det är en bugg som behöver åtgärdas!

Strängar

Dubbel- eller enkelfnutt? Prettier väljer den som kräver färre escape-tecken. "It's gettin' better!", inte 'It\'s gettin\' better!'. Vid lika resultat eller om strängen inte innehåller några fnuttar använder Prettier som standard dubbelfnutt (men detta kan ändras via alternativet singleQuote).

JSX har ett separat alternativ för fnuttar: jsxSingleQuote. JSX har sina rötter i HTML, där dubbelfnutt är standard för attribut. Webbläsarens utvecklarverktyg följer också denna konvention genom att alltid visa HTML med dubbelfnutt, även om källkoden använder enkelfnutt. Ett separat alternativ möjliggör enkelfnutt för JS och dubbelfnutt för "HTML" (JSX).

Prettier behåller ditt strängescapingssätt. Till exempel formateras inte "🙂" till "\uD83D\uDE42" eller vice versa.

Tomma rader

Tomma rader visar sig vara svåra att generera automatiskt. Prettiers strategi är att bevara tomma rader som de såg ut i originalkoden. Två extra regler gäller:

  • Prettier komprimerar flera tomma rader till en enda tom rad.

  • Tomma rader i början och slutet av block (och hela filer) tas bort. (Filer avslutas dock alltid med en ensam nyrad.)

Flerradsobjekt

Som standard skriver Prettiers utskriftsalgoritm ut uttryck på en rad om de får plats. Objekt används dock till många olika saker i JavaScript, och ibland gynnar läsbarheten om de förblir flerradiga. Se exempelvis objektlistor, kapslade konfigurationer, stilmallar och metodnycklar. Vi har inte funnit en bra regel för alla dessa fall, så som standard behåller Prettier objekt som flerradiga om det finns en nyrad mellan { och första nyckeln i originalkoden. Långa enradsobjekt expanderas automatiskt, medan korta flerradsobjekt komprimeras aldrig.

Du kan inaktivera detta beteende med alternativet objectWrap.

Tips: Om du har ett flerradsobjekt du vill slå ihop till en enda rad:

const user = {
name: "John Doe",
age: 30,
};

…behöver du bara ta bort nyraden efter {:

const user = {  name: "John Doe",
age: 30
};

…och sedan köra Prettier:

const user = { name: "John Doe", age: 30 };

Och vill du återgå till flerradsformat, lägg till en nyrad efter {:

const user = {
name: "John Doe", age: 30 };

…och kör Prettier:

const user = {
name: "John Doe",
age: 30,
};
En notis om formateringsreversibilitet

Den semi-manuella formateringen för objektliteraler är faktiskt en tillfällig lösning, inte en funktion. Den implementerades enbart för att en bra heuristik inte kunde hittas i tid och en brådskande fix behövdes. Som generell strategi undviker dock Prettier icke-reversibel formatering som denna. Teamet letar därför fortfarande efter heuristik som antingen skulle tillåta att helt ta bort detta beteende eller åtminstone minska antalet situationer där det tillämpas.

Vad menas med reversibel? När en objektliteral blir flerradig kommer Prettier inte att föra ihop den igen. Om vi i Prettier-formaterad kod lägger till en egenskap i en objektliteral, kör Prettier, sedan ångrar oss, tar bort den tillagda egenskapen och kör Prettier igen, kan vi sluta med en formatering som inte är identisk med den ursprungliga. Denna onödiga ändring kan till och med hamna i en commit, vilket är precis den typen av situation som Prettier skapades för att förhindra.

Decorators

Precis som med objekt används decorators för många olika saker. Ibland är det vettigt att skriva decorators ovanför raden de dekorerar, ibland är det trevligare om de är på samma rad. Vi har inte kunnat hitta en bra regel för detta, så Prettier behåller din decorators positionering som du skrev dem (om de får plats på raden). Detta är inte idealiskt, men en pragmatisk lösning på ett svårt problem.

@Component({
selector: "hero-button",
template: `<button>{{ label }}</button>`,
})
class HeroButtonComponent {
// These decorators were written inline and fit on the line so they stay
// inline.
@Output() change = new EventEmitter();
@Input() label: string;

// These were written multiline, so they stay multiline.
@readonly
@nonenumerable
NODE_TYPE: 2;
}

Det finns ett undantag: klasser. Vi anser aldrig att det är vettigt att ha decorators inline för dem, så de flyttas alltid till sina egna rader.

// Before running Prettier:
@observer class OrderLine {
@observable price: number = 0;
}
// After running Prettier:
@observer
class OrderLine {
@observable price: number = 0;
}

Notera: Prettier 1.14.x och äldre försökte automatiskt flytta dina decorators, så om du har kört en äldre version av Prettier på din kod kan du behöva manuellt slå ihop vissa decorators här och där för att undvika inkonsekvenser:

@observer
class OrderLine {
@observable price: number = 0;
@observable
amount: number = 0;
}

Mallliteraler (Template literals)

Mallliteraler kan innehålla interpolationer. Att avgöra om det är lämpligt att infoga en radbrytning inom en interpolation beror tyvärr på den semantiska innehållet i mallen - till exempel är det oftast oönskat att införa en radbrytning mitt i en mening på naturligt språk. Eftersom Prettier inte har tillräckligt med information för att själv fatta detta beslut använder den en heuristik som liknar den för objekt: den kommer bara dela upp ett interpolationsuttryck över flera rader om det redan fanns en radbrytning inom interpolationen.

Det innebär att en literal som följande inte kommer delas upp på flera rader, även om den överskrider utskriftsbredden:

`this is a long message which contains an interpolation: ${format(data)} <- like this`;

Om du vill att Prettier ska dela upp en interpolation måste du se till att det finns en radbrytning någonstans inom ${...}. Annars kommer den att behålla allt på en enda rad, oavsett hur långt det är.

Teamet föredrar att inte vara beroende av den ursprungliga formateringen på detta sätt, men det är den bästa heuristik vi har för tillfället.

Semikolon

Det här handlar om att använda alternativet noSemi.

Betrakta följande kodstycke:

if (shouldAddLines) {
[-1, 1].forEach(delta => addLine(delta * 20))
}

Även om ovanstående kod fungerar utmärkt utan semikolon, omvandlar Prettier den faktiskt till:

if (shouldAddLines) {
;[-1, 1].forEach(delta => addLine(delta * 20))
}

Detta för att hjälpa dig undvika misstag. Föreställ dig om Prettier inte satte dit det semikolonet och du lade till denna rad:

 if (shouldAddLines) {
+ console.log('Do we even get here??')
[-1, 1].forEach(delta => addLine(delta * 20))
}

Hoppsan! Ovanstående betyder faktiskt:

if (shouldAddLines) {
console.log('Do we even get here??')[-1, 1].forEach(delta => addLine(delta * 20))
}

Med ett semikolon framför [ uppstår aldrig sådana problem. Det gör raden oberoende av andra rader så du kan flytta och lägga till rader utan att behöva tänka på ASI-regler.

Denna praxis är också vanlig i standard som använder en semikolonfri stil.

Notera att om ditt program för närvarande har en bugg relaterad till semikolon kommer Prettier inte automatiskt åtgärda buggen åt dig. Kom ihåg: Prettier formaterar bara om koden, den ändrar inte kodens beteende. Ta denna buggiga kod som exempel, där utvecklaren glömde sätta ett semikolon före (:

console.log('Running a background task')
(async () => {
await doBackgroundWork()
})()

Om du matar in den här koden i Prettier kommer den inte att ändra kodens beteende. Istället formaterar den om koden på ett sätt som visar hur den faktiskt kommer att bete sig när den körs.

console.log("Running a background task")(async () => {
await doBackgroundWork();
})();

Utskriftsbredd

Alternativet printWidth är mer en riktlinje för Prettier än en strikt regel. Det är inte en övre gräns för radlängd. Det är ett sätt att ange ungefär hur långa rader du önskar. Prettier kommer att skapa både kortare och längre rader, men strävar generellt efter att uppfylla den angivna utskriftsbredden.

Det finns vissa specialfall, som mycket långa strängliteraler, regex-uttryck, kommentarer och variabelnamn, som inte kan delas över flera rader (utan kodtransformationer som Prettier inte utför). Eller om du nästlar din kod 50 nivåer djupt kommer dina rader naturligtvis mestadels bestå av indrag :)

Bortsett från det finns det några fall där Prettier medvetet överskrider utskriftsbredden.

Importer

Prettier kan dela långa import-uttryck över flera rader:

import {
CollectionDashboard,
DashboardPlaceholder,
} from "../components/collections/collection-dashboard/main";

Följande exempel får inte plats inom utskriftsbredden, men Prettier skriver ändå ut det på en enda rad:

import { CollectionDashboard } from "../components/collections/collection-dashboard/main";

Detta kan vara oväntat för vissa, men vi gör det på detta sätt eftersom det var en vanlig önskan att behålla import med enskilda element på en rad. Samma sak gäller för require-anrop.

Testfunktioner

En annan vanlig önskan var att behålla långa testbeskrivningar på en rad, även om de blir för långa. I sådana fall hjälper det inte mycket att bryta argumenten till nya rader.

describe("NodeRegistry", () => {
it("makes no request if there are no nodes to prefetch, even if the cache is stale", async () => {
// The above line exceeds the print width but stayed on one line anyway.
});
});

Prettier har specialhantering för vanliga testramverksfunktioner som describe, it och test.

JSX

Prettier formaterar saker lite annorlunda jämfört med vanlig JavaScript när JSX är inblandat:

function greet(user) {
return user
? `Welcome back, ${user.name}!`
: "Greetings, traveler! Sign up today!";
}

function Greet({ user }) {
return (
<div>
{user ? (
<p>Welcome back, {user.name}!</p>
) : (
<p>Greetings, traveler! Sign up today!</p>
)}
</div>
);
}

Det finns två skäl.

För det första har många redan omringat sin JSX med parenteser, särskilt i return-satser. Prettier följer denna vanliga stil.

För det andra gör den alternativa formateringen det enklare att redigera JSX. Det är lätt att lämna kvar ett semikolon. Till skillnad från vanlig JS kan ett kvarblivet semikolon i JSX bli vanlig text som visas på din sida.

<div>
<p>Greetings, traveler! Sign up today!</p>; {/* <-- Oops! */}
</div>

Kommentarer

När det gäller kommentarers innehåll kan Prettier inte göra så mycket. Kommentarer kan innehålla allt från prosastycken till bortkommenterad kod och ASCII-diagram. Eftersom de kan innehålla vad som helst kan Prettier inte veta hur de ska formateras eller brytas. Så de lämnas som de är. Enda undantaget är JSDoc-stilkommentarer (blockkommentarer där varje rad börjar med *), där Prettier kan justera indenteringen.

Sedan finns frågan om var kommentarerna ska placeras. Det visar sig vara ett mycket svårt problem. Prettier gör sitt bästa för att behålla kommentarer ungefär där de var, men det är ingen lätt uppgift eftersom kommentarer kan placeras nästan var som helst.

Generellt får du bäst resultat när du placerar kommentarer på egna rader, istället för i slutet av rader. Föredra // eslint-disable-next-line framför // eslint-disable-line.

Observera att "magiska kommentarer" som eslint-disable-next-line och $FlowFixMe ibland kan behöva flyttas manuellt eftersom Prettier delar upp uttryck på flera rader.

Föreställ dig denna kodbit:

// eslint-disable-next-line no-eval
const result = safeToEval ? eval(input) : fallback(input);

Sedan behöver du lägga till ett annat villkor:

// eslint-disable-next-line no-eval
const result = safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Prettier kommer att omvandla ovanstående till:

// eslint-disable-next-line no-eval
const result =
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Vilket innebär att kommentaren eslint-disable-next-line inte längre har effekt. I detta fall måste du flytta kommentaren:

const result =
// eslint-disable-next-line no-eval
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Om möjligt, föredra kommentarer som gäller linjeintervall (t.ex. eslint-disable och eslint-enable) eller på statements-nivå (t.ex. /* istanbul ignore next */), dessa är ännu säkrare. Det går att förbjuda användning av eslint-disable-line och eslint-disable-next-line-kommentarer med hjälp av eslint-plugin-eslint-comments.

Anmärkning om icke-standardiserad syntax

Prettier kan ofta känna igen och formatera icke-standardiserad syntax som tidiga ECMAScript-förslag och Markdown-syntaxutökningar som inte definieras i någon specifikation. Stödet för sådan syntax är best-effort och experimentellt. Inkompatibiliteter kan introduceras i vilken release som helst och bör inte ses som breaking changes.

Anmärkning om maskingenererade filer

Vissa filer, som package.json eller composer.lock, genereras automatiskt och uppdateras regelbundet av pakethanteraren. Om Prettier använde samma JSON-formateringsregler som för andra filer skulle det regelbundet orsaka konflikter med dessa verktyg. För att undvika detta använder Prettier en formaterare baserad på JSON.stringify för sådana filer. Du kan märka skillnader, som borttagning av vertikalt whitespace, men detta är avsiktligt beteende.

Vad Prettier inte bryr sig om

Prettier skriver bara ut kod. Den transformerar inte koden. Detta för att begränsa Prettiers omfattning. Låt oss fokusera på utskriften och göra det riktigt bra!

Här är några exempel på saker som ligger utanför Prettiers ansvarsområde:

  • Omvandla enkla eller dubbla citattecken till template literals eller vice versa.

  • Använda + för att dela upp långa strängliteraler i delar som passar utskriftsbredden.

  • Lägga till/ta bort {} och return där de är valfria.

  • Omvandla ?: till if-else-satser.

  • Sortera/flytta imports, objektnycklar, klassmedlemmar, JSX-nycklar, CSS-egenskaper eller annat. Förutom att det är en transformation snarare än bara utskrift (som nämnts ovan) är sortering potentiellt osäker på grund av sidoeffekter (t.ex. för imports) och försvårar verifieringen av det viktigaste korrekthetsmålet.