-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcss_inline.ex
More file actions
116 lines (95 loc) · 4.27 KB
/
css_inline.ex
File metadata and controls
116 lines (95 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
defmodule CSSInline do
@moduledoc """
High-performance CSS inlining for HTML documents.
This library uses a Rust NIF powered by the [css-inline](https://crates.io/crates/css-inline)
crate to inline CSS from `<style>` tags into element `style` attributes. This is particularly
useful for preparing HTML emails, which require inline styles for maximum compatibility
across email clients.
## Usage
iex> html = \"\"\"
...> <html>
...> <head>
...> <style>p { color: red; }</style>
...> </head>
...> <body><p>Hello</p></body>
...> </html>
...> \"\"\"
iex> {:ok, result} = CSSInline.inline(html)
iex> result =~ "color"
true
## Options
The following options can be passed to `inline/2` and `inline!/2`:
* `:inline_style_tags` - Whether to inline CSS from `<style>` tags. Defaults to `true`.
* `:keep_style_tags` - Whether to keep `<style>` tags after inlining. Defaults to `false`.
* `:keep_link_tags` - Whether to keep `<link>` tags after processing. Defaults to `false`.
* `:load_remote_stylesheets` - Whether to load remote stylesheets referenced in `<link>` tags.
Defaults to `true`. Set to `false` to skip external stylesheets.
* `:minify_css` - Whether to minify the inlined CSS. Defaults to `true`.
## Performance
The Rust NIF runs on a dirty CPU scheduler to avoid blocking the BEAM's main schedulers,
as CSS inlining can take several milliseconds for large documents. The implementation
uses zero-copy input handling and direct buffer writes for optimal performance.
"""
defmodule Options do
@moduledoc """
Options struct for CSS inlining configuration.
"""
defstruct inline_style_tags: true,
keep_style_tags: false,
keep_link_tags: false,
load_remote_stylesheets: true,
minify_css: true
@type t :: %__MODULE__{
inline_style_tags: boolean(),
keep_style_tags: boolean(),
keep_link_tags: boolean(),
load_remote_stylesheets: boolean(),
minify_css: boolean()
}
end
@type option ::
{:inline_style_tags, boolean()}
| {:keep_style_tags, boolean()}
| {:keep_link_tags, boolean()}
| {:load_remote_stylesheets, boolean()}
| {:minify_css, boolean()}
@doc """
Inlines CSS from `<style>` tags into element `style` attributes.
Returns `{:ok, inlined_html}` on success, or `{:error, reason}` on failure.
## Options
* `:inline_style_tags` - Whether to inline CSS from `<style>` tags. Defaults to `true`.
* `:keep_style_tags` - Whether to keep `<style>` tags after inlining. Defaults to `false`.
* `:keep_link_tags` - Whether to keep `<link>` tags after processing. Defaults to `false`.
* `:load_remote_stylesheets` - Whether to load remote stylesheets. Defaults to `true`.
* `:minify_css` - Whether to minify the inlined CSS. Defaults to `true`.
"""
@spec inline(String.t(), [option()]) :: {:ok, String.t()} | {:error, term()}
def inline(html, opts \\ []) when is_binary(html) and is_list(opts) do
options = struct(Options, opts)
case CSSInline.Native.inline_css(html, options) do
result when is_binary(result) or is_list(result) ->
{:ok, IO.iodata_to_binary(result)}
{:error, reason} ->
{:error, reason}
end
rescue
e -> {:error, {e, __STACKTRACE__}}
end
@doc """
Inlines CSS from `<style>` tags into element `style` attributes.
Returns the inlined HTML on success, or raises an exception on failure.
## Options
* `:inline_style_tags` - Whether to inline CSS from `<style>` tags. Defaults to `true`.
* `:keep_style_tags` - Whether to keep `<style>` tags after inlining. Defaults to `false`.
* `:keep_link_tags` - Whether to keep `<link>` tags after processing. Defaults to `false`.
* `:load_remote_stylesheets` - Whether to load remote stylesheets. Defaults to `true`.
* `:minify_css` - Whether to minify the inlined CSS. Defaults to `true`.
"""
@spec inline!(String.t(), [option()]) :: String.t()
def inline!(html, opts \\ []) when is_binary(html) and is_list(opts) do
case inline(html, opts) do
{:ok, result} -> result
{:error, reason} -> raise "CSS inlining failed: #{inspect(reason)}"
end
end
end