Skip to content

Commit db2f9b4

Browse files
committed
feat(cli): --extra-css-file option to load additional CSS from files
Signed-off-by: Dmitry Dygalo <[email protected]>
1 parent ef06d49 commit db2f9b4

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- **CLI**: `--extra-css-file` option to load additional CSS from files. [#67](https://github.com/Stranger6667/css-inline/pull/67)
8+
59
### Changed
610

711
- **CLI**: Remove `pico-args`

css-inline/src/main.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
5555
keep_link_tags: bool,
5656
base_url: Option<String>,
5757
extra_css: Option<String>,
58+
extra_css_files: Vec<String>,
5859
output_filename_prefix: Option<OsString>,
5960
load_remote_stylesheets: bool,
6061
#[cfg(feature = "stylesheet-cache")]
@@ -72,6 +73,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
7273
keep_link_tags: false,
7374
base_url: None,
7475
extra_css: None,
76+
extra_css_files: Vec::new(),
7577
output_filename_prefix: None,
7678
load_remote_stylesheets: false,
7779
#[cfg(feature = "stylesheet-cache")]
@@ -101,6 +103,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
101103
"inline-style-tags"
102104
| "base-url"
103105
| "extra-css"
106+
| "extra-css-file"
104107
| "output-filename-prefix"
105108
| if_cfg_feature_stylesheet_cache!("cache-size")
106109
)
@@ -125,6 +128,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
125128
"inline-style-tags" => parsed.inline_style_tags = parse_value(value, flag)?,
126129
"base-url" => parsed.base_url = Some(value.to_string()),
127130
"extra-css" => parsed.extra_css = Some(value.to_string()),
131+
"extra-css-file" => parsed.extra_css_files.push(value.to_string()),
128132
"output-filename-prefix" => {
129133
parsed.output_filename_prefix = Some(value.to_string().into());
130134
}
@@ -155,6 +159,32 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
155159
Ok(())
156160
}
157161

162+
fn combine_extra_css(
163+
extra_css: Option<String>,
164+
extra_css_files: Vec<String>,
165+
) -> Result<Option<String>, Box<dyn std::error::Error>> {
166+
let mut buffer = extra_css.unwrap_or_default();
167+
168+
if !buffer.is_empty() {
169+
buffer.push('\n');
170+
}
171+
172+
for path in extra_css_files {
173+
let mut file =
174+
File::open(&path).map_err(|e| format!("Failed to read CSS file '{path}': {e}"))?;
175+
file.read_to_string(&mut buffer)?;
176+
if !buffer.is_empty() {
177+
buffer.push('\n');
178+
}
179+
}
180+
181+
Ok(if buffer.is_empty() {
182+
None
183+
} else {
184+
Some(buffer)
185+
})
186+
}
187+
158188
fn format_error(filename: Option<&str>, error: impl fmt::Display) {
159189
let mut buffer = String::with_capacity(128);
160190
if let Some(filename) = filename {
@@ -212,6 +242,10 @@ OPTIONS:
212242
--extra-css
213243
Additional CSS to inline.
214244
245+
--extra-css-file <PATH>
246+
Load additional CSS from a file to inline. Can be used multiple times to load
247+
from several files. The CSS will be processed alongside any existing styles.
248+
215249
--output-filename-prefix
216250
Custom prefix for output files. Defaults to `inlined.`.
217251
"#
@@ -280,6 +314,13 @@ OPTIONS:
280314
} else {
281315
None
282316
};
317+
let extra_css = match combine_extra_css(args.extra_css, args.extra_css_files) {
318+
Ok(css) => css,
319+
Err(error) => {
320+
format_error(None, error);
321+
std::process::exit(1);
322+
}
323+
};
283324
let options = InlineOptions {
284325
inline_style_tags: args.inline_style_tags,
285326
keep_style_tags: args.keep_style_tags,
@@ -288,7 +329,7 @@ OPTIONS:
288329
load_remote_stylesheets: args.load_remote_stylesheets,
289330
#[cfg(feature = "stylesheet-cache")]
290331
cache,
291-
extra_css: args.extra_css.as_deref().map(Cow::Borrowed),
332+
extra_css: extra_css.as_deref().map(Cow::Borrowed),
292333
preallocate_node_capacity: 32,
293334
resolver: Arc::new(DefaultStylesheetResolver),
294335
};

css-inline/tests/extra.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.test-class {
2+
background: red;
3+
}

css-inline/tests/test_cli.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,55 @@ pub mod tests {
8989
.stderr("Status: ERROR\nDetails: empty host\n");
9090
}
9191

92+
#[test]
93+
fn extra_css_file() {
94+
css_inline()
95+
.arg("tests/example.html")
96+
.arg("--extra-css-file=tests/extra.css")
97+
.arg("--output-filename-prefix=inlined.extra-css.")
98+
.assert()
99+
.success()
100+
.stdout("tests/example.html: SUCCESS\n");
101+
102+
let content = fs::read_to_string("tests/inlined.extra-css.example.html").unwrap();
103+
assert!(
104+
content.contains(r#"style="color: #ffffff;background: red;""#),
105+
"inlined output did not include extra-css rules:\n{}",
106+
content
107+
);
108+
}
109+
110+
#[test]
111+
fn extra_css_file_not_found() {
112+
css_inline()
113+
.arg("tests/example.html")
114+
.arg("--extra-css-file=tests/nonexistent.css")
115+
.assert()
116+
.failure()
117+
.stderr(
118+
"Status: ERROR\n\
119+
Details: Failed to read CSS file 'tests/nonexistent.css': No such file or directory (os error 2)\n",
120+
);
121+
}
122+
123+
#[test]
124+
fn extra_css() {
125+
css_inline()
126+
.arg("tests/example.html")
127+
.arg("--extra-css=.test-class { background: green; }")
128+
.arg("--output-filename-prefix=inlined.extra-css-cli.")
129+
.assert()
130+
.success()
131+
.stdout("tests/example.html: SUCCESS\n");
132+
133+
let content = fs::read_to_string("tests/inlined.extra-css-cli.example.html").unwrap();
134+
assert!(
135+
content.contains(r#"style="color: #ffffff;background: green;""#),
136+
"expected inline background from --extra-css but got:\n{}",
137+
content
138+
);
139+
}
140+
92141
#[test]
93142
fn not_found() {
94143
css_inline().arg("unknown.html").assert().failure().stderr(

0 commit comments

Comments
 (0)