Adds support for Blade templates to Prettier. (work in progress)

Overview

Laravel Blade support for Prettier

⚠️ This plugin is still a work-in-progress. If you're trying it out, please keep this in mind.

This package includes a Prettier plugin that adds support for Laravel's Blade template engine. Much like Prettier, it is highly-opinionated and aims for consistency rather than configurability.

Installation

This package can be installed via npm:

npm install https://github.com/ryangjchandler/prettier-plugin-blade

This plugin is not currently available on NPM. The only way to install it currently is via the GitHub repository or a local symlink.

Missing features?

If this plugin is currently missing features, please open an issue. We would like to cover as many edge-cases as possible so all reports help.

Alternatively, you can contribute to the project by providing a failing test that we'll come back to fix at a later date.

Contributing

All pull requests are welcome. We ask that you open your pull-request as soon as possible, even if it is a draft, so that we can avoid conflicting changes.

Our test suite is incredibly straight-forward too. Here's how to write a test for the plugin:

  1. Create a new file inside of tests/__fixtures__. The file should be inside of a folder - if none of the existing folders make sense, create a new one.
  2. Write your raw Blade source code inside of a new .blade.php file. The file name should loosely describe the test case.
  3. After your raw Blade code, add a new line along with 4 - tokens (----).
  4. On a new line, write your expectation. This should match the post-formatted Blade code.
  5. Ensure the post-formatted Blade code has a trailing new line too (this is a common error and hard to spot in the console).
  6. Run the test suite using npm run test!
