Saltar al contenido principal

Fundamentos

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Prettier es un formateador de código con convenciones definidas. Este documento explica algunas de sus decisiones de diseño.

Aspectos prioritarios para Prettier

Corrección

El requisito principal de Prettier es generar código válido que mantenga exactamente el mismo comportamiento que antes del formateo. Por favor, reporta cualquier caso donde Prettier incumpla estas normas de corrección: ¡eso es un error que debe corregirse!

Cadenas de texto

¿Comillas dobles o simples? Prettier elige el tipo que requiera menos caracteres de escape. Ejemplo: "It's gettin' better!" en lugar de 'It\'s gettin\' better!'. En empate o ausencia de comillas, Prettier usa dobles por defecto (configurable con la opción singleQuote).

JSX tiene su propia opción: jsxSingleQuote. JSX hereda su sintaxis de HTML, donde predominan las comillas dobles para atributos. Las herramientas de desarrollo de navegadores mantienen esta convención mostrando HTML con comillas dobles incluso si el código fuente usa simples. Esta opción separada permite comillas simples en JS y dobles en "HTML" (JSX).

Prettier preserva el método de escape en tus cadenas. Por ejemplo, "🙂" no se convertirá en "\uD83D\uDE42" ni viceversa.

Líneas vacías

Las líneas vacías son complejas de generar automáticamente. Prettier conserva las líneas vacías presentes en el código original, con dos reglas adicionales:

  • Prettier reduce múltiples líneas vacías consecutivas a una sola.

  • Elimina líneas vacías al inicio/final de bloques (y archivos completos). (Los archivos siempre terminan con un salto de línea).

Objetos multilínea

