{"id":16746809,"url":"https://github.com/marcelotto/panpipe","last_synced_at":"2025-04-06T08:15:41.269Z","repository":{"id":53550832,"uuid":"203087037","full_name":"marcelotto/panpipe","owner":"marcelotto","description":"An Elixir wrapper around Pandoc","archived":false,"fork":false,"pushed_at":"2025-01-30T20:44:05.000Z","size":122,"stargazers_count":40,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-30T07:12:02.500Z","etag":null,"topics":["ast","markdown","pandoc"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/panpipe","language":"Elixir","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/marcelotto.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","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}},"created_at":"2019-08-19T02:50:46.000Z","updated_at":"2025-01-30T20:42:33.000Z","dependencies_parsed_at":"2025-02-24T08:10:40.689Z","dependency_job_id":"cf1b9776-3db2-41a9-b965-5d615cfed0c9","html_url":"https://github.com/marcelotto/panpipe","commit_stats":{"total_commits":71,"total_committers":2,"mean_commits":35.5,"dds":"0.014084507042253502","last_synced_commit":"217734c8ff6a8e8b53e8806bc872128ff03bd552"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcelotto%2Fpanpipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcelotto%2Fpanpipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcelotto%2Fpanpipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcelotto%2Fpanpipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcelotto","download_url":"https://codeload.github.com/marcelotto/panpipe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247451667,"owners_count":20940944,"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","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":["ast","markdown","pandoc"],"created_at":"2024-10-13T02:08:23.917Z","updated_at":"2025-04-06T08:15:41.244Z","avatar_url":"https://github.com/marcelotto.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Panpipe\n\n[![Hex.pm](https://img.shields.io/hexpm/v/panpipe.svg?style=flat-square)](https://hex.pm/packages/panpipe)\n[![License](https://img.shields.io/hexpm/l/panpipe.svg)](https://github.com/marcelotto/panpipe/blob/master/LICENSE.md)\n\n[![ExUnit Tests](https://github.com/marcelotto/panpipe/actions/workflows/elixir-build-and-test.yml/badge.svg)](https://github.com/marcelotto/panpipe/actions/workflows/elixir-build-and-test.yml)\n[![Quality Checks](https://github.com/marcelotto/panpipe/actions/workflows/elixir-quality-checks.yml/badge.svg)](https://github.com/marcelotto/panpipe/actions/workflows/elixir-quality-checks.yml)\n\n\nAn Elixir wrapper around [Pandoc].\n\n\n## Features\n\n- convenient ways to call the `pandoc` functions from Elixir\n- Elixir structs for the [Pandoc AST] - a read and writeable Markdown AST\n- ways to traverse and transform the AST via Elixir pattern matching (and pipes maybe)\n- ... everything you need to write [Pandoc filters] with Elixir\n\n\n## Installation\n\nYou'll need to have [Pandoc installed](https://pandoc.org/installing.html). \n\nThe Hex package can then be installed by adding `panpipe` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:panpipe, \"~\u003e 0.3\"}\n  ]\nend\n```\n\n\n## Usage\n\n### Calling Pandoc\n\nPandoc can be called with the `Panpipe.pandoc/2` function. Generally it takes the long-form arguments of Pandoc as the usual Keyword list options with dashes replaced by underscores, but there are a couple of differences. First, the input is either provided directly as a string as the first argument or when the input is another file via the `input` option.\n\n```elixir\niex\u003e Panpipe.pandoc(\"# Example doc\", to: :latex)\n{:ok, \"\\\\hypertarget{example-doc}{%\\n\\\\section{Example doc}\\\\label{example-doc}}\\n\"}\niex\u003e Panpipe.pandoc(input: \"file.md\", output: \"output.tex\")\n{:ok, nil}\niex\u003e Panpipe.pandoc(input: \"file.md\", output: \"output.pdf\", pdf_engine: :xelatex, variable: \"linkcolor=blue\")\n```\n\nAs you can see the `Panpipe.pandoc/2` returns an ok tuple in the success with the result as string if no output file is specified, or `nil` if the output was written to a file. If want directly get the result and fail in error cases, you can use the `Panpipe.pandoc!/2` function.\n\nExtensions for the input and output format can be specified by providing a tuple with the format and either a list of extensions to be enabled or a map with the keys `enable` and `disable`.\n\n``` elixir\nPanpipe.pandoc(\"# Example doc\", \n  from: {:markdown, [:emoji]}, \n  to: {:html, %{enable: [:emoji], disable: [:raw_html, :raw_attribute]}}\n)\n```\n\nAnother difference is that flag arguments of Pandoc must be provided as an option with the value `true`.\n\n```elixir\nPanpipe.pandoc(\"# Example doc\", to: :html, standalone: true)\n```\n\nYou can also call Pandoc with a specific output format with the `Panpipe.to_\u003cformat\u003e/2` functions, which are available for every output format supported by Pandoc. Other than setting the `to` option they are just a `Panpipe.pandoc/2` call taking the same arguments.\n\n```elixir\nPanpipe.to_latex(\"# Example doc\")\n\n\"# Example doc\"\n|\u003e Panpipe.to_html(standalone: true)\n```\n\n\n\n### The Panpipe AST\n\nYou can get an AST representation of some input with the `Panpipe.ast/2` or `Panpipe.ast!/2` functions. The input and Pandoc options can be given in the same way as for the `Panpipe.pandoc/2` function described above. \n\n```elixir\niex\u003e Panpipe.ast(\"# Example doc\")\n{:ok,\n %Panpipe.Document{\n   children: [\n     %Panpipe.AST.Header{\n       attr: %Panpipe.AST.Attr{\n         classes: [],\n         identifier: \"example-doc\",\n         key_value_pairs: %{}\n       },\n       children: [\n         %Panpipe.AST.Str{parent: nil, string: \"Example\"},\n         %Panpipe.AST.Space{parent: nil},\n         %Panpipe.AST.Str{parent: nil, string: \"doc\"}\n       ],\n       level: 1,\n       parent: nil\n     }\n   ],\n   meta: nil,\n   parent: nil\n }}\n```\n\nThe AST structure is an exact representation of the Pandoc AST returned during the conversion to JSON, but in nice Elixir structs for the nodes. \n\nIt can be traversed by using Elixirs `Enumerable` protocol which is implemented by all AST nodes and will yield the nodes in pre-order.\n\n```elixir\nPanpipe.ast!(input: \"file.md\")\n|\u003e Enum.filter(fn node -\u003e match?(%Panpipe.AST.Link{}, node) end)\n|\u003e Enum.map(fn %Panpipe.AST.Link{target: target} -\u003e target end)\n```\n\nThe AST can be transformed with the `Panpipe.transform/2` function. The transformation will be called with all nodes and replace the result with result value unless it is `nil`. Here's an example showing how to increase the level of all headers:\n\n```elixir\nPanpipe.ast!(input: \"file.md\")\n|\u003e Panpipe.transform(fn \n     %Panpipe.AST.Header{} = header -\u003e\n       %Panpipe.AST.Header{header | level: header.level + 1}\n     _ -\u003e nil\n   end)\n```\n\nIt's also possible to replace a single node with a sequence of new nodes by returning a list of nodes in the transformation function.\n\nYou may have noted the `parent` member in the AST example above. This is something not present in the original Pandoc AST representation. All of the nodes in the AST returned by `Panpipe.ast/2` have `nil` as the `parent` value. But the nodes emitted during traversal with the `Enumerable` protocol and on the `Panpipe.transform/2` function will have set this field to the respective parent node (but not recursively up to the root), which gives an additional criterion to pattern match on.\n\n\n## Contributing\n\nsee [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n\n\n## License and Copyright\n\n(c) 2019-present Marcel Otto. MIT Licensed, see [LICENSE](LICENSE.md) for details.\n\n\n[Pandoc]:           https://pandoc.org/\n[Pandoc filters]:   https://pandoc.org/filters.html\n[Pandoc AST]:       http://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcelotto%2Fpanpipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcelotto%2Fpanpipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcelotto%2Fpanpipe/lists"}