{"_id":"prettier-edge","name":"prettier-edge","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"prettier-edge","version":"1.0.0","description":"Prettier plugin for formatting AdonisJS Edge templates.","type":"module","main":"src/index.js","exports":{".":"./src/index.js"},"scripts":{"pretest":"npm run lint","test":"node --test","lint":"eslint","format":"prettier --write .","release":"release-it"},"keywords":["prettier","prettier-plugin","edge","edgejs","adonisjs","template","formatter"],"author":{"name":"Harminder Virk","email":"virk.officials@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/edge-js/prettier-plugin-edge.git"},"bugs":{"url":"https://github.com/edge-js/prettier-plugin-edge/issues"},"homepage":"https://github.com/edge-js/prettier-plugin-edge#readme","peerDependencies":{"prettier":"^3.0.0"},"devDependencies":{"@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^11.0.0","eslint":"^10.4.0","prettier":"^3.6.2","release-it":"^20.0.1"},"engines":{"node":">=18"},"dependencies":{"angular-html-parser":"^10.6.1","edge-lexer":"^6.0.5"},"publishConfig":{"access":"public","provenance":false},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"requireCleanWorkingDir":true,"requireUpstream":true,"commitMessage":"chore(release): ${version}","tagAnnotation":"v${version}","push":true,"tagName":"v${version}"},"github":{"release":true},"npm":{"publish":true,"skipChecks":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"},"infile":"CHANGELOG.md"}}},"gitHead":"82d69a6aede2cba683cf6e705fd3b65f992a00e3","_id":"prettier-edge@1.0.0","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-D8DwcGGPVWhMUrrE0+R242laEezGmvOpdCdvwVZiySpebZ1H5o7xwmDYhRw//ckOz1EcLOtQyJt/eQ2UC3eXOw==","shasum":"ce657464de7bd24122d58f21912c032a160c6269","tarball":"https://registry.npmjs.org/prettier-edge/-/prettier-edge-1.0.0.tgz","fileCount":13,"unpackedSize":79521,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDsjJEfFt0YJuC22Nwadf8fSClhKxxFB9SnnRUbL1O5/AIhAIWLpcGm9YpPhAjcgDlg5uqk2S/dpIU88/rvL4Uk0BL3"}]},"_npmUser":{"name":"virk","email":"virk@adonisjs.com"},"directories":{},"maintainers":[{"name":"virk","email":"virk@adonisjs.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/prettier-edge_1.0.0_1779966182115_0.6259889817613911"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-28T11:03:01.902Z","1.0.0":"2026-05-28T11:03:02.323Z","modified":"2026-05-28T11:03:02.545Z"},"maintainers":[{"name":"virk","email":"virk@adonisjs.com"}],"description":"Prettier plugin for formatting AdonisJS Edge templates.","homepage":"https://github.com/edge-js/prettier-plugin-edge#readme","keywords":["prettier","prettier-plugin","edge","edgejs","adonisjs","template","formatter"],"repository":{"type":"git","url":"git+https://github.com/edge-js/prettier-plugin-edge.git"},"author":{"name":"Harminder Virk","email":"virk.officials@gmail.com"},"bugs":{"url":"https://github.com/edge-js/prettier-plugin-edge/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n# prettier-plugin-edge\n\n**A [Prettier](https://prettier.io) plugin for AdonisJS [Edge](https://edgejs.dev) templates.**\n\n[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] [![npm-downloads-image]][npm-url] [![license-image]][license-url] [![node-image]][node-url] [![prettier-image]][prettier-url]\n\nOne pass formats your HTML, your Edge tags and interpolations, the JavaScript inside them, and the embedded `<script>` / `<style>` blocks.\n\n</div>\n\n---\n\n<table>\n<tr>\n<td><b>Before</b></td>\n<td><b>After</b></td>\n</tr>\n<tr>\n<td>\n\n```edge\n@if(showFooter)\n<footer class=\"border-t border-gray-200\">\n<div class=\"container mx-auto py-8\">\n@each(section in sections)\n<h3>{{section.title}}</h3>\n<ul>\n@each(item in section.items)\n<li><a href=\"{{item.href}}\">{{item.label}}</a></li>\n@end\n</ul>\n@end\n</div>\n</footer>\n@end\n```\n\n</td>\n<td>\n\n```edge\n@if(showFooter)\n  <footer class=\"border-t border-gray-200\">\n    <div class=\"container mx-auto py-8\">\n      @each(section in sections)\n        <h3>{{ section.title }}</h3>\n        <ul>\n          @each(item in section.items)\n            <li><a href=\"{{ item.href }}\">{{ item.label }}</a></li>\n          @end\n        </ul>\n      @end\n    </div>\n  </footer>\n@end\n```\n\n</td>\n</tr>\n</table>\n\n## Install\n\n```sh\nnpm install --save-dev prettier prettier-plugin-edge\n```\n\nPrettier auto-detects the plugin for `.edge` files when it is a project dependency — no `.prettierrc` changes required.\n\n```sh\nnpx prettier --write \"resources/views/**/*.edge\"\n```\n\n## What it formats\n\n|                                  |                                                                                                            |\n| -------------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| **HTML**                         | Tag and attribute layout, indentation, attribute wrapping at `printWidth`, self-closing void elements.     |\n| **Edge interpolations**          | `{{ … }}`, `{{{ … }}}`, `@{{ … }}`, `@{{{ … }}}`, `{{-- … --}}`.                                            |\n| **Edge tags**                    | Block (`@if`, `@each`, `@component`, `@slot`, `@pushTo`, `@can`, `@error`, …), inline (`@let`, `@include`, `@inject`, `@vite`, …), void (`@!card(...)`), branches (`@elseif`, `@else`), raw (`@raw … @end`). |\n| **Components-as-tags**           | `@card(...) … @end` and `@!card({...})` with arbitrarily nested object and array arguments.                |\n| **Embedded `<script>`**          | Prettier's JavaScript formatter.                                                                           |\n| **Embedded `<style>`**           | Prettier's CSS formatter.                                                                                  |\n| **JavaScript in `{{ … }}`**      | Prettier's JavaScript formatter, with `printWidth` adjusted for the host indent.                           |\n| **Alpine.js / Livewire attrs**   | `@click=\"…\"`, `@click.prevent=\"…\"`, `@toggle-mobile-nav.window=\"…\"` — recognised as HTML attributes, not Edge tags. |\n| **Preserved verbatim**           | `@raw … @end` bodies, `<pre>`, `<textarea>`, `<!DOCTYPE …>`, `<?xml … ?>`, `<![CDATA[ … ]]>`, conditional comments, HTML entities. |\n\n## Worked examples\n\n<details>\n<summary><b>Dynamic attributes with embedded JavaScript</b></summary>\n\n```edge\n<button {{\n  $props\n    .merge({ type: 'button', class: ['btn', 'btn-primary'] })\n    .toAttrs()\n}}>\n  {{{ await $slots.main() }}}\n</button>\n```\n\nThe `{{ … }}` body is formatted with Prettier's JavaScript formatter, and the printer glues `<button {{` and `}}>` around the indented expression so the layout reads naturally.\n\n</details>\n\n<details>\n<summary><b>Multi-line tag arguments collapse against the parens</b></summary>\n\n```edge\n@!input.control({\n  type: 'email',\n  name: 'email',\n  required: true,\n  placeholder: 'Subscribe to our newsletter',\n})\n```\n\nObject and array literals at the end of a `@tag(...)` argument list collapse onto the closing `)` rather than wrapping it onto its own line.\n\n</details>\n\n<details>\n<summary><b>Long HTML attributes wrap, but the value is never split</b></summary>\n\n```edge\n<div\n  class=\"container mx-auto px-5 lg:px-10 py-24 lg:border-x border-woodsmoke-900 relative\"\n>\n  …\n</div>\n```\n\nThe plugin never wraps inside a `class=\"…\"` value — Tailwind users keep their utility ordering intact.\n\n</details>\n\n<details>\n<summary><b>SVG: containers and self-closing leaves</b></summary>\n\n```edge\n<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n  <path d=\"M12 2L2 7l10 5 10-5-10-5z\" fill=\"currentColor\" />\n  <circle cx=\"12\" cy=\"12\" r=\"3\" />\n  <g transform=\"translate(4,4)\">\n    <rect width=\"16\" height=\"16\" rx=\"2\" />\n  </g>\n</svg>\n```\n\nSVG containers and leaves are treated as block-level so they always sit on their own lines, with a space before `/>` when attributes fit.\n\n</details>\n\n## Options\n\n<table>\n<tr><th colspan=\"3\">Standard Prettier options</th></tr>\n<tr><th>Option</th><th>Default</th><th>Notes</th></tr>\n<tr><td><code>printWidth</code></td><td><code>80</code></td><td>Drives HTML attribute wrapping and embedded-JS layout.</td></tr>\n<tr><td><code>tabWidth</code></td><td><code>2</code></td><td>Indent unit.</td></tr>\n<tr><td><code>useTabs</code></td><td><code>false</code></td><td>Indent character.</td></tr>\n<tr><td><code>singleQuote</code></td><td><code>false</code></td><td>Propagates to embedded JS. HTML attribute quoting is controlled by <code>edgeAttributeQuotes</code>.</td></tr>\n<tr><td><code>endOfLine</code></td><td><code>\"lf\"</code></td><td></td></tr>\n<tr><td><code>bracketSameLine</code></td><td><code>false</code></td><td>When <code>true</code>, the closing <code>&gt;</code> of a multi-line opening tag glues to the last attribute.</td></tr>\n<tr><td><code>singleAttributePerLine</code></td><td><code>false</code></td><td>When <code>true</code>, every HTML attribute is placed on its own line.</td></tr>\n<tr><th colspan=\"3\">Edge-specific options</th></tr>\n<tr><th>Option</th><th>Default</th><th>Notes</th></tr>\n<tr><td><code>edgeMustacheSpacing</code></td><td><code>1</code></td><td><code>0</code> or <code>1</code> space inside <code>{{ … }}</code>, <code>{{{ … }}}</code>, <code>@{{ … }}</code>, <code>{{-- … --}}</code>.</td></tr>\n<tr><td><code>edgeAttributeQuotes</code></td><td><code>\"double\"</code></td><td>One of <code>\"double\"</code>, <code>\"single\"</code>, <code>\"preserve\"</code>. <code>\"preserve\"</code> keeps the source quote per attribute.</td></tr>\n<tr><td><code>edgeBlankLinesInBlocks</code></td><td><code>1</code></td><td>Maximum blank lines preserved between block-level siblings.</td></tr>\n</table>\n\nExample `.prettierrc`:\n\n```json\n{\n  \"printWidth\": 100,\n  \"singleQuote\": true,\n  \"bracketSameLine\": true,\n  \"edgeAttributeQuotes\": \"preserve\",\n  \"edgeBlankLinesInBlocks\": 2\n}\n```\n\nSee [`docs/adr/0008-options-surface.md`](docs/adr/0008-options-surface.md) for the rationale and the list of Prettier options deliberately ignored.\n\n## Editor integration\n\nMost 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.\n\n```jsonc\n// .vscode/settings.json\n{\n  \"[edge]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n    \"editor.formatOnSave\": true\n  }\n}\n```\n\n## Status\n\n> **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.\n\nThe 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`](docs/adr/0001-pipeline-shape.md) for why.\n\n## Roadmap\n\n- [ ] Additional ecosystem tags as user reports surface them.\n- [ ] Public `edgeKnownBlockTags` / `edgeKnownInlineTags` config, if community templates show a real need.\n\n## How it works\n\nThe pipeline runs in this order, each stage scoped to one job:\n\n| Stage         | What it does                                                                                                |\n| ------------- | ----------------------------------------------------------------------------------------------------------- |\n| `preprocess`  | Strip `<script>`/`<style>`/`<pre>`/`<textarea>` bodies and `<?xml … ?>` PIs into a side table; mask Alpine.js attributes; extract multi-line tag args. |\n| `edge-lex`    | Tokenize Edge constructs via [edge-lexer](https://github.com/edge-js/lexer).                                |\n| `edge-parse`  | Walk the token stream and replace each Edge construct with an HTML stand-in; the original node lives in a side table. |\n| `parse-html`  | Parse the masked HTML with [angular-html-parser](https://github.com/ikatyang/angular-html-parser-for-prettier). |\n| `splice`      | Walk the HTML AST and substitute Edge nodes back in by stand-in id, producing one mixed tree.               |\n| `restore`     | Pull the raw-text bodies and processing instructions from the side table back onto their nodes.             |\n| `prepare`     | Async pre-pass: format every JS expression, `<script>`, and `<style>` body via `prettier.format(…)`.        |\n| `print`       | Walk the mixed AST and emit a Prettier doc using `group`/`indent`/`hardline`/`softline`/`line` builders.    |\n\nThe architecture decisions are documented as ADRs under [`docs/adr/`](docs/adr/).\n\n## Development\n\n```sh\nnpm test                                                  # 75 unit + corpus fixtures\nUPDATE_SNAPSHOTS=1 npm test                               # regenerate expected.edge files\nEDGE_SMOKE_DIRS=/path/to/views npm test                   # parse-only check against real templates\nEDGE_DEBUG_PARSE=1 npm test                               # log parse errors instead of swallowing\n```\n\nFixtures live under [`test/fixtures/`](test/fixtures):\n\n- `units/<category>/<name>/{input,expected}.edge` — focused cases. Optional `options.json` overrides Prettier options.\n- `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.\n\nTo 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.\n\n## License\n\n[MIT](LICENSE)\n\n[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/edge-js/prettier-plugin-edge/checks.yml?style=for-the-badge\n[gh-workflow-url]: https://github.com/edge-js/prettier-plugin-edge/actions/workflows/checks.yml \"Github action\"\n\n[npm-image]: https://img.shields.io/npm/v/prettier-plugin-edge.svg?style=for-the-badge&logo=npm\n[npm-url]: https://npmjs.org/package/prettier-plugin-edge \"npm\"\n\n[npm-downloads-image]: https://img.shields.io/npm/dm/prettier-plugin-edge?style=for-the-badge&color=cb3837\n[npm-downloads-url]: https://npmjs.org/package/prettier-plugin-edge \"npm downloads\"\n\n[license-image]: https://img.shields.io/npm/l/prettier-plugin-edge?color=blueviolet&style=for-the-badge\n[license-url]: LICENSE \"license\"\n\n[node-image]: https://img.shields.io/node/v/prettier-plugin-edge?style=for-the-badge&color=43853d\n[node-url]: https://nodejs.org \"node\"\n\n[prettier-image]: https://img.shields.io/npm/dependency-version/prettier-plugin-edge/peer/prettier?style=for-the-badge&color=f7b93e&label=prettier&logo=prettier\n[prettier-url]: https://prettier.io \"prettier\"\n","readmeFilename":"README.md","_rev":"1-edb6b106916b9455769e5b84d5389ae2"}