Skip to content

[1.x] Adds blade support#256

Closed
nunomaduro wants to merge 66 commits into
mainfrom
feat/blade
Closed

[1.x] Adds blade support#256
nunomaduro wants to merge 66 commits into
mainfrom
feat/blade

Conversation

@nunomaduro

@nunomaduro nunomaduro commented Mar 15, 2024

Copy link
Copy Markdown
Member

Note: This pull request is a work in progress, and you can help by trying it in your local project:

composer require laravel/pint:dev-feat/blade
./vendor/bin/pint --blade

Check the differences in your Blade templates and report any issues!


This pull request is a proof of concept that adds Laravel Blade support to Pint:

pint-banner-1

Here is the basic format applied:

<!-- Before -->
@foreach ($users as $user)
<p      class="p-4" >This is user {{ $user->id }}</p>
@endforeach

<!-- after: -->
@foreach ($users as $user)
    <p class="p-4">This is user {{ $user->id }}</p>
@endforeach

Tailwind classes are "sorted" using Tailwind's recommended class order:

<!-- Before -->
<button class="text-white px-4 sm:px-8 py-2 sm:py-3 bg-sky-700 hover:bg-sky-800">...</button>

<!-- After -->
<button class="bg-sky-700 px-4 py-2 text-white hover:bg-sky-800 sm:px-8 sm:py-3">...</button>

Technically, the way it works is simple - for the user, it is a completely fast experience, while behind the scenes, we are using Prettier and Prettier Plugin Blade to actually style those Blade templates. The dependencies are installed in Laravel Pint's own vendor directory on the first run, so there are no potential conflicts with existing user dependencies.

Of course, it requires Node to be installed on the user's machine.

@nunomaduro nunomaduro marked this pull request as draft March 15, 2024 18:20

