Hoppa till huvudinnehållet

Prettier 1.5: GraphQL, CSS-in-JS & JSON

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

Den här releasen lägger till stöd för formatering av GraphQL, CSS-in-JS (inklusive styled-components) och JSON i Prettier.

Det här är releasen jag väntat på så länge: en med minimala ändringar för JavaScript!

Under de senaste 6 månaderna har vi fortsatt att göra ändringar i olika aspekter av hur JavaScript skrivs ut, i hopp om att nå en stabil punkt någon gång. Inget automatiserat verktyg kommer någonsin att skriva ut perfekt kod för alla möjliga edge cases. Målet är att hitta en bra punkt där vi, när användare rapporterar konstigt formaterad kod, inte kan förbättra den utan att göra annan kod sämre, introducera svårförståeligt beteende eller orimlig komplexitet i kodbasen.

Vi är inte 100% där än, men närmare än någonsin!

Nu när behoven för JavaScript-stöd minskar är det en möjlighet att stödja andra språk som frontend-utvecklare arbetar med och vill formatera. Vi introducerade TypeScript och CSS i förra releasen och gör nu en serie korrigeringar för dem. Dessutom lägger vi till stöd för nya språk: GraphQL-frågor, inbäddad CSS-in-JS och JSON finns nu i prettier!

Blogginlägg: Att lägga till en ny layoutstrategi i Prettier av @karl

Prettier är inte bara ett användbart verktyg utan också en riktigt cool teknik. @karl ägnade mycket tid åt att förbättra JSX-stödet och implementerade samtidigt en ny primitiv i prettier: fill. Han skrev ett mycket intressant blogginlägg Att lägga till en ny layoutstrategi i Prettier som jag varmt rekommenderar om du är intresserad av hur prettier fungerar bakom kulisserna.

GraphQL

Tack vare @stubailo, @jnwng, @tgriesser och @azz stöder prettier nu utskrift av GraphQL-frågor!

Det fungerar för .graphql-filer och inom JavaScript-mallar som börjar med graphql, graphql.experimental och gql för att fungera med Relay och Apollo.

ReactDOM.render(
<QueryRenderer
query={graphql`
query appQuery {
viewer {
...TodoApp_viewer
}
}
`}
// ...
/>,
mountNode
);

Observera att det endast stöder GraphQLs öppenkällkodssyntax, så det fungerar inte med Relay Classic – endast med Relay Modern.

CSS-in-JS

Om du använder styled-components eller styled-jsx kommer prettier nu att formatera om CSS:en i dina template-uttryck. Stort tack till @pomber för fantastiskt arbete!

const EqualDivider = styled.div`
margin: 0.5rem;
padding: 1rem;
background: papayawhip;
> * {
flex: 1;
&:not(:first-child) {
${props => (props.vertical ? "margin-top" : "margin-left")}: 1rem;
}
}
`;

JSON

Detta var ganska enkelt att implementera men ändå mycket användbart. Tack till @josephfrazier för genomförandet :)

{
"name": "prettier",
"version": "1.5.0",
"description": "Prettier is an opinionated JavaScript formatter",
"bin": {
"prettier": "./bin/prettier.js"
}
}

CSS

Jag är riktigt entusiastisk eftersom vi bara ägnade några dagar åt det initiala CSS-stödet och det fungerat överraskande bra. Denna release innehåller flera viktiga förbättringar för CSS utan större ändringar.

