Prettier 1.5: GraphQL, CSS-in-JS y JSON
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Esta versión añade soporte para formateo de GraphQL, CSS-in-JS (incluyendo styled-components) y JSON a Prettier.
¡Esta es la versión que he estado esperando durante mucho tiempo: una con cambios mínimos en JavaScript!
Durante los últimos 6 meses, seguimos realizando cambios en varios aspectos del formateo de JavaScript, con la esperanza de alcanzar un punto estable algún día. Ninguna herramienta automatizada producirá código perfecto para todos los casos extremos posibles. El objetivo es encontrar un punto óptimo donde, cuando los usuarios reporten código formateado de manera peculiar, no podamos mejorarlo sin empeorar otras partes del código, introducir comportamientos difíciles de entender para humanos o añadir complejidad desproporcionada a la base de código.
¡Aún no hemos llegado al 100%, pero estamos más cerca que nunca!
Ahora que las necesidades de soporte para JavaScript están disminuyendo, es una oportunidad para dar soporte a otros lenguajes que los desarrolladores front-end utilizan y desean formatear. Introdujimos TypeScript y CSS en la versión anterior y estamos aplicando un conjunto de correcciones para ellos en esta versión. ¡También añadimos soporte para nuevos lenguajes: consultas GraphQL, CSS-in-JS embebido y JSON ahora están disponibles en prettier!
Artículo del blog: Adding a new layout strategy to Prettier por @karl
Prettier no es solo una herramienta útil sino también una pieza tecnológica fascinante. @karl dedicó mucho tiempo a mejorar el soporte de JSX e implementó un nuevo primitivo en prettier: fill. Escribió un artículo muy interesante Adding a new layout strategy to Prettier que recomiendo encarecidamente si te interesa cómo funciona prettier internamente.
GraphQL
¡Gracias a @stubailo, @jnwng, @tgriesser y @azz, prettier ahora soporta formateo de consultas GraphQL!
Funciona para archivos .graphql y dentro de plantillas JavaScript que comienzan con graphql, graphql.experimental y gql para funcionar con Relay y Apollo.
ReactDOM.render(
<QueryRenderer
query={graphql`
query appQuery {
viewer {
...TodoApp_viewer
}
}
`}
// ...
/>,
mountNode
);
Nota: solo soporta la sintaxis open source de GraphQL, por lo que no funciona con Relay Classic, solo con Relay Modern.
CSS-in-JS
Si usas styled-components o styled-jsx, prettier ahora reformateará el CSS dentro de tus expresiones de plantilla. ¡Gracias a @pomber por este excelente trabajo!
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
Esto fue bastante sencillo de implementar pero igualmente muy útil. ¡Gracias a @josephfrazier por hacerlo! :)
{
"name": "prettier",
"version": "1.5.0",
"description": "Prettier is an opinionated JavaScript formatter",
"bin": {
"prettier": "./bin/prettier.js"
}
}
CSS
Estoy muy entusiasmado porque dedicamos solo unos días al soporte inicial de CSS y ha funcionado sorprendentemente bien. Esta versión incluye varias mejoras importantes para CSS sin requerir cambios significativos.
CSS: Cada selector ahora se imprime en su propia línea (#2047) por @yuchi
La mayor incógnita al formatear CSS era cómo manejar múltiples selectores. Inicialmente adoptamos la regla de 80 columnas, dividiendo solo cuando se superaba ese límite. Muchos usuarios reportaron usar otra estrategia: saltar línea siempre después de una ,. Resulta que este enfoque es común en codebases populares y mejora la legibilidad al visualizar la estructura de selectores alineados verticalmente.
// Before
.clusterPlannerDialog input[type="text"], .clusterPlannerDialog .uiTypeahead {
color: #333;
}
// After
.clusterPlannerDialog input[type="text"],
.clusterPlannerDialog .uiTypeahead {
color: #333;
}
CSS: Colores hexadecimales en minúsculas (#2203) por @azz
El formato de código tiene límites difusos. El núcleo son los espacios en blanco, pero aspectos como comillas simples/dobles y punto y coma suelen incluírse. En Prettier para JavaScript, también reformateamos ligeramente las cadenas eliminando los \ adicionales y normalizamos los números. Para CSS, decidimos aplicar una lógica similar: convertimos letras hexadecimales a minúsculas, pero conversiones entre formatos rgb/hex o hex6/hex3 quedan fuera de alcance.
// Before
.foo {
color: #AAA;
-o-color: #fabcd3;
-ms-color: #AABBCC;
}
// After
.foo {
color: #aa;
-o-color: #fabcd3;
-ms-color: #aabbcc;
}
CSS: Usar fill para valores CSS (#2224)
El nuevo primitivo fill demostró ser muy útil en CSS. Para valores largos, en lugar de romper y poner un \n antes de cada elemento, solo añadimos un \n cuando se supera el límite, produciendo código más limpio.
// Before
foo {
border-left:
1px
solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
// After
foo {
border-left: 1px solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
CSS: Permitir ruptura en reglas @media largas (#2219)
Esta mejora es otro paso en la maduración del soporte CSS. Ahora manejamos correctamente la ruptura de líneas en reglas @media extensas.
// 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: Imprimir @else en la misma línea que } (#2088) por @azz
¡Less y SCSS evolucionan hacia lenguajes de programación reales! Poco a poco, formateamos sus estructuras como en JavaScript. Esta vez, mejoramos la ubicación de 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: Implementar prettier-ignore (#2089) por @azz
Aunque buscamos formateo completo, a veces necesitamos excepciones controladas. El comentario prettier-ignore es ese mecanismo, que ahora funciona correctamente en CSS.
// Before
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
// After
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
CSS: Corregir ruptura en composes de css-modules con líneas largas (#2190) por @tdeekens
Muchos empaquetadores detectan dependencias mediante regex simples, no análisis completo. Por eso evitamos romper require() largos, situación que también afecta a CSS Modules: saltos de línea en composes rompen la funcionalidad. Ahora mantenemos estas líneas intactas aunque superen 80 columnas.
// 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: Intentar primero SCSS en @import con comas (#2225)
Usamos un único "parser" para CSS/SCSS/Less con postcss-less y postcss-scss. Un regex determina qué parser probar primero, retrocediendo si hay error. Pero ciertas construcciones no generaban error en el parser incorrecto, omitiendo elementos. Mejoramos el regex para una detección más precisa.
Afortunadamente, este enfoque práctico funciona bien en la práctica. Si encontramos más casos límite, probablemente querremos hacer lo correcto(tm) y dividirlos en dos analizadores.
// Before
@import "text-shadow";
// After
@import "rounded-corners", "text-shadow";
TypeScript
El soporte para TypeScript ahora es sólido, todos los cambios en esta versión son para casos límite menores.
TypeScript: imprimir parámetros de tipo de funciones flecha en la misma línea que los parámetros (#2101) por @azz
El algoritmo central de Prettier es expandir un grupo si todos los elementos no caben. Funciona muy bien en la práctica para la mayoría de JavaScript, pero hay un caso que no maneja muy bien: cuando hay dos grupos adyacentes, en este caso: <Generics>(Arguments). Debemos crear grupos cuidadosamente para que los argumentos se expandan primero, ya que esto es lo que la gente generalmente espera.
// 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: mantener paréntesis con aserciones de no nulo en yield/await (#2149) por @azz
Para bien o para mal, decidimos manejar manualmente la adición de paréntesis. Así que cuando se introduce un nuevo operador, debemos asegurarnos de agregar los paréntesis correctos cuando se anida con cualquier otra combinación de operadores. En este caso, omitimos await dentro de ! en TypeScript.
// Before
const bar = await foo(false)!;
// After
const bar = (await foo(false))!;
TypeScript: Imprimir en import si está en el código fuente (#2150) por @azz
Usamos el proyecto typescript-eslint-parser que traduce el AST de TypeScript al AST estree para que Prettier lo imprima. De vez en cuando encontraremos casos límite que aún no maneja. En este caso, no proporcionaba una forma de indicar que hay un {} vacío, lo que aparentemente es importante para TypeScript. Afortunadamente, el equipo responde rápidamente y lo solucionó después de que implementamos un parche en Prettier.
// Before
import from "@types/googlemaps";
// After
import {} from "@types/googlemaps";
TypeScript: Romper siempre interfaces en múltiples líneas (#2161) por @azz
El código que implementa interface se comparte con el que imprime objects, que contiene una regla para mantenerlos expandidos si hay un \n dentro. Pero este no es el comportamiento deseado para interfaces. Siempre queremos expandirlas, como hacemos con las clases, incluso si caben en 80 columnas.
// Before
interface FooBar { [id: string]: number; }
// After
interface FooBar {
[id: string]: number;
}
TypeScript: Corregir punto y coma extra en declaraciones ambientales de TypeScript (#2167) por @azz
no-semi y semi se solicitan con frecuencia, pero en el equipo de Prettier estamos un paso adelante y ¡implementamos two-semi para ustedes! Bromeamos, era un error y ya está solucionado ;)
// 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: agrupar parámetros de función en firmas de llamada/construcción (#2169) por @azz
Agregar un comentario antes de un método solía considerar la longitud del comentario y a menudo expandía el método inesperadamente. Afortunadamente, la solución fue simple: solo envolver la salida en un 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: Actualizar tsep (#2183) por @azz
Este error era muy molesto si lo encontrabas: ¡cada vez que formateabas el código, agregaba un _ adicional a la clave del objeto!
// Before
obj = {
__: 42
___: 42
};
// After
obj = {
_: 42
__: 42
};
TypeScript: romper en múltiples extensiones de interfaz (#2085) por @azz
A diferencia de JavaScript, TypeScript permite extender varias clases simultáneamente. Resulta que los desarrolladores utilizan esta función, y Prettier ahora maneja mejor su formato.
// Before
export interface ThirdVeryLongAndBoringInterfaceName extends AVeryLongAndBoringInterfaceName, AnotherVeryLongAndBoringInterfaceName, AThirdVeryLongAndBoringInterfaceName {}
// After
export interface ThirdVeryLongAndBoringInterfaceName
extends AVeryLongAndBoringInterfaceName,
AnotherVeryLongAndBoringInterfaceName,
AThirdVeryLongAndBoringInterfaceName {}
TypeScript: manejar ObjectPattern en lugar de ObjectExpression dentro de BinaryExpression (#2238) por @azz
Este caso no es muy interesante, se trata de un caso límite que no se manejaba correctamente en la conversión de TypeScript a estree.
// Before
call(c => { bla: 1 }) || [];
// After
call(c => ({ bla: 1 })) || [];
Conservar líneas después de directivas (#2070)
Al admitir TypeScript, Prettier se usa ahora en muchos códigos base de Angular, lo que expone casos límite que no se manejaban adecuadamente. En este caso, no conservábamos líneas vacías después de directivas dentro de funciones.
// 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
Esta versión tiene muy pocos cambios en JavaScript, lo cual es excelente. Estamos empezando a ver la luz al final del túnel y avanzando hacia un formateador robusto. Nunca alcanzaremos un formateador automático 100% perfecto. El objetivo es que para cada problema reportado, no existan formas claras de mejorar su formato sin degradar otras partes.
Permitir recombinar líneas de JSX (#1831) por @karl
El objetivo de Prettier es tener un formato consistente: dado un AST, siempre imprimimos igual. En dos áreas tuvimos que comprometernos y leer el formato original: JSX y objetos. Con este cambio, ya no dependemos de la entrada original para JSX con contenido textual. Esto nos permite redistribuir texto dentro de 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>
);
}
Romper en expresiones de miembro calculadas no literales (#2087) por @azz
Formatear cadenas de miembros es la parte más compleja de Prettier, y seguimos encontrando ajustes para mejorar la experiencia.
// 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",
});
Indentar la primera variable en escenarios de una variable (#2095) por @azz
Hasta hace poco no priorizábamos el formato de múltiples variables en una sola declaración, pues lo común es declarar variables individualmente. Para declaraciones únicas no queremos indentar, pero descubrimos que sí debe hacerse cuando hay múltiples declaraciones posteriores, pues de otro modo se ve extraño.
// 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>';
Permitir ruptura con importación nombrada y por defecto (#2096) por @azz
Esto fue una lamentable regresión de la versión 1.4 donde alineábamos importaciones con un solo elemento. Resultó que nuestra definición de "elemento único" permitía un tipo y un elemento simultáneamente. ¡Ahora está corregido!
// Before
import transformRouterContext, { type TransformedContextRouter } from '../../helpers/transformRouterContext';
// After
import transformRouterContext, {
type TransformedContextRouter
} from '../../helpers/transformRouterContext';
Activar allowImportExportEverywhere (#2207) por @zimme
El objetivo de Prettier es formatear código real, así que habilitamos modos flexibles/experimentales en todos los parsers. Babylon permite imports dentro de funciones (fuera del estándar), pero no nos cuesta nada admitirlo.
// Before
ParseError
// After
function f() {
import x from 'x';
}
Admitir plantillas en línea para llamadas con new (#2222)
Seguimos añadiendo funciones para llamadas y debemos adaptarlas a llamadas con new, que tienen diferente nodo AST pero requieren igual tratamiento. Esta corrección unificó ambos casos bajo la misma lógica, lo que debería evitar futuras inconsistencias.
// 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.
`);
No indentar el signo + en valores de objetos (#2227)
Cuando cambiamos para usar la misma heurística para asignaciones (a = b) y para objetos ({a: b}), olvidamos corregir la indentación. Ahora está arreglado.
// Before
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
// After
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
Manejar condiciones dentro de un operador ternario (#2228)
Prettier ya tenía un caso especial cuando la expresión era un condicional, pero no se aplicaba cuando el condicional era la parte izquierda de un ternario. Ahora sí se aplica.
// 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
)
);
Agregar caché para la impresión (#2259)
Con el lanzamiento de la versión 1.0, corregimos un error en la impresión que introducía un comportamiento exponencial. Pudimos mitigar el mayor problema de modo que el código razonable no agotara el tiempo de espera, pero no estaba completamente arreglado. Al agregar una capa de caché en el lugar correcto, ahora deberíamos estar libres de este problema.
Esto debería hacer que la impresión del IR de prettier usando prettier en modo depuración ya no agote el tiempo de espera.
// 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();
});
});
});
});
});
});
});
});
});
Corregir la ubicación de la varianza (#2261)
Refactorizamos el código que imprime los modificadores cuando introdujimos el soporte para TypeScript y accidentalmente movimos la parte de varianza (+) antes de static, lo cual no es válido en Flow. Esto ahora está corregido.
// Before
class Route {
+static param: T;
}
// After
class Route {
static +param: T;
}
Varios
Diversas correcciones para seguimiento de rangos y cursores (#2266, #2248, #2250, #2136) por @CiGit y @josephfrazier
Ambas características se introdujeron en el último lanzamiento y descubrimos varios problemas al usarlas en producción. Muchos de ellos fueron corregidos; si ves más, ¡por favor repórtalos!
