Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.x] Adds blade support #256

Draft
wants to merge 66 commits into
base: main
Choose a base branch
from
Draft

[1.x] Adds blade support #256

wants to merge 66 commits into from

Conversation

nunomaduro
Copy link
Member

@nunomaduro nunomaduro commented Mar 15, 2024

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
Copy link
Contributor

@Jubeki Jubeki left a comment

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)

app/Fixers/LaravelBladeFixer.php Outdated Show resolved Hide resolved
resources/presets/laravel.php Outdated Show resolved Hide resolved
@Jubeki
Copy link
Contributor

Jubeki commented Mar 15, 2024

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
Copy link
Member Author

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

@0528Makoto
Copy link

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
Member Author

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

@0528Makoto
Copy link

0528Makoto commented Mar 15, 2024

@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
Copy link
Contributor

Jubeki commented Mar 16, 2024

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
Copy link

adevade commented Apr 17, 2024

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
Copy link

adevade commented May 7, 2024

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
Copy link

adevade commented May 11, 2024

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
Copy link

wescopeland commented May 11, 2024

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

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
Copy link

adevade commented May 12, 2024

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
Copy link

chrillep commented Sep 4, 2024

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

@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
Copy link

chrillep commented Oct 11, 2024

@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 👌

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.