@Jubeki Jubeki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about naming the rule Laravel/blade instead? (Doesn't really matter, but I think the name is a little better)

Comment thread app/Fixers/LaravelBladeFixer.php Outdated
Comment thread resources/presets/laravel.php Outdated
@Jubeki

Jubeki commented Mar 15, 2024

Copy link
Copy Markdown
Contributor

I really love the idea and wanted to test it under a fresh repository.

Setup

New Laravel Project with Breeze (Blade+Alpine+Darkmode)

The commit with the changes:
Jubeki/laravel-pint-blade-test@504200c

Feedback

Overall a very good result, only with some little things bugging me.

It is a little slow on the first run. But the second run (probably due to cache) is much faster.

Somethings I noticed:

  • in components/application-logo, the d="..." was moved to a new line, were as in components/modal, the x-data was moved to the previous line.
  • If there are multiple attributes in multiple lines, the closing tag is not on a new line anymore (components/modal see this line
  • components/nav-link is formatted weirdly

nunomaduro and others added 3 commits March 15, 2024 19:04
Co-authored-by: Julius Kiekbusch <contact@julius-kiekbusch.de>
Co-authored-by: Julius Kiekbusch <contact@julius-kiekbusch.de>
@nunomaduro

Copy link
Copy Markdown
Member Author

@Jubeki We've just renamed the fixer name. Regarding your remarks, can you try to see (or even PR) your suggestions?

@cmayorgahilario

Copy link
Copy Markdown

I currently use this Blade Prettier Plugin package for prettier with blade, would it conflict? Since I have jobs that are executed every time I commit, the pint job and the prettier job, would the parse and format for the blade files be executed twice?

pd. This package npm also parses and formats alpine

@nunomaduro

Copy link
Copy Markdown
Member Author

@DevMakoto, in that case, you would either disable blade formatting in Pint or disable blade formatting in Prettier.

@cmayorgahilario

cmayorgahilario commented Mar 15, 2024

Copy link
Copy Markdown

@DevMakoto, in that case, you would either disable blade formatting in Pint or disable blade formatting in Prettier.

@nunomaduro, this package of shufo doesn't parse or format Alpine.js for me. I would like to only use Pint if it has parsing and formatting for Blade.

@Jubeki

Jubeki commented Mar 16, 2024

Copy link
Copy Markdown
Contributor

I think the new prettier plugins works much better. ❤️

Same Setup as before, with the following result: Jubeki/laravel-pint-blade-test@b756cd0

The only remaining thing bothering me, is that the class attribute is sometimes moved to a new line, if it is too long (at least, I think that is the reason). (See Jubeki/laravel-pint-blade-test@b756cd0#diff-e720f286fb3c46871afbdac94bfe0293e346e36a23d16d636485431ddefe8324R3-R5 or the image below).
I also found no configuration option to change it, like if a single attribute, keep it on same line.

I like that if there are multiple attributes, they are always moved to a new line each. (though it looked weird on the first glance, if they are all very short)

Single Attribute moved to new line (should not happen in my opinion):

Screenshot 2024-03-16 at 16 04 02

Multiple attributes moved to new lines (good in my opinion)

Screenshot 2024-03-16 at 16 08 19

@adevade

adevade commented Apr 17, 2024

Copy link
Copy Markdown

Very exited for this feature! 🚀

I tried running on my Windows 11 machine and I'm getting strange behaviour, using Git Bash. Same results using PowerShell.

It takes the contents of one file and moves to another. For example it copies and replaces the content from admin/categories/edit.blade.php to admin/documents/edit.blade.php. But it also does the same from admin/documents/create.blade.php to admin/trashed-documents/index.blade.php, so it's not based on the filename.

Also getting different results and errors on each subsequent run.

Please let me know if I can provide more helpful info in some way!


Windows 11
Node 20.12.1
npm 10.5.2

Laravel Version ...... 10.48.8
PHP Version .......... 8.2.8
Composer Version ..... 2.7.2
Environment .......... local
Debug Mode ........... ENABLED
First run
❯ pint --blade

  ...............................................................!!!!!!✓✓!!!!!!!!!!!!✓✓!✓✓!✓..................

  ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Laravel  
    FAIL   ............................................................................................................................................................. 110 files, 20 errors, 9 style issues fixed  
  ! resources\views\admin\categories\create.blade.php                                                                    '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\categories\edit.blade.php                                                                      '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\categories\index.blade.php                                                                     '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\admin\documents\create.blade.php                                                                                                                                                  Laravel/blade  
  ! resources\views\admin\documents\edit.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\documents\index.blade.php                                                                      '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\admin\trashed-documents\index.blade.php                                                                                                                                           Laravel/blade  
  ! resources\views\auth\login.blade.php                                                                                 '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\components\button.blade.php                                                                                                                                                       Laravel/blade  
  ✓ resources\views\components\card.blade.php                                                                                                                                                         Laravel/blade  
  ! resources\views\components\container.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\empty-state.blade.php                                                                     '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\form.blade.php                                                                            '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\halle-logo.blade.php                                                                      '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\header-meta-item.blade.php                                                                '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\input-group.blade.php                                                                     '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\input.blade.php                                                                           '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\list-container.blade.php (node:21508) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive…  
  ! resources\views\components\list-item.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\select.blade.php                                                                                                      Unexpected non-whitespace character after JSON at position 321  
  ! resources\views\errors\404.blade.php                                                                                 '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\errors\503.blade.php                                                                                                            Unexpected non-whitespace character after JSON at position 1402  
  ✓ resources\views\layouts\alert.blade.php                                                                                                                                                           Laravel/blade  
  ✓ resources\views\layouts\app.blade.php                                                                                                                                                             Laravel/blade  
  ! resources\views\layouts\footer.blade.php                                                                                                        Unexpected non-whitespace character after JSON at position 1224  
  ✓ resources\views\layouts\head.blade.php                                                                                                                                                            Laravel/blade  
  ✓ resources\views\layouts\header.blade.php                                                                                                                                                          Laravel/blade  
  ! resources\views\layouts\nav.blade.php                                                                                                            Unexpected non-whitespace character after JSON at position 473  
  ✓ resources\views\layouts\split.blade.php                                                                                                                                                           Laravel/blade  
Second run
❯ pint --blade

  ...............................................................!!!.!✓.✓..!!!!!✓✓!!!✓..!..✓...................

  ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Laravel  
    FAIL   ............................................................................................................................................................. 110 files, 13 errors, 7 style issues fixed  
  ! resources\views\admin\categories\create.blade.php                                                                    '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\categories\edit.blade.php                                                                      '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\categories\index.blade.php                                                                     '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\admin\documents\edit.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\admin\documents\index.blade.php                                                                                                                                                   Laravel/blade  
  ✓ resources\views\auth\login.blade.php                                                                                                                                                              Laravel/blade  
  ! resources\views\components\container.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\components\empty-state.blade.php                                                                                                                                                  Laravel/blade  
  ! resources\views\components\form.blade.php  (node:23912) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true })…  
  ! resources\views\components\halle-logo.blade.php                                                                                                 Unexpected non-whitespace character after JSON at position 1435  
  ! resources\views\components\header-meta-item.blade.php                                                                '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\input-group.blade.php                                                                     '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\components\input.blade.php                                                                                                                                                        Laravel/blade  
  ✓ resources\views\components\list-container.blade.php                                                                                                                                               Laravel/blade  
  ! resources\views\components\list-item.blade.php                                                                       '..' is not recognized as an internal or external command, operable program or batch file.  
  ! resources\views\components\select.blade.php Unexpected closing tag "b_kOgI1AW3xQB". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.ht</b_kOgI1AW3xQB><b_nk0xB><div>! resources\views\errors\404.blade.php                                                                                 '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\errors\503.blade.php                                                                                                                                                              Laravel/blade  
  ! resources\views\layouts\footer.blade.php                                                                             '..' is not recognized as an internal or external command, operable program or batch file.  
  ✓ resources\views\layouts\nav.blade.php                                                                                                                                                             Laravel/blade  
Simple Blade Component with error
❯ pint --blade resources/views/components/form.blade.php 

  !

  ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Laravel  
    FAIL   ........................................................................................................................................................................................ 1 file, 1 error  
  ! resources\views\components\form.blade.php                                                                            '..' is not recognized as an internal or external command, operable program or batch file.  
@props([
    'method' => 'POST',
    'action',
    'noCsrf' => false,
])

<form
    method="{{ in_array(strtoupper($method), ['POST', 'PUT', 'PATCH', 'DELETE']) ? 'POST' : 'GET' }}"
    action="{{ $action }}"
    {{ $attributes }}
>
    @if (!in_array($method, ['GET', 'POST']))
        @method($method)
    @endif

    @if (!$noCsrf)
        @csrf
    @endif

    {{ $slot }}
</form>

@adevade

adevade commented May 7, 2024

Copy link
Copy Markdown

I got it working on my machine by tweaking the pintCommand in .blade.format.json! After adding php to the beginning of the command everything works as expected, and the files are parsed.

- "pintCommand": "../builds/pint {file}",
+ "pintCommand": "php ../builds/pint {file}",

@adevade

adevade commented May 11, 2024

Copy link
Copy Markdown

I like that if there are multiple attributes, they are always moved to a new line each. (though it looked weird on the first glance, if they are all very short)
-- @Jubeki

I actually think that 2 attributes on the same line is fine most of the time, since they're often short. Maybe this setting could be configurable?

In your screenshots there are some tags that are not moved to separate lines, despite having 2 attributes. <x-input-label for="..." :value="..." /> and <x-input-error :messages="..." class="..." />. Is this because of the Blade syntax colon? Or did you change the formatting by hand for the screenshots?


Some examples that would look better on the same line in my opinion:

image

image

image

@wescopeland

wescopeland commented May 11, 2024

Copy link
Copy Markdown

Part of the joy of using Prettier is not having to worry about even the possibility of debates regarding the config. Even the few settings it supports, printWidth in particular, have been the source of debates on my teams.

If possible, please consider not adding additional config.

Additional reading: Prettier Option Philosophy

@mansoorkhan96

Copy link
Copy Markdown

Thank you for working on this. I have tried it in a project.

Here is an issue:

It does not format anchor tags properly

<!-- Before -->
<li class="mb-3.5"><a class="text-white hover:text-gray-200 font-medium leading-relaxed" href="{{ route('team') }}">Team</a></li>

<!-- After -->
<li class="mb-3.5">
    <a
        class="font-medium leading-relaxed text-white hover:text-gray-200"
        href="{{ route('team') }}"
        >Team</a
    >
</li>

<!-- Expected -->
<li class="mb-3.5">
    <a
        class="font-medium leading-relaxed text-white hover:text-gray-200"
        href="{{ route('team') }}"
    >
        Team
    </a>
</li>

@adevade

adevade commented May 12, 2024

Copy link
Copy Markdown

Part of the joy of using Prettier is not having to worry about even the possibility of debates regarding the config. Even the few settings it supports, printWidth in particular, have been the source of debates on my teams.
-- @wescopeland

Yeah I guess you're right. It's just sooo close to how I actually want to format it 😅 I'll just have to get used to it 👍

@chrillep

chrillep commented Sep 4, 2024

Copy link
Copy Markdown

love the idea!
In the meantime we use

with code-guide setting

.prettierrc.json

{
  "plugins": [
    "prettier-plugin-tailwindcss",
    "@shufo/prettier-plugin-blade"
  ],
  "overrides": [
    {
      "files": [
        "*.blade.php"
      ],
      "options": {
        "parser": "blade",
        "wrapAttributes": "force-expand-multiline",
        "sortTailwindcssClasses": true,
        "tailwindcssConfigPath": "tailwind.config.cjs",
        "sortHtmlAttributes": "code-guide"
      }
    }
  ]
}
--wrap-attributes The way to wrap attributes. [auto|force|force-aligned|force-expand-multiline|aligned-multiple|preserve|preserve-aligned]. default: auto
--wrap-attributes-min-attrs Minimum number of html tag attributes for force wrap attribute options. Wrap the first attribute only if 'force-expand-multiline' is specified in wrap attributes. default: 2.
--sort-tailwindcss-classes Sort Tailwind CSS classes. It will automatically look for and respect tailwind.config.js if it exists. default: false
--sort-html-attributes Sort HTML Attributes in the specified order. [none | alphabetical | code-guide | idiomatic | vuejs] default: none

@francoism90

Copy link
Copy Markdown

@chrillep The problem is that it doesn't always format the blade correctly:

It would be really great if Laravel Pint could handle this, including Tailwind class sorting. :)

