This document outlines the configuration steps in your project.
Prettier is an opinionated code formatter that helps maintain consistent code style across your project.
To install Prettier, run the following command:
npm install prettier --save-dev
Create a Prettier configuration file named .prettierrc
in the root directory of your project.
{
"printWidth": 120,
"singleQuote": true,
"useTabs": false,
"tabWidth": 2,
"semi": true,
"bracketSpacing": true,
"quoteProps": "as-needed",
"bracketSameLine": true,
"arrowParens": "always",
"trailingComma": "es5",
"htmlWhitespaceSensitivity": "ignore"
}
Here's a breakdown of each property in the .prettierrc
file and its purpose:
-
Value:
120
.Description: Specifies the maximum line width before code is wrapped.
Rationale: This value helps maintain readability by preventing lines from becoming too long and requiring horizontal scrolling.
Example:
// β Bad const exampleFunction = (arg1, arg2, arg3, arg4, arg5, arg6) => { // Function body... };
// βοΈ Good const exampleFunction = ( arg1, arg2, arg3, arg4, arg5, arg6 ) => { // Function body... };
-
Value:
true
.Description: Determines whether to use single quotes (
true
) or double quotes (false
) for string literals.Rationale: By choosing single quotes, we adhere to a common convention in JavaScript and maintain consistency throughout the codebase.
Example:
// β Bad const greeting = "Hello, world!";
// βοΈ Good const greeting = 'Hello, world!';
-
Value:
false
.Description: Specifies whether to use tabs (
true
) or spaces (false
) for indentation.Rationale: Opting for spaces instead of tabs promotes consistent alignment across different editors and platforms.
Example:
// β Bad function exampleFunction() { // Indented code with tab }
// βοΈ Good function exampleFunction() { // Indented code with spaces }
-
Value:
2
.Description: Defines the number of spaces or tabs to be used for each indentation level.
Rationale: A value of
2
spaces strikes a balance between preserving indentation depth and minimizing horizontal space usage.Example:
// β Bad function exampleFunction() { // Indented code with tab (4 spaces) }
// βοΈ Good function exampleFunction() { // Indented code with tab (2 spaces) }
-
Value:
true
.Description: Determines whether to add semicolons at the end of statements.
Rationale: Setting it to true enforces the use of semicolons, which aids in preventing potential issues with automatic semicolon insertion.
Example:
// β Bad const exampleVariable = 42
// βοΈ Good const exampleVariable = 42;
-
Value:
true
.Description: Specifies whether to add spaces inside object literals.
Rationale: Enabling this property by setting it to
true
enhances code readability by adding spaces between brackets and their contents.Example:
// β Bad const exampleObject = {key: 'value'};
// βοΈ Good const exampleObject = { key: 'value' };
-
Value:
"as-needed"
.Description: Determines when to quote object literal property names.
Rationale: The
"as-needed"
setting quotes property names only when necessary, reducing visual clutter and improving code readability.Example:
// β Bad const exampleObject = { 'key': 123, 'another-key': 'value' };
// βοΈ Good const exampleObject = { key: 123, 'another-key': 'value' };
-
Value:
true
.Description: Put the
>
of a multi-line HTML element at the end of the last line instead of being alone on the next line (does not apply to self-closing elements).Rationale: The selected value is optional. It depends on the code style of your team.
Example:
// βοΈ Good <button class="some-class" id="some-id" (click)="handleClick($event)" > Click Here </button>
// βοΈ Also good <button class="some-class" id="some-id" (click)="handleClick($event)"> Click Here </button>
-
Value:
"always"
.Description: Controls the use of parentheses around arrow function parameters.
Rationale: At first glance, avoiding parentheses may look like a better choice because of less visual noise. However, when Prettier removes parentheses, it becomes harder to add type annotations, extra arguments or default values as well as making other changes. Consistent use of parentheses provides a better developer experience when editing real codebases, which justifies the default value for the option.
Example:
// β Bad const exampleFunction = arg => { // Function body... };
// β Bad exampleArray.forEach(item => /* Some actions */);
// βοΈ Good const exampleFunction = (arg) => { // Function body... };
// βοΈ Good exampleArray.forEach((item) => /* Some actions */);
Disadvantages: The
"arrowParens": "always"
configuration wraps the body of a callback function in parentheses, it can be seen as a potential drawback when it comes to the ESLint ruleno-return-assign
.The only two options for this rule are to allow when parenthesis are present or disallow always.
// π Before formatting exampleArray.forEach((item) => this.someField = item);
// π After formatting exampleArray.forEach((item) => (this.someField = item));
-
Value:
"all"
.Description: Specifies the use of trailing commas in multiline lists.
Rationale: Including trailing commas in multiline lists offers several benefits. Firstly, it simplifies adding or removing items from the list, as each item is followed by a comma, making the version control diffs cleaner and more manageable. Secondly, it helps prevent formatting-related merge conflicts when multiple developers work on the same codebase concurrently. Finally, it promotes a consistent style and avoids debates over whether to include or omit the trailing comma in different scenarios. By adopting the
"all"
setting, we ensure compatibility and leverage these advantages to enhance the maintainability and collaboration aspects of our codebase.Example:
// β Bad const exampleArray = [ 'item1', 'item2', 'item3' ];
// βοΈ Good const exampleArray = [ 'item1', 'item2', 'item3', ];
-
Value:
"ignore"
.Description: Defines how HTML whitespace is handled.
Rationale: With "ignore", whitespace in HTML files is disregarded, avoiding unnecessary diffs and promoting consistency.
Example:
// β Bad <span class="dolorum atque aspernatur">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci autem eligendi iste iusto necessitatibus non quaerat, quibusdam ratione saepe tenetur!</span>
// β Bad - When we use the "css" or "strict" values of this property <span class="dolorum atque aspernatur" >Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci autem eligendi iste iusto necessitatibus non quaerat, quibusdam ratione saepe tenetur!</span >
// β Bad <span *ngIf="condition" class="dolorum atque aspernatur" [class.some-class]="condition" (click)="someClickAction()"> Lorem ipsum dolor sit amet </span>
// βοΈ Good <span class="dolorum atque aspernatur"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci autem eligendi iste iusto necessitatibus non quaerat, quibusdam ratione saepe tenetur! </span>
// βοΈ Good <span *ngIf="condition" class="dolorum atque aspernatur" [class.some-class]="condition" (click)="someClickAction()"> Lorem ipsum dolor sit amet </span>
By customizing these properties in the .prettierrc
file, you establish a consistent code formatting style across
your project, enhancing readability and maintainability. Feel free to modify these settings based on your specific
project requirements and personal preferences.
Despite its many advantages, Prettier, the code formatting tool, is not without its limitations. While Prettier excels at enforcing a consistent and opinionated code style, there are certain scenarios where its behavior may not align with specific project requirements or coding conventions. In such cases, Prettier's rigid formatting rules can present challenges or conflicts that developers need to be aware of. Let's explore some examples where Prettier may encounter difficulties or produce unexpected results.
// π Before formatting
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({...});
})
);
// π After formatting
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({...});
}));
Β
// π Before formatting
facadeService.someMethodWithLongName = jasmine.createSpy('someMethodWithLongName')
.and.callThrough();
// π After formatting
facadeService.someMethodWithLongName = jasmine
.createSpy('someMethodWithLongName')
.and.callThrough();
Β
// π Before formatting
const result = exampleArray?.some((item) => !!Object.keys(this.facadeService.someMethod(item.id) || {}).length);
// π€ Expectation
const result = exampleArray?.some((item) => {
return !!Object.keys(this.facadeService.someMethod(item.id) || {}).length;
});
// π After formatting
const result = exampleArray?.some(
(item) =>
!!Object.keys(this.facadeService.someMethod(item.id) || {}).length
);
Β
// π Before formatting
<app-some-component
class="col-{{ index + 1 }} row-{{ index + 1 }} {{ backgroundColor }} border border-transparent"
></app-some-component>
// π After formatting
<app-some-component
class="col-{{ index + 1 }} row-{{
index + 1
}} {{ backgroundColor }} border border-transparent"
></app-some-component>