Comments
  • Installation Instructions on README

    Installation Instructions on README

    I would love to start playing with this and potentially help contribute, but I'm not sure the best way to install this into my project. I found this: https://www.npmjs.com/package/prettier-plugin-blade but it's not your package and doesn't even exist anymore.

    If you're waiting until the package is complete to add installation instructions, then no worries.

    opened by iAmKevinMcKee 12
  • feat: support for echos in HTML elements using placeholder strings (closes #45)

    feat: support for echos in HTML elements using placeholder strings (closes #45)

    This implements the changes to echo placeholders I droned on about in #45. In short, it changes the placeholders used for echo (and comment) statements from <e-1-xxx /> to e_1_xxxxxx. This allows us to support blade echos within elements like <input {{ $attribute }} />, which are otherwise sent through the HTML formatter as <input <e-1-xxx /> />. This works if those don't get wrapped, but if there is wrapping, our replacement technique starts to fall apart.

    So, benefits: it works in all of the cases thrown at it so far.

    Downsides: the current implementation relies on things like <e-1-xxx /> being unique in the document, and this feels like a safe assumption. e-1 is not an HTML element and it's an odd-enough name for a web component that it feels unlikely to collide w/ code the user is working with. On the other hand, something like e_1 feels a lot more likely to collide w/ the user's code, resulting in us replacing the wrong parts of the document. Now, realistically, the shortest echo statement we could be replacing is {{ 1 }} which is 7 chars, so these placeholders would be more like e_1_xxx. This feels a lot less likely to collide, but still not collision proof. It might be good enough for now, though, while we're in development.

    Alternatives:

    • This is honestly my last idea for how to use our current system for replacements while making it work within elements and JS. Aside from this, I suppose we could look at a much expanded, more HTML aware parser/lexer that could use different placeholders depending on where the statement is located.
    • We could use a random string to generate the padding; this would reduce the likelihood of collisions
    • We could perhaps just admit that there are going to be limits to what we can do, draw a line in the sand and call some of these things "known issues".
    • I tried using "attribute" style placeholders like e-1="xxx" and this worked OK until we tried something like class="{{ $classes }}", which would be sent through the HTML formatter as class="e-1="xxx"" and the extra " broke things. This approached worked OK w/ JS, but also felt frail b/c it's going to turn things like var foo = {{ $number }} into var foo = e-1="" and the JS parser might complain about that.

    Eager to hear thoughts (and other ideas) about this!

    opened by claytonrcarter 11
  • fix(plugin): passthrough prettier options into html/php formatters (closes #41)

    fix(plugin): passthrough prettier options into html/php formatters (closes #41)

    I'm parking this here for discussion b/c I need to play w/ it some more before I'm comfortable w/ it. In short, this tries to pass all user-configurable options from the main prettier instance into each of the formatters we use. As is, I'm fairly certain we're ignoring user configured settings when calling the the html/php formatters. See #41, but you can also demonstrate this on the command line like so:

    ❯ npm run build
    
    ❯ cat foo.blade.php
    <body>
      {{ 'hello' }}
      <script>
        var foo = 'abc';
      </script>
    </body>
    
    ❯ ./node_modules/.bin/prettier --parser blade --plugin . foo.blade.php
    <body>
        {{ "hello" }}
        <script>
            var foo = "abc";
        </script>
    </body>
    
    # OK, so far, so good, now lets specify some config options:
    ❯ ./node_modules/.bin/prettier --parser blade --plugin . foo.blade.php --single-quote
    <body>
        {{ "hello" }}
        <script>
            var foo = "abc";
        </script>
    </body>
    
    # uh oh, where's our single quotes? let's try this branch...
    ❯ git co passthrough-options
    ❯ npm run build
    
    # first w/o options:
    ❯ ./node_modules/.bin/prettier --parser blade --plugin . foo.blade.php
    <body>
        {{ "hello" }}
        <script>
            var foo = "abc";
        </script>
    </body>
    
    # Ok, that's the same as above. And now with an option:
    ❯ ./node_modules/.bin/prettier --parser blade --plugin . foo.blade.php --single-quote
    <body>
        {{ 'hello' }}
        <script>
            var foo = 'abc';
        </script>
    </body>
    
    # tada!
    

    So, I think that this is getting closer to what we want, but I hate how I did it. I just don't know enough about prettier to know if there is a more idiomatic way to get just the user-configurable options, or if there is some other way to handle the global options that are passed around. As I understand, the global options include some add'l state that is used for keeping track of where the formatter is in the document, but I tried to pass these through directly to each plugin and that was causing incorrect formatting. So the method in use here is to make a copy of the options and then customize each as needed before deleting the properties we don't want/need.

    A couple more items:

    1. I think that Object.assign() is a shallow copy, so the array and objects copied from the main options might be getting copied by reference vs being cloned. This hasn't been an issue in practice, but I can see how it could.

    2. I also removed the default option of 4 space tabwidth. Was the though on that to force all Blade files to 4 spaces regardless of user's preference (the php plugin does this for php code, fwiw), or was that aimed for something else? Personally, if the user likes 2 space tabs for their HTML, I'm inclined to let them have it. Note that b/c PHP is hardcoded to 4 spaces, that won't affect that code at all.

    Would love some input on this, if you have any. Thank you!

    opened by claytonrcarter 9
  • Tests failing on new clone?

    Tests failing on new clone?

    👋 Hi. Thanks for working on this! I tried this out and noticed a bunch of weird/unexpected formatting issues so I cloned it to see if I could contribute anything. But ... the tests aren't passing for me. This is a fresh install/clone w/ a successful npm install. I'm currently using node 14 w/ npm 6, but I also tried node 16 w/ npm 8 and got the same results. Any ideas on what I'm doing wrong?

    ~/src                                                                                           
    ❯ node --version
    v14.19.0
    
    ~/src
    ❯ npm --version
    6.14.16
    
    ~/src
    ❯ hub clone https://github.com/ryangjchandler/prettier-plugin-blade
    Cloning into 'prettier-plugin-blade'...
    remote: Enumerating objects: 907, done.
    remote: Counting objects: 100% (907/907), done.
    remote: Compressing objects: 100% (508/508), done.
    remote: Total 907 (delta 428), reused 781 (delta 322), pack-reused 0
    Receiving objects: 100% (907/907), 6.49 MiB | 10.85 MiB/s, done.
    Resolving deltas: 100% (428/428), done.
    
    ~/src
    ❯ cd prettier-plugin-blade
    
    ~/src/prettier-plugin-blade main
    ❯ npm i
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN [email protected] requires a peer of autoprefixer@^10.0.2 but none is installed. You must install peer dependencies yourself.
    npm WARN @ryangjchandler/prettier-plugin-blade@ No repository field.
    npm WARN @ryangjchandler/prettier-plugin-blade@ No license field.
    
    added 412 packages from 353 contributors and audited 412 packages in 7.968s
    
    38 packages are looking for funding
      run `npm fund` for details
    
    found 0 vulnerabilities
    
    
    ~/src/prettier-plugin-blade main                                                                                            
    ❯ npm run test
    
    > @ryangjchandler/prettier-plugin-blade@ test /Users/crcarter/src/prettier-plugin-blade
    > jest
    
     FAIL  tests/lexer.test.ts
      ● can generate directive tokens
    
        expect(received).toHaveProperty(path, value)
    
        Expected path: "raw"
    
        Expected value: "@if(auth()) @if(')' === '@test')"
        Received value: "@if(')"
    
          51 |
          52 |     expect(tokens[5]).toHaveProperty("type", TokenType.Directive);
        > 53 |     expect(tokens[5]).toHaveProperty("raw", "@if(auth()) @if(')' === '@test')");
             |                       ^
          54 |
          55 |     expect(tokens).toHaveLength(6);
          56 | });
    
          at Object.<anonymous> (tests/lexer.test.ts:53:23)
    
      ● can generate raw echo tokens when wrapped in parenthesis
    
        expect(received).toHaveProperty(path, value)
    
        Expected path: "type"
    
        Expected value: 2
        Received value: 1
    
           95 |     const raw = lex("{{!! 'yes' !!}}")[0];
           96 |
        >  97 |     expect(raw).toHaveProperty("type", TokenType.RawEcho);
              |                 ^
           98 |     expect(raw).toHaveProperty("raw", "{!! 'yes' !!}");
           99 | });
          100 |
    
          at Object.<anonymous> (tests/lexer.test.ts:97:17)
    
     FAIL  tests/fixtures.test.ts
      ● components/component-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 8
        + Received  + 4
    
        - @component('view')
        -     @slot('name', 'value')
        -     @slot('name2', true)
        -     @slot('name3')
        + @component('view') @slot('name', 'value') @slot('name2', true) @slot('name3')
        -         <div>Name3 Slot</div>
        + <div>Name3 Slot</div>
        -     @endslot
        + @endslot
    
        -     <div>Default Slot</div>
        + <div>Default Slot</div>
          @endcomponent
        -
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● components/component-namespace-tag.blade.php
    
        SyntaxError: Unexpected closing tag "x-slot". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (9:1)
           7 |     </x-slot:trigger><x-slot:footer>
           8 | <div>Footer Slot</div>
        >  9 | </x-slot>
             | ^^^^^^^^^
          10 | <div>
          11 |         Default Slot</div>
          12 |         </x-view>
           7 |     </x-slot:trigger><x-slot:footer>
           8 | <div>Footer Slot</div>
        >  9 | </x-slot>
             | ^^^^^^^^^
          10 | <div>
          11 |         Default Slot</div>
          12 |         </x-view>
    
          at y (node_modules/prettier/parser-html.js:40:2906)
          at Vl (node_modules/prettier/parser-html.js:111:2567)
          at Xl (node_modules/prettier/parser-html.js:111:4116)
          at Object.parse (node_modules/prettier/parser-html.js:111:4885)
          at Object.parse (node_modules/prettier-plugin-tailwindcss/dist/index.js:146:3296)
          at Object.parse$d [as parse] (node_modules/prettier/index.js:12975:19)
          at coreFormat (node_modules/prettier/index.js:14525:16)
          at formatWithCursor$1 (node_modules/prettier/index.js:14765:14)
          at node_modules/prettier/index.js:60959:12
          at format (node_modules/prettier/index.js:60979:12)
    
      ● components/component-tag.blade.php
    
        SyntaxError: Unexpected closing tag "x-slot". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (9:1)
           7 |     </x-slot:trigger><x-slot:footer>
           8 | <div>Footer Slot</div>
        >  9 | </x-slot>
             | ^^^^^^^^^
          10 | <div>
          11 |         Default Slot</div>
          12 |         </x-view>
           7 |     </x-slot:trigger><x-slot:footer>
           8 | <div>Footer Slot</div>
        >  9 | </x-slot>
             | ^^^^^^^^^
          10 | <div>
          11 |         Default Slot</div>
          12 |         </x-view>
    
          at y (node_modules/prettier/parser-html.js:40:2906)
          at Vl (node_modules/prettier/parser-html.js:111:2567)
          at Xl (node_modules/prettier/parser-html.js:111:4116)
          at Object.parse (node_modules/prettier/parser-html.js:111:4885)
          at Object.parse (node_modules/prettier-plugin-tailwindcss/dist/index.js:146:3296)
          at Object.parse$d [as parse] (node_modules/prettier/index.js:12975:19)
          at coreFormat (node_modules/prettier/index.js:14525:16)
          at formatWithCursor$1 (node_modules/prettier/index.js:14765:14)
          at node_modules/prettier/index.js:60959:12
          at format (node_modules/prettier/index.js:60979:12)
    
      ● directive/auth-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 1
        + Received  + 1
    
          @auth("admin")
        -     The user is authenticated...
        +  The user is authenticated...
          @endauth
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/custom-if-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 12
        + Received  +  2
    
        - @disk('local')
        -     local!
        - @elsedisk('s3')
        -     s3!
        - @else
        -     not sure?
        - @enddisk
        + @disk('local') local! @elsedisk('s3') s3! @else not sure?@enddisk
        -
        - @unlessdisk('local')
        -     something not local
        - @enddisk
        + @unlessdisk('local')something not local@enddisk
        -
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/error-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 2
        + Received  + 6
    
          <label for="title">Post Title</label>
    
        - <input id="title" type="text" class="@error('title') is-invalid @enderror">
        + <input id="title" type="text" class="@error('title')
        +  is-invalid
        + @enderror" />
    
          @error('title')
        -     <div class="alert alert-danger">{{ $message }}</div>
        +
        + <div class="alert alert-danger">{{ $message }}</div>
        +
          @enderror
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/if-else-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 6
        + Received  + 2
    
          @if(count($records) === 1)
        -     I have one record!
        - @elseif(count($records) > 1)
        +  I have one record! @elseif(count($records) > 1)I
        -     I have multiple records!
        - @else
        -     I don't have any records!
        + have multiple records! @else I don't have any records!
          @endif
        -
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/prepend-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 1
        + Received  + 3
    
          @prepend('scripts')
        -     <script src="/example.js"></script>
        +
        + <script src="/example.js"></script>
        +
          @endprepend
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/push-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 1
        + Received  + 3
    
          @push('scripts')
        -     <script src="/example.js"></script>
        +
        + <script src="/example.js"></script>
        +
          @endpush
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● directive/superfluous-spaces-directive.blade.php
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 9
        + Received  + 5
    
        - <h1>
        -     @if(true)
        + <h1>@if(true)
        -         goood
        +  goood
        -     @endif
        -
        -     @if("test" === "test")
        + @endif @if("test" === "test")
        -         baaad
        +  baaad
        -     @endif
        - </h1>
        + @endif</h1>
          ↵
    
          14 |         .map((part) => part.trimStart());
          15 |
        > 16 |     expect(format(original)).toEqual(expected);
             |                              ^
          17 | });
          18 |
    
          at tests/fixtures.test.ts:16:30
    
      ● php-tags/php-with-html-in-string.blade.php
    
        SyntaxError: Unexpected closing tag "title". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (2:40)
          1 | <?
        > 2 |     echo "<title>Some to be happy about</title>"
            |                                        ^^^^^^^^
          3 |     ?>
          4 | <h1>Awesome
          5 | </h1>
          1 | <?
        > 2 |     echo "<title>Some to be happy about</title>"
            |                                        ^^^^^^^^
          3 |     ?>
          4 | <h1>Awesome
          5 | </h1>
    
          at y (node_modules/prettier/parser-html.js:40:2906)
          at Vl (node_modules/prettier/parser-html.js:111:2567)
          at Xl (node_modules/prettier/parser-html.js:111:4116)
          at Object.parse (node_modules/prettier/parser-html.js:111:4885)
          at Object.parse (node_modules/prettier-plugin-tailwindcss/dist/index.js:146:3296)
          at Object.parse$d [as parse] (node_modules/prettier/index.js:12975:19)
          at coreFormat (node_modules/prettier/index.js:14525:16)
          at formatWithCursor$1 (node_modules/prettier/index.js:14765:14)
          at node_modules/prettier/index.js:60959:12
          at format (node_modules/prettier/index.js:60979:12)
    
    Test Suites: 2 failed, 2 total
    Tests:       13 failed, 29 passed, 42 total
    Snapshots:   0 total
    Time:        3.686 s
    Ran all test suites.
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! @ryangjchandler/prettier-plugin-blade@ test: `jest`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the @ryangjchandler/prettier-plugin-blade@ test script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/crcarter/.npm/_logs/2022-02-20T20_40_45_433Z-debug.log
    
    opened by claytonrcarter 9
  • Another round of misc fixes

    Another round of misc fixes

    Added:

    • support for all "first party" conditionals defined in CompilesConditionals.php (but still not @switch)
    • semi-support for @checked/@selected/@disabled (see below)

    Fixed:

    • formatting an @elseif that was indented was leaving behind an </else-if-${id}>
    • Vue/Alpine event bindings can include periods, eg @click.stop="foo"
    • Destructive formatting of empty conditionals (#44)

    Notes:

    • @checked et al are special b/c they really only make sense w/i an element, but they were being formatted as if they were elements, which led to invalid markup and formatting errors. This is the same issue as #45. As noted, this is an MVP fix for that: basically if we have a DirectiveNode that matches checked/selected/disabled, we use a string placeholder instead of an element placeholder. The thinking here is that these will most often be used w/i elements. There is the chance that someone has defined their own @disabled etc directive; if so, this will lead to funky formatting for that poor soul.
    • For the Vue/Alpine fix, this was easy enough, but I wonder if there are other characters we should be on the lookout for.
    • #44 is closed by changing the regexps to only match whitespace (\s*) instead of anything (.*).

    These have been working splendidly for me locally. I'll park this here for a while for folks to peek at/comment if they'd like.

    opened by claytonrcarter 5
  • bracketSameLine setting not being obeyed?

    bracketSameLine setting not being obeyed?

    In my prettier.config.js file I have the following ...

    module.exports = { tailwindConfig: "./tailwind.config.js", bracketSameLine: true, };

    The bracketSameLine setting is obeyed in html and js files, but not blade.

    Is this a bug or am I doing something wrong?

    Thanks.

    opened by yabdab 5
  • Empty directives cause destructive formatting

    Empty directives cause destructive formatting

    Input:

    @if($foo)
    @endif
    

    Output:

     @endif
    

    Expected:

    @if($foo)
    @endif
    
    // or 
    
    @if($foo) @endif
    

    Needless to say, this is undesirable. 😄

    I think that this has to do w/ this pattern: ``\n?.*<\/if-open-${uuid}>` b/c the input is transformed into this:

    <if-open-2> <div x-delete-x></div>
     </if-open-2>  <if-close-2 />
    

    which is then formatted into this:

    <if-open-2> <div x-delete-x></div> </if-open-2> <if-close-2 />
    

    So that pattern is too aggressive and is gobbling up everything up to the start of the line.

    bug 
    opened by claytonrcarter 4
  • Loops + other misc fixes

    Loops + other misc fixes

    Hi again. (FYI This builds on the branch for #39, so I'll just rebase this onto main once that's merged.)

    This adds:

    1. Initial support for loops: @for, @foreach, @forelse and @while.

    OK, this is great and was easy, but to be clear, it takes the cowards way out. Instead of adding AST nodes/type/etc for the various loops, I am relying on string manipulation w/i DirectiveNode. This seemed like the easiest way forward, and also made # 2 trivial. For example, w/o this change, @for( $i = 1; $i++, $i < 5 ) would be handed off to prettier as <?php $i = 1; $i++, $i < 5; and @foreach ( $foo as $bar ) would be handed off to prettier as <?php $foo as bar;. The former is always broken onto multiple lines (b/c it just looks like multiple statements on 1 line) while the latter is invalid PHP, so is never actually rerformatted. With this change, these are instead handed off to prettier as just <?php for ($i = 1; $i++, $i < 5) {} and <?php foreach ( $foo as $bar ) {}, and then the for/foreach syntax is stripped off again before we put it back into the Blade.

    Note that, when formatting loops, I have opted to put a space between the directive name and the opening paren. This is easy to remove if desired, but it matches how prettier formats loops. For example @while (true) vs @while(true). Loops are the only directives have that space.

    Main caveat w/ loops: @empty doesn't work. This is because forelse is just a special foreach loop that allows the @empty case/directive, but @empty($var) is also valid directive in it's own right. In this PR (and FWIW on main, I think) the @empty directive w/o any params totally breaks the formatter. This PR does not solve this. A @forelse loop w/o an @empty directive will format just fine, but any blade w/ an @empty directive w/o any arguments will halt formatting completely.

    More context, @empty is one of a handful of special directives that have multiple forms based on their context:

    • @empty is a paired directive if written w/ parameters, or child directive of @forelse if written w/o
    • @slot and @section are paired if written w/ 1 parameter, but are solo directives if written w/ 2 params

    In this regard, I kind of feel like this PR is "correct" in that it allows/unlocks formatting of loops, and that handling of these whacky directives is out of scope here and might be best left to someone w/ more knowledge of chevrotain than I. On the other hand, it's hard to say we "support" forelse w/o supporting empty.

    2. Changes how the code w/i directive arguments is formatted.

    Most directives only take a single parameter, and we can get by pretty easily by treating that param as regular php. Eg @method($foo) works just fine if we hand the params to prettier as <?php $foo;. However, many directives take several parameters, and this approach doesn't work on those: @inject($foo, $bar) is currently handed to prettier as <?php $foo, $bar;, which is invalid PHP, which means we leave it as is and never format it.

    My solution is to follow the approach of # 1 and hand all directive parameters to prettier as if they were params to a function call, then remove the fake function call before putting them back into blade. So the previous examples will be handed to prettier as <?php a($foo); and <?php a($foo, $bar);, resp. In all the cases I threw at it, this worked correctly, though I wonder if there's some edge cases I haven't thought of.

    3. Fixes an edge case where echo braces appear at the end of a line.

    No idea why this was needed; the existing regexps should have allowed for option line breaks, but the regression fixture I added would only pass if the those optional break were removed. This was uncovered while working on # 1, but isn't directly related.

    4. Fixes an edge (??) case where comments were having extra whitespace added to them.

    Totally unrelated, but I noticed that {{-- foo --}} was sometimes being reformatted as {{-- foo --}}. This fix prevents that.

    5. Adds a micro-optimization that prevents us from even trying to format empty strings because ... there's nothing to format!

    Not much to this. I just noticed that there were a number of times when we're asking prettier to format an empty string (ie <?php ;).

    As always, I'm happy for any feedback or comments you may have. Thank you!

    opened by claytonrcarter 4
  • Add new component slot syntax

    Add new component slot syntax

    https://laravel.com/docs/9.x/blade#slot-attributes

    The new Syntax allows more options on how to define which slot the content is for.

    In my opinion the formatter should change how the slot name is defined to one default. What I would prefer is the following:

    {{-- Before Formatting --}}
    <x-view x="X" :y="'Y'">
        <x-slot name="Name">
            <div>Named Slot</div>
        </x-slot>
    
        <x-slot:trigger>
            <div>Trigger Slot</div>
        </x-slot:trigger>
    
        <x-slot:footer>
            <div>Footer Slot</div>
        </x-slot>
        
        <div>Default Slot</div>
    </x-view>
    
    {{-- After Formatting --}}
    <x-view x="X" :y="'Y'">
        <x-slot:Name>
            <div>Named Slot</div>
        </x-slot:Name>
    
        <x-slot:trigger>
            <div>Trigger Slot</div>
        </x-slot:trigger>
    
        <x-slot:footer>
            <div>Footer Slot</div>
        </x-slot:footer>
        
        <div>Default Slot</div>
    </x-view>
    

    I will send another PR which implements my personal style. So that you can decide which one you want to accept.

    opened by Jubeki 4
  • Migrate to typescript and add a basic test

    Migrate to typescript and add a basic test

    Love the initiative for this project!

    I was looking into building this myself, but then I got told you already started, so thought I would help contribute a bit.

    This pr is for migrating to typescript and setting up a basic testing setup using snapshots and github actions, hope you like it!

    The types could def. be improved more than what I did here, but I think it's a good starting point

    opened by olivernybroe 4
  • Echo statements w/i element attributes

    Echo statements w/i element attributes

    Input

    <input type="text" name="foobarfoobarfoobar" value="foobarfoobarfoobar" {{ $attributes}} />
    

    Output:

    <input
        type="text"
        name="foobarfoobarfoobar"
        value="foobarfoobarfoobar"
        <e-3-xxxxxxxxx
    />
    />
    

    Expected:

    <input
        type="text"
        name="foobarfoobarfoobar"
        value="foobarfoobarfoobar"
        {{ $attributes }}
    />
    

    This is caused when our placeholder creates invalid markup (eg <input <e-3-x /> />, note the double /> />). The best idea I have ATM is to change how we create placeholders for echo statements to something that is valid anywhere in HTML. For example, something like e-3="xxxx" might work.

    opened by claytonrcarter 3
  • Multi line echos and comments

    Multi line echos and comments

    This is still a work in progress, but it's good so far. When I tried to make the regexps for echo tokens multiline, Chevrotain complained and slapped my wrist. My alternative was to write a lexer function to scan the doc over multiple lines, looking for the closing braces. Again, so far so good. Comments, regular echo ({{ }}) and raw echo ({!! !!}) are done and working.

    TODO

    • [ ] ~~Echo and unescaped echo need to be "string aware", eg {{ "my silly }} string" }}~~ Edit: this is not a thing!
    • [ ] Need support for (undocumented?) escaped echo: {{{ $foo }}}
    opened by claytonrcarter 17
  • not enough line breaks for blade statements

    not enough line breaks for blade statements

    When I format this

    @can('can') @endcan @if (true) @else @endif @switch($type) @case(1) @case(2) @case(3) @break @endswitch
    

    This is the result:

    @can('can') @endcan @if(true)
     @else
    @endif @switch($type)
     @case(1) @case(2)
    @case(3) @break
    @endswitch
    

    I expected a line break before each @

    opened by Polfo 5
  • Add PnP support

    Add PnP support

    Thank you for writing this plugin.

    When trying to use on a repo also using yarn 2, it errors out.

    Screen Shot 2022-02-20 at 6 56 44 PM

    package.json

    "prettier": {
    		"useTabs": true,
    		"plugins": [
    			"@prettier/plugin-php",
    			"prettier-plugin-blade"
    		]
    	},
    

    On inspection of the package content, it shows that something need to update in regards of the build

    Screen Shot 2022-02-20 at 6 55 22 PM
    opened by genintho 0
  • Add issues template for bugs

    Add issues template for bugs

    As I expect people to find many bugs, let's add a template to GitHub for bugs, which has input, expected, actual sections, which people can fill out.

    This would make it really easy for us to make a test from the bug, as if we agree with the expected, we can just copy paste it into a new test file.

    enhancement 
    opened by olivernybroe 0
  • Potential configuration options

    Potential configuration options

    We should start thinking about some of the configuration options that we're going to want to use now before we get too deep into implementation details.

    • tabWidth - 4
    • spaceAfterDirectiveName - false
    question 
    opened by ryangjchandler 11
Owner
Ryan Chandler
Ryan Chandler
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
A Laravel Blade parser, compiler, and static analyzer written in TypeScript.

Blade Parser This library provides a Laravel Blade parser written in TypeScript. In addition to being able to parse Blade template files, this library

Stillat 7 Jan 4, 2023
a VS Code Extension for Easily Localize any blade/php text in any Laravel project.

Laravel Easy Localize a VS Code Extension for Easily Localize any blade/php text in any Laravel project. Features Custom array key for each translatio

Moataz Hajres 6 Oct 31, 2022
Simple patch that adds a 'progress' callback to jquery Ajax calls

Jquery Ajax Progresss A simple patch to jQuery that will call a 'progress' callback, using the XHR.onProgress event Usage Simply include the script on

Chad Engler 118 Sep 8, 2022
A full stack application that uses an authentication system to allow FAA Inspectors, Airliners, and Aircraft Technicians to update progress on their work all while keeping a log of records on projects completed.

A full stack application that uses an authentication system to allow FAA Inspectors, Airliners, and Aircraft Technicians to update progress on their work all while keeping a log of records on projects completed.

BinaryBitBytes 3 Jun 13, 2022
YADS: A work in progress dcs server

To run this repo: make an empty mission (any map) modify your dcs to run dcs-grpc clone this repo install deps with npm install create generated code

null 5 Nov 22, 2022
A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebWorker like neither of those.

Amuchina A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebW

Fabio Spampinato 9 Sep 17, 2022
A JavaScript Library To Make Your Work Work Easier/Faster

Functionalty.js (beta) About ✍️ This Is A JavaScript Library To Make Your Work Easier/Faster, You Can See Functionalty.js Website From Here Project Cr

Ali-Eldeba 16 Aug 30, 2022
A JavaScript Library To Make Your Work Work Easier/Faster

Functionality.js (beta) About ✍️ This Is A JavaScript Library To Make Your Work Easier/Faster, You Can See Functionalty.js Website From Here Project C

Ali-Eldeba 9 May 25, 2022
A JavaScript Library To Make Your Work Work Easier/Faster

Functionality.js About ✍️ This Is A JavaScript Library To Make Your Work Easier/Faster, You Can See Functionalty.js Website From Here Project Created

functionality 16 Jun 23, 2022
Query for CSS brower support data, combined from caniuse and MDN, including version support started and global support percentages.

css-browser-support Query for CSS browser support data, combined from caniuse and MDN, including version support started and global support percentage

Stephanie Eckles 65 Nov 2, 2022
This is a Homebridge plugin that adds HomeKit support to Tidbyt devices.

Tidbyt Platform Plugin This is a Homebridge plugin that adds HomeKit support to Tidbyt devices. Built with node-tidbyt. This project is not endorsed o

Nicholas Penree 18 Nov 20, 2022
Adds promise support (rejects(), doesNotReject()) to tape by decorating it using tape-promise.

Tape With Promises Adds promise support (rejects(), doesNotReject()) to tape by decorating it using tape-promise. Install npm install --save-dev @smal

Small Technology Foundation 3 Mar 21, 2022
A jQuery plugin that adds cross-browser mouse wheel support.

jQuery Mouse Wheel Plugin A jQuery plugin that adds cross-browser mouse wheel support with delta normalization. In order to use the plugin, simply bin

jQuery 3.9k Dec 26, 2022
A small plugin for Frappe that adds the support of customizations to the attach control.

Frappe Better Attach Control A small plugin for Frappe that adds the support of customizations to the attach control. Table of Contents Requirements S

Ameen Ahmed 17 Dec 25, 2022
A website for tracking community support for BIP21 QR codes that support on-chain and lightning bitcoin payments.

BIP21 Microsite This is a WIP microsite to promote the usage of a BIP21 payment URI QR code that can include lightning invoices or offers. Wallet supp

Stephen DeLorme 16 Nov 27, 2022