{"id":50914192,"url":"https://github.com/marko-js/tree-sitter","last_synced_at":"2026-06-16T13:02:33.813Z","repository":{"id":364652825,"uuid":"1267984744","full_name":"marko-js/tree-sitter","owner":"marko-js","description":"Tree-sitter grammar for the Marko templating language","archived":false,"fork":false,"pushed_at":"2026-06-13T22:06:11.000Z","size":104,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T23:23:29.746Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marko-js.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T03:19:52.000Z","updated_at":"2026-06-13T22:04:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marko-js/tree-sitter","commit_stats":null,"previous_names":["marko-js/tree-sitter"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/marko-js/tree-sitter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marko-js%2Ftree-sitter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marko-js%2Ftree-sitter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marko-js%2Ftree-sitter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marko-js%2Ftree-sitter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marko-js","download_url":"https://codeload.github.com/marko-js/tree-sitter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marko-js%2Ftree-sitter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34406824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"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":[],"created_at":"2026-06-16T13:02:32.953Z","updated_at":"2026-06-16T13:02:33.806Z","avatar_url":"https://github.com/marko-js.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @marko/tree-sitter\n\nA [tree-sitter](https://tree-sitter.github.io/) grammar for the\n[Marko](https://markojs.com/) templating language.\n\nIt covers the full language: both authoring modes (HTML and\nconcise/indentation-based), `${placeholders}`, tag variables, arguments and\nparameters, shorthand `#id`/`.class` and method attributes, attribute\ngroups, statement tags, raw-text tags, `--` content blocks, and `$`\nscriptlets, all with the exact source ranges the Marko compiler sees, so the\ntree is reliable for highlighting, folding, structural editing, and tooling.\n\n## Usage\n\nWith the node bindings:\n\n```js\nconst Parser = require(\"tree-sitter\");\nconst Marko = require(\"@marko/tree-sitter\");\n\nconst parser = new Parser();\nparser.setLanguage(Marko);\n\nconst tree = parser.parse(\"\u003cbutton onClick() { count++ }\u003e${count}\u003c/button\u003e\");\nconsole.log(tree.rootNode.toString());\n```\n\nIn the browser (or anywhere native bindings are unwanted), use the wasm\nbuild with [web-tree-sitter](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web):\n\n```js\nimport { Language, Parser } from \"web-tree-sitter\";\n\nawait Parser.init();\nconst Marko = await Language.load(\"tree-sitter-marko.wasm\");\nconst parser = new Parser();\nparser.setLanguage(Marko);\n```\n\nBuilding from a checkout:\n\n```sh\nnpm install\nnpm run build        # tree-sitter generate \u0026\u0026 node-gyp rebuild\nnpm run build:wasm   # tree-sitter-marko.wasm (the CLI fetches wasi-sdk itself)\n```\n\nFor editors, the grammar ships ready-to-use queries: `queries/highlights.scm`\nfor syntax highlighting captures and `queries/injections.scm` for\nembedded-language injections (described below).\n\n## Editors and tools\n\nEvery integration consumes the same artifacts: the generated parser\n(`src/parser.c` + `src/scanner.c`), the queries in `queries/`, and, for\nembedded highlighting, the `typescript`, `css`, and `scss` grammars installed\nin the host tool like any other language.\n\n### tree-sitter CLI\n\nFrom this directory:\n\n```sh\nnpx tree-sitter playground            # interactive tree in the browser\nnpx tree-sitter parse file.marko      # print the syntax tree\nnpx tree-sitter highlight file.marko  # ANSI highlighting (--html for a page)\n```\n\n`highlight` resolves the injected languages through the CLI config: run\n`tree-sitter init-config`, then make sure one of the `parser-directories`\nin `~/.config/tree-sitter/config.json` holds clones of\n`tree-sitter-typescript` (run `npm install --ignore-scripts` inside it, as\nits queries reference its `tree-sitter-javascript` dependency),\n`tree-sitter-css`, and `tree-sitter-scss`. Note the loader only discovers\ngrammars that have a `tree-sitter.json`.\n\n### Neovim\n\nNeovim (0.10+) needs no plugins. Compile the parser onto the runtime path\nand start treesitter for the filetype:\n\n```sh\n# from this directory\nmkdir -p ~/.local/share/nvim/site/parser ~/.local/share/nvim/site/queries\nnpx tree-sitter build -o ~/.local/share/nvim/site/parser/marko.so\ncp -R queries ~/.local/share/nvim/site/queries/marko\n```\n\n```lua\n-- init.lua\nvim.filetype.add({ extension = { marko = \"marko\" } })\nvim.api.nvim_create_autocmd(\"FileType\", {\n  pattern = \"marko\",\n  callback = function()\n    vim.treesitter.start()\n  end,\n})\n```\n\nEmbedded highlighting uses whatever parsers are installed; with\nnvim-treesitter that's `:TSInstall typescript css scss`.\n\n### Helix\n\nRegister the grammar and language in `~/.config/helix/languages.toml`:\n\n```toml\n[[language]]\nname = \"marko\"\nscope = \"source.marko\"\nfile-types = [\"marko\"]\ncomment-token = \"//\"\nblock-comment-tokens = { start = \"\u003c!--\", end = \"--\u003e\" }\ninjection-regex = \"^marko$\"\n\n[[grammar]]\nname = \"marko\"\nsource = { git = \"https://github.com/marko-js/tree-sitter\", rev = \"\u003ccommit\u003e\" }\n```\n\n(or `source = { path = \"/path/to/tree-sitter\" }` for a local\ncheckout), then:\n\n```sh\nhx --grammar fetch \u0026\u0026 hx --grammar build\n```\n\nand copy the queries to `~/.config/helix/runtime/queries/marko/`.\n\n### Zed\n\nMarko support in Zed is provided by the\n[marko-js/zed](https://github.com/marko-js/zed) extension, which bundles this\ngrammar (via its `[grammars.marko]` entry pointing here), the language config,\nthe editor queries, and the\n[Marko language server](https://github.com/marko-js/language-server). Install\nit from Zed's extension registry, or run `zed: install dev extension` on a\nlocal checkout of that repo. Its `highlights.scm`/`injections.scm` are copies\nof the `queries/` here, used as-is: Zed evaluates the\n`#eq?`/`#any-of?`/`#not-any-of?` predicates, `injection.combined`, and the\ndynamic `@injection.language` dialects. The Zed-specific `brackets.scm` and\n`outline.scm` are maintained in that extension repo.\n\n### VS Code\n\nVS Code does not consume tree-sitter grammars. Marko support there remains\nthe tmLanguage-based\n[Marko VS Code extension](https://github.com/marko-js/language-server),\nwhich this grammar's queries are designed to match visually.\n\n## The tree\n\n```marko\ndiv.panel\n  \u003cbutton onClick() { count++ }\u003e${count}\u003c/button\u003e\n```\n\n```\n(document\n  (element\n    (tag_name (tag_name_fragment))\n    (shorthand_class (tag_name_fragment))\n    (concise_open_tag_end)\n    (element\n      (open_tag_start)\n      (tag_name (tag_name_fragment))\n      (attr_name)\n      (args)\n      (method_body (method_body_expr))\n      (open_tag_end)\n      (placeholder (placeholder_start) (placeholder_expr))\n      (close_tag (close_tag_start) (close_tag_name) (close_tag_end))\n      (element_end))\n    (element_end)))\n```\n\nCommonly queried nodes:\n\n| node                                                                                       | meaning                                                      |\n| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |\n| `element`                                                                                  | a tag in either mode, including its body and close           |\n| `tag_name`, `tag_name_fragment`                                                            | tag names; interpolated names contain `placeholder` children |\n| `shorthand_id`, `shorthand_class`                                                          | `#id` / `.class` shorthands                                  |\n| `attr_name`, `attr_value`, `attr_bound_value`, `attr_spread`                               | attributes (`x=…`, `x:=…`, `...spread`)                      |\n| `args`, `method_body`                                                                      | `(…)` arguments and `{ … }` shorthand-method bodies          |\n| `tag_var` → `var_pattern`, `var_type`                                                      | tag variables: `/pattern` with an optional `: type`          |\n| `params` → `param` → `param_pattern`, `param_type`, `param_default`                        | `\\|a: T = 1, b\\|` tag parameters                             |\n| `type_args`, `type_params` → `type_expr`                                                   | `\u003cT\u003e` type arguments/parameters                              |\n| `placeholder`, `placeholder_expr`                                                          | `${…}` and `$!{…}`                                           |\n| `scriptlet`, `scriptlet_expr`, `scriptlet_block_expr`                                      | `$ statement` / `$ { block }`                                |\n| `statement_expr`                                                                           | the body of statement tags (`import`, `static`, `server`, …) |\n| `text`, `html_comment`, `line_comment`, `block_comment`, `cdata`, `doctype`, `declaration` | content                                                      |\n| `html_block`                                                                               | `--` delimited / single-line content blocks in concise mode  |\n\nA few structural notes:\n\n- `element_end` is a zero-width node marking where an element closes\n  implicitly (concise dedent, void tags, self-closing, end of input).\n  `concise_open_tag_end` is similarly zero-width (or covers a trailing `;`).\n- Delimiters are visible named tokens (`args_open`/`args_close`,\n  `params_open`/`params_close`, `type_open`/`type_close`,\n  `attr_group_open`/`attr_group_close`, `method_body_open`/\n  `method_body_close`, `scriptlet_block_open`/`scriptlet_block_close`,\n  `placeholder_start`/`placeholder_end`), so bracket-matching and\n  punctuation queries can target them.\n- Whitespace is significant in Marko, so the grammar has no `extras`; text\n  nodes contain their exact whitespace.\n- Invalid input produces an `ERROR` node from the first error onward,\n  matching how Marko itself reports a single error per template rather than\n  guessing at recovery.\n\n## Tag types\n\nTag parsing in Marko depends on the tag. This grammar follows the\n[tags API](https://markojs.com/docs/reference/core-tag)'s `parseOptions` (and\nthe vscode tmLanguage grammar it replaces), which sorts tags into three\ngroups.\n\nVoid tags take no children and no closing tag: the html void elements plus\n`const`, `debug`, `id`, `let`, `lifecycle`, `log`, and `return`.\n\nRaw-text tags (`script`, `style`, `html-script`, `html-style`,\n`html-comment`) have bodies of text and placeholders only.\n\nStatement tags (`import`, `export`, `static`, `server`, `client`, plus\n`class` for class-API compatibility) treat the rest of the line, and any\nindented continuation, as a single embedded statement.\n\n## Embedded languages\n\nMarko templates embed TypeScript and CSS throughout, and\n`queries/injections.scm` maps it all. TypeScript is injected into every\nexpression position: placeholders, attribute values/spreads, arguments,\nshorthand-method bodies, scriptlets, statement-tag bodies, and the\npattern/default parts of tag variables and parameters.\n\nStatement tags split into two groups, mirroring how the compiler consumes\nthem. `import`, `export`, and `class` are pass-through TypeScript, where the\nkeyword is part of the statement, so the whole element (keyword included) is\ninjected and the keyword takes its color from the TS grammar. For `static`,\n`server`, and `client` the keyword is Marko syntax that the compiler strips,\nso only the body is injected and `highlights.scm` captures the keyword as\n`@keyword` (the tmLanguage grammar makes the same split). `\u003cscript\u003e`/\n`\u003chtml-script\u003e` bodies inject TypeScript and `\u003cstyle\u003e`/`\u003chtml-style\u003e` bodies\ninject CSS, including their concise `script --` / `style --` block forms,\nwith `injection.combined` merging chunks split by placeholders. `style`\ndialect shorthands are honored the way the compiler resolves them: the last\nshorthand segment names the injected language, so `\u003cstyle.scss\u003e` and\n`style.module.scss --` inject `scss` while plain `\u003cstyle\u003e` falls back to\n`css`. `html-style` is exempt, since its shorthand is a real class attribute,\nnot a dialect.\n\nType annotations (tag var/param types, `\u003cT\u003e` type args/params) are captured\nas `@type` instead of injected: a bare type is not a valid TypeScript\nprogram, so flat coloring is the accurate option (the tmLanguage grammar\nmakes the same approximation). One residual case: the parameters of a\nshorthand method (`onClick(event) { … }`) inject as a whole TS program,\nwhich renders typed parameters slightly off, since that position cannot be\nsplit without lookahead the scanner doesn't have.\n\n## Fidelity and development\n\nMarko's parser is [htmljs-parser](https://github.com/marko-js/htmljs-parser);\nthis grammar's external scanner reimplements its state machine, and the test\nsuite (`npm test`, Node 22) asserts the tree reproduces the parser's event\nstream with byte-precise ranges across the parser's full fixture suite, plus\nthousands of templates from the Marko ecosystem during development.\n\nThe reference parser and its fixtures are always the same htmljs-parser\nrevision: they are fetched together into `.cache/` with degit, so the suite\ncompares the grammar against the exact parser the fixtures came from (set\n`HTMLJS_FIXTURES` to point at a local checkout's fixtures instead, and the\nadjacent sources are used as the parser; if this directory is dropped inside\nan htmljs-parser checkout, the local parser sources are used automatically).\nThe published `htmljs-parser` package is only a last-resort fallback. The\ngrammar tracks the parser, so the suite is only expected to be green against\nthe htmljs-parser revision the grammar was synced with.\n\nSee `__tests__/` and `tools/` to work on the grammar itself; the scanner\ninternals are documented at the top of `src/scanner.c`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarko-js%2Ftree-sitter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarko-js%2Ftree-sitter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarko-js%2Ftree-sitter/lists"}