CSS: Varje selektor skrivs nu på egen rad (#2047) av @yuchi

Den största okända faktorn vid formatering av CSS var hur man skulle hantera flera selektorer. Vårt initiala tillvägagångssätt var att använda 80-kolumnersregeln där vi endast delade upp om det överskred den gränsen. Många användare rapporterade att de använde en annan strategi: alltid bryta efter ett ,. Det visar sig att många populära kodbaser använder detta tillvägagångssätt och det ger en bra överblick över selektorns struktur när de placeras ovanpå varandra.

// Before
.clusterPlannerDialog input[type="text"], .clusterPlannerDialog .uiTypeahead {
color: #333;
}

// After
.clusterPlannerDialog input[type="text"],
.clusterPlannerDialog .uiTypeahead {
color: #333;
}

CSS: gemener i hexfärger (#2203) av @azz

Begreppet kodformatering har suddiga gränser. Kärnan handlar om blanksteg, men vissa aspekter som enkelfjong vs dubbelfjong och semikolon ingår vanligtvis. Med Prettier för JavaScript formaterar vi också strängar lätt genom att ta bort extra \ och normaliserar siffror. För CSS behöver vi göra en liknande tolkning av var gränsen går. För färger bestämde vi oss för att göra alla bokstäver till gemener och sluta där. Att omvandla rgb() till hex eller 6-hex till 3-hex ligger utanför scope.

// Before
.foo {
color: #AAA;
-o-color: #fabcd3;
-ms-color: #AABBCC;
}

// After
.foo {
color: #aa;
-o-color: #fabcd3;
-ms-color: #aabbcc;
}

CSS: Använd fill för CSS-värden (#2224)

Den nya fill-primitiven visade sig vara mycket användbar för CSS. För långa värden kan vi istället för att bryta och sätta \n före varje element endast sätta \n när det överskrider gränsen. Det resulterar i mycket snyggare kod.

// Before
foo {
border-left:
1px
solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}

// After
foo {
border-left: 1px solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}

CSS: Tillåt långa media-regler att brytas (#2219)

Detta är ytterligare en liten förbättring i resan mot fullt stöd för ett nytt språk. Vi kodar nu in möjligheten att bryta långa @media-regler.

// Before
@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {
}

// After
@media all and (-webkit-min-device-pixel-ratio: 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
}

CSS: Skriv @else på samma rad som } (#2088) av @azz

Less och SCSS utvecklas till riktiga programmeringsspråk :) Steg för steg börjar vi formatera alla deras konstruktioner på samma sätt som JavaScript. Den här gången handlar det om placeringen av else.

// Before
@if $media == phonePortrait {
$k: .15625;
}
@else if $media == tabletPortrait {
$k: .065106;
}

// After
@if $media == phonePortrait {
$k: .15625;
} @else if $media == tabletPortrait {
$k: .065106;
}

CSS: implementera prettier-ignore (#2089) av @azz

Även om vi vill att Prettier ska formatera hela kodbasen, finns det tillfällen då vi "vet bättre" och vill ha en nödutgång. Det är här kommentaren prettier-ignore kommer in. Den fungerade inte för CSS men det var ett förbiseende, nu är den implementerad :)

// Before
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}

// After
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}

CSS: Fixa att css-modules composes bryts vid lång radbredd (#2190) av @tdeekens

För att vara snabba använder många "packagers" inte parsning för att extrahera beroenden utan en enkel regex. Det är anledningen till att vi inte bryter långa require()-anrop och det påverkar också CSS Modules. Om du lägger till radbrytningar i composes-fältet känns de inte längre igen. Därför bryter vi inte längre där, även om det överskrider 80 kolumner.

// Before
.reference {
composes:
selector
from
"a/long/file/path/exceeding/the/maximum/length/forcing/a/line-wrap/file.css";
}

// After
.reference {
composes: selector from "a/long/file/path/exceeding/the/maximum/length/forcing/a/line-wrap/file.css";
}

CSS: Försök med scss först när det finns ett @import med kommatecken (#2225)

Vi tog beslutet att endast ha en enda parser på hög nivå för CSS, SCSS och Less trots att vi använder postcss-less och postcss-scss under huven. Vi använder regex för att avgöra vilken parser som ska provas först och faller tillbaka på den andra om syntaxfel uppstår. Tyvärr kastar den första (felaktiga) parsern inte fel för vissa funktioner utan hoppar istället över vissa element. Därför måste vi förstärka regexen för att säkerställa korrekt tidig detektering.

Lyckligtvis fungerar denna lösning bra i praktiken. Om vi stöter på fler edge cases kommer vi troligen behöva göra den "rätta grejen" och dela upp dem i två separata parsers.

// Before
@import "text-shadow";

// After
@import "rounded-corners", "text-shadow";

TypeScript

TypeScript-stödet är nu stabilt, alla ändringar i denna release rör små edge cases.

TypeScript: skriv typ-parametrar för pilar-funktioner på samma rad som parametrarna (#2101) av @azz

Prettiers kärnalgoritm expanderar en grupp om alla element inte får plats. Det fungerar utmärkt för det mesta av JavaScript, men ett fall den hanterar mindre bra är när två grupper är sida vid sida, här: <Generics>(Arguments). Vi måste noga skapa grupper så att argument expanderas först, vilket är vad användare generellt förväntar sig.

// Before
export const forwardS = R.curry(<
V,
T
>(prop: string, reducer: ReducerFunction<V, T>, value: V, state: {[name: string]: T}) =>
R.assoc(prop, reducer(value, state[prop]), state)
);

// After
export const forwardS = R.curry(
<V, T>(
prop: string,
reducer: ReducerFunction<V, T>,
value: V,
state: { [name: string]: T }
) => R.assoc(prop, reducer(value, state[prop]), state)
);

TypeScript: behåll parenteser kring yield/await med non-null assertion (#2149) av @azz

Vi har valt att manuellt hantera parenteser. När en ny operator introduceras måste vi säkerställa att vi lägger till korrekta parenteser när den kombineras med andra operatorer. Här missade vi await inuti TypeScripts !.

// Before
const bar = await foo(false)!;

// After
const bar = (await foo(false))!;

TypeScript: Skriv ut i import om det finns i källkoden (#2150) av @azz

Vi använder typescript-eslint-parser som översätter TypeScript-AST till estree-AST. Ibland hittar vi edge cases den inte hanterar, som här med tomma {} som är viktiga i TypeScript. Teamet fixade detta snabbt efter vår tillfälliga lösning.

// Before
import from "@types/googlemaps";

// After
import {} from "@types/googlemaps";

TypeScript: Dela alltid upp interfaces på flera rader (#2161) av @azz

Koden som implementerar interface delas med koden som skriver ut objects, som innehåller en regel att behålla dem expanderade om det finns ett \n inuti. Men detta är inte det avsedda beteendet för interfaces. Vi vill alltid expandera, som vi gör för klasser, även om det får plats på 80 kolumner.

// Before
interface FooBar { [id: string]: number; }

// After
interface FooBar {
[id: string]: number;
}

TypeScript: Fixa extra semikolon i TypeScript-deklarationer (#2167) av @azz

no-semi och semi är ofta efterfrågade, men vi på Prettier-teamet är ett steg före och implementerade two-semi åt dig! Skämt åsido, det var en bugg som nu är fixad ;)

// Before
declare module "classnames" {
export default function classnames(
...inputs: (string | number | false | object | undefined)[]
): string;;
}

// After
declare module "classnames" {
export default function classnames(
...inputs: (string | number | false | object | undefined)[]
): string;
}

TypeScript: Gruppera funktionsparametrar i anrops-/konstruktionssignaturer (#2169) av @azz

Kommentarer före metoder kunde trigga oväntad expansion. Lösningen var enkel: omsluta utskriften med en group.

// Before
interface TimerConstructor {
// Line-splitting comment
new (
interval: number,
callback: (handler: Timer) => void
): Timer;
}

interface TimerConstructor {
// Line-splitting comment
new (interval: number, callback: (handler: Timer) => void): Timer;
}

TypeScript: Uppgradera tsep (#2183) av @azz

Denna bugg var irriterande: formateringen lade till ytterligare ett _ vid varje körning!

// Before
obj = {
__: 42
___: 42
};

// After
obj = {
_: 42
__: 42
};

TypeScript: Bryt vid flera interface-arv (#2085) av @azz

Till skillnad från JavaScript låter TypeScript dig ärva från flera klasser samtidigt. Det visar sig att den här funktionen används flitigt, och Prettier hanterar nu utskriften bättre.

// Before
export interface ThirdVeryLongAndBoringInterfaceName extends AVeryLongAndBoringInterfaceName, AnotherVeryLongAndBoringInterfaceName, AThirdVeryLongAndBoringInterfaceName {}

// After
export interface ThirdVeryLongAndBoringInterfaceName
extends AVeryLongAndBoringInterfaceName,
AnotherVeryLongAndBoringInterfaceName,
AThirdVeryLongAndBoringInterfaceName {}

TypeScript: hantera ObjectPattern istället för ObjectExpression inuti BinaryExpression (#2238) av @azz

Det här är inte särskilt intressant – det rör ett hörnfall som inte hanterades korrekt i konverteringen från TypeScript till estree.

// Before
call(c => { bla: 1 }) || [];

// After
call(c => ({ bla: 1 })) || [];

Bevara tomma rader efter direktiv (#2070)

Genom stödet för TypeScript används Prettier nu i många Angular-kodbaser, vilket avslöjar hörnfall som inte hanterades korrekt tidigare. I det här fallet bevarades inte tomma rader efter direktiv inuti funktioner.

// Before
export default class {
constructor($log, $uibModal) {
"ngInject";
Object.assign(this, { $log, $uibModal });

// After
export default class {
constructor($log, $uibModal) {
"ngInject";

Object.assign(this, { $log, $uibModal });

JavaScript

Den här releasen har få JavaScript-förändringar – vilket är fantastiskt! Vi börjar se ljuset i slutet av tunneln och närmar oss en stabil kodformaterare. Vi kommer aldrig nå en 100% perfekt automatisk formaterare. Målet är att varje rapporterat problem ska vara sådant att det inte går att förbättra utskriften utan att försämra andra delar.

Tillåt omkombinering av JSX-rader (#1831) av @karl

Prettiers mål är konsekvent kodformatering: med samma AST skriver vi alltid ut identiskt. Tidigare behövde vi kompromissa på två ställen genom att läsa originalformatet: JSX och objekt. Med den här ändringen förlitar vi oss inte längre på originalinmatningen för JSX med textinnehåll, vilket möjliggör dynamisk omlastning av text i JSX.

// Before
const Abc = () => {
return (
<div>
Please state your
{" "}
<b>name</b>
{" "}
and
{" "}
<b>occupation</b>
{" "}
for the board of directors.
</div>
);
};

// After
const Abc = () => {
return (
<div>
Please state your <b>name</b> and <b>occupation</b> for the board of
directors.
</div>
);
}

Bryt vid icke-literal beräknad medlemsexpression (#2087) av @azz

Utskrift av medlemskedjor är Prettiers mest komplexa del, och vi hittar ständigt små justeringar som förbättrar användarupplevelsen.

// Before
nock(/test/)
.matchHeader("Accept", "application/json")[httpMethodNock(method)]("/foo")
.reply(200, {
foo: "bar",
});

// After
nock(/test/)
.matchHeader("Accept", "application/json")
[httpMethodNock(method)]("/foo")
.reply(200, {
foo: "bar",
});

Indentera första variabeln i "one-var"-scenario (#2095) av @azz

Hittills har vi inte fokuserat på att stödja utskrift av flera variabler i en deklaration, eftersom en variabel per deklaration är vanligast. För enstaka deklarationer vill vi inte indentera, men när flera följer efter ser det konstigt ut utan indentering.

// Before
var templateTagsMapping = {
'%{itemIndex}': 'index',
'%{itemContentMetaTextViews}': 'views'
},
separator = '<span class="item__content__meta__separator">•</span>';

// After
var templateTagsMapping = {
'%{itemIndex}': 'index',
'%{itemContentMetaTextViews}': 'views'
},
separator = '<span class="item__content__meta__separator">•</span>';

Tillåt radbrytning vid import med både standard och namngiven import (#2096) av @azz

Det här var en olycklig regression från 1.4 där vi placerade import på en rad när den bara innehöll ett element. Problemet var att definitionen av "ett element" inkluderade både typ och element – nu är detta korrigerat!

// Before
import transformRouterContext, { type TransformedContextRouter } from '../../helpers/transformRouterContext';

// After
import transformRouterContext, {
type TransformedContextRouter
} from '../../helpers/transformRouterContext';

Aktivera allowImportExportEverywhere (#2207) av @zimme

Prettiers mål är att formatera verklig kod, så vi aktiverar lösa/experimentella lägen i alla parsers vi stödjer. Babylon tillåter import inuti funktioner (ej enligt standarden), men det kostar oss nästan inget att stödja detta.

// Before
ParseError

// After
function f() {
import x from 'x';
}

Stöd inline-mallar för nya anrop (#2222)

Vi lägger ständigt till funktioner för funktionsanrop och måste bakåtportera dem till nya anrop eftersom de har olika AST-nodtyper men i praktiken ska behandlas likadant. Den här fixen refaktorerade båda så de delar samma anropslogik, vilket bör förhindra liknande problem framöver.

// Before
new Error(
formatErrorMessage`
This is a really bad error.
Which has more than one line.
`
);

// After
new Error(formatErrorMessage`
This is a really bad error.
Which has more than one line.
`);

Indentera inte + i objektvärden (#2227)

När vi bytte till samma heuristik för tilldelning (a = b) för objekt ({a: b}), glömde vi fixa indenteringen. Nu är det löst.

// Before
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}

// After
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}

Hantera villkor inuti ett ternärt uttryck (#2228)

Prettier hade redan ett specialfall när uttrycket var ett villkor men det tillämpades inte när villkoret var vänsterdelen av ett ternärt uttryck. Nu gör det det.

// Before
room = room.map((row, rowIndex) =>
row.map(
(col, colIndex) =>
rowIndex === 0 ||
colIndex === 0 ||
rowIndex === height ||
colIndex === width
? 1
: 0
)
);

// After
room = room.map((row, rowIndex) =>
row.map(
(col, colIndex) =>
rowIndex === 0 ||
colIndex === 0 ||
rowIndex === height ||
colIndex === width
? 1
: 0
)
);

Lägg till cachning för utskrift (#2259)

Med version 1.0 fixade vi en bugg i utskriften som orsakade exponentiell komplexitet. Vi mildrade de värsta fallen så att rimlig kod inte tog för lång tid, men det var inte helt löst. Genom att lägga till ett cache-lager på rätt ställe borde problemet nu vara löst.

Detta borde göra att utskriften av Prettiers IR när man använder Prettier i debug-läge inte längre tar timeout.

// Before
...times out...

// After
someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
anotherFunction();
});
});
});
});
});
});
});
});
});

Fixa plats för varians (#2261)

Vi refaktorerade koden för utskrift av modifikatorer när vi introducerade TypeScript-stöd och flyttade av misstag variansdelen (+) före static, vilket är ogiltigt i Flow. Detta är nu fixat.

// Before
class Route {
+static param: T;
}

// After
class Route {
static +param: T;
}

Diverse

Olika fixar för range- och cursorspårning (#2266, #2248, #2250, #2136) av @CiGit och @josephfrazier

Båda dessa funktioner introducerades i senaste release och vi upptäckte flera problem när de användes i produktion. Flera av dessa är nu fixade - om ni ser fler, rapportera dem!