@chrillep

chrillep commented Oct 11, 2024

Copy link
Copy Markdown

@chrillep The problem is that it doesn't always format the blade correctly:

It would be really great if Laravel Pint could handle this, including Tailwind class sorting. :)

I agree. But maybe Bring @shufo into the fold since he has added great functionality to blade linting!
The sortHtmlAttributes is 👌

@robsontenorio

robsontenorio commented Dec 1, 2024

Copy link
Copy Markdown

@nunomaduro Currently I can't see how it get my own .prettierrc / .prettierrc.json / prettierrc.json file. Is this a limitation for now ? It does not take effect.

@bartdenhoed

Copy link
Copy Markdown

Is it save to use this branch right now?

@bartdenhoed

Copy link
Copy Markdown

Any progress on this feature branch? It's currently not usable with PHP 8.4

@adevade

adevade commented Mar 12, 2026

Copy link
Copy Markdown

@nunomaduro Sorry to ping you, but I just wanted to let you know that @JohnathonKoster has released Chisel (GitHub) which seems to be a perfect match for something like this.

It would be so awesome to have blade formatting (preferably along with Tailwind class sorting) in Pint, and just use one tool for all of our formatting 🎉

@nunomaduro

Copy link
Copy Markdown
Member Author

I've picked this again on separate branch.

@nunomaduro nunomaduro closed this Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.