{"id":23418474,"url":"https://github.com/astynax/hemmet","last_synced_at":"2025-09-07T02:06:31.681Z","repository":{"id":46332976,"uuid":"84166085","full_name":"astynax/hemmet","owner":"astynax","description":"Emmet-like text expansion tool capable to produce HTML, CSS, file trees, that stuff","archived":false,"fork":false,"pushed_at":"2024-09-19T16:12:33.000Z","size":125,"stargazers_count":21,"open_issues_count":9,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T11:41:02.492Z","etag":null,"topics":["bem","cli","emmet","hacktoberfest","hacktoberfest2022","haskell","snippets","templating"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/astynax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-03-07T07:01:29.000Z","updated_at":"2025-03-11T09:57:30.000Z","dependencies_parsed_at":"2025-04-12T11:45:13.204Z","dependency_job_id":null,"html_url":"https://github.com/astynax/hemmet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/astynax/hemmet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astynax%2Fhemmet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astynax%2Fhemmet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astynax%2Fhemmet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astynax%2Fhemmet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astynax","download_url":"https://codeload.github.com/astynax/hemmet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astynax%2Fhemmet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273986629,"owners_count":25202708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bem","cli","emmet","hacktoberfest","hacktoberfest2022","haskell","snippets","templating"],"created_at":"2024-12-23T00:19:52.200Z","updated_at":"2025-09-07T02:06:31.656Z","avatar_url":"https://github.com/astynax.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hemmet\n\n**hemmet** is a CLI-tool, that expands text snippets to markup blocks in\nHaskell/Elm/HTML/CSS/Bash. The template language is similar to [Emmet](http://emmet.io/)/[ZenCoding](http://www.456bereastreet.com/archive/200909/write_html_and_css_quicker_with_with_zen_coding/) (has a subset of their features)\nand has an optional [BEM](https://bem.info/) flavour :) Also hemmet can generate **file trees** (useful for project scaffolding).\n\n```shell\n$ echo \"#root\u003eh1.red+p.article\" | hemmet dom html\n\u003cdiv id=\"root\"\u003e\n  \u003ch1 class=\"red\"\u003e\u003c/h1\u003e\n  \u003cp class=\"article\"\u003e\u003c/p\u003e\n\u003c/div\u003e\n```\n\n# TUI\n\nIn addition to the CLI-tool there is a TUI-app **hemmeti** which one can use to write templates having a live preview.\n\nThat's how the HTML generation looks like:\n\n[![asciicast](https://asciinema.org/a/386500.svg)](https://asciinema.org/a/386500)\n\nAnd this is a file tree scaffolding:\n\n[![asciicast](https://asciinema.org/a/365732.svg)](https://asciinema.org/a/365732)\n\n## Usage\n\n`$ hemmet INPUT OUTPUT -e EXPRESSION`\n\nor\n\n`$ echo \"EXPRESSION\" | hemmet INPUT OUTPUT`\n\nSee `hemmet --help` for full options list.\n\n`hemmeti` uses the same options but runs interactively.\n\n## Inputs (syntaxes)\n\n- `dom` works with [DOM-templates](#dom-templates),\n- `bem` works with [BEM-templates](#bem-templates),\n- `ftree` works with [file tree templates](#file-trees).\n\n# DOM-templates\n\nHemmet expands Emmet-like templates and produces these formats (outputs)\n\n- `html`, just HTML\n\n`echo \"#root\u003eh1.red+p.article\" | hemmet dom html`\n```html\n\u003cdiv id=\"root\"\u003e\n  \u003ch1 class=\"red\"\u003e\u003c/h1\u003e\n  \u003cp class=\"article\"\u003e\u003c/p\u003e\n\u003c/div\u003e\n```\n\n- `css`, styles for all classes in the template\n\n`echo \"#root\u003eh1.red+p.article\" | hemmet dom css`\n```css\n.article {\n}\n\n.red {\n}\n```\n\n- `elm`, an [Elm.Html](https://package.elm-lang.org/packages/elm/html/latest/) markup\n\n`echo \"#root\u003eh1.red+p.article\" | hemmet dom css`\n```elm\ndiv [ id \"root\" ]\n    [ h1 [ class \"red\" ] []\n    , p [ class \"article\" ] []\n    ]\n```\n\n- `lucid`, the [Lucid](https://hackage.haskell.org/package/lucid) HTML eDSL\n\n`echo \"#root\u003eh1.red+p.article\" | hemmet dom css`\n```haskell\ndiv_ [id_ \"root\"] $ do\n  h1_ [class_ \"red\"]\n  p_ [class_ \"article\"]\n```\n\n## Template syntax\n\n### Nesting\n\n`p+ul\u003e(li+li+ul\u003eli+li)+p`\n```html\n\u003cp\u003e\u003c/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\u003c/li\u003e\n  \u003cli\u003e\u003c/li\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003c/li\u003e\n    \u003cli\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003c/p\u003e\n```\n\n###  Tags\n\nTag name prepends the id or classes if any. If no tag was defined the `div` will be used.\n\n### Id\n\nJust `#id`, one at time.\n\n### Classes\n\nJust `.class.another`, simple that.\n\n# BEM-templates\n\nHemmet expands BEM-templates with structure checking and produce outputs:\n\n- `react-flux` — eDSL for [react-flux](https://bitbucket.org/s9gf4ult/react-flux) Haskell library\n\n`$ echo \":foo\u003e.bar\" | hemmet bem react-flux`\n```haskell\ndivc_ \"foo\" $ do\n  divc_ \"foo__bar\" $ pure ()\n```\n\n- `html`\n\n`$ echo \":foo\u003e.bar\" | hemmet bem html`\n```html\n\u003cdiv class=\"foo\"\u003e\n  \u003cdiv class=\"foo__bar\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n```\n\n- `css`\n\n`$ echo \":foo\u003e.bar\" | hemmet bem css`\n```css\n.foo {\n}\n\n.foo__bar {\n}\n```\n\n## Template syntax\n\nTags are the same.\n\n### Nesting\n\n`form:form\u003e.submit:button\u003eimg.icon:icon+.label:label`\n\n```html\n\u003cform class=\"form\"\u003e\n  \u003cdiv class=\"button form__submit\"\u003e\n    \u003cimg class=\"icon button__icon\"\u003e\u003c/img\u003e\n    \u003cdiv class=\"label button__label\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/form\u003e\n```\n\n### Modifiers\n\n`form:login-form\u003ebutton.submit-button:button~small~disabled`\n```html\n\u003cform class=\"login-form\"\u003e\n  \u003cbutton class=\"button button_small button_disabled login-form__submit-button\"\u003e\u003c/button\u003e\n\u003c/form\u003e\n```\n\n### Variables\n\n`:foo$bar~baz`\n```haskell\ndivc_ (\"foo foo_baz\" \u003c\u003e bar) $ pure ()\n```\n\n**Note:** at the moment variables are available only for the `react-flux` output!\n\n### Root node stripping\n\n`\u003c:foo\u003e.bar+.baz` (note leading `\u003c`)\n\n```html\n\u003cdiv class=\"foo__bar\"\u003e\u003c/div\u003e\n\u003cdiv class=\"foo__baz\"\u003e\u003c/div\u003e\n```\n\n# File trees\n\nThe `ftree` templates can be transformed to:\n\n- `tree`, the pseudographical file tree representation.\n\n`$ echo \"docs/{todo.txt to_read.txt}\" | hemmet ftree tree`\n```\n.\n└── docs/\n    ├── to_read.txt\n    └── todo.txt\n```\n\n- `bash` script, that constructs a real tree!\n\n`$ echo \"docs/{todo.txt to_read.txt}\" | hemmet ftree bash`\n```bash\n#!/bin/bash\ncat \u003c\u003cPREVIEW_END\nThis file tree will be created:\n.\n└── docs/\n    ├── to_read.txt\n    └── todo.txt\nPREVIEW_END\nread -p \"Press any key to continue...\" -n1 -s\nset -euf -o pipefail\nmkdir \"docs\" \u0026\u0026 pushd \"docs\"\n  touch \"to_read.txt\"\n  touch \"todo.txt\"\npopd\n```\n\nYou can even make a [shell script](examples/mktree) that will call the TUI and then execute the result of generation automatically.\n\n## Generating Haskell source trees\n\nWith `|hs|` prefix you can scaffold Haskell projects:\n`$ echo \"|hs|app/main src/*lib/{data-types utils} !foo-bar\" | hemmet ftree tree`\n```\n.\n├── App/\n│   └── Main.hs\n├── Src/\n│   ├── Lib/\n│   │   ├── DataTypes.hs\n│   │   └── Utils.hs\n│   └── Lib.hs\n└── foo-bar\n```\n\nNote that\n- files get `.hs` extension,\n- `*` before `lib` means \"also create a `.hs` module for this folder\",\n- `!` before any name means \"don't touch this item\"\n- \"kebab-case\" morphs to \"CamelCase\"\n\n## Generating Python source trees\n\nWith `|py|` prefix you can scaffold Python projects:\n`$ echo \"|py|src/*package/{core str-utils} !foo-bar\" | hemmet ftree tree`\n```\n.\n├── foo-bar\n└── src/\n    └── package/\n        ├── __init__.py\n        ├── core.py\n        └── str_utils.py\n```\n\nNote that\n- files get `.py` extension,\n- `*` before `package` means \"also create an `__init__.py` module\",\n- `!` before any name means \"don't touch this item\"\n- \"kebab-case\" morphs to \"snake_case\"\n\n# Integration with Emacs\n\n1. put a `hemmet` binary somewhere in `$PATH`\n1. add to your `.emacs`\n```elisp\n(defun hemmet-expand-region ()\n  (interactive)\n  (let ((f (lambda (b e)\n             (shell-command-on-region\n              b e \"hemmet dom html\" t t \"*hemmet error*\" t))))\n    (if (region-active-p)\n        (funcall f (region-beginning) (region-end))\n      (funcall f (line-beginning-position) (line-end-position)))\n    ))\n;; bind using a function from \"bind-key\" package\n(bind-key \"C-c C-j\" 'hemmet-expand-region html-mode-map)\n;; or just use built-in function\n(define-key haskell-mode-map (kbd \"C-c C-j\") 'hemmet-expand-region)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastynax%2Fhemmet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastynax%2Fhemmet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastynax%2Fhemmet/lists"}