prettier-plugin-blade
Version:
Prettier plugin for Laravel Blade templates
394 lines (307 loc) • 12 kB
Markdown
# Chisel
An opinionated Prettier plugin for Laravel Blade templates.
## Installation
Requires Node.js 18 or newer.
Install Prettier and the plugin in your project:
```bash
npm i -D prettier prettier-plugin-blade@^3
```
Optional integrations:
```bash
npm i -D /plugin-php prettier-plugin-tailwindcss
```
## Migration
Chisel v3 (`prettier-plugin-blade`) is a ground-up rewrite. Expect output changes compared to previous versions.
If you rely on previous versions, you should specify the exact version you'd like to use in your `package.json`.
## Quick Start
Create or update your `.prettierrc`:
```json
{
"plugins": [
"prettier-plugin-blade"
],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade"
}
}
]
}
```
Format:
```bash
npx prettier --write "resources/views/**/*.blade.php"
```
### CLI Flags
You can pass Blade plugin options directly to the Prettier CLI as kebab-case flags:
```bash
npx prettier "resources/views/**/*.blade.php" \
--write \
--plugin prettier-plugin-blade \
--plugin /plugin-php \
--parser blade \
--blade-php-formatting safe \
--blade-php-formatting-targets echo \
--blade-php-formatting-targets directiveArgs \
--blade-component-prefixes x \
--blade-component-prefixes flux \
--blade-directive-arg-spacing space \
--blade-directive-arg-spacing-overrides if \
--blade-directive-arg-spacing-overrides section=0 \
--blade-echo-spacing tight
```
Notes:
- Use kebab-case in CLI flags (for example `bladePhpFormatting` -> `--blade-php-formatting`).
- Load `/plugin-php` when using `bladePhpFormatting: "safe"` from the CLI.
- Array options use repeated flags in CLI (for example: `--blade-inline-intent-elements p --blade-inline-intent-elements svg --blade-inline-intent-elements svg:*`).
- `bladeDirectiveCaseMap` takes a JSON object string when passed via CLI, but shell quoting is easy to get wrong. Prefer setting it in `.prettierrc`.
## Blade Plugins
Use `bladeSyntaxPlugins` to enable framework-specific or package-specific Blade behavior.
Available plugins:
- `statamic`
- `log1x/sage-directives`: for improved compatibility with [https://github.com/Log1x/sage-directives](https://github.com/Log1x/sage-directives)
Example:
```json
{
"plugins": [
"prettier-plugin-blade",
"@prettier/plugin-php"
],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade",
"bladePhpFormatting": "safe",
"bladeSyntaxPlugins": [
"statamic",
"log1x/sage-directives"
]
}
}
]
}
```
## PHP Formatting
To format embedded PHP fragments inside Blade, install `/plugin-php` and include it in `plugins`:
```json
{
"plugins": [
"prettier-plugin-blade",
"@prettier/plugin-php"
],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade",
"bladePhpFormatting": "safe"
}
}
]
}
```
Notes:
- Without `/plugin-php`, the formatter still works. It falls back gracefully and leaves PHP fragments unchanged.
- `bladePhpFormatting` modes documented here: `"off"`, `"safe"`.
## Enable Tailwind CSS Class Sorting
Install the Tailwind CSS plugin and include it in `plugins`:
```json
{
"plugins": [
"prettier-plugin-blade",
"prettier-plugin-tailwindcss"
],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade"
}
}
]
}
```
## VS Code Setup
1. Install the `Prettier - Code formatter` extension (`esbenp.prettier-vscode`).
2. Ensure your project has local `devDependencies` for Prettier and this plugin.
3. Add workspace settings in `.vscode/settings.json`:
```json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.requireConfig": true,
"files.associations": {
"*.blade.php": "blade"
},
"[blade]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
```
## Options
### Blade Plugin Options
| Option | Type | Default | Values |
| --- | --- | --- | --- |
| `bladePhpFormatting` | `choice` | `"safe"` | `"off"`, `"safe"`, `"aggressive"` |
| `bladePhpFormattingTargets` | `string[]` | `["directiveArgs", "echo", "phpBlock", "phpTag"]` | `echo`, `directiveArgs`, `phpBlock`, `phpTag`; use `[]` (or CLI `none`) to disable all |
| `bladeSyntaxPlugins` | `string[]` | `["statamic"]` | plugin names (for example `["statamic"]`) |
| `bladeDirectiveCase` | `choice` | `"preserve"` | `"preserve"`, `"canonical"`, `"lower"` |
| `bladeDirectiveCaseMap` | `string` | `""` | JSON object string, e.g. `{"disk":"Disk"}` |
| `bladeDirectiveArgSpacing` | `choice` | `"space"` | `"preserve"`, `"none"`, `"space"` |
| `bladeDirectiveArgSpacingOverrides` | `string[]` | `["if", "elseif", "unless", "while", "for", "foreach", "forelse", "switch", "case"]` | tokens like `if`, `section=none`, `php=2`, `custom=preserve` |
| `bladeDirectiveBlockStyle` | `choice` | `"preserve"` | `"preserve"`, `"inline-if-short"`, `"multiline"` |
| `bladeBlankLinesAroundDirectives` | `choice` | `"preserve"` | `"preserve"`, `"always"` |
| `bladeEchoSpacing` | `choice` | `"preserve"` | `"preserve"`, `"space"`, `"tight"` |
| `bladeSlotClosingTag` | `choice` | `"canonical"` | `"canonical"`, `"preserve"` |
| `bladeVoidElementSlash` | `choice` | `"always"` | `"always"`, `"never"`, `"preserve"` |
| `bladeInlineIntentElements` | `string[]` | `["p", "svg", "svg:*"]` | elements and namespace wildcards |
| `bladeComponentPrefixes` | `string[]` | `["x", "s", "statamic", "flux", "livewire", "native"]` | component prefixes |
| `bladeInsertOptionalClosingTags` | `boolean` | `false` | `true`, `false` |
| `bladeKeepHeadAndBodyAtRoot` | `boolean` | `true` | `true`, `false` |
`bladePhpFormattingTargets` aliases supported in each array entry:
- `echoes` -> `echo`
- `directive-args`, `directive_args` -> `directiveArgs`
- `php-block`, `php_block` -> `phpBlock`
- `php-tag`, `php_tag` -> `phpTag`
`bladeComponentPrefixes` behavior:
- Bare prefix tokens expand to both Blade component separator forms:
- `x` -> `x-`, `x:`
- `widget` -> `widget-`, `widget:`
- Explicit separator tokens are preserved as-is:
- `x-` matches only dash form tags
- `x:` matches only colon form tags
`bladeVoidElementSlash` behavior:
- Applies only to standard HTML void elements such as `meta`, `link`, `input`, and `br`.
- `"always"` prints `<meta />`, matching the current default behavior.
- `"never"` prints `<meta>`.
- `"preserve"` keeps the source style for each void element.
- Non-void self-closing tags and Blade components keep `/>`.
`bladeInlineIntentElements` SVG behavior:
- `svg` keeps single-line `<svg>...</svg>` container intent when the source is inline.
- `svg:*` keeps inline attribute and style intent for SVG namespace elements such as `<path>` and `<line>`.
- Remove one or both entries to opt out of those SVG-specific inline layouts.
### Directive Arg Spacing
Directive argument spacing has one base rule plus optional per-directive overrides.
- `bladeDirectiveArgSpacing` is the base rule.
- `bladeDirectiveArgSpacingOverrides` is a `string[]` of `directive[=rule]` options.
- Directive names, without a specified rule, default to `space`.
- Directive names are case-insensitive and may be written with or without `@`.
- Rules may be `"preserve"`, `"none"`, `"space"`, or a non-negative integer.
- Providing `bladeDirectiveArgSpacingOverrides` replaces the built-in defaults (`if`, `elseif`, `unless`, `while`, `for`, `foreach`, `forelse`, `switch`, `case`).
Example:
```json
{
"bladeDirectiveArgSpacing": "none",
"bladeDirectiveArgSpacingOverrides": ["if", "section=1", "php=2"]
}
```
With that configuration:
- `($x)` becomes ` ($x)`
- ` ($view)` becomes `($view)`
- `($name)` becomes ` ($name)`
- `($expr)` becomes ` ($expr)`
### `bladeBlankLinesAroundDirectives`
This option controls blank lines between directive branches inside a structured directive block.
Examples of branch separators this option affects:
- ` ... ... `
- ` ... ... ... `
- ` ... `
Primary scope:
- It decides how much vertical space to print between one branch and the next branch marker.
- In `preserve` mode it can also keep some existing blank lines between structured siblings inside a directive body when those blank lines were already present in source.
That means it can affect spacing before ``, ``, ``, ``, ``, ``, and similar closers/openers inside the same directive block.
Supported values:
- `"preserve"`
- Keeps an existing blank line between directive branches if one existed in the source.
- Otherwise prints a single newline between branches.
- `"always"`
- Always inserts a blank line between directive branches.
- In practice this means two line breaks between branches.
Example input:
```blade
($x)
<p>a</p>
<p>b</p>
```
With `bladeBlankLinesAroundDirectives: "preserve"`:
```blade
($x)
<p>a</p>
<p>b</p>
```
With `bladeBlankLinesAroundDirectives: "always"`:
```blade
($x)
<p>a</p>
<p>b</p>
```
Notes:
- This option is most visible when directive blocks print in multiline form.
- If a block is kept inline, there are no multiline branch separators for this option to manage.
- In `preserve` mode, some authored blank lines inside a directive body can also survive between structured siblings when the source already had them.
### Relevant Core Prettier Options
This plugin also respects standard Prettier options, including:
- `printWidth`
- `tabWidth`
- `useTabs`
- `singleQuote`
- `singleAttributePerLine`
- `bracketSameLine`
- `endOfLine`
- `htmlWhitespaceSensitivity`
## Example Full Config
```json
{
"plugins": [
"prettier-plugin-blade",
"@prettier/plugin-php",
"prettier-plugin-tailwindcss"
],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade",
"htmlWhitespaceSensitivity": "css",
"bladePhpFormatting": "safe",
"bladePhpFormattingTargets": ["directiveArgs", "echo", "phpBlock", "phpTag"],
"bladeSyntaxPlugins": ["statamic"],
"bladeDirectiveCase": "preserve",
"bladeDirectiveArgSpacing": "space",
"bladeDirectiveArgSpacingOverrides": ["if", "elseif", "unless", "while", "for", "foreach", "forelse", "switch", "case"],
"bladeDirectiveBlockStyle": "preserve",
"bladeBlankLinesAroundDirectives": "preserve",
"bladeEchoSpacing": "space",
"bladeSlotClosingTag": "canonical",
"bladeVoidElementSlash": "always",
"bladeInlineIntentElements": ["p", "svg", "svg:*"],
"bladeComponentPrefixes": ["x", "s", "statamic", "flux", "livewire", "native"],
"bladeInsertOptionalClosingTags": false,
"bladeKeepHeadAndBodyAtRoot": true
}
}
]
}
```
## Troubleshooting
### Tailwind CSS classes are not sorting
- Confirm `prettier-plugin-tailwindcss` is installed.
- Confirm it is listed in `plugins`.
- Confirm class values are static strings (not mixed with Blade interpolation).
### PHP formatting is not running
- Install `/plugin-php`.
- Set `bladePhpFormatting` to `"safe"`.
- Ensure target is enabled in `bladePhpFormattingTargets`.
### VS Code formats with wrong Prettier
- Use local project dependencies.
- Set `"prettier.requireConfig": true`.
- Set `"prettier.prettierPath"` when needed.