npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

prettier-edge

v1.0.0

Published

Prettier plugin for formatting AdonisJS Edge templates.

Readme

prettier-plugin-edge

A Prettier plugin for AdonisJS Edge templates.

gh-workflow-image npm-image npm-downloads-image license-image node-image prettier-image

One pass formats your HTML, your Edge tags and interpolations, the JavaScript inside them, and the embedded <script> / <style> blocks.


@if(showFooter)
<footer class="border-t border-gray-200">
<div class="container mx-auto py-8">
@each(section in sections)
<h3>{{section.title}}</h3>
<ul>
@each(item in section.items)
<li><a href="{{item.href}}">{{item.label}}</a></li>
@end
</ul>
@end
</div>
</footer>
@end
@if(showFooter)
  <footer class="border-t border-gray-200">
    <div class="container mx-auto py-8">
      @each(section in sections)
        <h3>{{ section.title }}</h3>
        <ul>
          @each(item in section.items)
            <li><a href="{{ item.href }}">{{ item.label }}</a></li>
          @end
        </ul>
      @end
    </div>
  </footer>
@end

Install

npm install --save-dev prettier prettier-plugin-edge

Prettier auto-detects the plugin for .edge files when it is a project dependency — no .prettierrc changes required.

npx prettier --write "resources/views/**/*.edge"

What it formats

| | | | -------------------------------- | ---------------------------------------------------------------------------------------------------------- | | HTML | Tag and attribute layout, indentation, attribute wrapping at printWidth, self-closing void elements. | | Edge interpolations | {{ … }}, {{{ … }}}, @{{ … }}, @{{{ … }}}, {{-- … --}}. | | Edge tags | Block (@if, @each, @component, @slot, @pushTo, @can, @error, …), inline (@let, @include, @inject, @vite, …), void (@!card(...)), branches (@elseif, @else), raw (@raw … @end). | | Components-as-tags | @card(...) … @end and @!card({...}) with arbitrarily nested object and array arguments. | | Embedded <script> | Prettier's JavaScript formatter. | | Embedded <style> | Prettier's CSS formatter. | | JavaScript in {{ … }} | Prettier's JavaScript formatter, with printWidth adjusted for the host indent. | | Alpine.js / Livewire attrs | @click="…", @click.prevent="…", @toggle-mobile-nav.window="…" — recognised as HTML attributes, not Edge tags. | | Preserved verbatim | @raw … @end bodies, <pre>, <textarea>, <!DOCTYPE …>, <?xml … ?>, <![CDATA[ … ]]>, conditional comments, HTML entities. |

Worked examples

<button {{
  $props
    .merge({ type: 'button', class: ['btn', 'btn-primary'] })
    .toAttrs()
}}>
  {{{ await $slots.main() }}}
</button>

The {{ … }} body is formatted with Prettier's JavaScript formatter, and the printer glues <button {{ and }}> around the indented expression so the layout reads naturally.

@!input.control({
  type: 'email',
  name: 'email',
  required: true,
  placeholder: 'Subscribe to our newsletter',
})

Object and array literals at the end of a @tag(...) argument list collapse onto the closing ) rather than wrapping it onto its own line.

<div
  class="container mx-auto px-5 lg:px-10 py-24 lg:border-x border-woodsmoke-900 relative"
>
  …
</div>

The plugin never wraps inside a class="…" value — Tailwind users keep their utility ordering intact.

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2L2 7l10 5 10-5-10-5z" fill="currentColor" />
  <circle cx="12" cy="12" r="3" />
  <g transform="translate(4,4)">
    <rect width="16" height="16" rx="2" />
  </g>
</svg>

SVG containers and leaves are treated as block-level so they always sit on their own lines, with a space before /> when attributes fit.

Options

Example .prettierrc:

{
  "printWidth": 100,
  "singleQuote": true,
  "bracketSameLine": true,
  "edgeAttributeQuotes": "preserve",
  "edgeBlankLinesInBlocks": 2
}

See docs/adr/0008-options-surface.md for the rationale and the list of Prettier options deliberately ignored.

Editor integration

Most Prettier IDE integrations pick the plugin up automatically once it is installed. In VS Code the standard Prettier extension is enough — point editor.defaultFormatter at it for .edge files.

// .vscode/settings.json
{
  "[edge]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  }
}

Status

Early release. Validated against two production AdonisJS sites — 256 of 256 templates round-trip cleanly. Files that fail to parse are written back unchanged, never corrupted.

The plugin owns its HTML printer end-to-end; it does not delegate to Prettier's html parser. HTML semantics — entity preservation, boolean attributes, <!DOCTYPE>, CDATA, conditional comments, processing instructions, SVG self-closing — are handled in this codebase. See docs/adr/0001-pipeline-shape.md for why.

Roadmap

  • [ ] Additional ecosystem tags as user reports surface them.
  • [ ] Public edgeKnownBlockTags / edgeKnownInlineTags config, if community templates show a real need.

How it works

The pipeline runs in this order, each stage scoped to one job:

| Stage | What it does | | ------------- | ----------------------------------------------------------------------------------------------------------- | | preprocess | Strip <script>/<style>/<pre>/<textarea> bodies and <?xml … ?> PIs into a side table; mask Alpine.js attributes; extract multi-line tag args. | | edge-lex | Tokenize Edge constructs via edge-lexer. | | edge-parse | Walk the token stream and replace each Edge construct with an HTML stand-in; the original node lives in a side table. | | parse-html | Parse the masked HTML with angular-html-parser. | | splice | Walk the HTML AST and substitute Edge nodes back in by stand-in id, producing one mixed tree. | | restore | Pull the raw-text bodies and processing instructions from the side table back onto their nodes. | | prepare | Async pre-pass: format every JS expression, <script>, and <style> body via prettier.format(…). | | print | Walk the mixed AST and emit a Prettier doc using group/indent/hardline/softline/line builders. |

The architecture decisions are documented as ADRs under docs/adr/.

Development

npm test                                                  # 75 unit + corpus fixtures
UPDATE_SNAPSHOTS=1 npm test                               # regenerate expected.edge files
EDGE_SMOKE_DIRS=/path/to/views npm test                   # parse-only check against real templates
EDGE_DEBUG_PARSE=1 npm test                               # log parse errors instead of swallowing

Fixtures live under test/fixtures/:

  • units/<category>/<name>/{input,expected}.edge — focused cases. Optional options.json overrides Prettier options.
  • corpus/<project>/<file>.edge — whole real-world files. Each one is asserted to parse cleanly and be idempotent. A sibling <file>.expected.edge makes the baseline frozen.

To add a new test case, drop a file in the right place and run UPDATE_SNAPSHOTS=1 npm test. Review the diff in the generated expected.edge before committing.

License

MIT