Repository navigation
Expose error and warning locations as data, not only in the message #18
Description
Summary
LightningCSS::ParseError and Result#warnings only give a message string, with the location appended as at file:line:column. A tool that wants to point at the source (an asset pipeline building a backtrace, an editor integration, a linter) has to pull the location out of the message with a regex. Lightning CSS's Node binding returns the location and the error type as separate fields.
Current behaviour
lightningcss 1.33.0.1 (crate 1.0.0-alpha.72), Ruby 4.0.5, arm64-darwin
css = ".a { color: red; }\n.b, { color: blue; }\n"
begin
LightningCSS.transform(css, filename: "main.css")
rescue LightningCSS::ParseError => e
e.message #=> "Invalid empty selector at main.css:1:5"
e.methods - StandardError.instance_methods #=> []
end
LightningCSS.transform(css, filename: "main.css", error_recovery: true).warnings
#=> ["Invalid empty selector at main.css:1:5"]The same input through the npm package (1.33.0):
// thrown error
e.name // "SyntaxError"
e.message // "Invalid empty selector"
e.fileName // "main.css"
e.loc // { line: 2, column: 5 }
// with errorRecovery: true
warnings // [{ message: "Invalid empty selector", type: "SelectorError", value: { type: "EmptySelector" }, loc: { filename: "main.css", line: 2, column: 5 } }]Cause
rust/src/lib.rs (on main, aa4ee79) turns errors into a string with Failure::parse(error.to_string()) (e.g. lines 165 and 237), and warnings with warning.to_string() (e.g. line 206). The C extension then raises the error class with only that string. The location and kind are thrown away at the FFI boundary.
Suggested API
Keep the current messages, so rescue => e; e.message and warnings.first keep working, and add the fields:
rescue LightningCSS::ParseError => e
e.message #=> "Invalid empty selector at main.css:2:5"
e.reason #=> "Invalid empty selector" # message without the location
e.filename #=> "main.css"
e.line #=> 2 # 1-based, see #<0-based lines issue>
e.column #=> 5
end
warning = result.warnings.first # LightningCSS::Warning
warning.to_s #=> "Invalid empty selector at main.css:2:5" (as today)
warning.message #=> "Invalid empty selector"
warning.type #=> "SelectorError"
warning.filename #=> "main.css"
warning.line #=> 2
warning.column #=> 5The FFI could return failures as JSON like results already are ({"message", "type", "loc": {...}}), with the C extension or lib/ building the exception from it. BundleError would also benefit from filename, since a bundle error can come from any imported file.
Making warnings an array of Warning objects instead of strings would change its type. Keeping to_s, and possibly == against a String, would keep code that treats warnings as strings working.
This overlaps with #<0-based lines issue>. The two could be fixed together, but they're filed separately because that one changes existing output and this one adds API.
Activity
marcoroth commentedon Oct 14, 2026
Thanks @malomalo, this is now resolved via #22