Packages
earmark
1.4.15
1.5.0-pre1
retired
1.5.0-pre
retired
1.4.49
retired
1.4.48
retired
1.4.47
retired
1.4.46
retired
1.4.45
retired
1.4.44
retired
1.4.43
retired
1.4.42
retired
1.4.41
retired
1.4.40
retired
1.4.39
retired
1.4.38
retired
1.4.37
retired
1.4.36
retired
1.4.35
retired
1.4.34
retired
1.4.33
retired
1.4.32
retired
1.4.31
retired
1.4.30
retired
1.4.29
retired
1.4.28
retired
1.4.27
retired
1.4.26
retired
1.4.25
retired
1.4.24
retired
1.4.23
retired
1.4.22
retired
1.4.21
retired
1.4.20
retired
1.4.19
retired
1.4.18
retired
1.4.17
retired
1.4.16
retired
1.4.16-pre2
retired
1.4.16-pre1
retired
1.4.16-pre
retired
1.4.15
retired
1.4.14
retired
1.4.13
retired
1.4.12
retired
1.4.10
retired
1.4.9
retired
1.4.8
retired
1.4.7
retired
1.4.6
retired
1.4.5
retired
1.4.4
retired
1.4.3
retired
1.4.2
retired
1.4.1
retired
1.4.0
retired
1.3.6
retired
1.3.5
retired
1.3.4
retired
1.3.3
retired
1.3.2
retired
1.3.1
retired
1.3.0
retired
1.2.6
retired
1.2.5
retired
1.2.4
retired
1.2.3
retired
1.2.2
retired
1.2.1
retired
1.2.0
retired
1.1.1
retired
1.1.0
retired
1.0.3
retired
1.0.2
retired
1.0.1
retired
1.0.0
retired
0.2.1
retired
0.2.0
retired
0.1.19
retired
0.1.18
retired
0.1.17
retired
0.1.16
retired
0.1.15
retired
0.1.14
retired
0.1.13
retired
0.1.12
retired
0.1.10
retired
0.1.9
retired
0.1.8
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as a command-line tool (run mix escript.build first). Output generation is pluggable.
Retired package: Deprecated - Earmark is no longer maintained. Migrate to a replacement, for example MDEx (https://hex.pm/packages/mdex).
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/earmark.ex
defmodule Earmark do
@type ast_meta :: map()
@type ast_tag :: binary()
@type ast_attribute_name :: binary()
@type ast_attribute_value :: binary()
@type ast_attribute :: {ast_attribute_name(), ast_attribute_value()}
@type ast_attributes :: list(ast_attribute())
@type ast_tuple :: {ast_tag(), ast_attributes(), ast(), ast_meta()}
@type ast_node :: binary() | ast_tuple()
@type ast :: list(ast_node())
@moduledoc """
## Earmark
### Abstract Syntax Tree and Rendering
The AST generation has now been moved out to [`EarmarkParser`](https://github.com/robertdober/earmark_parser)
which is installed as a dependency.
This brings some changes to this documentation and also deprecates the usage of `Earmark.as_ast`
Earmark takes care of rendering the AST to HTML, exposing some AST Transformation Tools and providing a CLI as escript.
Therefore you will not find a detailed description of the supported Markdown here anymore as this is done in
[here](https://hexdocs.pm/earmark_parser/EarmarkParser.html)
#### Earmark.as_ast
WARNING: This is just a proxy towards `EarmarkParser.as_ast` and is deprecated, it will be removed in version 1.5!
Replace your calls to `Earmark.as_ast` with `EarmarkParse.as_ast` as soon as possible.
**N.B.** If all you use is `Earmark.as_ast` consider _only_ using `EarmarkParser`.
Also please refer yourself to the documentation of [`EarmarkParser`](https://hexdocs.pm/earmark_parser/EarmarkParser.html)
The function is described below and the other two API functions `as_html` and `as_html!` are now based upon
the structure of the result of `as_ast`.
{:ok, ast, []} = EarmarkParser.as_ast(markdown)
{:ok, ast, deprecation_messages} = EarmarkParser.as_ast(markdown)
{:error, ast, error_messages} = EarmarkParser.as_ast(markdown)
#### Earmark.as_html
{:ok, html_doc, []} = Earmark.as_html(markdown)
{:ok, html_doc, deprecation_messages} = Earmark.as_html(markdown)
{:error, html_doc, error_messages} = Earmark.as_html(markdown)
#### Earmark.as_html!
html_doc = Earmark.as_html!(markdown, options)
Formats the error_messages returned by `as_html` and adds the filename to each.
Then prints them to stderr and just returns the html_doc
#### Options
Options can be passed into as `as_html/2` or `as_html!/2` according to the documentation.
A keyword list with legal options (c.f. `Earmark.Options`) or an `Earmark.Options` struct are accepted.
{status, html_doc, errors} = Earmark.as_html(markdown, options)
html_doc = Earmark.as_html!(markdown, options)
{status, ast, errors} = EarmarkParser.as_ast(markdown, options)
### Rendering
All options passed through to `EarmarkParser.as_ast` are defined therein, however some options concern only
the rendering of the returned AST
These are:
* `compact_output:` defaults to `false`
Normally `Earmark` aims to produce _Human Readable_ output.
This will give results like these:
iex(0)> markdown = "# Hello\\nWorld"
...(0)> Earmark.as_html!(markdown, compact_output: false)
"<h1>\\nHello</h1>\\n<p>\\nWorld</p>\\n"
But sometimes whitespace is not desired:
iex(1)> markdown = "# Hello\\nWorld"
...(1)> Earmark.as_html!(markdown, compact_output: true)
"<h1>Hello</h1><p>World</p>"
Be cautions though when using this options, lines will become loooooong.
#### `escape:` defaulting to `true`
If set HTML will be properly escaped
iex(2)> markdown = "Hello<br />World"
...(2)> Earmark.as_html!(markdown)
"<p>\\nHello<br />World</p>\\n"
However disabling `escape:` gives you maximum control of the created document, which in some
cases (e.g. inside tables) might even be necessary
iex(3)> markdown = "Hello<br />World"
...(3)> Earmark.as_html!(markdown, escape: false)
"<p>\\nHello<br />World</p>\\n"
* `postprocessor:` defaults to nil
Before rendering the AST is transformed by a postprocessor.
For details see the description of `Earmark.Transform.map_ast·` below which will accept the same postprocessor as
a matter of fact specifying `postprocessor: fun` is conecptionnaly the same as
markdown
|> EarmarkParser.as_ast
|> Earmark.Transform.map_ast(fun)
|> Earmark.Transform.transform
with all the necessary bookkeeping for options and messages
* `renderer:` defaults to `Earmark.HtmlRenderer`
The module used to render the final document.
#### `smartypants:` defaulting to `true`
If set the following replacements will be made during rendering of inline text
"---" → "—"
"--" → "–"
"' → "’"
?" → "”"
"..." → "…"
### Command line
$ mix escript.build
$ ./earmark file.md
Some options defined in the `Earmark.Options` struct can be specified as command line switches.
Use
$ ./earmark --help
to find out more, but here is a short example
$ ./earmark --smartypants false --code-class-prefix "a- b-" file.md
will call
Earmark.as_html!( ..., %Earmark.Options{smartypants: false, code_class_prefix: "a- b-"})
## Timeouts
By default, that is if the `timeout` option is not set Earmark uses parallel mapping as implemented in `Earmark.pmap/2`,
which uses `Task.await` with its default timeout of 5000ms.
In rare cases that might not be enough.
By indicating a longer `timeout` option in milliseconds Earmark will use parallel mapping as implemented in `Earmark.pmap/3`,
which will pass `timeout` to `Task.await`.
In both cases one can override the mapper function with either the `mapper` option (used if and only if `timeout` is nil) or the
`mapper_with_timeout` function (used otherwise).
For the escript only the `timeout` command line argument can be used.
## Security
Please be aware that Markdown is not a secure format. It produces
HTML from Markdown and HTML. It is your job to sanitize and or
filter the output of `Earmark.as_html` if you cannot trust the input
and are to serve the produced HTML on the Web.
"""
alias Earmark.Error
alias Earmark.Options
import Earmark.Message, only: [emit_messages: 2]
@doc false
def as_html(lines, options \\ %Options{})
def as_html(lines, options) when is_list(options) do
case Options.make_options(options) do
{:ok, options1} -> as_html(lines, options1)
{:error, messages} -> {:error, "", messages}
end
end
def as_html(lines, options) do
{status, ast, messages} = postprocessed_ast(lines, options)
{status, Earmark.Transform.transform(ast, options), messages}
end
@doc """
DEPRECATED call `EarmarkParser.as_ast` instead
"""
def as_ast(lines, options \\ %Options{}) do
{status, ast, messages} = _as_ast(lines, options)
message =
{:warning, 0,
"DEPRECATION: Earmark.as_ast will be removed in version 1.5, please use EarmarkParser.as_ast, which is of the same type"}
messages1 = [message | messages]
{status, ast, messages1}
end
@line_end ~r{\n\r?}
def postprocessed_ast(lines, options\\%{})
def postprocessed_ast(lines, options) when is_binary(lines), do: lines |> String.split(@line_end) |> postprocessed_ast(options)
def postprocessed_ast(lines, %{postprocessor: nil}=options), do: EarmarkParser.as_ast(lines, options)
def postprocessed_ast(lines, %{postprocessor: prep}=options) do
{status, ast, messages} = EarmarkParser.as_ast(lines, options)
ast1 = Earmark.Transform.map_ast(ast, prep, Map.get(options, :ignore_strings))
{status, ast1, messages}
end
@doc """
A convenience method that *always* returns an HTML representation of the markdown document passed in.
In case of the presence of any error messages they are printed to stderr.
Otherwise it behaves exactly as `as_html`.
"""
def as_html!(lines, options \\ %Options{})
def as_html!(lines, options) do
{_status, html, messages} = as_html(lines, options)
emit_messages(messages, options)
html
end
@doc """
Accesses current hex version of the `Earmark` application. Convenience for
`iex` usage.
"""
def version() do
with {:ok, version} = :application.get_key(:earmark, :vsn),
do: to_string(version)
end
@default_timeout_in_ms 5000
@doc false
def pmap(collection, func, timeout \\ @default_timeout_in_ms) do
collection
|> Enum.map(fn item -> Task.async(fn -> func.(item) end) end)
|> Task.yield_many(timeout)
|> Enum.map(&_join_pmap_results_or_raise(&1, timeout))
end
defp _as_ast(lines, options)
defp _as_ast(lines, %Options{} = options) do
EarmarkParser.as_ast(lines, options |> Map.delete(:__struct__) |> Enum.into([]))
end
defp _as_ast(lines, options) do
EarmarkParser.as_ast(lines, options)
end
defp _join_pmap_results_or_raise(yield_tuples, timeout)
defp _join_pmap_results_or_raise({_task, {:ok, result}}, _timeout), do: result
defp _join_pmap_results_or_raise({task, {:error, reason}}, _timeout),
do: raise(Error, "#{inspect(task)} has died with reason #{inspect(reason)}")
defp _join_pmap_results_or_raise({task, nil}, timeout),
do:
raise(
Error,
"#{inspect(task)} has not responded within the set timeout of #{timeout}ms, consider increasing it"
)
end
# SPDX-License-Identifier: Apache-2.0