Por defecto, Prettier imprime expresiones en una línea si caben. Los objetos en JavaScript tienen múltiples usos y a veces conviene mantenerlos en múltiples líneas para mejor legibilidad (ver ejemplos en object lists, nested configs, stylesheets y keyed methods). Al no encontrar una regla universal, Prettier mantiene objetos en múltiples líneas si existe un salto de línea entre { y la primera clave en el código original. Así, los objetos largos de una línea se expanden automáticamente, pero los objetos cortos multilínea nunca se colapsan.

Puedes desactivar este comportamiento condicional con la opción objectWrap.

Consejo: Para convertir un objeto multilínea en una sola línea:

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

…basta eliminar el salto de línea después de {:

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

…y ejecutar Prettier:

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

Para volver a formato multilínea, añade un salto de línea después de {:

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

…y ejecuta Prettier:

const user = {
name: "John Doe",
age: 30,
};
Una nota sobre la reversibilidad del formato

El formato semi-manual para literales de objetos es en realidad una solución temporal, no una función. Se implementó solo porque en ese momento no se encontró una buena heurística y se necesitaba una solución urgente. Sin embargo, como estrategia general, Prettier evita el formato no reversible como este, por lo que el equipo sigue buscando heurísticas que permitan eliminar este comportamiento por completo o al menos reducir el número de situaciones en las que se aplica.

¿Qué significa reversible? Una vez que un literal de objeto se convierte en multilínea, Prettier no lo colapsará nuevamente. Si en el código formateado con Prettier añadimos una propiedad a un literal de objeto, ejecutamos Prettier, luego cambiamos de opinión, eliminamos la propiedad añadida y volvemos a ejecutar Prettier, podríamos terminar con un formato no idéntico al inicial. Este cambio inútil incluso podría incluirse en un commit, que es exactamente el tipo de situación que Prettier fue creado para prevenir.

Decoradores

Al igual que con los objetos, los decoradores se utilizan para muchas cosas diferentes. A veces tiene sentido escribir decoradores encima de la línea que decoran, a veces es mejor si están en la misma línea. No hemos podido encontrar una buena regla para esto, por lo que Prettier mantiene tus decoradores posicionados como los escribiste (si caben en la línea). Esto no es ideal, pero es una solución pragmática a un problema difícil.

@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;
}

Hay una excepción: las clases. No creemos que tenga sentido alinear los decoradores para ellas, por lo que siempre se mueven a su propia línea.

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

Nota: Prettier 1.14.x y versiones anteriores intentaban mover automáticamente tus decoradores, por lo que si has ejecutado una versión antigua de Prettier en tu código, es posible que necesites unir manualmente algunos decoradores aquí y allá para evitar inconsistencias:

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

Literales de plantilla

Literales de plantilla

Los literales de plantilla pueden contener interpolaciones. Decidir si es apropiado insertar un salto de línea dentro de una interpolación desafortunadamente depende del contenido semántico de la plantilla; por ejemplo, introducir un salto de línea en medio de una oración en lenguaje natural generalmente no es deseable. Como Prettier no tiene suficiente información para tomar esta decisión por sí mismo, utiliza una heurística similar a la usada para objetos: solo dividirá una expresión de interpolación en múltiples líneas si ya había un salto de línea dentro de esa interpolación.

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

Si quieres que Prettier divida una interpolación, debes asegurarte de que haya un salto de línea en algún lugar dentro del ${...}. De lo contrario, mantendrá todo en una sola línea, sin importar cuán largo sea.

El equipo preferiría no depender del formato original de esta manera, pero es la mejor heurística que tenemos en este momento.

Punto y coma

Esto se refiere al uso de la opción noSemi.

Considere este fragmento de código:

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

Aunque el código anterior funciona perfectamente sin punto y coma, Prettier de hecho lo convierte en:

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

Si bien el código anterior funciona bien sin punto y coma, Prettier en realidad lo convierte en:

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

¡Ups! Lo anterior en realidad significa:

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

Con un punto y coma delante de ese [, estos problemas nunca ocurren. Hace que la línea sea independiente de otras líneas, por lo que puedes mover y agregar líneas sin tener que pensar en las reglas de ASI.

Esta práctica también es común en standard que utiliza un estilo sin punto y coma.

Ten en cuenta que si tu programa actualmente tiene un error relacionado con punto y coma, Prettier no corregirá automáticamente el error. Recuerda, Prettier solo reformatea el código, no cambia el comportamiento del código. Tomemos como ejemplo este código defectuoso, donde el desarrollador olvidó colocar un punto y coma antes del (:

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

Si introduces este código en Prettier, no alterará su comportamiento, sino que lo reformateará mostrando cómo se ejecutará realmente.

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

Ancho de impresión

La opción printWidth es más una guía para Prettier que una regla estricta. No es un límite máximo absoluto de longitud de línea, sino una indicación aproximada del largo deseado. Prettier generará líneas más cortas y más largas, pero en general intentará ajustarse al ancho especificado.

Existen casos extremos, como literales de cadena muy largos, expresiones regulares, comentarios y nombres de variables que no pueden dividirse en varias líneas (sin transformaciones de código que Prettier no realiza). O si anidas tu código 50 niveles, las líneas consistirán principalmente en indentaciones :)

Además, hay algunos casos donde Prettier excede intencionalmente el ancho de impresión.

Importaciones

Prettier puede dividir declaraciones import largas en varias líneas:

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

El siguiente ejemplo no cabe dentro del ancho de impresión, pero Prettier lo imprime en una sola línea:

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

Esto podría sorprender a algunos, pero se hace así debido a una solicitud común de mantener las import con un solo elemento en una línea. Lo mismo aplica para llamadas require.

Funciones de prueba

Otra solicitud frecuente fue mantener descripciones extensas de pruebas en una sola línea, incluso si se vuelven demasiado largas. En tales casos, dividir los argumentos en nuevas líneas no ayuda mucho.

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 tiene casos especiales para funciones comunes de frameworks de pruebas como describe, it y test.

JSX

Prettier maneja JSX de forma ligeramente diferente al JavaScript normal:

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>
);
}

Hay dos razones principales.

Primero, mucha gente ya envolvía su JSX entre paréntesis, especialmente en declaraciones return. Prettier sigue esta convención común.

Segundo, el formato alternativo facilita la edición de JSX. Es fácil dejar un punto y coma residual. A diferencia de JS normal, un punto y coma sobrante en JSX puede mostrarse como texto plano en tu página.

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

Comentarios

Respecto al contenido de los comentarios, Prettier puede hacer poco realmente. Los comentarios pueden contener desde prosa hasta código comentado o diagramas ASCII. Al ser tan diversos, Prettier no sabe cómo formatearlos o ajustarlos, así que se mantienen como están. La única excepción son los comentarios estilo JSDoc (bloques donde cada línea empieza con *), donde Prettier puede corregir la indentación.

Luego está el dilema de dónde ubicar los comentarios. Resulta ser un problema complejo. Prettier intenta mantenerlos aproximadamente en su posición original, pero es difícil porque los comentarios pueden colocarse casi en cualquier lugar.

Generalmente obtienes mejores resultados colocando comentarios en líneas independientes, en lugar de al final de líneas existentes. Prefiere // eslint-disable-next-line sobre // eslint-disable-line.

Nota que los "comentarios mágicos" como eslint-disable-next-line y $FlowFixMe a veces requieren movimiento manual cuando Prettier divide una expresión en múltiples líneas.

Imagina este código:

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

Luego necesitas agregar otra condición:

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

Prettier convertirá lo anterior en:

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

Lo que significa que el comentario eslint-disable-next-line ya no es efectivo. En este caso debes mover el comentario:

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

Cuando sea posible, prefiera comentarios que operen en rangos de líneas (ej. eslint-disable y eslint-enable) o a nivel de sentencia (ej. /* istanbul ignore next */), ya que son aún más seguros. Puede prohibir el uso de comentarios eslint-disable-line y eslint-disable-next-line usando eslint-plugin-eslint-comments.

Descargo de responsabilidad sobre sintaxis no estándar

Prettier suele reconocer y formatear sintaxis no estándar como propuestas en etapas tempranas de ECMAScript y extensiones de sintaxis Markdown no definidas en ninguna especificación. El soporte para dicha sintaxis se considera de mejor esfuerzo y experimental. Pueden introducirse incompatibilidades en cualquier versión y no deben considerarse cambios disruptivos.

Descargo de responsabilidad sobre archivos generados automáticamente

Algunos archivos como package.json o composer.lock son generados automáticamente y actualizados periódicamente por el gestor de paquetes. Si Prettier aplicara las mismas reglas de formato JSON que en otros archivos, generaría conflictos constantes con estas herramientas. Para evitar esta inconveniencia, Prettier usa un formateador basado en JSON.stringify para dichos archivos. Puede notar diferencias como la eliminación de espacios verticales, pero este es un comportamiento intencional.

Qué no le importa a Prettier

Prettier solo imprime código. No lo transforma. Esto limita el alcance de Prettier. ¡Enfoquémonos en la impresión y hagámosla realmente bien!

Algunos ejemplos de aspectos fuera del alcance de Prettier:

  • Convertir cadenas entre comillas simples/dobles en template literals o viceversa.

  • Usar + para dividir literales de cadena largos según el ancho de impresión.

  • Añadir/eliminar {} y return donde son opcionales.

  • Convertir ?: en sentencias if-else.

  • Ordenar/mover imports, claves de objetos, miembros de clases, claves JSX, propiedades CSS u otros elementos. Además de ser una transformación y no solo impresión (como se mencionó), el ordenamiento es potencialmente inseguro por efectos secundarios (ej. en imports) y dificulta verificar el objetivo principal de corrección.