lightningcss 1.33.0-aarch64-linux-gnu → 1.33.0.2-aarch64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +153 -5
- data/ext/lightningcss/include/lightningcss.h +2 -0
- data/ext/lightningcss/lightningcss.c +9 -2
- data/lib/lightningcss/3.2/lightningcss.so +0 -0
- data/lib/lightningcss/3.3/lightningcss.so +0 -0
- data/lib/lightningcss/3.4/lightningcss.so +0 -0
- data/lib/lightningcss/4.0/lightningcss.so +0 -0
- data/lib/lightningcss/backend.rb +5 -0
- data/lib/lightningcss/errors.rb +22 -1
- data/lib/lightningcss/location.rb +21 -0
- data/lib/lightningcss/node.rb +220 -0
- data/lib/lightningcss/options.rb +5 -0
- data/lib/lightningcss/parse_result.rb +52 -0
- data/lib/lightningcss/result.rb +4 -4
- data/lib/lightningcss/transformer.rb +5 -0
- data/lib/lightningcss/version.rb +1 -1
- data/lib/lightningcss/visitor.rb +37 -0
- data/lib/lightningcss/warning.rb +60 -0
- data/lib/lightningcss.rb +18 -0
- data/rust/Cargo.lock +32 -0
- data/rust/Cargo.toml +1 -1
- data/rust/src/lib.rs +71 -37
- data/rust/src/options.rs +119 -6
- data/rust/src/scope.rs +2 -1
- data/sig/lightningcss/backend.rbs +3 -0
- data/sig/lightningcss/errors.rbs +9 -0
- data/sig/lightningcss/location.rbs +26 -0
- data/sig/lightningcss/node.rbs +104 -0
- data/sig/lightningcss/options.rbs +2 -0
- data/sig/lightningcss/parse_result.rbs +30 -0
- data/sig/lightningcss/result.rbs +3 -3
- data/sig/lightningcss/transformer.rbs +3 -0
- data/sig/lightningcss/types.rbs +12 -10
- data/sig/lightningcss/visitor.rbs +24 -0
- data/sig/lightningcss/warning.rbs +36 -0
- data/sig/lightningcss.rbs +8 -0
- metadata +12 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# Walks an AST, answering each node with the method named after it and walking through anything
|
|
5
|
+
# nothing answers.
|
|
6
|
+
#
|
|
7
|
+
# class Colors < LightningCSS::Visitor
|
|
8
|
+
# def visit_declaration(node)
|
|
9
|
+
# puts node.value.type if node.property == "color"
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Colors.new.visit(LightningCSS.parse(".a { color: red }"))
|
|
14
|
+
#
|
|
15
|
+
# A rule is answered as one, as in `visit_style_rule` or `visit_media_rule`, since `style`,
|
|
16
|
+
# `nesting`, and `namespace` are also what a selector is made of.
|
|
17
|
+
#
|
|
18
|
+
class Visitor
|
|
19
|
+
#: (LightningCSS::Node | LightningCSS::ParseResult) -> void
|
|
20
|
+
def visit(node)
|
|
21
|
+
node = node.root if node.is_a?(ParseResult)
|
|
22
|
+
|
|
23
|
+
method_name = "visit_#{node.underscored_type}" if node.underscored_type
|
|
24
|
+
|
|
25
|
+
method_name && respond_to?(method_name) ? public_send(method_name, node) : visit_children(node)
|
|
26
|
+
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#: (LightningCSS::Node) -> void
|
|
31
|
+
def visit_children(node)
|
|
32
|
+
node.child_nodes.each { |child| visit(child) }
|
|
33
|
+
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
class Warning
|
|
5
|
+
attr_reader :message #: String
|
|
6
|
+
attr_reader :type #: String?
|
|
7
|
+
attr_reader :location #: LightningCSS::Location?
|
|
8
|
+
|
|
9
|
+
#: (Hash[String, untyped]) -> LightningCSS::Warning
|
|
10
|
+
def self.from_hash(hash)
|
|
11
|
+
new(
|
|
12
|
+
message: hash.fetch("message"),
|
|
13
|
+
type: hash["type"],
|
|
14
|
+
location: hash["location"] && Location.from_hash(hash["location"])
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#: (message: String, ?type: String?, ?location: LightningCSS::Location?) -> void
|
|
19
|
+
def initialize(message:, type: nil, location: nil)
|
|
20
|
+
@message = message
|
|
21
|
+
@type = type
|
|
22
|
+
@location = location
|
|
23
|
+
|
|
24
|
+
freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#: () -> Hash[Symbol, untyped]
|
|
28
|
+
def to_h
|
|
29
|
+
{ message: message, type: type, location: location&.to_h }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#: () -> String
|
|
33
|
+
def to_s
|
|
34
|
+
location ? "#{message} at #{location}" : message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias to_str to_s
|
|
38
|
+
|
|
39
|
+
#: (untyped) -> bool
|
|
40
|
+
def ==(other)
|
|
41
|
+
case other
|
|
42
|
+
when Warning then to_h == other.to_h
|
|
43
|
+
when String then to_s == other
|
|
44
|
+
else false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
alias eql? ==
|
|
49
|
+
|
|
50
|
+
#: () -> Integer
|
|
51
|
+
def hash
|
|
52
|
+
to_h.hash
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#: () -> String
|
|
56
|
+
def inspect
|
|
57
|
+
"#<#{self.class.name} #{to_s.inspect}>"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/lightningcss.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
require_relative "lightningcss/version"
|
|
6
|
+
require_relative "lightningcss/location"
|
|
6
7
|
require_relative "lightningcss/errors"
|
|
7
8
|
require_relative "lightningcss/backend"
|
|
8
9
|
|
|
@@ -14,7 +15,11 @@ rescue LoadError
|
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
require_relative "lightningcss/options"
|
|
18
|
+
require_relative "lightningcss/warning"
|
|
17
19
|
require_relative "lightningcss/result"
|
|
20
|
+
require_relative "lightningcss/node"
|
|
21
|
+
require_relative "lightningcss/parse_result"
|
|
22
|
+
require_relative "lightningcss/visitor"
|
|
18
23
|
require_relative "lightningcss/transformer"
|
|
19
24
|
|
|
20
25
|
# Ruby bindings for Lightning CSS.
|
|
@@ -28,14 +33,27 @@ require_relative "lightningcss/transformer"
|
|
|
28
33
|
# LightningCSS.transform(".title { color: red }", scope: "[data-scope-abc]").code
|
|
29
34
|
# #=> ".title[data-scope-abc]{color:red}"
|
|
30
35
|
#
|
|
36
|
+
# `parse` returns the AST instead, to walk with `LightningCSS::Node` and `LightningCSS::Visitor`:
|
|
37
|
+
#
|
|
38
|
+
# LightningCSS.parse(".a { color: red }").root.every("declaration").first.property
|
|
39
|
+
# #=> "color"
|
|
40
|
+
#
|
|
31
41
|
# `LightningCSS::Options` is what the options are read by, and `LightningCSS::Transformer` holds a
|
|
32
42
|
# set of them to reuse across many stylesheets.
|
|
43
|
+
#
|
|
33
44
|
module LightningCSS
|
|
34
45
|
#: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
35
46
|
def self.transform(code, **options)
|
|
36
47
|
Result.from_json(Backend.transform(code.to_s, Options.serialize(options)))
|
|
37
48
|
end
|
|
38
49
|
|
|
50
|
+
#: (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
51
|
+
def self.parse(code, **options)
|
|
52
|
+
serialized = Options.serialize(options, Options::PARSE, "a parse")
|
|
53
|
+
|
|
54
|
+
ParseResult.from_json(Backend.parse(code.to_s, serialized), code.to_s)
|
|
55
|
+
end
|
|
56
|
+
|
|
39
57
|
#: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
40
58
|
def self.bundle(path, **options)
|
|
41
59
|
serialized = Options.serialize(options, Options::BUNDLE, "a bundle")
|
data/rust/Cargo.lock
CHANGED
|
@@ -90,6 +90,9 @@ name = "bitflags"
|
|
|
90
90
|
version = "2.13.1"
|
|
91
91
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
92
92
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
|
93
|
+
dependencies = [
|
|
94
|
+
"serde_core",
|
|
95
|
+
]
|
|
93
96
|
|
|
94
97
|
[[package]]
|
|
95
98
|
name = "bitvec"
|
|
@@ -245,6 +248,7 @@ dependencies = [
|
|
|
245
248
|
"dtoa-short",
|
|
246
249
|
"itoa",
|
|
247
250
|
"phf 0.13.1",
|
|
251
|
+
"serde",
|
|
248
252
|
"smallvec",
|
|
249
253
|
]
|
|
250
254
|
|
|
@@ -511,6 +515,7 @@ dependencies = [
|
|
|
511
515
|
"serde",
|
|
512
516
|
"serde-content",
|
|
513
517
|
"smallvec",
|
|
518
|
+
"static-self",
|
|
514
519
|
]
|
|
515
520
|
|
|
516
521
|
[[package]]
|
|
@@ -599,7 +604,9 @@ dependencies = [
|
|
|
599
604
|
"phf_codegen",
|
|
600
605
|
"precomputed-hash",
|
|
601
606
|
"rustc-hash",
|
|
607
|
+
"serde",
|
|
602
608
|
"smallvec",
|
|
609
|
+
"static-self",
|
|
603
610
|
]
|
|
604
611
|
|
|
605
612
|
[[package]]
|
|
@@ -1002,6 +1009,31 @@ name = "smallvec"
|
|
|
1002
1009
|
version = "1.15.2"
|
|
1003
1010
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1004
1011
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
|
1012
|
+
dependencies = [
|
|
1013
|
+
"serde",
|
|
1014
|
+
]
|
|
1015
|
+
|
|
1016
|
+
[[package]]
|
|
1017
|
+
name = "static-self"
|
|
1018
|
+
version = "0.1.2"
|
|
1019
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1020
|
+
checksum = "f6635404b73efc136af3a7956e53c53d4f34b2f16c95a15c438929add0f69412"
|
|
1021
|
+
dependencies = [
|
|
1022
|
+
"indexmap",
|
|
1023
|
+
"smallvec",
|
|
1024
|
+
"static-self-derive",
|
|
1025
|
+
]
|
|
1026
|
+
|
|
1027
|
+
[[package]]
|
|
1028
|
+
name = "static-self-derive"
|
|
1029
|
+
version = "0.1.1"
|
|
1030
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1031
|
+
checksum = "5268c96d4b907c558a9a52d8492522d6c7b559651a5e1d8f2d551e461b9425d5"
|
|
1032
|
+
dependencies = [
|
|
1033
|
+
"proc-macro2",
|
|
1034
|
+
"quote",
|
|
1035
|
+
"syn 1.0.109",
|
|
1036
|
+
]
|
|
1005
1037
|
|
|
1006
1038
|
[[package]]
|
|
1007
1039
|
name = "strsim"
|
data/rust/Cargo.toml
CHANGED
|
@@ -15,7 +15,7 @@ path = "src/lib.rs"
|
|
|
15
15
|
crate-type = ["cdylib", "staticlib", "rlib"]
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
|
-
lightningcss = { version = "=1.0.0-alpha.72", features = ["visitor"] }
|
|
18
|
+
lightningcss = { version = "=1.0.0-alpha.72", features = ["visitor", "serde"] }
|
|
19
19
|
serde = { version = "1", features = ["derive"] }
|
|
20
20
|
serde_json = "1"
|
|
21
21
|
|
data/rust/src/lib.rs
CHANGED
|
@@ -21,7 +21,7 @@ use lightningcss::bundler::{BundleErrorKind, Bundler, FileProvider};
|
|
|
21
21
|
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleAttribute, StyleSheet};
|
|
22
22
|
use lightningcss::visitor::Visit;
|
|
23
23
|
|
|
24
|
-
use crate::options::{TransformOptions, TransformResult};
|
|
24
|
+
use crate::options::{Diagnostic, ParseResult, TransformOptions, TransformResult};
|
|
25
25
|
use crate::scope::Scoper;
|
|
26
26
|
|
|
27
27
|
pub const VERSION: &str = env!("GEM_VERSION");
|
|
@@ -56,7 +56,7 @@ impl LightningCssResult {
|
|
|
56
56
|
fn err(failure: Failure) -> Self {
|
|
57
57
|
Self {
|
|
58
58
|
value: ptr::null_mut(),
|
|
59
|
-
error: into_c_string(failure.message),
|
|
59
|
+
error: into_c_string(serde_json::to_string(&failure.diagnostic).unwrap_or(failure.diagnostic.message)),
|
|
60
60
|
code: failure.code,
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -64,35 +64,35 @@ impl LightningCssResult {
|
|
|
64
64
|
|
|
65
65
|
pub struct Failure {
|
|
66
66
|
code: LightningCssErrorCode,
|
|
67
|
-
|
|
67
|
+
diagnostic: Diagnostic,
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
impl Failure {
|
|
71
|
-
fn parse(
|
|
71
|
+
fn parse(diagnostic: impl Into<Diagnostic>) -> Self {
|
|
72
72
|
Self {
|
|
73
73
|
code: LightningCssErrorCode::Parse,
|
|
74
|
-
|
|
74
|
+
diagnostic: diagnostic.into(),
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
fn option(
|
|
78
|
+
fn option(diagnostic: impl Into<Diagnostic>) -> Self {
|
|
79
79
|
Self {
|
|
80
80
|
code: LightningCssErrorCode::Option,
|
|
81
|
-
|
|
81
|
+
diagnostic: diagnostic.into(),
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
fn bundle(
|
|
85
|
+
fn bundle(diagnostic: impl Into<Diagnostic>) -> Self {
|
|
86
86
|
Self {
|
|
87
87
|
code: LightningCssErrorCode::Bundle,
|
|
88
|
-
|
|
88
|
+
diagnostic: diagnostic.into(),
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
fn internal(
|
|
92
|
+
fn internal(diagnostic: impl Into<Diagnostic>) -> Self {
|
|
93
93
|
Self {
|
|
94
94
|
code: LightningCssErrorCode::Internal,
|
|
95
|
-
|
|
95
|
+
diagnostic: diagnostic.into(),
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -125,6 +125,27 @@ unsafe fn borrow_options(pointer: *const c_char) -> Result<TransformOptions, Fai
|
|
|
125
125
|
serde_json::from_str(json).map_err(|error| Failure::option(format!("Invalid options: {error}")))
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
fn parse_source<'i>(code: &'i str, options: &TransformOptions) -> Result<ParseResult<StyleSheet<'i>>, Failure> {
|
|
129
|
+
let collected = Arc::new(RwLock::new(Vec::new()));
|
|
130
|
+
|
|
131
|
+
let parser_options = ParserOptions {
|
|
132
|
+
filename: options.filename.clone().unwrap_or_default(),
|
|
133
|
+
error_recovery: options.error_recovery,
|
|
134
|
+
warnings: Some(collected.clone()),
|
|
135
|
+
..ParserOptions::default()
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
let stylesheet =
|
|
139
|
+
StyleSheet::parse(code, parser_options).map_err(|error| Failure::parse(Diagnostic::from_error(&error)))?;
|
|
140
|
+
|
|
141
|
+
let warnings = collected
|
|
142
|
+
.read()
|
|
143
|
+
.map(|warnings| warnings.iter().map(Diagnostic::from_error).collect())
|
|
144
|
+
.unwrap_or_default();
|
|
145
|
+
|
|
146
|
+
Ok(ParseResult { stylesheet, warnings })
|
|
147
|
+
}
|
|
148
|
+
|
|
128
149
|
fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformResult, Failure> {
|
|
129
150
|
let filename = options.filename.clone().unwrap_or_default();
|
|
130
151
|
let css_modules = match &options.css_modules {
|
|
@@ -142,7 +163,8 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
|
|
|
142
163
|
..ParserOptions::default()
|
|
143
164
|
};
|
|
144
165
|
|
|
145
|
-
let mut stylesheet =
|
|
166
|
+
let mut stylesheet =
|
|
167
|
+
StyleSheet::parse(code, parser_options).map_err(|error| Failure::parse(Diagnostic::from_error(&error)))?;
|
|
146
168
|
|
|
147
169
|
if let Some(fragment) = &options.scope {
|
|
148
170
|
let mut scoper = Scoper::parse(fragment).map_err(Failure::option)?;
|
|
@@ -152,16 +174,14 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
|
|
|
152
174
|
.map_err(|error| Failure::internal(format!("Failed to scope stylesheet: {error:?}")))?;
|
|
153
175
|
}
|
|
154
176
|
|
|
155
|
-
let targets = options.to_targets()
|
|
177
|
+
let targets = options.to_targets().map_err(Failure::option)?;
|
|
156
178
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
|
|
164
|
-
}
|
|
179
|
+
stylesheet
|
|
180
|
+
.minify(MinifyOptions {
|
|
181
|
+
targets,
|
|
182
|
+
..MinifyOptions::default()
|
|
183
|
+
})
|
|
184
|
+
.map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
|
|
165
185
|
|
|
166
186
|
let printed = stylesheet
|
|
167
187
|
.to_css(PrinterOptions {
|
|
@@ -183,7 +203,7 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
|
|
|
183
203
|
|
|
184
204
|
let warnings = collected
|
|
185
205
|
.read()
|
|
186
|
-
.map(|warnings| warnings.iter().map(
|
|
206
|
+
.map(|warnings| warnings.iter().map(Diagnostic::from_error).collect())
|
|
187
207
|
.unwrap_or_default();
|
|
188
208
|
|
|
189
209
|
Ok(TransformResult {
|
|
@@ -214,8 +234,8 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
|
|
|
214
234
|
let mut stylesheet = bundler
|
|
215
235
|
.bundle(std::path::Path::new(path))
|
|
216
236
|
.map_err(|error| match &error.kind {
|
|
217
|
-
BundleErrorKind::ParserError(_) => Failure::parse(error
|
|
218
|
-
_ => Failure::bundle(error
|
|
237
|
+
BundleErrorKind::ParserError(_) => Failure::parse(Diagnostic::from_error(&error)),
|
|
238
|
+
_ => Failure::bundle(Diagnostic::from_error(&error)),
|
|
219
239
|
})?;
|
|
220
240
|
|
|
221
241
|
if let Some(fragment) = &options.scope {
|
|
@@ -226,16 +246,14 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
|
|
|
226
246
|
.map_err(|error| Failure::internal(format!("Failed to scope stylesheet: {error:?}")))?;
|
|
227
247
|
}
|
|
228
248
|
|
|
229
|
-
let targets = options.to_targets()
|
|
249
|
+
let targets = options.to_targets().map_err(Failure::option)?;
|
|
230
250
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
.map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
|
|
238
|
-
}
|
|
251
|
+
stylesheet
|
|
252
|
+
.minify(MinifyOptions {
|
|
253
|
+
targets,
|
|
254
|
+
..MinifyOptions::default()
|
|
255
|
+
})
|
|
256
|
+
.map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
|
|
239
257
|
|
|
240
258
|
let printed = stylesheet
|
|
241
259
|
.to_css(PrinterOptions {
|
|
@@ -254,7 +272,7 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
|
|
|
254
272
|
|
|
255
273
|
let warnings = collected
|
|
256
274
|
.read()
|
|
257
|
-
.map(|warnings| warnings.iter().map(
|
|
275
|
+
.map(|warnings| warnings.iter().map(Diagnostic::from_error).collect())
|
|
258
276
|
.unwrap_or_default();
|
|
259
277
|
|
|
260
278
|
Ok(TransformResult {
|
|
@@ -271,9 +289,10 @@ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<Transfo
|
|
|
271
289
|
..ParserOptions::default()
|
|
272
290
|
};
|
|
273
291
|
|
|
274
|
-
let mut attribute =
|
|
292
|
+
let mut attribute =
|
|
293
|
+
StyleAttribute::parse(code, parser_options).map_err(|error| Failure::parse(Diagnostic::from_error(&error)))?;
|
|
275
294
|
|
|
276
|
-
let targets = options.to_targets()
|
|
295
|
+
let targets = options.to_targets().map_err(Failure::option)?;
|
|
277
296
|
|
|
278
297
|
attribute.minify(MinifyOptions {
|
|
279
298
|
targets,
|
|
@@ -295,7 +314,7 @@ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<Transfo
|
|
|
295
314
|
})
|
|
296
315
|
}
|
|
297
316
|
|
|
298
|
-
fn to_result(outcome: Result<
|
|
317
|
+
fn to_result(outcome: Result<impl serde::Serialize, Failure>) -> LightningCssResult {
|
|
299
318
|
match outcome {
|
|
300
319
|
Ok(result) => match serde_json::to_string(&result) {
|
|
301
320
|
Ok(json) => LightningCssResult::ok(json),
|
|
@@ -323,6 +342,21 @@ pub unsafe extern "C" fn lightningcss_transform(
|
|
|
323
342
|
to_result(transform_source(code, &options))
|
|
324
343
|
}
|
|
325
344
|
|
|
345
|
+
#[no_mangle]
|
|
346
|
+
pub unsafe extern "C" fn lightningcss_parse(code: *const c_char, options_json: *const c_char) -> LightningCssResult {
|
|
347
|
+
let code = match borrow_str(code, "code") {
|
|
348
|
+
Ok(code) => code,
|
|
349
|
+
Err(failure) => return LightningCssResult::err(failure),
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
let options = match borrow_options(options_json) {
|
|
353
|
+
Ok(options) => options,
|
|
354
|
+
Err(failure) => return LightningCssResult::err(failure),
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
to_result(parse_source(code, &options))
|
|
358
|
+
}
|
|
359
|
+
|
|
326
360
|
#[no_mangle]
|
|
327
361
|
pub unsafe extern "C" fn lightningcss_transform_style_attribute(
|
|
328
362
|
code: *const c_char,
|
data/rust/src/options.rs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
3
|
use lightningcss::css_modules::{Config as CssModulesConfig, Pattern};
|
|
4
|
+
use lightningcss::error::Error;
|
|
4
5
|
use lightningcss::targets::{Browsers, Targets};
|
|
5
6
|
use serde::{Deserialize, Serialize};
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ pub struct TransformOptions {
|
|
|
10
11
|
pub filename: Option<String>,
|
|
11
12
|
pub minify: bool,
|
|
12
13
|
pub error_recovery: bool,
|
|
13
|
-
pub targets: Option<HashMap<String,
|
|
14
|
+
pub targets: Option<HashMap<String, Version>>,
|
|
14
15
|
pub css_modules: Option<CssModulesOptions>,
|
|
15
16
|
pub scope: Option<String>,
|
|
16
17
|
}
|
|
@@ -60,16 +61,61 @@ impl CssModulesOptions {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
#[derive(Debug, Deserialize)]
|
|
65
|
+
#[serde(untagged)]
|
|
66
|
+
pub enum Version {
|
|
67
|
+
Major(u32),
|
|
68
|
+
Float(f64),
|
|
69
|
+
String(String),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl Version {
|
|
73
|
+
/// Encodes a version the way Lightning CSS reads it, as `major << 16 | minor << 8 | patch`.
|
|
74
|
+
fn encode(&self) -> Option<u32> {
|
|
75
|
+
match self {
|
|
76
|
+
Version::Major(major) => (*major <= 0xffff).then(|| major << 16),
|
|
77
|
+
Version::Float(float) => Self::parse(&float.to_string()),
|
|
78
|
+
Version::String(string) => Self::parse(string),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fn parse(version: &str) -> Option<u32> {
|
|
83
|
+
let mut parts = version.trim().split('.');
|
|
84
|
+
|
|
85
|
+
let major: u16 = parts.next()?.parse().ok()?;
|
|
86
|
+
let minor: u8 = parts.next().map_or(Some(0), |part| part.parse().ok())?;
|
|
87
|
+
let patch: u8 = parts.next().map_or(Some(0), |part| part.parse().ok())?;
|
|
88
|
+
|
|
89
|
+
if parts.next().is_some() {
|
|
90
|
+
return None;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Some(u32::from(major) << 16 | u32::from(minor) << 8 | u32::from(patch))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fn describe(&self) -> String {
|
|
97
|
+
match self {
|
|
98
|
+
Version::Major(major) => major.to_string(),
|
|
99
|
+
Version::Float(float) => float.to_string(),
|
|
100
|
+
Version::String(string) => format!("{string:?}"),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
63
105
|
impl TransformOptions {
|
|
64
|
-
pub fn to_targets(&self) -> Targets {
|
|
106
|
+
pub fn to_targets(&self) -> Result<Targets, String> {
|
|
65
107
|
let Some(versions) = &self.targets else {
|
|
66
|
-
return Targets::default();
|
|
108
|
+
return Ok(Targets::default());
|
|
67
109
|
};
|
|
68
110
|
|
|
69
111
|
let mut browsers = Browsers::default();
|
|
70
112
|
|
|
71
113
|
for (name, version) in versions {
|
|
72
|
-
let encoded = Some(
|
|
114
|
+
let encoded = Some(
|
|
115
|
+
version
|
|
116
|
+
.encode()
|
|
117
|
+
.ok_or_else(|| format!("Invalid version {} for {name} in targets", version.describe()))?,
|
|
118
|
+
);
|
|
73
119
|
|
|
74
120
|
match name.as_str() {
|
|
75
121
|
"android" => browsers.android = encoded,
|
|
@@ -85,14 +131,81 @@ impl TransformOptions {
|
|
|
85
131
|
}
|
|
86
132
|
}
|
|
87
133
|
|
|
88
|
-
Targets::from(browsers)
|
|
134
|
+
Ok(Targets::from(browsers))
|
|
89
135
|
}
|
|
90
136
|
}
|
|
91
137
|
|
|
138
|
+
#[derive(Debug, Serialize)]
|
|
139
|
+
pub struct ParseResult<T> {
|
|
140
|
+
pub stylesheet: T,
|
|
141
|
+
pub warnings: Vec<Diagnostic>,
|
|
142
|
+
}
|
|
143
|
+
|
|
92
144
|
#[derive(Debug, Serialize)]
|
|
93
145
|
pub struct TransformResult {
|
|
94
146
|
pub code: String,
|
|
95
147
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
96
148
|
pub exports: Option<HashMap<String, String>>,
|
|
97
|
-
pub warnings: Vec<
|
|
149
|
+
pub warnings: Vec<Diagnostic>,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[derive(Debug, Serialize)]
|
|
153
|
+
pub struct Diagnostic {
|
|
154
|
+
pub message: String,
|
|
155
|
+
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
|
156
|
+
pub kind: Option<String>,
|
|
157
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
158
|
+
pub location: Option<Location>,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[derive(Debug, Serialize)]
|
|
162
|
+
pub struct Location {
|
|
163
|
+
pub filename: Option<String>,
|
|
164
|
+
pub line: u32,
|
|
165
|
+
pub column: u32,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
impl Diagnostic {
|
|
169
|
+
pub fn from_error<T: std::fmt::Display + Serialize>(error: &Error<T>) -> Self {
|
|
170
|
+
Self {
|
|
171
|
+
message: error.kind.to_string(),
|
|
172
|
+
kind: serde_json::to_value(&error.kind).ok().as_ref().and_then(type_of),
|
|
173
|
+
location: error.loc.as_ref().map(|location| Location {
|
|
174
|
+
filename: (!location.filename.is_empty()).then(|| location.filename.clone()),
|
|
175
|
+
line: location.line + 1,
|
|
176
|
+
column: location.column.saturating_sub(1),
|
|
177
|
+
}),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
impl From<String> for Diagnostic {
|
|
183
|
+
fn from(message: String) -> Self {
|
|
184
|
+
Self {
|
|
185
|
+
message,
|
|
186
|
+
kind: None,
|
|
187
|
+
location: None,
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
impl From<&str> for Diagnostic {
|
|
193
|
+
fn from(message: &str) -> Self {
|
|
194
|
+
Self::from(message.to_string())
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fn type_of(value: &serde_json::Value) -> Option<String> {
|
|
199
|
+
match value {
|
|
200
|
+
serde_json::Value::String(name) => Some(name.clone()),
|
|
201
|
+
serde_json::Value::Object(fields) => match fields.get("type") {
|
|
202
|
+
Some(serde_json::Value::String(name)) => Some(name.clone()),
|
|
203
|
+
_ if fields.len() == 1 => fields.iter().next().map(|(name, inner)| match inner.get("type") {
|
|
204
|
+
Some(serde_json::Value::String(inner_name)) => inner_name.clone(),
|
|
205
|
+
_ => name.clone(),
|
|
206
|
+
}),
|
|
207
|
+
_ => None,
|
|
208
|
+
},
|
|
209
|
+
_ => None,
|
|
210
|
+
}
|
|
98
211
|
}
|
data/rust/src/scope.rs
CHANGED
|
@@ -29,7 +29,8 @@ impl<'i> Scoper<'i> {
|
|
|
29
29
|
let list = SelectorList::parse_string_with_options(fragment, ParserOptions::default()).map_err(|error| {
|
|
30
30
|
format!(
|
|
31
31
|
"Invalid scope selector {fragment:?} at :{}:{}",
|
|
32
|
-
error.location.line,
|
|
32
|
+
error.location.line + 1,
|
|
33
|
+
error.location.column
|
|
33
34
|
)
|
|
34
35
|
})?;
|
|
35
36
|
|
|
@@ -6,6 +6,9 @@ module LightningCSS
|
|
|
6
6
|
# : (String, String) -> String
|
|
7
7
|
def transform: (String, String) -> String
|
|
8
8
|
|
|
9
|
+
# : (String, String) -> String
|
|
10
|
+
def parse: (String, String) -> String
|
|
11
|
+
|
|
9
12
|
# : (String, String) -> String
|
|
10
13
|
def transform_style_attribute: (String, String) -> String
|
|
11
14
|
|
data/sig/lightningcss/errors.rbs
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module LightningCSS
|
|
4
4
|
class Error < StandardError
|
|
5
|
+
attr_reader type: String?
|
|
6
|
+
|
|
7
|
+
attr_reader location: LightningCSS::Location?
|
|
8
|
+
|
|
9
|
+
# : (String) -> instance
|
|
10
|
+
def self.from_json: (String) -> instance
|
|
11
|
+
|
|
12
|
+
# : (?String?, ?type: String?, ?location: LightningCSS::Location?) -> void
|
|
13
|
+
def initialize: (?String?, ?type: String?, ?location: LightningCSS::Location?) -> void
|
|
5
14
|
end
|
|
6
15
|
|
|
7
16
|
class ParseError < Error
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Generated from lib/lightningcss/location.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
class Location < Data
|
|
5
|
+
attr_reader filename(): String?
|
|
6
|
+
|
|
7
|
+
attr_reader line(): Integer
|
|
8
|
+
|
|
9
|
+
attr_reader column(): Integer
|
|
10
|
+
|
|
11
|
+
def self.new: (String? filename, Integer line, Integer column) -> instance
|
|
12
|
+
| (filename: String?, line: Integer, column: Integer) -> instance
|
|
13
|
+
|
|
14
|
+
def self.members: () -> [ :filename, :line, :column ]
|
|
15
|
+
|
|
16
|
+
def members: () -> [ :filename, :line, :column ]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Location
|
|
20
|
+
# : (Hash[String, untyped]) -> LightningCSS::Location
|
|
21
|
+
def self.from_hash: (Hash[String, untyped]) -> LightningCSS::Location
|
|
22
|
+
|
|
23
|
+
# : () -> String
|
|
24
|
+
def to_s: () -> String
|
|
25
|
+
end
|
|
26
|
+
end
|