Skip to content

Commit 3a5a30a

Browse files
CLI support for piping (parcel-bundler#380)
1 parent 50c066b commit 3a5a30a

File tree

5 files changed

+39
-58
lines changed

5 files changed

+39
-58
lines changed

Cargo.lock

Lines changed: 3 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ crate-type = ["rlib"]
3030
default = ["bundler", "grid", "nodejs", "sourcemap"]
3131
browserslist = ["browserslist-rs"]
3232
bundler = ["dashmap", "sourcemap", "rayon"]
33-
cli = ["clap", "serde_json", "browserslist", "jemallocator"]
33+
cli = ["atty", "clap", "serde_json", "browserslist", "jemallocator"]
3434
grid = []
3535
jsonschema = ["schemars", "serde", "parcel_selectors/jsonschema"]
3636
nodejs = ["dep:serde"]
@@ -51,6 +51,7 @@ lazy_static = "1.4.0"
5151
const-str = "0.3.1"
5252
pathdiff = "0.2.1"
5353
# CLI deps
54+
atty = { version = "0.2", optional = true }
5455
clap = { version = "3.0.6", features = ["derive"], optional = true }
5556
browserslist-rs = { version = "0.7.0", optional = true }
5657
rayon = { version = "1.5.1", optional = true }

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
2-
channel = "1.63.0"
2+
channel = "1.65.0"
33
components = ["rustfmt", "clippy"]

src/main.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use atty::Stream;
12
use clap::{ArgGroup, Parser};
23
use lightningcss::bundler::{Bundler, FileProvider};
34
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet};
@@ -18,9 +19,9 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
1819
.args(&["targets", "browserslist"]),
1920
))]
2021
struct CliArgs {
21-
/// Target CSS file
22+
/// Target CSS file (default: stdin)
2223
#[clap(value_parser)]
23-
input_file: String,
24+
input_file: Option<String>,
2425
/// Destination file for the output
2526
#[clap(short, long, group = "output_file", value_parser)]
2627
output_file: Option<String>,
@@ -67,13 +68,36 @@ struct SourceMapJson<'a> {
6768

6869
pub fn main() -> Result<(), std::io::Error> {
6970
let cli_args = CliArgs::parse();
70-
let source = fs::read_to_string(&cli_args.input_file)?;
71-
7271
let project_root = std::env::current_dir()?;
73-
let absolute_path = fs::canonicalize(&cli_args.input_file)?;
74-
let filename = pathdiff::diff_paths(absolute_path, &project_root).unwrap();
75-
let filename = filename.to_str().unwrap();
7672

73+
// If we're given an input file, read from it and adjust its name.
74+
//
75+
// If we're not given an input file and stdin was redirected, read
76+
// from it and create a fake name. Return an error if stdin was not
77+
// redirected (otherwise the program will hang waiting for input).
78+
//
79+
let (filename, source) = match &cli_args.input_file {
80+
Some(f) => {
81+
let absolute_path = fs::canonicalize(f)?;
82+
let filename = pathdiff::diff_paths(absolute_path, &project_root).unwrap();
83+
let filename = filename.to_string_lossy().into_owned();
84+
let contents = fs::read_to_string(f)?;
85+
(filename, contents)
86+
}
87+
None => {
88+
// Don't silently wait for input if stdin was not redirected.
89+
if atty::is(Stream::Stdin) {
90+
return Err(io::Error::new(
91+
io::ErrorKind::Other,
92+
"Not reading from stdin as it was not redirected",
93+
));
94+
}
95+
let filename = format!("stdin-{}", std::process::id());
96+
let contents = io::read_to_string(io::stdin())?;
97+
(filename, contents)
98+
}
99+
};
100+
77101
let css_modules = if let Some(_) = cli_args.css_modules {
78102
let pattern = if let Some(pattern) = cli_args.css_modules_pattern.as_ref() {
79103
match lightningcss::css_modules::Pattern::parse(pattern) {
@@ -121,13 +145,13 @@ pub fn main() -> Result<(), std::io::Error> {
121145

122146
let mut stylesheet = if cli_args.bundle {
123147
let mut bundler = Bundler::new(&fs, source_map.as_mut(), options);
124-
bundler.bundle(Path::new(&cli_args.input_file)).unwrap()
148+
bundler.bundle(Path::new(&filename)).unwrap()
125149
} else {
126150
if let Some(sm) = &mut source_map {
127151
sm.add_source(&filename);
128152
let _ = sm.set_source_content(0, &source);
129153
}
130-
options.filename = filename.to_owned();
154+
options.filename = filename;
131155
StyleSheet::parse(&source, options).unwrap()
132156
};
133157

tests/cli_integration_tests.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,6 @@ fn valid_input_file() -> Result<(), Box<dyn std::error::Error>> {
154154
Ok(())
155155
}
156156

157-
#[test]
158-
fn no_input_file() -> Result<(), Box<dyn std::error::Error>> {
159-
let mut cmd = Command::cargo_bin("lightningcss")?;
160-
cmd.assert().failure().stderr(predicate::str::contains(
161-
"The following required arguments were not provided:\n <INPUT_FILE>",
162-
));
163-
164-
Ok(())
165-
}
166-
167157
#[test]
168158
fn empty_input_file() -> Result<(), Box<dyn std::error::Error>> {
169159
let file = assert_fs::NamedTempFile::new("test.css")?;

0 commit comments

Comments
 (0)